def bespin_spec(self): """Spec for bespin options""" formatted_string = formatted(string_spec(), MergedOptionStringFormatter, expected_type=six.string_types) formatted_boolean = formatted(boolean(), MergedOptionStringFormatter, expected_type=bool) return create_spec(Bespin , validators.deprecated_key("region", "Please use ``environments.<env>.region``") , config = file_spec() , configuration = any_spec() , assume_role = optional_spec(string_spec()) , dry_run = defaulted(boolean(), False) , flat = defaulted(boolean(), False) , environment = optional_spec(string_spec()) , no_assume_role = defaulted(formatted_boolean, False) , chosen_task = defaulted(formatted_string, "list_tasks") , chosen_stack = defaulted(formatted_string, "") , chosen_artifact = defaulted(formatted_string, "") , extra_imports = listof(imports.import_spec()) )
def normalise_filled(self, meta, val): typ = formatted(overridden("{_key_name_1}"), formatter=MergedOptionStringFormatter).normalise(meta, val) name = formatted(overridden("{_key_name_0}"), formatter=MergedOptionStringFormatter).normalise(meta, val) special = {} kls = special.get(typ, GenericNetscalerConfig) formatted_string = formatted(string_spec(), formatter=MergedOptionStringFormatter) formatted_options = dictof(string_spec(), match_spec((six.string_types, formatted_string), fallback=any_spec())) options = dict( typ=overridden(typ) , name=overridden(name) , bindings=dictof(string_spec() , netscaler_binding_spec()) , tags=listof(string_spec()) , options=formatted_options , overrides=formatted_options , binding_options=formatted_options , environments=optional_spec(listof(valid_environment_spec())) ) if typ == "sslcertkey": options["link"] = listof(string_spec()) as_dict = set_options(**options).normalise(meta, val) return kls(**dict((name, as_dict[name]) for name in options))
def url_checker_spec(self): return create_spec(deployment_check.UrlChecker , check_url = required(formatted(string_spec(), formatter=MergedOptionStringFormatter)) , endpoint = required(delayed(stack_specs.var_spec())) , expect = required(formatted(string_spec(), formatter=MergedOptionStringFormatter)) , timeout_after = defaulted(integer_spec(), 600) )
def harpoon_spec(self): """Spec for harpoon options""" formatted_string = formatted(string_spec(), MergedOptionStringFormatter, expected_type=six.string_types) formatted_boolean = formatted(boolean(), MergedOptionStringFormatter, expected_type=bool) return create_spec( Harpoon, config=file_spec(), extra=defaulted(formatted_string, ""), debug=defaulted(boolean(), False), chosen_task=defaulted(formatted_string, "list_tasks"), chosen_image=defaulted(formatted_string, ""), flat=defaulted(formatted_boolean, False), no_cleanup=defaulted(formatted_boolean, False), interactive=defaulted(formatted_boolean, True), silent_build=defaulted(formatted_boolean, False), keep_replaced=defaulted(formatted_boolean, False), ignore_missing=defaulted(formatted_boolean, False), no_intervention=defaulted(formatted_boolean, False), intervene_afterwards=defaulted(formatted_boolean, False), do_push=defaulted(formatted_boolean, False), only_pushable=defaulted(formatted_boolean, False), docker_context=any_spec(), docker_context_maker=any_spec(), stdout=defaulted(any_spec(), sys.stdout), tty_stdout=defaulted(any_spec(), lambda: sys.stdout), tty_stderr=defaulted(any_spec(), lambda: sys.stderr), )
def normalise(self, meta, val): if "content" in val: spec = sb.set_options(dest=sb.required( sb.formatted(sb.string_spec(), formatter=MergedOptionStringFormatter)), content=sb.string_spec()) result = spec.normalise(meta, val) context_name = "{0}-{1}".format( hashlib.md5(result['content'].encode('utf-8')).hexdigest(), result["dest"].replace("/", "-").replace(" ", "--")) return Command( ("ADD", "{0} {1}".format(context_name, result["dest"])), (result["content"], context_name)) else: spec = sb.set_options(get=sb.required( sb.listof( sb.formatted(sb.string_spec(), formatter=MergedOptionStringFormatter))), prefix=sb.defaulted(sb.string_spec(), "")) result = spec.normalise(meta, val) final = [] for val in result["get"]: final.append( Command( ("ADD", "{0} {1}/{2}".format(val, result["prefix"], val)))) return final
def bespin_spec(self): """Spec for bespin options""" formatted_string = formatted(string_spec(), MergedOptionStringFormatter, expected_type=six.string_types) formatted_boolean = formatted(boolean(), MergedOptionStringFormatter, expected_type=bool) return create_spec(Bespin, validators.deprecated_key( "region", "Please use ``environments.<env>.region``"), config=file_spec(), configuration=any_spec(), assume_role=optional_spec(string_spec()), extra=defaulted(string_spec(), ""), dry_run=defaulted(boolean(), False), flat=defaulted(boolean(), False), environment=optional_spec(string_spec()), no_assume_role=defaulted(formatted_boolean, False), chosen_task=defaulted(formatted_string, "list_tasks"), chosen_stack=defaulted(formatted_string, ""), chosen_artifact=defaulted(formatted_string, ""), extra_imports=listof(imports.import_spec()))
def harpoon_spec(self): """Spec for harpoon options""" formatted_string = formatted(string_spec(), MergedOptionStringFormatter, expected_type=six.string_types) formatted_boolean = formatted(boolean(), MergedOptionStringFormatter, expected_type=bool) return create_spec( Harpoon, config=file_spec(), extra=defaulted(formatted_string, ""), debug=defaulted(boolean(), False), chosen_task=defaulted(formatted_string, "list_tasks"), chosen_image=defaulted(formatted_string, ""), flat=defaulted(formatted_boolean, False), no_cleanup=defaulted(formatted_boolean, False), interactive=defaulted(formatted_boolean, True), silent_build=defaulted(formatted_boolean, False), keep_replaced=defaulted(formatted_boolean, False), ignore_missing=defaulted(formatted_boolean, False), no_intervention=defaulted(formatted_boolean, False), intervene_afterwards=defaulted(formatted_boolean, False), do_push=defaulted(formatted_boolean, False), only_pushable=defaulted(formatted_boolean, False), docker_context=any_spec(), docker_context_maker=any_spec(), stdout=defaulted(any_spec(), sys.stdout), tty_stdout=defaulted(any_spec(), lambda: sys.stdout), tty_stderr=defaulted(any_spec(), lambda: sys.stderr))
def alerting_system_spec(self): return create_spec(stack_objs.AlertingSystem , name = formatted(overridden("{_key_name_1}"), formatter=MergedOptionStringFormatter) , type = string_choice_spec(["nagios"]) , endpoint = required(formatted(string_spec(), formatter=MergedOptionStringFormatter)) , verify_ssl = defaulted(boolean(), True) )
class logging_statement_spec(statement_spec): args = lambda s, self_type, self_name: { "prefix": sb.formatted(sb.string_spec(), formatter=MergedOptionStringFormatter), "destination": sb.formatted(sb.string_spec(), formatter=MergedOptionStringFormatter) } required = ["prefix", "destination"] final_kls = lambda s, *args, **kwargs: LoggingConfig(*args, **kwargs)
def ultradns_site_spec(self, this): formatted_string = sb.formatted(sb.string_spec(), formatter=MergedOptionStringFormatter) return sb.create_spec(UltraDNSSite , name = sb.formatted(sb.overridden("{_key_name_1}"), formatter=MergedOptionStringFormatter) , ttl = sb.optional_spec(sb.integer_spec()) , provider = sb.any_spec() , record_type = sb.required(formatted_string) , zone = sb.required(formatted_string) , domain = sb.required(formatted_string) , environments = sb.required(self.dns_environment_spec(this)) )
def normalise_filled(self, meta, val): val = sb.dictof(sb.string_choice_spec(["s3", "inline", "directory"]), sb.any_spec()).normalise(meta, val) if not val: raise BadSpecValue( "Please specify s3, inline or directory for your code", meta=meta) if len(val) > 1: raise BadSpecValue( "Please only specify one of s3, inline or directory for your code", got=list(val.keys()), meta=meta) formatted_string = sb.formatted(sb.string_spec(), formatter=MergedOptionStringFormatter) if "s3" in val: return sb.create_spec( S3Code, key=formatted_string, bucket=formatted_string, version=sb.defaulted(sb.string_spec(), NotSpecified)).normalise(meta, val['s3']) elif "inline" in val: path = [p for p, _ in meta._path] path.pop() runtime = meta.everything['.'.join(path)].get("runtime", "python") runtime = sb.formatted( sb.string_spec(), formatter=MergedOptionStringFormatter).normalise( meta.at("runtime"), runtime) return sb.create_spec(InlineCode, code=sb.string_spec(), runtime=sb.overridden(runtime)).normalise( meta, {"code": val['inline']}) else: directory = val['directory'] if isinstance(val['directory'], six.string_types): directory = {"directory": val['directory']} if 'directory' in directory: formatted_string = sb.formatted( sb.string_spec(), formatter=MergedOptionStringFormatter) directory['directory'] = formatted_string.normalise( meta.at("directory").at("directory"), directory['directory']) return sb.create_spec(DirectoryCode, directory=sb.directory_spec(), exclude=sb.listof( sb.string_spec())).normalise( meta, directory)
def password_spec(self): formatted_string = formatted(string_spec(), formatter=MergedOptionStringFormatter) return create_spec(stack_objs.Password , name = formatted(overridden("{_key_name_1}"), formatter=MergedOptionStringFormatter) , bespin = formatted(overridden("{bespin}"), formatter=MergedOptionStringFormatter) , KMSMasterKey = required(formatted_string) , encryption_context = optional_spec(dictionary_spec()) , grant_tokens = optional_spec(listof(formatted_string)) , crypto_text = required(formatted_string) , vars = dictionary_spec() )
def ultradns_site_spec(self, this): formatted_string = sb.formatted(sb.string_spec(), formatter=MergedOptionStringFormatter) return sb.create_spec( UltraDNSSite, name=sb.formatted(sb.overridden("{_key_name_1}"), formatter=MergedOptionStringFormatter), ttl=sb.optional_spec(sb.integer_spec()), provider=sb.any_spec(), record_type=sb.required(formatted_string), zone=sb.required(formatted_string), domain=sb.required(formatted_string), environments=sb.required(self.dns_environment_spec(this)))
def normalise(self, meta, val): pairs = [] has_self = False for account_id in self.accounts(meta): users = sb.listof(sb.string_spec()).normalise( meta.at("users"), self.resource.get('users', NotSpecified)) for index, name in enumerate( sb.listof(sb.any_spec()).normalise(meta, val)): if name == "__self__": if self.self_type != 'role': raise BadPolicy("No __self__ iam role for this policy", meta=meta) else: has_self = True else: if isinstance(name, six.string_types): name = sb.formatted( sb.string_spec(), formatter=MergedOptionStringFormatter).normalise( meta.indexed_at(index), name) pairs.append((name, account_id)) if has_self: pairs.append(("role/{0}".format(self.self_name), self.default_account_id(meta))) for name, account_id in pairs: service = "sts" if name.startswith("assumed-role") else "iam" arn = "arn:aws:{0}::{1}:{2}".format(service, account_id, name) if not users: yield arn else: for user in users: yield "{0}/{1}".format(arn, user)
def normalise(self, meta, val): from harpoon.option_spec.harpoon_specs import HarpoonSpec if "content" in val or "context" in val: spec = sb.set_options(mtime=sb.optional_spec(sb.integer_spec()), dest=sb.required(sb.formatted(sb.string_spec(), formatter=MergedOptionStringFormatter)), content=sb.string_spec(), context=sb.optional_spec(HarpoonSpec().context_spec)) result = spec.normalise(meta, val) if result["content"] != "" and result["context"] is not NotSpecified: raise BadOption("Please don't specify both context and content") mtime = result["mtime"] if mtime is NotSpecified: ctxt = type("Context", (object, ), {"use_git": True})() mtime = meta.everything["mtime"](ctxt) context_name = "{0}-{1}-mtime({2})".format(hashlib.md5(result['content'].encode('utf-8')).hexdigest(), result["dest"].replace("/", "-").replace(" ", "--"), mtime) extra_context = (result["content"], context_name) if result["context"] is not NotSpecified: context_name = "{0}.tar".format(context_name) extra_context = ({"context": result["context"]}, context_name) return Command(("ADD", "{0} {1}".format(context_name, result["dest"])), extra_context) else: spec = sb.set_options( get=sb.required(sb.listof(sb.formatted(sb.string_spec(), formatter=MergedOptionStringFormatter))) , prefix = sb.defaulted(sb.string_spec(), "") ) result = spec.normalise(meta, val) final = [] for val in result["get"]: final.append(Command(("ADD", "{0} {1}/{2}".format(val, result["prefix"], val)))) return final
def ultradns_provider_spec(self): return sb.create_spec(UltraDNSProvider , name = sb.formatted(sb.overridden("{_key_name_1}"), formatter=MergedOptionStringFormatter) , provider_type = sb.required(sb.string_spec()) , username = sb.required(formatted_string) , password = sb.required(formatted_string) )
def tags_spec(self): # http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/Using_Tags.html#tag-restrictions # Keys = 127 UTF-8 '^aws:' reserved. Values = 255 UTF-8 return dictof( valid_string_spec(validators.regexed("^.{0,127}$")) , formatted(string_spec(), after_format=valid_string_spec(validators.regexed("^(?!aws:).{0,255}$")), formatter=MergedOptionStringFormatter) )
def normalise(self, meta, val): if 'use' in val: template = val['use'] if template not in meta.everything['templates']: available = list(meta.everything['templates'].keys()) raise BadTemplate("Template doesn't exist!", wanted=template, available=available, meta=meta) val = MergedOptions.using(meta.everything['templates'][template], val) formatted_string = sb.formatted(sb.string_spec(), MergedOptionStringFormatter) route_name = meta.key_names()['_key_name_0'] val = sb.create_spec(DNSRoute , name = sb.overridden(route_name) , zone = formatted_string , record_type = sb.string_choice_spec(["CNAME"]) , record_target = formatted_string ).normalise(meta, val) if not val.zone.endswith("."): val.zone = "{0}.".format(val.zone) if not isinstance(val.record_target, six.string_types): if not hasattr(val.record_target, "cname"): raise BadSpecValue("record_target must point at an object with a cname property", got=type(val.record_target), meta=meta) val.record_target = val.record_target.cname return val
class array_command_spec(many_item_formatted_spec): value_name = "Command" specs = [ # First item is just a string sb.string_spec() # Second item is a required list of either dicts or strings , sb.required( sb.listof( sb.match_spec( (dict, complex_ADD_spec()), (six.string_types + (list, ), sb.formatted(sb.string_spec(), formatter=MergedOptionStringFormatter))))) ] def create_result(self, action, command, meta, val, dividers): if callable(command) or isinstance(command, six.string_types): command = [command] result = [] for cmd in command: if not isinstance(cmd, list): cmd = [cmd] for c in cmd: if isinstance(c, Command): result.append(c) else: result.append(Command((action, c))) return result
def normalise(self, meta, val): if 'use' in val: template = val['use'] if template not in meta.everything['templates']: available = list(meta.everything['templates'].keys()) raise BadTemplate("Template doesn't exist!", wanted=template, available=available, meta=meta) val = MergedOptions.using(meta.everything['templates'][template], val) formatted_string = sb.formatted(sb.string_or_int_as_string_spec(), MergedOptionStringFormatter, expected_type=six.string_types) key_name = meta.key_names()['_key_name_0'] key = sb.create_spec(EncryptionKey , name = sb.overridden(key_name) , location = sb.required(formatted_string) , description = formatted_string , grant = sb.listof(grant_statement_spec('key', key_name)) , admin_users = sb.listof(sb.any_spec()) , permission = sb.listof(sb.dictionary_spec()) , no_root_access = sb.defaulted(sb.boolean(), False) ).normalise(meta, val) statements = key.permission if not key.no_root_access: statements.append({"principal": {"iam": "root"}, "action": "kms:*", "resource": "*", "Sid": ""}) if key.admin_users: for admin_user in key.admin_users: statements.append({"principal": admin_user, "action": "kms:*", "resource": { "kms": "__self__" }, "Sid": ""}) key.policy = sb.container_spec(Document, sb.listof(resource_policy_statement_spec('key', key_name))).normalise(meta.at("admin_users"), statements) return key
def normalise(self, meta, val): canned_acls = [ "private", "public-read", "public-read-write", "aws-exec-read" , "authenticated-read", "log-delivery-write" ] acl = sb.defaulted( sb.formatted(sb.string_choice_spec(canned_acls), formatter=MergedOptionStringFormatter) , "private" ).normalise(meta, val) def ret(owner): """http://docs.aws.amazon.com/AmazonS3/latest/dev/acl-overview.html#canned-acl""" if acl == "private": new_grants = [Acls.FullControl(owner)] elif acl == "public-read": new_grants = [Acls.FullControl(owner), Acls.Read(Acls.AllUsersGroup)] elif acl == "public-read-write": new_grants = [Acls.FullControl(owner), Acls.Read(Acls.AllUsersGroup), Acls.Write(Acls.AllUsersGroup)] elif acl == "aws-exec-read": new_grants = [Acls.FullControl(owner), Acls.Read(Acls.EC2Group)] elif acl == "authenticated-read": new_grants = [Acls.FullControl(owner), Acls.Read(Acls.AuthenticatedUsersGroup)] elif acl == "log-delivery-write": new_grants = [Acls.FullControl(owner), Acls.Write(Acls.LogDeliveryGroup), Acls.ReadACP(Acls.LogDeliveryGroup)] return {"ACL": acl, "AccessControlPolicy": {"Grants": new_grants}} return ret
def normalise(self, meta, val): if 'use' in val: template = val['use'] if template not in meta.everything['templates']: available = list(meta.everything['templates'].keys()) raise BadTemplate("Template doesn't exist!", wanted=template, available=available, meta=meta) val = MergedOptions.using(meta.everything['templates'][template], val) formatted_string = sb.formatted(sb.string_or_int_as_string_spec(), MergedOptionStringFormatter, expected_type=six.string_types) function_name = meta.key_names()['_key_name_0'] val = sb.create_spec(Lambda , name = sb.overridden(function_name) , role = sb.required(only_one_spec(resource_spec("lambda", function_name, only=["iam"]))) , code = sb.required(function_code_spec()) , handler = function_handler_spec() , timeout = sb.integer_spec() , runtime = sb.required(formatted_string) , location = sb.required(formatted_string) , description = formatted_string , sample_event = sb.defaulted(sb.or_spec(formatted_dictionary(), sb.string_spec()), "") , desired_output_for_test = sb.defaulted(sb.or_spec(formatted_dictionary(), sb.string_spec()), "") , memory_size = sb.defaulted(divisible_by_spec(64), 128) ).normalise(meta, val) # Hack to make sample_event and desired_output_for_test not appear as a MergedOptions for key in ('sample_event', 'desired_output_for_test'): if isinstance(val[key], MergedOptions): v = val[key].as_dict() class Arbritrary(dictobj): fields = list(v.keys()) val[key] = Arbritrary(**v) return val
def wait_condition_spec(self): """Spec for a wait_condition block""" from harpoon.option_spec import image_objs formatted_string = formatted(string_spec(), formatter=MergedOptionStringFormatter) return create_spec(image_objs.WaitCondition , harpoon = formatted(overridden("{harpoon}"), formatter=MergedOptionStringFormatter) , timeout = defaulted(integer_spec(), 300) , wait_between_attempts = defaulted(float_spec(), 5) , greps = optional_spec(dictof(formatted_string, formatted_string)) , command = optional_spec(listof(formatted_string)) , port_open = optional_spec(listof(integer_spec())) , file_value = optional_spec(dictof(formatted_string, formatted_string)) , curl_result = optional_spec(dictof(formatted_string, formatted_string)) , file_exists = optional_spec(listof(formatted_string)) )
def normalise(self, meta, val): pairs = [] has_self = False for account_id in self.accounts(meta): users = sb.listof(sb.string_spec()).normalise(meta.at("users"), self.resource.get('users', NotSpecified)) for index, name in enumerate(sb.listof(sb.any_spec()).normalise(meta, val)): if name == "__self__": if self.self_type != 'role': raise BadPolicy("No __self__ iam role for this policy", meta=meta) else: has_self = True else: if isinstance(name, six.string_types): name = sb.formatted(sb.string_spec(), formatter=MergedOptionStringFormatter).normalise(meta.indexed_at(index), name) pairs.append((name, account_id)) if has_self: pairs.append(("role/{0}".format(self.self_name), self.default_account_id(meta))) for name, account_id in pairs: service = "sts" if name.startswith("assumed-role") else "iam" arn = "arn:aws:{0}::{1}:{2}".format(service, account_id, name) if not users: yield arn else: for user in users: yield "{0}/{1}".format(arn, user)
def normalise_filled(self, meta, val): """Only care about valid image names""" available = list(meta.everything["images"].keys()) val = sb.formatted(sb.string_spec(), formatter=MergedOptionStringFormatter).normalise(meta, val) if val not in available: raise BadConfiguration("Specified image doesn't exist", specified=val, available=available) return val
def normalise(self, meta, val): if 'use' in val: template = val['use'] if template not in meta.everything['templates']: available = list(meta.everything['templates'].keys()) raise BadTemplate("Template doesn't exist!", wanted=template, available=available, meta=meta) val = MergedOptions.using(meta.everything['templates'][template], val) formatted_string = sb.formatted(sb.string_spec(), MergedOptionStringFormatter, expected_type=six.string_types) role_name = meta.key_names()['_key_name_0'] original_permission = sb.listof(permission_dict()).normalise(meta.at("permission"), NotSpecified if "permission" not in val else val["permission"]) deny_permission = sb.listof(permission_dict(effect='Deny')).normalise(meta.at("deny_permission"), NotSpecified if "deny_permission" not in val else val["deny_permission"]) allow_permission = sb.listof(permission_dict(effect='Allow')).normalise(meta.at("allow_permission"), NotSpecified if "allow_permission" not in val else val["allow_permission"]) allow_to_assume_me = sb.listof(trust_dict("principal")).normalise(meta.at("allow_to_assume_me"), val.get("allow_to_assume_me", NotSpecified)) disallow_to_assume_me = sb.listof(trust_dict("notprincipal")).normalise(meta.at("disallow_to_assume_me"), val.get("disallow_to_assume_me", NotSpecified)) val = val.wrapped() val['trust'] = allow_to_assume_me + disallow_to_assume_me val['permission'] = original_permission + deny_permission + allow_permission return sb.create_spec(Role , name = sb.overridden(role_name) , description = formatted_string , trust = sb.container_spec(Document, sb.listof(trust_statement_spec('role', role_name))) , permission = sb.container_spec(Document, sb.listof(permission_statement_spec('role', role_name))) , make_instance_profile = sb.defaulted(sb.boolean(), False) ).normalise(meta, val)
def normalise(self, meta, val): if 'use' in val: template = val['use'] if template not in meta.everything['templates']: available = list(meta.everything['templates'].keys()) raise BadTemplate("Template doesn't exist!", wanted=template, available=available, meta=meta) val = MergedOptions.using(meta.everything['templates'][template], val) formatted_string = sb.formatted(sb.string_or_int_as_string_spec(), MergedOptionStringFormatter, expected_type=six.string_types) bucket_name = meta.key_names()['_key_name_0'] original_permission = sb.listof(resource_policy_dict()).normalise(meta.at("permission"), NotSpecified if "permission" not in val else val["permission"]) deny_permission = sb.listof(resource_policy_dict(effect='Deny')).normalise(meta.at("deny_permission"), NotSpecified if "deny_permission" not in val else val["deny_permission"]) allow_permission = sb.listof(resource_policy_dict(effect='Allow')).normalise(meta.at("allow_permission"), NotSpecified if "allow_permission" not in val else val["allow_permission"]) # require_mfa_to_delete is an alias for this permission if val.get("require_mfa_to_delete") is True: delete_policy = {"action": "s3:DeleteBucket", "resource": { "s3": "__self__" }, "Condition": { "Bool": { "aws:MultiFactorAuthPresent": True } } } normalised_delete_policy = resource_policy_dict(effect='Allow').normalise(meta.at("require_mfa_to_delete"), delete_policy) allow_permission.append(normalised_delete_policy) val = val.wrapped() val['permission'] = original_permission + deny_permission + allow_permission return sb.create_spec(Bucket , acl = sb.defaulted(sb.match_spec((six.string_types, canned_acl_spec()), (dict, acl_statement_spec('acl', 'acl'))), None) , name = sb.overridden(bucket_name) , location = sb.defaulted(formatted_string, None) , permission = sb.container_spec(Document, sb.listof(resource_policy_statement_spec('bucket', bucket_name))) , tags = sb.dictof(sb.string_spec(), formatted_string) , website = sb.defaulted(website_statement_spec("website", "website"), None) , logging = sb.defaulted(logging_statement_spec("logging", "logging"), None) , lifecycle = sb.defaulted(sb.listof(lifecycle_statement_spec("lifecycle", "lifecycle")), None) ).normalise(meta, val)
def normalise(self, meta, val): if "use" in val: template = val["use"] if template not in meta.everything["templates"]: available = list(meta.everything["templates"].keys()) raise BadTemplate("Template doesn't exist!", wanted=template, available=available, meta=meta) val = MergedOptions.using(meta.everything["templates"][template], val) formatted_string = sb.formatted( sb.string_or_int_as_string_spec(), MergedOptionStringFormatter, expected_type=six.string_types ) key_name = meta.key_names()["_key_name_0"] key = sb.create_spec( EncryptionKey, name=sb.overridden(key_name), location=sb.required(formatted_string), description=formatted_string, grant=sb.listof(grant_statement_spec("key", key_name)), admin_users=sb.listof(sb.any_spec()), ).normalise(meta, val) statements = [{"principal": {"iam": "root"}, "action": "kms:*", "resource": "*", "Sid": ""}] if key.admin_users: for admin_user in key.admin_users: statements.append( {"principal": admin_user, "action": "kms:*", "resource": {"kms": "__self__"}, "Sid": ""} ) key.policy = sb.container_spec(Document, sb.listof(resource_policy_statement_spec("key", key_name))).normalise( meta.at("admin_users"), statements ) return key
def add_configuration(self, configuration, collect_another_source, done, result, src): """Used to add a file to the configuration, result here is the yaml.load of the src""" def make_mtime_func(source): """Lazily calculate the mtime to avoid wasted computation""" return lambda context: self.get_committime_or_mtime( context, source) if "harpoon" in result: if "extra_files" in result["harpoon"]: spec = sb.listof( sb.formatted(sb.string_spec(), formatter=MergedOptionStringFormatter)) meta = Meta(MergedOptions.using(result), []).at("harpoon").at("extra_files") for extra in spec.normalise(meta, result["harpoon"]["extra_files"]): if os.path.abspath(extra) not in done: if not os.path.exists(extra): raise BadConfiguration( "Specified extra file doesn't exist", extra=extra, source=src) collect_another_source(extra) if "images" in result and "__images_from__" in result["images"]: images_from_path = result["images"]["__images_from__"] if isinstance(images_from_path, six.string_types): images_from_path = [images_from_path] for ifp in images_from_path: if not ifp.startswith("/"): ifp = os.path.join(os.path.dirname(src), ifp) if not os.path.exists(ifp) or not os.path.isdir(ifp): raise self.BadConfigurationErrorKls( "Specified folder for other configuration files points to a folder that doesn't exist", path="images.__images_from__", value=ifp) for root, dirs, files in os.walk(ifp): for fle in files: location = os.path.join(root, fle) if fle.endswith(".yml") or fle.endswith(".yaml"): collect_another_source( location, prefix=[ "images", os.path.splitext(os.path.basename(fle))[0] ], extra={"mtime": make_mtime_func(location)}) del result["images"]["__images_from__"] if "mtime" not in result: result["mtime"] = make_mtime_func(src) configuration.update(result, source=src)
def confirm_deployment_spec(self): return create_spec(deployment_check.ConfirmDeployment , deploys_s3_path = optional_spec(listof(stack_specs.s3_address())) , zero_instances_is_ok = defaulted(boolean(), False) , auto_scaling_group_name = optional_spec(formatted(string_spec(), formatter=MergedOptionStringFormatter)) , url_checker = optional_spec(self.url_checker_spec) , sns_confirmation = optional_spec(create_spec(deployment_check.SNSConfirmation , validators.deprecated_key("auto_scaling_group_id", "Use ``confirm_deployment.auto_scaling_group_name``") , validators.deprecated_key("env", "Use ``stack.<stack>.env`` instead``") , timeout = defaulted(integer_spec(), 300) , version_message = required(formatted(string_spec(), formatter=MergedOptionStringFormatter)) , deployment_queue = required(formatted(string_spec(), formatter=MergedOptionStringFormatter)) )) )
def ultradns_provider_spec(self): return sb.create_spec(UltraDNSProvider, name=sb.formatted( sb.overridden("{_key_name_1}"), formatter=MergedOptionStringFormatter), provider_type=sb.required(sb.string_spec()), username=sb.required(formatted_string), password=sb.required(formatted_string))
def netscaler_spec(self): class to_boolean(Spec): def setup(self, spec): self.spec = spec def normalise_either(self, meta, val): val = self.spec.normalise(meta, val) if type(val) is bool: return val if val == 'False': return False elif val == 'True': return True raise BadConfiguration("Expected a boolean", got=val, meta=meta) return create_spec(netscaler_specs.NetScaler , host = required(formatted(string_spec(), formatter=MergedOptionStringFormatter)) , dry_run = to_boolean(formatted(overridden("{bespin.dry_run}"), formatter=MergedOptionStringFormatter)) , username = required(formatted(string_spec(), formatter=MergedOptionStringFormatter)) , configuration_username = optional_spec(formatted(string_spec(), formatter=MergedOptionStringFormatter)) , password = delayed(required(formatted(string_spec(), formatter=MergedOptionStringFormatter))) , configuration_password = optional_spec(formatted(string_spec(), formatter=MergedOptionStringFormatter)) , verify_ssl = defaulted(boolean(), True) , nitro_api_version = defaulted(formatted(string_spec(), formatter=MergedOptionStringFormatter), "v1") , configuration = optional_spec(netscaler_specs.configuration_spec()) , syncable_environments = optional_spec(listof(valid_environment_spec())) )
def dns_environment_spec(self, this): formatted_string = sb.formatted(sb.string_spec(), formatter=MergedOptionStringFormatter) class spec(Spec): def normalise_filled(s, meta, val): meta.everything = meta.everything.wrapped() meta.everything["__site_environments__"] = this["environments"].as_dict() spec = sb.dictof(sb.string_spec(), sb.listof(formatted_string)) return spec.normalise(meta, val.as_dict()) return spec()
def normalise(self, meta, val): if 'use' in val: template = val['use'] if template not in meta.everything['templates']: available = list(meta.everything['templates'].keys()) raise BadTemplate("Template doesn't exist!", wanted=template, available=available, meta=meta) val = MergedOptions.using(meta.everything['templates'][template], val) formatted_string = sb.formatted(sb.string_spec(), MergedOptionStringFormatter, expected_type=six.string_types) role_name = meta.key_names()['_key_name_0'] original_permission = sb.listof(permission_dict()).normalise( meta.at("permission"), NotSpecified if "permission" not in val else val["permission"]) deny_permission = sb.listof(permission_dict(effect='Deny')).normalise( meta.at("deny_permission"), NotSpecified if "deny_permission" not in val else val["deny_permission"]) allow_permission = sb.listof( permission_dict(effect='Allow')).normalise( meta.at("allow_permission"), NotSpecified if "allow_permission" not in val else val["allow_permission"]) allow_to_assume_me = sb.listof(trust_dict("principal")).normalise( meta.at("allow_to_assume_me"), val.get("allow_to_assume_me", NotSpecified)) disallow_to_assume_me = sb.listof( trust_dict("notprincipal")).normalise( meta.at("disallow_to_assume_me"), val.get("disallow_to_assume_me", NotSpecified)) if not allow_to_assume_me and not disallow_to_assume_me: raise BadSpecValue( "Roles must have either allow_to_assume_me or disallow_to_assume_me specified", meta=meta) val = val.wrapped() val['trust'] = allow_to_assume_me + disallow_to_assume_me val['permission'] = original_permission + deny_permission + allow_permission return sb.create_spec( Role, name=sb.overridden(role_name), description=formatted_string, attached_policies=sb.listof(formatted_string), trust=sb.container_spec( Document, sb.listof(trust_statement_spec('role', role_name))), permission=sb.container_spec( Document, sb.listof(permission_statement_spec('role', role_name))), make_instance_profile=sb.defaulted(sb.boolean(), False)).normalise(meta, val)
def normalise(self, meta, val): shell = defaulted(string_spec(), "/bin/bash").normalise( meta, meta.everything[[ "images", meta.key_names()["_key_name_2"] ]].get("shell", NotSpecified)) shell = defaulted( formatted(string_spec(), formatter=MergedOptionStringFormatter), shell).normalise(meta, val) return shell
def wait_condition_spec(self): """Spec for a wait_condition block""" from harpoon.option_spec import image_objs formatted_string = formatted(string_spec(), formatter=MergedOptionStringFormatter) return create_spec( image_objs.WaitCondition, harpoon=formatted(overridden("{harpoon}"), formatter=MergedOptionStringFormatter), timeout=defaulted(integer_spec(), 300), wait_between_attempts=defaulted(float_spec(), 5), greps=optional_spec(dictof(formatted_string, formatted_string)), command=optional_spec(listof(formatted_string)), port_open=optional_spec(listof(integer_spec())), file_value=optional_spec(dictof(formatted_string, formatted_string)), curl_result=optional_spec( dictof(formatted_string, formatted_string)), file_exists=optional_spec(listof(formatted_string)))
def formatted_dict(self, meta, val, chain=None): result = {} for key, val in val.items(): if type(val) is dict: result[key] = self.formatted_dict(meta.at(key), val, chain) elif isinstance(val, six.string_types): result[key] = sb.formatted(sb.string_spec(), formatter=MergedOptionStringFormatter).normalise(meta.at(key), val) else: result[key] = val return result
def target_register_spec(self): """ Make a TargetRegister object .. autoclass:: photons_app.option_spec.photons_app_spec.TargetRegister """ return sb.create_spec(TargetRegister, collector=sb.formatted( sb.overridden("{collector}"), formatter=MergedOptionStringFormatter))
def normalise_val(self, spec, meta, val): """ Normalise with a spec If we have a formatter, use that as well """ if getattr(self, "formatter", None): return formatted(spec, formatter=self.formatter).normalise(meta, val) else: return spec.normalise(meta, val)
def normalise_filled(self, meta, val): """Only care about valid image names""" available = list(meta.everything["images"].keys()) val = sb.formatted(sb.string_spec(), formatter=MergedOptionStringFormatter).normalise( meta, val) if val not in available: raise BadConfiguration("Specified image doesn't exist", specified=val, available=available) return val
def add_configuration(self, configuration, collect_another_source, done, result, src): """Used to add a file to the configuration, result here is the yaml.load of the src""" configuration.update(result, dont_prefix=[dictobj], source=src) if "bespin" in configuration: if "extra_files" in configuration["bespin"]: for extra in sb.listof(sb.formatted(sb.string_spec(), formatter=MergedOptionStringFormatter)).normalise(Meta(configuration, [("bespin", ""), ("extra_files", "")]), configuration["bespin"]["extra_files"]): if os.path.abspath(extra) not in done: if not os.path.exists(extra): raise BadConfiguration("Specified extra file doesn't exist", extra=extra, source=src) collect_another_source(extra)
def normalise(self, meta, val): default_location = "" if "identity" not in self.resource: raise BadPolicy("Generic arn specified without specifying 'identity'", meta=meta) location = self.location(meta) identities = sb.listof(sb.formatted(sb.string_spec(), formatter=MergedOptionStringFormatter)).normalise(meta.at("identity"), self.resource.get("identity")) for account_id in self.accounts(meta): for identity in identities: yield "arn:aws:{0}:{1}:{2}:{3}".format(val, location, account_id, identity)
def dns_environment_spec(self, this): formatted_string = sb.formatted(sb.string_spec(), formatter=MergedOptionStringFormatter) class spec(Spec): def normalise_filled(s, meta, val): meta.everything = meta.everything.wrapped() meta.everything["__site_environments__"] = this[ "environments"].as_dict() spec = sb.dictof(sb.string_spec(), sb.listof(formatted_string)) return spec.normalise(meta, val.as_dict()) return spec()
def formatted_dict(self, meta, val, chain=None): result = {} for key, val in val.items(): if type(val) is dict: result[key] = self.formatted_dict(meta.at(key), val, chain) elif isinstance(val, six.string_types): result[key] = sb.formatted( sb.string_spec(), formatter=MergedOptionStringFormatter).normalise( meta.at(key), val) else: result[key] = val return result
def normalise(self, meta, val): formatted_string = sb.formatted(sb.string_spec(), formatter=MergedOptionStringFormatter) img, conf = complex_from_image_spec().normalise(meta.at("image"), val["image"]) val["conf"] = conf return sb.create_spec(CommandContentAddDict , image = sb.overridden(img) , conf = sb.any_spec() , path = formatted_string , images = sb.overridden(meta.everything.get("images", [])) , docker_api = sb.overridden(meta.everything["harpoon"].docker_api) ).normalise(meta, val)
def aws_syncr_spec(self): """Spec for aws_syncr options""" formatted_string = formatted(string_spec(), MergedOptionStringFormatter, expected_type=six.string_types) return create_spec(AwsSyncr , extra = defaulted(formatted_string, "") , stage = defaulted(formatted_string, "") , debug = defaulted(boolean(), False) , dry_run = defaulted(boolean(), False) , location = defaulted(formatted_string, "ap-southeast-2") , artifact = formatted_string , environment = formatted_string , config_folder = directory_spec() )
def normalise(self, meta, val): if 'use' in val: template = val['use'] if template not in meta.everything['templates']: available = list(meta.everything['templates'].keys()) raise BadTemplate("Template doesn't exist!", wanted=template, available=available, meta=meta) val = MergedOptions.using(meta.everything['templates'][template], val) formatted_string = sb.formatted(sb.string_or_int_as_string_spec(), MergedOptionStringFormatter, expected_type=six.string_types) function_name = meta.key_names()['_key_name_0'] val = sb.create_spec(Lambda, name=sb.overridden(function_name), role=sb.required( only_one_spec( resource_spec("lambda", function_name, only=["iam"]))), code=sb.required(function_code_spec()), handler=function_handler_spec(), timeout=sb.integer_spec(), runtime=sb.required(formatted_string), location=sb.required(formatted_string), description=formatted_string, sample_event=sb.defaulted( sb.or_spec(formatted_dictionary(), sb.string_spec()), ""), desired_output_for_test=sb.defaulted( sb.or_spec(formatted_dictionary(), sb.string_spec()), ""), memory_size=sb.defaulted(divisible_by_spec(64), 128)).normalise( meta, val) # Hack to make sample_event and desired_output_for_test not appear as a MergedOptions for key in ('sample_event', 'desired_output_for_test'): if isinstance(val[key], MergedOptions): v = val[key].as_dict() class Arbritrary(dictobj): fields = list(v.keys()) val[key] = Arbritrary(**v) return val
def normalise(self, meta, val): canned_acls = [ "private", "public-read", "public-read-write", "aws-exec-read", "authenticated-read", "log-delivery-write" ] acl = sb.defaulted( sb.formatted(sb.string_choice_spec(canned_acls), formatter=MergedOptionStringFormatter), "private").normalise(meta, val) def ret(owner): """http://docs.aws.amazon.com/AmazonS3/latest/dev/acl-overview.html#canned-acl""" if acl == "private": new_grants = [Acls.FullControl(owner)] elif acl == "public-read": new_grants = [ Acls.FullControl(owner), Acls.Read(Acls.AllUsersGroup) ] elif acl == "public-read-write": new_grants = [ Acls.FullControl(owner), Acls.Read(Acls.AllUsersGroup), Acls.Write(Acls.AllUsersGroup) ] elif acl == "aws-exec-read": new_grants = [ Acls.FullControl(owner), Acls.Read(Acls.EC2Group) ] elif acl == "authenticated-read": new_grants = [ Acls.FullControl(owner), Acls.Read(Acls.AuthenticatedUsersGroup) ] elif acl == "log-delivery-write": new_grants = [ Acls.FullControl(owner), Acls.Write(Acls.LogDeliveryGroup), Acls.ReadACP(Acls.LogDeliveryGroup) ] return {"ACL": acl, "AccessControlPolicy": {"Grants": new_grants}} return ret
def normalise_filled(self, meta, val): val = sb.dictof(sb.string_choice_spec(["s3", "inline", "directory"]), sb.any_spec()).normalise(meta, val) if not val: raise BadSpecValue("Please specify s3, inline or directory for your code", meta=meta) if len(val) > 1: raise BadSpecValue("Please only specify one of s3, inline or directory for your code", got=list(val.keys()), meta=meta) formatted_string = sb.formatted(sb.string_spec(), formatter=MergedOptionStringFormatter) if "s3" in val: return sb.create_spec(S3Code , key = formatted_string , bucket = formatted_string , version = sb.defaulted(sb.string_spec(), NotSpecified) ).normalise(meta, val['s3']) elif "inline" in val: path = [p for p, _ in meta._path] path.pop() runtime = meta.everything['.'.join(path)].get("runtime", "python") runtime = sb.formatted(sb.string_spec(), formatter=MergedOptionStringFormatter).normalise(meta.at("runtime"), runtime) return sb.create_spec(InlineCode , code = sb.string_spec() , runtime = sb.overridden(runtime) ).normalise(meta, {"code": val['inline']}) else: directory = val['directory'] if isinstance(val['directory'], six.string_types): directory = {"directory": val['directory']} if 'directory' in directory: formatted_string = sb.formatted(sb.string_spec(), formatter=MergedOptionStringFormatter) directory['directory'] = formatted_string.normalise(meta.at("directory").at("directory"), directory['directory']) return sb.create_spec(DirectoryCode , directory = sb.directory_spec() , exclude = sb.listof(sb.string_spec()) ).normalise(meta, directory)
def normalise_empty(self, meta): path = [p for p, _ in meta._path] path.pop() runtime = meta.everything['.'.join(path)].get("runtime", "python") runtime = sb.formatted(sb.string_spec(), formatter=MergedOptionStringFormatter).normalise(meta.at("runtime"), runtime) if runtime == 'java8': raise BadSpecValue("No default function handler for java", meta=meta) elif runtime == 'nodejs': return "index.handler" elif runtime == 'python2.7': return "lambda_function.lambda_handler" else: raise BadSpecValue("No default function handler for {0}".format(runtime), meta=meta)
class website_statement_spec(statement_spec): formatted_string = sb.formatted(sb.string_spec(), formatter=MergedOptionStringFormatter) args = lambda s, self_type, self_name: { (("sep", "_"), ("parts", ("index", "document"))): made_up_dict(s.formatted_string, ("Suffix", )), (("sep", "_"), ("parts", ("error", "document"))): made_up_dict(s.formatted_string, ("Key", )), (("sep", "_"), ("parts", ("redirect", "all", "requests", "to"))): redirect_all_requests_to_spec(s.formatted_string), (("sep", "_"), ("parts", ("routing", "rules"))): sb.listof(sb.dictionary_spec()) } final_kls = lambda s, *args, **kwargs: WebsiteConfig(*args, **kwargs)
def context_spec(self): """Spec for specifying context options""" from harpoon.option_spec import image_objs return dict_from_bool_spec(lambda meta, val: {"enabled": val} , create_spec(image_objs.Context , include = listof(string_spec()) , exclude = listof(string_spec()) , enabled = defaulted(boolean(), True) , parent_dir = directory_spec(formatted(defaulted(string_spec(), "{config_root}"), formatter=MergedOptionStringFormatter)) , use_gitignore = defaulted(boolean(), False) , use_git_timestamps = defaulted(or_spec(boolean(), listof(string_spec())), False) ) )
def aws_syncr_spec(self): """Spec for aws_syncr options""" formatted_string = formatted(string_spec(), MergedOptionStringFormatter, expected_type=six.string_types) return create_spec(AwsSyncr, extra=defaulted(formatted_string, ""), stage=defaulted(formatted_string, ""), debug=defaulted(boolean(), False), dry_run=defaulted(boolean(), False), location=defaulted(formatted_string, "ap-southeast-2"), artifact=formatted_string, environment=formatted_string, config_folder=directory_spec())