Fix path matching bugs by moving to pathlib
This commit is contained in:
parent
6cadc18397
commit
7782c180f3
@ -1,5 +1,9 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 0.7.7
|
||||||
|
- Add support for CLOUDBENDER_PROJECT_ROOT env variable to specify your root project
|
||||||
|
- Switch most os.path operations to pathlib to fix various corner cases caused by string matching
|
||||||
|
|
||||||
## 0.7.6
|
## 0.7.6
|
||||||
- Added warning if rendered templates exceed max. inline size of 51200 bytes
|
- Added warning if rendered templates exceed max. inline size of 51200 bytes
|
||||||
- Added optional removal of comments during include_raw processing to reduce user-data size
|
- Added optional removal of comments during include_raw processing to reduce user-data size
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import os
|
import pathlib
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from .utils import ensure_dir
|
from .utils import ensure_dir
|
||||||
@ -12,24 +12,24 @@ logger = logging.getLogger(__name__)
|
|||||||
class CloudBender(object):
|
class CloudBender(object):
|
||||||
""" Config Class to handle recursive conf/* config tree """
|
""" Config Class to handle recursive conf/* config tree """
|
||||||
def __init__(self, root_path):
|
def __init__(self, root_path):
|
||||||
self.root = root_path
|
self.root = pathlib.Path(root_path)
|
||||||
self.sg = None
|
self.sg = None
|
||||||
self.all_stacks = []
|
self.all_stacks = []
|
||||||
self.ctx = {
|
self.ctx = {
|
||||||
"config_path": os.path.join(self.root, "config"),
|
"config_path": self.root.joinpath("config"),
|
||||||
"template_path": os.path.join(self.root, "cloudformation"),
|
"template_path": self.root.joinpath("cloudformation"),
|
||||||
"parameter_path": os.path.join(self.root, "parameters"),
|
"parameter_path": self.root.joinpath("parameters"),
|
||||||
"artifact_paths": [os.path.join(self.root, "artifacts")]
|
"artifact_paths": [self.root.joinpath("artifacts")]
|
||||||
}
|
}
|
||||||
|
|
||||||
if not os.path.isdir(self.ctx['config_path']):
|
if not self.ctx['config_path'].is_dir():
|
||||||
raise InvalidProjectDir("Check '{0}' exists and is a valid CloudBender project folder.".format(root_path))
|
raise InvalidProjectDir("Check '{0}' exists and is a valid CloudBender project folder.".format(self.ctx['config_path']))
|
||||||
|
|
||||||
def read_config(self):
|
def read_config(self):
|
||||||
"""Load the <path>/config.yaml, <path>/*.yaml as stacks, sub-folders are sub-groups """
|
"""Load the <path>/config.yaml, <path>/*.yaml as stacks, sub-folders are sub-groups """
|
||||||
|
|
||||||
# Read top level config.yaml and extract CloudBender CTX
|
# Read top level config.yaml and extract CloudBender CTX
|
||||||
_config = read_config_file(os.path.join(self.ctx['config_path'], 'config.yaml'))
|
_config = read_config_file(self.ctx['config_path'].joinpath('config.yaml'))
|
||||||
if _config and _config.get('CloudBender'):
|
if _config and _config.get('CloudBender'):
|
||||||
self.ctx.update(_config.get('CloudBender'))
|
self.ctx.update(_config.get('CloudBender'))
|
||||||
|
|
||||||
@ -38,16 +38,17 @@ class CloudBender(object):
|
|||||||
if k in ['config_path', 'template_path', 'parameter_path', 'artifact_paths']:
|
if k in ['config_path', 'template_path', 'parameter_path', 'artifact_paths']:
|
||||||
if isinstance(v, list):
|
if isinstance(v, list):
|
||||||
new_list = []
|
new_list = []
|
||||||
for path in v:
|
for p in v:
|
||||||
if not os.path.isabs(path):
|
path = pathlib.Path(p)
|
||||||
new_list.append(os.path.normpath(os.path.join(self.root, path)))
|
if not path.is_absolute():
|
||||||
|
new_list.append(self.root.joinpath(path))
|
||||||
else:
|
else:
|
||||||
new_list.append(path)
|
new_list.append(path)
|
||||||
self.ctx[k] = new_list
|
self.ctx[k] = new_list
|
||||||
|
|
||||||
elif isinstance(v, str):
|
elif isinstance(v, str):
|
||||||
if not os.path.isabs(v):
|
if not v.is_absolute():
|
||||||
self.ctx[k] = os.path.normpath(os.path.join(self.root, v))
|
self.ctx[k] = self.root.joinpath(v)
|
||||||
|
|
||||||
if k in ['template_path', 'parameter_path']:
|
if k in ['template_path', 'parameter_path']:
|
||||||
ensure_dir(self.ctx[k])
|
ensure_dir(self.ctx[k])
|
||||||
|
@ -6,5 +6,5 @@ class ParameterIllegalValue(Exception):
|
|||||||
"""My documentation"""
|
"""My documentation"""
|
||||||
|
|
||||||
|
|
||||||
class InvalidProjectDir(BaseException):
|
class InvalidProjectDir(Exception):
|
||||||
"""My documentation"""
|
"""My documentation"""
|
||||||
|
@ -179,7 +179,7 @@ def JinjaEnv(template_locations=[]):
|
|||||||
|
|
||||||
jinja_loaders = []
|
jinja_loaders = []
|
||||||
for _dir in template_locations:
|
for _dir in template_locations:
|
||||||
jinja_loaders.append(jinja2.FileSystemLoader(_dir))
|
jinja_loaders.append(jinja2.FileSystemLoader(str(_dir)))
|
||||||
jenv.loader = jinja2.ChoiceLoader(jinja_loaders)
|
jenv.loader = jinja2.ChoiceLoader(jinja_loaders)
|
||||||
|
|
||||||
jenv.globals['include_raw'] = include_raw_gz
|
jenv.globals['include_raw'] = include_raw_gz
|
||||||
@ -206,14 +206,14 @@ def read_config_file(path, variables={}):
|
|||||||
jinja_variables = copy.deepcopy(variables)
|
jinja_variables = copy.deepcopy(variables)
|
||||||
jinja_variables['ENV'] = os.environ
|
jinja_variables['ENV'] = os.environ
|
||||||
|
|
||||||
if os.path.exists(path):
|
if path.exists():
|
||||||
logger.debug("Reading config file: {}".format(path))
|
logger.debug("Reading config file: {}".format(path))
|
||||||
try:
|
try:
|
||||||
jenv = jinja2.Environment(
|
jenv = jinja2.Environment(
|
||||||
loader=jinja2.FileSystemLoader(os.path.dirname(path)),
|
loader=jinja2.FileSystemLoader(str(path.parent)),
|
||||||
undefined=jinja2.StrictUndefined,
|
undefined=jinja2.StrictUndefined,
|
||||||
extensions=['jinja2.ext.loopcontrols'])
|
extensions=['jinja2.ext.loopcontrols'])
|
||||||
template = jenv.get_template(os.path.basename(path))
|
template = jenv.get_template(path.name)
|
||||||
rendered_template = template.render(jinja_variables)
|
rendered_template = template.render(jinja_variables)
|
||||||
data = yaml.safe_load(rendered_template)
|
data = yaml.safe_load(rendered_template)
|
||||||
if data:
|
if data:
|
||||||
|
@ -4,6 +4,7 @@ import hashlib
|
|||||||
import oyaml as yaml
|
import oyaml as yaml
|
||||||
import json
|
import json
|
||||||
import time
|
import time
|
||||||
|
import pathlib
|
||||||
import pprint
|
import pprint
|
||||||
|
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
@ -37,7 +38,7 @@ class Stack(object):
|
|||||||
def __init__(self, name, template, path, rel_path, ctx):
|
def __init__(self, name, template, path, rel_path, ctx):
|
||||||
self.stackname = name
|
self.stackname = name
|
||||||
self.template = template
|
self.template = template
|
||||||
self.path = path
|
self.path = pathlib.Path(path)
|
||||||
self.rel_path = rel_path
|
self.rel_path = rel_path
|
||||||
self.ctx = ctx
|
self.ctx = ctx
|
||||||
|
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
import os
|
|
||||||
import glob
|
|
||||||
import logging
|
import logging
|
||||||
import pprint
|
import pprint
|
||||||
|
|
||||||
@ -15,7 +13,7 @@ class StackGroup(object):
|
|||||||
self.name = None
|
self.name = None
|
||||||
self.ctx = ctx
|
self.ctx = ctx
|
||||||
self.path = path
|
self.path = path
|
||||||
self.rel_path = os.path.relpath(path, ctx['config_path'])
|
self.rel_path = path.relative_to(ctx['config_path'])
|
||||||
self.config = {}
|
self.config = {}
|
||||||
self.sgs = []
|
self.sgs = []
|
||||||
self.stacks = []
|
self.stacks = []
|
||||||
@ -33,17 +31,17 @@ class StackGroup(object):
|
|||||||
s.dump_config()
|
s.dump_config()
|
||||||
|
|
||||||
def read_config(self, parent_config={}):
|
def read_config(self, parent_config={}):
|
||||||
if not os.path.isdir(self.path):
|
if not self.path.is_dir():
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# First read config.yaml if present
|
# First read config.yaml if present
|
||||||
_config = read_config_file(os.path.join(self.path, 'config.yaml'), parent_config.get('variables', {}))
|
_config = read_config_file(self.path.joinpath('config.yaml'), parent_config.get('variables', {}))
|
||||||
|
|
||||||
# Stack Group name if not explicit via config is derived from subfolder, or in case of root object the parent folder
|
# Stack Group name if not explicit via config is derived from subfolder, or in case of root object the parent folder
|
||||||
if "stackgroupname" in _config:
|
if "stackgroupname" in _config:
|
||||||
self.name = _config["stackgroupname"]
|
self.name = _config["stackgroupname"]
|
||||||
elif not self.name:
|
elif not self.name:
|
||||||
self.name = os.path.split(self.path)[1]
|
self.name = self.path.stem
|
||||||
|
|
||||||
# Merge config with parent config
|
# Merge config with parent config
|
||||||
self.config = dict_merge(parent_config, _config)
|
self.config = dict_merge(parent_config, _config)
|
||||||
@ -52,9 +50,9 @@ class StackGroup(object):
|
|||||||
logger.debug("StackGroup {} added.".format(self.name))
|
logger.debug("StackGroup {} added.".format(self.name))
|
||||||
|
|
||||||
# Add stacks
|
# Add stacks
|
||||||
stacks = [s for s in glob.glob(os.path.join(self.path, '*.yaml')) if not s.endswith("config.yaml")]
|
stacks = [s for s in self.path.glob('*.yaml') if not s.name == "config.yaml"]
|
||||||
for stack_path in stacks:
|
for stack_path in stacks:
|
||||||
stackname = os.path.basename(stack_path).split('.')[0]
|
stackname = stack_path.name.split('.')[0]
|
||||||
template = stackname
|
template = stackname
|
||||||
if stackname_prefix:
|
if stackname_prefix:
|
||||||
stackname = stackname_prefix + stackname
|
stackname = stackname_prefix + stackname
|
||||||
@ -64,7 +62,7 @@ class StackGroup(object):
|
|||||||
self.stacks.append(new_stack)
|
self.stacks.append(new_stack)
|
||||||
|
|
||||||
# Create StackGroups recursively
|
# Create StackGroups recursively
|
||||||
for sub_group in [f.path for f in os.scandir(self.path) if f.is_dir()]:
|
for sub_group in [s for s in self.path.iterdir() if s.is_dir()]:
|
||||||
sg = StackGroup(sub_group, self.ctx)
|
sg = StackGroup(sub_group, self.ctx)
|
||||||
sg.read_config(self.config)
|
sg.read_config(self.config)
|
||||||
|
|
||||||
@ -77,7 +75,13 @@ class StackGroup(object):
|
|||||||
logger.debug("Looking for stack {} in group {}".format(name, self.name))
|
logger.debug("Looking for stack {} in group {}".format(name, self.name))
|
||||||
|
|
||||||
for s in self.stacks:
|
for s in self.stacks:
|
||||||
if not name or (s.stackname == name and match_by == 'name') or (s.path.endswith(name) and match_by == 'path'):
|
if name:
|
||||||
|
if match_by == 'name' and s.stackname != name:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if match_by == 'path' and not s.path.match(name):
|
||||||
|
continue
|
||||||
|
|
||||||
if self.rel_path:
|
if self.rel_path:
|
||||||
logger.debug("Found stack {} in group {}".format(s.stackname, self.rel_path))
|
logger.debug("Found stack {} in group {}".format(s.stackname, self.rel_path))
|
||||||
else:
|
else:
|
||||||
@ -94,11 +98,11 @@ class StackGroup(object):
|
|||||||
|
|
||||||
def get_stackgroup(self, name=None, recursive=True, match_by='name'):
|
def get_stackgroup(self, name=None, recursive=True, match_by='name'):
|
||||||
""" Returns stack group matching stackgroup_name or all if None """
|
""" Returns stack group matching stackgroup_name or all if None """
|
||||||
if not name or (self.name == name and match_by == 'name') or (self.path.endswith(name) and match_by == 'path'):
|
if not name or (self.name == name and match_by == 'name') or (self.path.match(name) and match_by == 'path'):
|
||||||
logger.debug("Found stack_group {}".format(self.name))
|
logger.debug("Found stack_group {}".format(self.name))
|
||||||
return self
|
return self
|
||||||
|
|
||||||
if name and name != 'config':
|
if name and self.name != 'config':
|
||||||
logger.debug("Looking for stack_group {} in group {}".format(name, self.name))
|
logger.debug("Looking for stack_group {} in group {}".format(name, self.name))
|
||||||
|
|
||||||
if recursive:
|
if recursive:
|
||||||
|
Loading…
Reference in New Issue
Block a user