def __init__(self, serialized_service_description, alias): validate(serialized_service_description, "yac/schema/description.json") self.name = search('"name"', serialized_service_description,"") self.summary = search('summary', serialized_service_description,"") self.version = search('version', serialized_service_description,"") self.repo = search('repo', serialized_service_description,"") self.default_alias = search('"default-alias"', serialized_service_description,"") if alias: self.alias = alias else: self.alias = self.default_alias
def __init__(self, serialized_inputs_cacher): validate(serialized_inputs_cacher, "yac/schema/inputs_cacher.json") self.enabled = search("enabled", serialized_inputs_cacher, False) self.path = search("path", serialized_inputs_cacher, "") self.exclusions = search('"exclusions"', serialized_inputs_cacher, [])
def __init__(self, credentials_descriptor): validate(credentials_descriptor, "yac/schema/stacks/k8s/credentialer.json") self.namespace = search("namespace", credentials_descriptor,"") self.clusters = search("clusters", credentials_descriptor, ["nonprod","prod"]) # if tokens are input there should be one per cluster self.tokens = search("tokens", credentials_descriptor, []) self.secrets = Secrets(search('"Secrets"', credentials_descriptor,{})) # initialize the inputs (for driving user prompts) self.inputs = Inputs(search("Inputs", credentials_descriptor,{})) # for integration testing it is useful to write files to a # root director other than user's home self.rootdir = search("rootdir", credentials_descriptor,"")
def __init__(self, serialized_input): """ Inputs are typically used when a service provider wants to create an easy installation experience for their service. Args: serialized_input: A dictionary containing serialized input, satisfying the yac/schema/input.json schema Raises: ValidationError: if a inputs fails schema validation """ validate(serialized_input, "yac/schema/input.json") self.key = search("key",serialized_input,"") self.type = search("type",serialized_input,"") self.title = search("description",serialized_input,"") self.help = search("help",serialized_input,"") self.required = search("required",serialized_input,True) self.options = search("options",serialized_input,[]) self.conditions = search("conditions",serialized_input,{})
def __init__(self, credentials_descriptor): """ Generates aws credentials for nordstrom users. Args: credentials_descriptor: A dictionary containing serialized credentialer, satisfying the yac/schema/aws/credentialer.json schema Raises: ValidationError: if a inputs fails schema validation """ validate(credentials_descriptor, "yac/schema/stacks/aws/credentialer.json") self.accounts = search("accounts", credentials_descriptor, []) self.region = search("region", credentials_descriptor, []) # if urls not provided, use defaults self.token_endpoint_url = search('"token-endpoint-url"', credentials_descriptor, TOKEN_ENDPOINT_URL) self.role_endpoint_url = search('"role-endpoint-url"', credentials_descriptor, ROLE_ENDPOINT_URL) # initialize the inputs (for driving user prompts) self.inputs = Inputs(search("Inputs", credentials_descriptor, {}))
def __init__(self, serialized_artifact): validate(serialized_artifact, "yac/schema/makers/container_image.json") self.name = search("name",serialized_artifact,"") self.description = search("description",serialized_artifact,"") self.image = search("image",serialized_artifact) # the registry where the images should be pushed # defaults to artifactory self.registry = search('registry', serialized_artifact, ARTIFACTORY_URL) # initialize the inputs (for driving user prompts) self.inputs = Inputs(search("Inputs", serialized_artifact,{})) self.secrets = Secrets(search('"Secrets"', serialized_artifact,{})) # client for most operations self.client = docker.DockerClient('tcp://%s:%s'%(BUILDER_HOST, BUILDER_PORT)) # client for "low-level" build operations (e.g. builds that send # the details on each layer built to stdout ) # TODO: figure out why auth isn't working from inside a container # with this one self.api_client = docker.APIClient('tcp://%s:%s'%(BUILDER_HOST, BUILDER_PORT))
def __init__(self, serialized_secrets): # validate. this will raise a validation error if # required fields aren't present validate(serialized_secrets, "yac/schema/secrets.json") self.values = serialized_secrets self.errors = []
def __init__(self, vault_configs): validate(vault_configs, "yac/schema/vaults/file.json") self.vault_path = search('"vault-path"', vault_configs) # default to json format self.format = search("format", vault_configs, "json") self.initialized = False self.ready = False
def __init__(self, serialized_tasks): # first validate. this should raise an error if # required fields aren't present validate(serialized_tasks, "yac/schema/tasks.json") self.values = {} for serialized_task in serialized_tasks: this_task = Task(serialized_task) self.values[this_task.name] = this_task
def get_engine_configs(configs_path=""): # raises: # validation error if anything is missing if not configs_path: configs_path = os.path.join(get_root_path(), "config/engines.json") engine_configs = {} with open(configs_path, 'r') as config_file: engine_configs = json.load(config_file) validate(engine_configs, "yac/schema/engines.json") return engine_configs
def __init__(self, serialized_stack): # args: # serialized_stack: dict containing the stack object from the Stack stanza # in the Servicefile # use the yac.lib.schema module to validate the serialized_stack, e.g.: validate(serialized_stack, "yac/schema/stacks/gcp/stack.json") # use the yac.lib.search module to grab fields from the serialized_stack, e.g.: self.name = search('name', serialized_stack) self.resources = search("Resources",serialized_stack, {}) self.conditions = search("Conditions",serialized_stack, {})
def load_from_file(self, params_file_arg): params_str = "" # user may have specified a params file as a command line arg if params_file_arg and os.path.exists(params_file_arg): params_str = get_file_contents(params_file) serialized_params = json.loads(params_str) # validate validate(serialized_params, "yac/schema/params.json") self.values.update(Params(serialized_params))
def __init__(self, vault_configs): validate(vault_configs, "yac/schema/vaults/keepass.json") self.vault_path = search('"vault-path"', vault_configs) vault_pwd_path = search('"vault-pwd-path"', vault_configs) self.vault_pwd = search('"vault-pwd"', vault_configs) if not self.vault_pwd and vault_pwd_path and os.path.exists( vault_pwd_path): self.vault_pwd = read_pwd_file(vault_pwd_path) self.ready = False self.initialized = False self.kp = None
def __init__(self, int_test_descriptor): # validate the test descriptor # note: this will raise an exception if validation fails validate(int_test_descriptor, "yac/schema/integration_tests.json") # raw because it my contain instrinsics self.raw_target_map = search('"target-map"', int_test_descriptor, {}) self.results_store = search('"results-store"', int_test_descriptor, {}) self.tests = search('tests', int_test_descriptor, []) self.test_groups = search('"test-groups"', int_test_descriptor, []) self.test_results = TestResults()
def __init__(self, serialized_stack): self.type = "aws-cloudformation" # validate. this should raise an error if required # fields aren't present validate(serialized_stack, "yac/schema/stacks/aws/stack.json") self.parameters = search("Parameters", serialized_stack, {}) self.resources = search("Resources", serialized_stack, {}) self.conditions = search("Conditions", serialized_stack, {}) # for mapping cloud formation parameters to yac params self.cf_param_map = ParameterMapping( search("ParameterMapping", serialized_stack, {})) self.boot_files = BootFiles(search('"BootFiles"', serialized_stack, {}))
def __init__(self, notifier_descriptor, logger=None): # first validate. this should throw an exception if # required fields aren't present validate(notifier_descriptor, "yac/schema/notifier/slack.json") self.info_channel = search('"info-channel"', notifier_descriptor, "") self.warning_channel = search('"warning-channel"', notifier_descriptor, "") self.api_key = search('"api-key"', notifier_descriptor, "") self.client = SlackClient(self.api_key) if logger: self.logger = logger else: self.logger = get_yac_logger()
def __init__(self, serialized_secret_vaults): validate(serialized_secret_vaults, "yac/schema/vaults.json") self.vaults = {} for vault_descriptor in serialized_secret_vaults: vault_key = vault_descriptor['name'] vault_type = vault_descriptor['type'] self.vaults[vault_key], err = get_vault_provider(vault_type, vault_descriptor['configs']) if err: print("vault provider for type '%s' not available. err: %s ... exiting"%(vault_type,err)) exit(1)
def __init__(self, vault_configs={}): validate(vault_configs, "yac/schema/vaults/s3.json") self.vault_bucket = search('"bucket"', vault_configs) self.vault_s3_path = search('"vault-path"', vault_configs) self.format = search("format", vault_configs, "json") vault_file_local_path = get_vault_local_path(self.vault_bucket, self.vault_s3_path) file_vault_config = { "vault-path": vault_file_local_path, "format": self.format } FileVault.__init__(self, file_vault_config) self.session = None
def __init__(self, serialized_artifact): validate(serialized_artifact, "yac/schema/makers/ami.json") self.name = search('name', serialized_artifact) self.description = search('description', serialized_artifact) # the aws profile aliasing the account to build in self.profile = search('profile', serialized_artifact) # path to the packer file self.packer_file = search('"packer-file"', serialized_artifact) # directory containing files that should be included in the build self.packable_dir = search('"packer-dir"', serialized_artifact, "") self.secrets = Secrets(search('"Secrets"', serialized_artifact, {})) # initialize the inputs (for driving user prompts) self.inputs = Inputs(search("Inputs", serialized_artifact, {}))
def __init__(self, serialized_pipeline): # first validate. this should throw an exception if # required fields aren't present validate(serialized_pipeline, "yac/schema/pipeline.json") self.deploy_branch = jmespath.search('"deploy-branch"', serialized_pipeline) self.rollback_branch = jmespath.search('"rollback-branch"', serialized_pipeline) self.setup = jmespath.search('setup', serialized_pipeline) self.notifications = search('notifications',serialized_pipeline) self.stages = search('stages',serialized_pipeline,[]) self.stage_names = search('stages[*].name',serialized_pipeline,[])
def __init__(self, serialized_stack): """ Args: serialized_stack: A dictionary containing serialized k8s stack, satisfying the yac/schema/stacks/k8s/stack.json schema Returns: A K8sStack instance Raises: ValidationError: if a serialized_stack fails schema validation """ # first validate. this should raise an error if # required fields aren't present validate(serialized_stack, "yac/schema/stacks/k8s/stack.json") self.type = "kubernetes" self.namespace = search("namespace", serialized_stack, "") self.configmaps = ConfigMaps(serialized_stack) self.secrets = Secrets(serialized_stack) self.deployments = Deployments(serialized_stack) self.ingresses = Ingresses(serialized_stack) self.statefulsets = StatefulSets(serialized_stack) self.services = Services(serialized_stack) self.pvcs = PVCs(serialized_stack) # generic resources (orchestrated via kubectl) self.resources = Resources(serialized_stack)
def __init__(self, serialized_service, service_path, alias="", params_path="", kvps=""): # first validate. this should throw an exception if # required fields aren't present validate(serialized_service, "yac/schema/service.json") self.path = service_path self.kvps_str = kvps self.params_file_path = params_path self.description = Description(search('Description', serialized_service,{}), alias) self.vaults = SecretVaults(search('"Vaults"', serialized_service,[])) # a service can references other services that it includes self.includes = search("includes", serialized_service,{}) # initialize stack params (key/value pairs and maps), including static params specified # in the serialized service, params from an external file, # params specified in a key-value pair string (kvps), self.params = Params(search('"Params"', serialized_service, {})) # initialize the dictionary that will hold all params (statics+secrets+inputs) self.all_params = {} inputs_cacher = InputsCacher(search('InputsCache', serialized_service,{})) self.inputs = Inputs(search('Inputs', serialized_service,{}), inputs_cacher) self.secrets = Secrets(search('"Secrets"', serialized_service,{})) # inialize the stack associated with this service self.stack = Stack(search('Stack', serialized_service, {})) self.tasks = Tasks(search('Tasks', serialized_service,{})) # initialize the tests associated with this service self.tests = IntegrationTests(search('"IntegrationTests"', serialized_service,{})) # initialize the artifacts associate with this service self.artifacts = Artifacts(search('Artifacts', serialized_service,[])) # initialize the credentialer associated with this service self.credentialers = Credentialers(search("Credentialers",serialized_service,[])) # initialize the pipeline associated with this service self.pipeline = Pipeline(search('Pipeline', serialized_service,{})) # load the objects from each included service self.load_includes() # save a copy of the full serialized version of the # service to support the serialize() method self.serialized_service = serialized_service
def apply_intrinsics(source_dict, params): """ Replaces yac intrinsics in a dictionary Args: source_dict: A dictionary with intrinsics params: A Params instance Returns: A dictionary with yac intrinsics replaced with values Raises: IntrinsicsError: message includes what instrinsics failed to render Currently supported instrisics include: {"yac-ref": "<param key>"} where, <param-key> is the key of a parameter to be dereferenced {"yac-join": ["<delimitter>", [elem1, elem2, ...,elemN]]} where, <delimitter> is the delimitter to be placed between each element <elem1-N> are any object (including another intrinsic) {"yac-name": "<resource>"} where, <resource> is an arbitrary string that helps define the resource (i.e. "elb", "asg", "stack", etc.) the 'name' instrinsic is a short-hand form of the 'join' intrinsic, since resource naming is such a common occurrence in stack templates. by default, it appends the service alias with the resource label, separated by a '-' delimmter. this default convention can be overridden via service-supplied 'naming-convention' parameter, formatted as: ... "naming-convention": { "comment": "name resources using the alias followed by the environment", "value": { "param-keys": ['service-alias','env'], "delimitter": "-" } } ... } {"yac-include": <arg>} where <arg> is either: * a relative path to a json or yaml file containing a stack resource, or * a relative path to a python module with a 'generate_resource()' fxn {"yac-calc": <argv>} where, <argv> is an argv-style array containing the calculator to run and any arguments to pass to the calculator. Each element in argv is comma separated. the following 'stock'calculators are available: - {"yac-calc": ["vpc-id"]}, which calculates the id of the vpc associated with the current aws credential - {"yac-calc": ["vpc-cidr"]}, which calculates the cidr of the vpc associated with the current aws credential - {"yac-calc": ["subnet-ids","<label-str>",[<availability-zones>]], which calculates the subnets ids for all subnets in the vpc associated with the current aws credential. subnet ids returned are per the order of zones in the [<availability-zones>] array. In addition to the stock calculators, the yac-calc intrinsic will accept a path to any service-supplied module that contains a 'do_calc()' method, as: - {"yac-calc": ["<path to module>","arg1","arg2", etc.]}, an example might look like: {"yac-calc": ["/lib/foo.py","bar1","bar2"]} the foo module is loaded, and its do_calc() function is called as: do_calc(["bar1", "bar2"]) note: the module path is relative to the servicefile. """ # make sure input is in the expected object format validate(source_dict, 'yac/schema/intrinsics_input.json') # Take a copy of template before applying intrinsics. source_dict_copy = copy.deepcopy(source_dict) # source_dict can contain intrinsics at any level. # _apply_intrinsics() uses recursion in processing intrinsics, and thus does not # support intrinsics at the first-child level. to address, do the following: # 1) create a placeholder first child that keys the source_dict # 2) process using _apply_intrinsics() # 3) return the rendered dictionary under placeholder placeholder_key = "placeholder" placeholder_dict = {placeholder_key: source_dict_copy} rendered_dictionary = _apply_intrinsics(placeholder_dict, params) # raise an IntrinsicsError if any failures were encountered if get_failures(params): raise IntrinsicsError(_pp_failures(params)) else: return rendered_dictionary[placeholder_key]