Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/ops/hierarchical/config_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ def add_dynamic_data(self):
self.merge_value(self.generated_data, remote_states)

def resolve_interpolations(self):
resolver = InterpolationResolver(self.generated_data)
resolver = InterpolationResolver()
self.generated_data = resolver.resolve_interpolations(self.generated_data)

def validate_interpolations(self):
Expand Down
4 changes: 2 additions & 2 deletions src/ops/hierarchical/inject_secrets.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ class SecretInjector(object):
{{vault.kv2.path(ethos/k8s-ethos-config/thrash/aws/ClusterIngressTLS).field(Key)}}
"""

def __init__(self):
self.resolver = AggregatedSecretResolver()
def __init__(self, default_aws_profile=None):
self.resolver = AggregatedSecretResolver(default_aws_profile)

def is_interpolation(self, value):
return value.startswith('{{') and value.endswith('}}')
Expand Down
66 changes: 56 additions & 10 deletions src/ops/hierarchical/interpolation.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,36 @@ def is_interpolation(input):
return '{{' in input and '}}' in input


class InterpolationResolver(object):

def resolve_interpolations(self, data):
# Resolve from dictionary. Do one iteration before secret resolving, in order to resolve interpolations such as
# the aws.profile
# Example:
# my_profile: test
# aws:
# profile: "{{my_profile}}"
from_dict_injector = DictInterpolationResolver(data, FromDictInjector())
from_dict_injector.resolve_interpolations(data)

# Resolve interpolations representing secrets
# Example:
# value1: "{{ssm.path(mysecret)}}"
secrets_injector = SecretsInterpolationResolver(self.get_secret_injector(data))
secrets_injector.resolve_interpolations(data)

# Perform another resolving, in case some secrets are used as interpolations.
# Example:
# value1: "{{ssm.mysecret}}"
# value2: "something-{{value1}} <--- this will be resolved at this step
from_dict_injector = DictInterpolationResolver(data, FromDictInjector())
from_dict_injector.resolve_interpolations(data)

def get_secret_injector(self, data):
default_aws_profile = data['aws']['profile'] if 'aws' in data and 'profile' in data['aws'] else None
return SecretInjector(default_aws_profile)


class DictIterator():

def loop_all_items(self, data, process_func):
Expand All @@ -33,23 +63,39 @@ def loop_all_items(self, data, process_func):
return data


class InterpolationResolver(DictIterator):

def __init__(self, data, secrets_injector=SecretInjector()):
self.generated_data = data
self.secrets_injector = secrets_injector
self.from_dict_injector = FromDictInjector()
class AbstractInterpolationResolver(DictIterator):
def __init__(self):
pass

def resolve_interpolations(self, data):
return self.loop_all_items(data, self.resolve_interpolation)

def resolve_interpolation(self, line):
if not is_interpolation(line):
return line
return self.do_resolve_interpolation(line)

def do_resolve_interpolation(self, line):
pass


class DictInterpolationResolver(AbstractInterpolationResolver):
def __init__(self, data, from_dict_injector):
AbstractInterpolationResolver.__init__(self)
self.data = data
self.from_dict_injector = from_dict_injector

def do_resolve_interpolation(self, line):
return self.from_dict_injector.resolve(line, self.data)


class SecretsInterpolationResolver(AbstractInterpolationResolver):
def __init__(self, secrets_injector):
AbstractInterpolationResolver.__init__(self)
self.secrets_injector = secrets_injector

updated_line = self.secrets_injector.inject_secret(line)
updated_line = self.from_dict_injector.resolve(updated_line, self.generated_data)
return updated_line
def do_resolve_interpolation(self, line):
return self.secrets_injector.inject_secret(line)


class InterpolationValidator(DictIterator):
Expand Down Expand Up @@ -85,7 +131,7 @@ def resolve(self, line, data):
continue
elif isinstance(value, (int, bool)):
return value
else:
elif not is_interpolation(value):
line = line.replace(placeholder, value)
return line

Expand Down
15 changes: 11 additions & 4 deletions src/ops/hierarchical/secret_resolvers.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,18 @@ def resolve(self, secret_type, secret_params):


class SSMSecretResolver(SecretResolver):
def __init__(self, default_aws_profile=None):
self.default_aws_profile = default_aws_profile

def supports(self, secret_type):
return secret_type == "ssm"

def resolve(self, secret_type, secret_params):
aws_profile = secret_params.get("aws_profile", self.default_aws_profile)
if not aws_profile:
raise Exception("Could not find the aws_profile in the secret params: {}".format(secret_params))

path = self.get_param_or_exception("path", secret_params)
aws_profile = self.get_param_or_exception("aws_profile", secret_params)
region_name = secret_params.get("region_name", "us-east-1")
ssm = SimpleSSM(aws_profile, region_name)
return ssm.get(path)
Expand All @@ -46,13 +52,14 @@ def resolve(self, secret_type, secret_params):

class AggregatedSecretResolver(SecretResolver):

SECRET_RESOLVERS = (SSMSecretResolver(), VaultSecretResolver())
def __init__(self, default_aws_profile=None):
self.secret_resolvers = (SSMSecretResolver(default_aws_profile), VaultSecretResolver())

def supports(self, secret_type):
return any([resolver.supports(secret_type) for resolver in self.SECRET_RESOLVERS])
return any([resolver.supports(secret_type) for resolver in self.secret_resolvers])

def resolve(self, secret_type, secret_params):
for resolver in self.SECRET_RESOLVERS:
for resolver in self.secret_resolvers:
if resolver.supports(secret_type):
return resolver.resolve(secret_type, secret_params)

Expand Down
2 changes: 1 addition & 1 deletion src/ops/terraform/terraform_cmd_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ def generate(self, args):
else:
cmd = "cd {root_dir}/{terraform_path} && " \
"terraform apply " \
"-refresh=true {state_out_argument} {plan_file} {variables_file}; code=$?; rm -f {plan_file}; exit $code".format(
"-refresh=true {state_out_argument} {plan_file}; code=$?; rm -f {plan_file}; exit $code".format(
plan_file=plan_file,
root_dir=self.root_dir,
state_out_argument=state_out_argument,
Expand Down