コード例 #1
0
 def _create_configuration_table(self):
     self.dynamodb.create_table(TableName=ENVIRONMENT_CONFIGURATION_TABLE,
                                KeySchema=[{
                                    'AttributeName': 'environment',
                                    'KeyType': 'HASH'
                                }],
                                AttributeDefinitions=[{
                                    'AttributeName': 'environment',
                                    'AttributeType': 'S'
                                }],
                                BillingMode='PAY_PER_REQUEST')
     log_bold("Configuration table created!")
コード例 #2
0
    def _get_environment_stack(self):
        try:
            log("Looking for " + self.environment + " cluster.")
            environment_stack = self.client.describe_stacks(
                StackName=get_cluster_name(self.environment))['Stacks'][0]
            log_bold(self.environment + " stack found. Using stack with ID: " +
                     environment_stack['StackId'])
        except ClientError:
            log_err(self.environment +
                    " cluster not found. Create the environment \
cluster using `create_environment` command.")
            exit(1)
        return environment_stack
コード例 #3
0
 def _print_progress(self):
     while True:
         response = self.client.describe_stacks(StackName=self.stack_name)
         if "IN_PROGRESS" not in response['Stacks'][0]['StackStatus']:
             break
         all_events = get_stack_events(self.client, self.stack_name)
         print_new_events(all_events, self.existing_events)
         self.existing_events = all_events
         sleep(5)
     final_status = response['Stacks'][0]['StackStatus']
     if "FAIL" in final_status:
         log_err("Finished with status: %s" % (final_status))
     else:
         log_bold("Finished with status: %s" % (final_status))
コード例 #4
0
    def create(self):
        '''
            Create and execute CloudFormation template for ECS service
            and related dependencies
        '''
        log_bold("Initiating service creation")
        self.service_configuration.edit_config()

        template_generator = ServiceTemplateGenerator(
            self.service_configuration, self.environment_stack)
        service_template_body, template_source, key = template_generator.generate_service(
        )

        try:
            if template_source == 'TemplateBody':
                self.client.create_stack(
                    StackName=self.stack_name,
                    TemplateBody=service_template_body,
                    Parameters=[{
                        'ParameterKey': 'Environment',
                        'ParameterValue': self.environment,
                    }],
                    OnFailure='DO_NOTHING',
                    Capabilities=['CAPABILITY_NAMED_IAM'],
                )
            elif template_source == 'TemplateURL':
                self.client.create_stack(
                    StackName=self.stack_name,
                    TemplateURL=service_template_body,
                    Parameters=[{
                        'ParameterKey': 'Environment',
                        'ParameterValue': self.environment,
                    }],
                    OnFailure='DO_NOTHING',
                    Capabilities=['CAPABILITY_NAMED_IAM'],
                )
            log_bold("Submitted to cloudformation. Checking progress...")
            self.delete_template(key)
            self._print_progress()
        except ClientError as boto_client_error:
            self.delete_template(key)
            error_code = boto_client_error.response['Error']['Code']
            if error_code == 'AlreadyExistsException':
                raise UnrecoverableException("Stack " + self.stack_name +
                                             " already exists.")
            else:
                raise boto_client_error
コード例 #5
0
ファイル: mfa.py プロジェクト: thulasi-ram/cloudlift
def get_mfa_session(mfa_code=None, region='ap-south-1'):
    username = get_username()
    if not mfa_code:
        mfa_code = input("MFA Code: ")
    mfa_arn = "arn:aws:iam::%s:mfa/%s" % (get_account_id(), username)

    log_bold("Using credentials for " + username)
    try:
        session_params = client('sts').get_session_token(
            DurationSeconds=900, SerialNumber=mfa_arn, TokenCode=str(mfa_code))
        credentials = session_params['Credentials']
        return Session(aws_access_key_id=credentials['AccessKeyId'],
                       aws_secret_access_key=credentials['SecretAccessKey'],
                       aws_session_token=credentials['SessionToken'],
                       region_name=region)
    except botocore.exceptions.ClientError as client_error:
        raise UnrecoverableException(str(client_error))
コード例 #6
0
ファイル: service_updater.py プロジェクト: techiev2/cloudlift
    def ensure_image_in_ecr(self):
        if self.version:
            try:
                commit_sha = self._find_commit_sha(self.version)
            except:
                commit_sha = self.version
            log_intent("Using commit hash " + commit_sha + " to find image")
            image = self._find_image_in_ecr(commit_sha)
            if not image:
                log_warning("Please build, tag and upload the image for the \
commit " + commit_sha)
                raise UnrecoverableException("Image for given version could not be found.")
        else:
            dirty = subprocess.check_output(
                ["git", "status", "--short"]
            ).decode("utf-8")
            if dirty:
                self.version = 'dirty'
                log_intent("Version parameter was not provided. Determined \
version to be " + self.version + " based on current status")
                image = None
            else:
                self.version = self._find_commit_sha()
                log_intent("Version parameter was not provided. Determined \
version to be " + self.version + " based on current status")
                image = self._find_image_in_ecr(self.version)

            if image:
                log_intent("Image found in ECR")
            else:
                log_bold("Image not found in ECR. Building image")
                image_name = spinalcase(self.name) + ':' + self.version
                ecr_name = self.ecr_image_uri + ':' + self.version
                self._build_image(image_name)
                self._push_image(image_name, ecr_name)
                image = self._find_image_in_ecr(self.version)

        try:
            image_manifest = image['imageManifest']
            self.ecr_client.put_image(
                repositoryName=self.repo_name,
                imageTag=self.version,
                imageManifest=image_manifest
            )
        except Exception:
            pass
コード例 #7
0
 def _create_configuration_table(self):
     key_schema = [{
         'AttributeName': self.kv_pairs[0][0],
         'KeyType': 'HASH'
     }]
     key_schema.extend([{
         'AttributeName': key,
         'KeyType': 'RANGE'
     } for key, _ in self.kv_pairs[1:]])
     self.dynamodb.create_table(TableName=self.table_name,
                                KeySchema=key_schema,
                                AttributeDefinitions=[{
                                    'AttributeName': key,
                                    'AttributeType': 'S'
                                } for key, _ in self.kv_pairs],
                                BillingMode='PAY_PER_REQUEST')
     log_bold("{} table created!".format(self.table_name))
コード例 #8
0
def print_parameter_changes(differences):
    changes_to_show = [["Type", "Config", "Old val", "New val"]]
    for difference in differences:
        if difference[0] == 'change':
            changes_to_show.append(
                ['change', difference[1], difference[2][0], difference[2][1]])
        if difference[0] == 'add':
            difference[2].sort(key=lambda x: x[0])
            for added_item in difference[2]:
                changes_to_show.append(
                    ['add', added_item[0], '', added_item[1]])
        if difference[0] == 'remove':
            difference[2].sort(key=lambda x: x[0])
            for removed_item in difference[2]:
                changes_to_show.append(
                    ['remove', removed_item[0], removed_item[1], ''])
    log_bold("Modifications to config:")
    print(SingleTable(changes_to_show).table)
コード例 #9
0
ファイル: mfa.py プロジェクト: thulasi-ram/cloudlift
def do_mfa_login(mfa_code=None, region='ap-south-1'):
    username = get_username()
    if not mfa_code:
        mfa_code = input("MFA Code: ")
    mfa_arn = "arn:aws:iam::%s:mfa/%s" % (get_account_id(), username)

    log_bold("Using credentials for " + username)
    try:
        session_params = client('sts').get_session_token(
            DurationSeconds=900, SerialNumber=mfa_arn, TokenCode=str(mfa_code))
        credentials = session_params['Credentials']
        os.environ['AWS_ACCESS_KEY_ID'] = credentials['AccessKeyId']
        os.environ['AWS_SECRET_ACCESS_KEY'] = credentials['SecretAccessKey']
        os.environ['AWS_SESSION_TOKEN'] = credentials['SessionToken']
        os.environ['AWS_DEFAULT_REGION'] = region
        return session_params
    except botocore.exceptions.ClientError as client_error:
        raise UnrecoverableException(str(client_error))
コード例 #10
0
ファイル: service_creator.py プロジェクト: Rippling/cloudlift
    def update(self):
        '''
            Create and execute changeset for existing CloudFormation template
            for ECS service and related dependencies
        '''

        log_bold("Starting to update service")
        self.service_configuration.edit_config()
        self.service_configuration.validate()

        information_fetcher = ServiceInformationFetcher(
            self.service_configuration.service_name,
            self.environment,
            self.service_configuration.get_config(),
        )
        try:
            current_image_uri = information_fetcher.get_current_image_uri()
            desired_counts = information_fetcher.fetch_current_desired_count()

            template_generator = ServiceTemplateGenerator(
                self.service_configuration,
                self.environment_stack,
                self.env_sample_file,
                current_image_uri,
                desired_counts,
            )
            service_template_body = template_generator.generate_service()
            change_set = create_change_set(self.client, service_template_body,
                                           self.stack_name, None,
                                           self.environment)
            if change_set is None:
                return
            self.service_configuration.update_cloudlift_version()
            log_bold("Executing changeset. Checking progress...")
            self.client.execute_change_set(
                ChangeSetName=change_set['ChangeSetId'])
            self._print_progress()
        except ClientError as exc:
            if "No updates are to be performed." in str(exc):
                log_err("No updates are to be performed")
            else:
                raise exc
コード例 #11
0
    def ensure_image_in_ecr(self):
        if self.version:
            log_intent("Using commit hash " + self.version + " to find image")
            image = self._find_image_in_ecr(self.version)
            if not image:
                log_warning("Please build, tag and upload the image for the \
commit " + self.version)
                raise UnrecoverableException(
                    "Image for given version could not be found.")
        else:
            dirty = subprocess.check_output(["git", "status",
                                             "--short"]).decode("utf-8")
            if dirty:
                log_intent(
                    "Repository has uncommitted changes. Marking version as dirty."
                )
                self.version = '{}-dirty'.format(self._derive_version())
                image = None
            else:
                self.version = self._derive_version()
                image = self._find_image_in_ecr(self.version)

            log_intent(
                "Version parameter was not provided. Determined version to be "
                + self.version + " based on current status")
            if image:
                log_intent("Image found in ECR")
            else:
                log_bold("Image not found in ECR. Building image")
                self._build_image()
                self._push_image()
                image = self._find_image_in_ecr(self.version)
        try:
            image_manifest = image['imageManifest']
            self.client.put_image(repositoryName=self.repo_name,
                                  imageTag=self.version,
                                  imageManifest=image_manifest)
        except Exception:
            pass
        self._add_image_tag(self.version,
                            f'{self.version}-{self._git_epoch_time()}')
コード例 #12
0
    def get_version(self, short):
        commit_sha = self._fetch_current_task_definition_tag()
        if commit_sha is None:
            log_err("Current task definition tag could not be found. \
Is it deployed?")
        elif commit_sha == "dirty":
            log("Dirty version is deployed. Commit information could not be \
fetched.")
        else:
            log("Currently deployed version: " + commit_sha)
            if not short:
                log("Running `git fetch --all`")
                call(["git", "fetch", "--all"])
                log_bold("Commit Info:")
                call([
                    "git", "--no-pager", "show", "-s", "--format=medium",
                    commit_sha
                ])
                log_bold("Branch Info:")
                call(["git", "branch", "-r", "--contains", commit_sha])
                log("")
コード例 #13
0
ファイル: service_updater.py プロジェクト: Rippling/cloudlift
    def run(self):
        log_warning("Deploying to {self.region}".format(**locals()))
        if not os.path.exists(self.env_sample_file):
            raise UnrecoverableException('env.sample not found. Exiting.')
        log_intent("name: " + self.name + " | environment: " +
                   self.environment + " | version: " + str(self.version) +
                   " | deployment_identifier: " + self.deployment_identifier)
        log_bold("Checking image in ECR")
        self.ecr.upload_artefacts()
        log_bold("Initiating deployment\n")
        ecs_client = EcsClient(None, None, self.region)

        image_url = self.ecr.image_uri
        target = deployer.deploy_new_version
        kwargs = dict(client=ecs_client, cluster_name=self.cluster_name,
                      service_name=self.name, sample_env_file_path=self.env_sample_file,
                      timeout_seconds=self.timeout_seconds, env_name=self.environment,
                      ecr_image_uri=image_url,
                      deployment_identifier=self.deployment_identifier,
                      )
        self.run_job_for_all_services("Deploy", target, kwargs)
コード例 #14
0
    def create(self):
        log_warning(
            "Create task definition to {self.region}".format(**locals()))
        if not os.path.exists(self.env_sample_file):
            raise UnrecoverableException('env.sample not found. Exiting.')
        ecr_client = EcrClient(self.name, self.region, self.build_args)
        ecr_client.set_version(self.version)
        log_intent("name: " + self.name + " | environment: " +
                   self.environment + " | version: " + str(ecr_client.version))
        log_bold("Checking image in ECR")
        ecr_client.build_and_upload_image()
        log_bold("Creating task definition\n")
        env_config = build_config(self.environment, self.name,
                                  self.env_sample_file)
        container_definition_arguments = {
            "environment": [{
                "name": k,
                "value": v
            } for (k, v) in env_config],
            "name": pascalcase(self.name) + "Container",
            "image": _complete_image_url(ecr_client),
            "essential": True,
            "logConfiguration": self._gen_log_config(pascalcase(self.name)),
            "memoryReservation": 1024
        }

        ecs_client = EcsClient(region=self.region)
        ecs_client.register_task_definition(self._task_defn_family(),
                                            [container_definition_arguments],
                                            [], None)
        log_bold("Task definition successfully created\n")
コード例 #15
0
 def run_update(self, update_ecs_agents):
     if update_ecs_agents:
         self.__run_ecs_container_agent_udpate()
     try:
         log("Initiating environment stack update.")
         environment_stack_template_body = ClusterTemplateGenerator(
             self.environment, self.configuration,
             self.__get_desired_count()).generate_cluster()
         log("Template generation complete.")
         change_set = create_change_set(self.client,
                                        environment_stack_template_body,
                                        self.cluster_name, self.key_name,
                                        self.environment)
         self.existing_events = get_stack_events(self.client,
                                                 self.cluster_name)
         log_bold("Executing changeset. Checking progress...")
         self.client.execute_change_set(
             ChangeSetName=change_set['ChangeSetId'])
         self.__print_progress()
     except ClientError as e:
         log_err("No updates are to be performed")
     except Exception as e:
         raise e
コード例 #16
0
def create_change_set(client, service_template_body, stack_name, key_name,
                      environment):
    change_set_parameters = [{
        'ParameterKey': 'Environment',
        'ParameterValue': environment
    }]
    if key_name:
        change_set_parameters.append({
            'ParameterKey': 'KeyPair',
            'ParameterValue': key_name
        })
    create_change_set_res = client.create_change_set(
        StackName=stack_name,
        ChangeSetName="cg" + uuid.uuid4().hex,
        TemplateBody=service_template_body,
        Parameters=change_set_parameters,
        Capabilities=['CAPABILITY_NAMED_IAM'],
        ChangeSetType='UPDATE')
    log("Changeset creation initiated. Checking the progress...")
    change_set = client.describe_change_set(
        ChangeSetName=create_change_set_res['Id'])
    while change_set['Status'] in ['CREATE_PENDING', 'CREATE_IN_PROGRESS']:
        sleep(1)
        status_string = '\x1b[2K\rChecking changeset status.  Status: ' + \
                        change_set['Status']
        sys.stdout.write(status_string)
        sys.stdout.flush()
        change_set = client.describe_change_set(
            ChangeSetName=create_change_set_res['Id'])
    status_string = '\x1b[2K\rChecking changeset status..  Status: ' + \
                    change_set['Status']+'\n'
    sys.stdout.write(status_string)
    if change_set['Status'] == 'FAILED':
        log_err("Changeset creation failed!")
        log_bold(
            change_set.get('StatusReason', "Check AWS console for reason."))
        client.delete_change_set(ChangeSetName=create_change_set_res['Id'])
        exit(0)
    else:
        log_bold("Changeset created.. Following are the changes")
        _print_changes(change_set)
        if click.confirm('Do you want to execute the changeset?'):
            return change_set
        log_bold("Deleting changeset...")
        client.delete_change_set(ChangeSetName=create_change_set_res['Id'])
        log_bold("Done. Bye!")
        exit(0)
コード例 #17
0
    def run_job_for_all_services(self, job_name, target, kwargs):
        log_bold("{} concurrency: {}".format(job_name, DEPLOYMENT_CONCURRENCY))
        jobs = []
        service_info = self.service_info_fetcher.service_info
        for index, ecs_service_logical_name in enumerate(service_info):
            ecs_service_info = service_info[ecs_service_logical_name]
            log_bold(f"Queueing {job_name} of " +
                     ecs_service_info['ecs_service_name'])
            color = DEPLOYMENT_COLORS[index % 3]
            services_configuration = self.service_configuration['services']
            kwargs.update(
                dict(
                    ecs_service_name=ecs_service_info['ecs_service_name'],
                    secrets_name=ecs_service_info.get('secrets_name'),
                    ecs_service_logical_name=ecs_service_logical_name,
                    color=color,
                    service_configuration=services_configuration.get(
                        ecs_service_logical_name),
                    region=self.region,
                ))
            process = multiprocessing.Process(target=target, kwargs=kwargs)
            jobs.append(process)
        all_exit_codes = []
        for chunk_of_jobs in chunks(jobs, DEPLOYMENT_CONCURRENCY):
            for process in chunk_of_jobs:
                process.start()

            while True:
                sleep(1)
                exit_codes = [proc.exitcode for proc in chunk_of_jobs]
                if None not in exit_codes:
                    break

            for exit_code in exit_codes:
                all_exit_codes.append(exit_code)
        if any(all_exit_codes) != 0:
            raise UnrecoverableException(f"{job_name} failed")
コード例 #18
0
    def _ensure_image_in_ecr(self):
        if self.version == 'dirty':
            image = None
        else:
            image = self._find_image_in_ecr(self.version)
        if image:
            log_intent("Image found in ECR")
        else:
            log_bold(
                f"Image not found in ECR. Building image {self.name} {self.version}"
            )
            image_name = spinalcase(self.name) + ':' + self.version
            ecr_name = self.ecr_image_uri + ':' + self.version
            self._build_image(image_name)
            self._push_image(image_name, ecr_name)
            image = self._find_image_in_ecr(self.version)

        try:
            image_manifest = image['imageManifest']
            self.ecr_client.put_image(repositoryName=self.repo_name,
                                      imageTag=self.version,
                                      imageManifest=image_manifest)
        except Exception:
            pass
コード例 #19
0
    def _build_image(self):
        image_name = self.local_image_uri
        log_bold(
            f'Building docker image {image_name} using {"default Dockerfile" if self.dockerfile is None else self.dockerfile}'
        )
        command = self._build_command(image_name)
        env = os.environ
        if self._should_enable_buildkit():
            env['DOCKER_BUILDKIT'] = '1'
        try:
            subprocess.check_call(command, env=env, shell=True)
        except subprocess.CalledProcessError as e:
            message = 'docker build exited with status: {}'.format(
                e.returncode)
            if e.output:
                message += '\n'
                message += e.output

            if e.stderr:
                message += '\n'
                message += e.output

            raise UnrecoverableException(message)
        log_bold("Built " + image_name)
コード例 #20
0
 def log_ips(self):
     for service in self.ecs_service_names:
         task_arns = self.ecs_client.list_tasks(
             cluster=self.cluster_name, serviceName=service)['taskArns']
         tasks = self.ecs_client.describe_tasks(cluster=self.cluster_name,
                                                tasks=task_arns)['tasks']
         container_instance_arns = [
             task['containerInstanceArn'] for task in tasks
         ]
         container_instances = self.ecs_client.describe_container_instances(
             cluster=self.cluster_name,
             containerInstances=container_instance_arns
         )['containerInstances']
         ecs_instance_ids = [
             container['ec2InstanceId'] for container in container_instances
         ]
         ec2_reservations = self.ec2_client.describe_instances(
             InstanceIds=ecs_instance_ids)['Reservations']
         log_bold(service, )
         for reservation in ec2_reservations:
             instances = reservation['Instances']
             ips = [instance['PrivateIpAddress'] for instance in instances]
             [log_intent(ip) for ip in ips]
         log("")
コード例 #21
0
 def run(self):
     try:
         log("Check if stack already exists for " + self.cluster_name)
         environment_stack = self.client.describe_stacks(
             StackName=self.cluster_name
         )['Stacks'][0]
         log(self.cluster_name + " stack exists. ID: " +
             environment_stack['StackId'])
         log_err("Cannot create environment with duplicate name: " +
                 self.cluster_name)
     except Exception:
         log(self.cluster_name +
             " stack does not exist. Creating new stack.")
         # When creating a cluster, desired_instance count is same
         # as min_instance count
         environment_stack_template_body = ClusterTemplateGenerator(
             self.environment,
             self.configuration
         ).generate_cluster()
         self.existing_events = get_stack_events(
             self.client,
             self.cluster_name
         )
         options = prepare_stack_options_for_template(
             environment_stack_template_body, self.environment, self.cluster_name)
         environment_stack = self.client.create_stack(
             StackName=self.cluster_name,
             Parameters=self.__get_parameter_values(),
             OnFailure='DO_NOTHING',
             Capabilities=['CAPABILITY_NAMED_IAM'],
             **options,
         )
         log_bold("Submitted to cloudformation. Checking progress...")
         self.__print_progress()
         log_bold(self.cluster_name + " stack created. ID: " +
                  environment_stack['StackId'])
コード例 #22
0
ファイル: service_updater.py プロジェクト: techiev2/cloudlift
    def run(self):
        log_warning("Deploying to {self.region}".format(**locals()))
        self.init_stack_info()
        if not os.path.exists(self.env_sample_file):
            raise UnrecoverableException('env.sample not found. Exiting.')
        log_intent("name: " + self.name + " | environment: " +
                   self.environment + " | version: " + str(self.version))
        log_bold("Checking image in ECR")
        self.upload_artefacts()
        log_bold("Initiating deployment\n")
        ecs_client = EcsClient(None, None, self.region)

        jobs = []
        for index, service_name in enumerate(self.ecs_service_names):
            log_bold("Starting to deploy " + service_name)
            color = DEPLOYMENT_COLORS[index % 3]
            image_url = self.ecr_image_uri
            image_url += (':' + self.version)
            process = multiprocessing.Process(
                target=deployer.deploy_new_version,
                args=(
                    ecs_client,
                    self.cluster_name,
                    service_name,
                    self.version,
                    self.name,
                    self.env_sample_file,
                    self.environment,
                    color,
                    image_url
                )
            )
            jobs.append(process)
            process.start()

        exit_codes = []
        while True:
            sleep(1)
            exit_codes = [proc.exitcode for proc in jobs]
            if None not in exit_codes:
                break

        if any(exit_codes) != 0:
            raise UnrecoverableException("Deployment failed")
コード例 #23
0
 def update(self):
     log_warning(
         "Update task definition to {self.region}".format(**locals()))
     if not os.path.exists(self.env_sample_file):
         raise UnrecoverableException('env.sample not found. Exiting.')
     ecr_client = EcrClient(self.name, self.region, self.build_args)
     ecr_client.set_version(self.version)
     log_intent("name: " + self.name + " | environment: " +
                self.environment + " | version: " + str(ecr_client.version))
     log_bold("Checking image in ECR")
     ecr_client.build_and_upload_image()
     log_bold("Updating task definition\n")
     env_config = build_config(self.environment, self.name,
                               self.env_sample_file)
     ecs_client = EcsClient(region=self.region)
     deployment = DeployAction(ecs_client, self.cluster_name, None)
     task_defn = self._apply_changes_over_current_task_defn(
         env_config, ecs_client, ecr_client, deployment)
     deployment.update_task_definition(task_defn)
     log_bold("Task definition successfully updated\n")
コード例 #24
0
ファイル: service_updater.py プロジェクト: devat19/cloudlift
 def _build_image(self, image_name):
     log_bold("Building docker image " + image_name)
     subprocess.check_call(
         ["docker", "build", "-t", image_name, self.working_dir])
     log_bold("Built " + image_name)
コード例 #25
0
ファイル: service_updater.py プロジェクト: techiev2/cloudlift
 def _build_image(self, image_name):
     log_bold("Building docker image " + image_name)
     command = self._build_command(image_name)
     subprocess.check_call(command, shell=True)
     log_bold("Built " + image_name)
コード例 #26
0
 def _validate_changes(self, configuration):
     service_schema = {
         "title": "service",
         "type": "object",
         "properties": {
             "http_interface": {
                 "type":
                 "object",
                 "properties": {
                     "internal": {
                         "type": "boolean"
                     },
                     "restrict_access_to": {
                         "type": "array",
                         "items": {
                             "type": "string"
                         }
                     },
                     "container_port": {
                         "type": "number"
                     },
                     "health_check_path": {
                         "type": "string",
                         "pattern": "^\/.*$"
                     }
                 },
                 "required":
                 ["internal", "restrict_access_to", "container_port"]
             },
             "memory_reservation": {
                 "type": "number",
                 "minimum": 10,
                 "maximum": 30000
             },
             "fargate": {
                 "type": "object",
                 "properties": {
                     "cpu": {
                         "type": "number",
                         "minimum": 256,
                         "maximum": 4096
                     },
                     "memory": {
                         "type": "number",
                         "minimum": 512,
                         "maximum": 30720
                     }
                 }
             },
             "command": {
                 "oneOf": [{
                     "type": "string"
                 }, {
                     "type": "null"
                 }]
             }
         },
         "required": ["memory_reservation", "command"]
     }
     schema = {
         # "$schema": "http://json-schema.org/draft-04/schema#",
         "title": "configuration",
         "type": "object",
         "properties": {
             "notifications_arn": {
                 "type": "string"
             },
             "services": {
                 "type": "object",
                 "patternProperties": {
                     "^[a-zA-Z]+$": service_schema
                 }
             },
             "cloudlift_version": {
                 "type": "string"
             }
         },
         "required": ["cloudlift_version", "services"]
     }
     try:
         validate(configuration, schema)
     except ValidationError as validation_error:
         log_err(validation_error.message + " in " +
                 str(".".join(list(validation_error.relative_path))))
         exit(0)
     log_bold("Schema valid!")
コード例 #27
0
def deduce_name(name):
    if name is None:
        name = os.path.basename(os.getcwd())
        log_bold("Assuming the service name to be: " + name)
    return stringcase.spinalcase(name)
コード例 #28
0
 def validate(self):
     log_bold("Running post-save validation:")
     self._validate_changes(self.get_config(strip_cloudlift_version=False))
コード例 #29
0
    def _validate_changes(self, configuration):
        log_bold("\nValidating schema..")
        # TODO: add cidr etc validation
        schema = {
            # "$schema": "http://json-schema.org/draft-04/schema#",
            "title": "configuration",
            "type": "object",
            "properties": {
                self.environment: {
                    "type": "object",
                    "properties": {
                        "cluster": {
                            "type": "object",
                            "properties": {
                                "min_instances": {"type": "integer"},
                                "max_instances": {"type": "integer"},
                                "instance_type": {"type": "string"},
                                "key_name": {"type": "string"},
                            },
                            "required": [
                                "min_instances",
                                "max_instances",
                                "instance_type",
                                "key_name"
                            ]
                        },
                        "environment": {
                            "type": "object",
                            "properties": {
                                "notifications_arn": {"type": "string"},
                                "ssl_certificate_arn": {"type": "string"}
                            },
                            "required": [
                                "notifications_arn",
                                "ssl_certificate_arn"
                            ]
                        },
                        "region": {"type": "string"},
                        "vpc": {
                            "type": "object",
                            "properties": {
                                "cidr": {
                                    "type": "string"
                                },
                                "nat-gateway": {
                                    "type": "object",
                                    "properties": {
                                        "elastic-ip-allocation-id": {
                                            "type": "string"
                                        }
                                    },
                                    "required": [
                                        "elastic-ip-allocation-id"
                                    ]
                                },
                                "subnets": {
                                    "type": "object",
                                    "properties": {
                                        "private": {
                                            "type": "object",
                                            "properties": {
                                                "subnet-1": {
                                                    "type": "object",
                                                    "properties": {
                                                        "cidr": {
                                                            "type": "string"
                                                        }
                                                    },
                                                    "required": [
                                                        "cidr"
                                                    ]
                                                },
                                                "subnet-2": {
                                                    "type": "object",
                                                    "properties": {
                                                        "cidr": {
                                                            "type": "string"
                                                        }
                                                    },
                                                    "required": [
                                                        "cidr"
                                                    ]
                                                }
                                            },
                                            "required": [
                                                "subnet-1",
                                                "subnet-2"
                                            ]
                                        },
                                        "public": {
                                            "type": "object",
                                            "properties": {
                                                "subnet-1": {
                                                    "type": "object",
                                                    "properties": {
                                                        "cidr": {
                                                            "type": "string"
                                                        }
                                                    },
                                                    "required": [
                                                        "cidr"
                                                    ]
                                                },
                                                "subnet-2": {
                                                    "type": "object",
                                                    "properties": {
                                                        "cidr": {
                                                            "type": "string"
                                                        }
                                                    },
                                                    "required": [
                                                        "cidr"
                                                    ]
                                                }
                                            },
                                            "required": [
                                                "subnet-1",
                                                "subnet-2"
                                            ]
                                        }
                                    },
                                    "required": [
                                        "private",
                                        "public"
                                    ]
                                }
                            },
                            "required": [
                                "cidr",
                                "nat-gateway",
                                "subnets"
                            ]
                        }

                    },
                    "required": [
                        "cluster",
                        "environment",
                        "region",
                        "vpc"
                    ]
                }
            },
            "required": [self.environment]
        }
        try:
            validate(configuration, schema)
        except ValidationError as validation_error:
            error_path = str(".".join(list(validation_error.relative_path)))
            if error_path:
                raise UnrecoverableException(validation_error.message + " in " + error_path)
            else:
                raise UnrecoverableException(validation_error.message)
        log_bold("Schema valid!")
コード例 #30
0
 def _validate_changes(self, configuration):
     service_schema = {
         "title": "service",
         "type": "object",
         "properties": {
             "http_interface": {
                 "type": "object",
                 "properties": {
                     "internal": {
                         "type": "boolean"
                     },
                     "alb": {
                         "type": "object",
                         "properties": {
                             "create_new": {
                                 "type": "boolean",
                             },
                             "listener_arn": {
                                 "type": "string"
                             },
                             "target_5xx_error_threshold": {
                                 "type": "number"
                             },
                             "host": {
                                 "type": "string"
                             },
                             "path": {
                                 "type": "string"
                             },
                             "priority": {
                                 "type": "number"
                             }
                         },
                         "required": [
                             "create_new", "target_5xx_error_threshold"
                         ]
                     },
                     "restrict_access_to": {
                         "type": "array",
                         "items": {
                             "type": "string"
                         }
                     },
                     "container_port": {
                         "type": "number"
                     },
                     "health_check_path": {
                         "type": "string",
                         "pattern": "^\/.*$"
                     },
                     "health_check_healthy_threshold_count": {
                         "type": "number",
                         "minimum": 2,
                         "maximum": 10
                     },
                     "health_check_unhealthy_threshold_count": {
                         "type": "number",
                         "minimum": 2,
                         "maxium": 10
                     },
                     "health_check_interval_seconds": {
                         "type": "number",
                         "minimum": 5,
                         "maximum": 300
                     },
                     "health_check_timeout_seconds": {
                         "type": "number",
                         "minimum": 2,
                         "maximum": 120
                     },
                     "load_balancing_algorithm": {
                         "type": "string",
                         "enum": ["round_robin", "least_outstanding_requests"]
                     },
                     "deregistration_delay": {
                         "type": "number"
                     }
                 },
                 "required": [
                     "internal",
                     "restrict_access_to",
                     "container_port"
                 ]
             },
             "tcp_interface": {
                 "container_port": {
                     "type": "number"
                 },
                 "target_group_arn": {
                     "type": "string"
                 },
                 "target_security_group": {
                     "type": "string"
                 },
                 "required": ["container_port",
                              "target_group_arn",
                              "target_security_group"]
             },
             "memory_reservation": {
                 "type": "number",
                 "minimum": 10,
                 "maximum": 30000
             },
             "deployment": {
                 "type": "object",
                 "properties": {
                     "maximum_percent": {
                         "type": "number",
                         "minimum": 100,
                         "maximum": 200
                     },
                 },
                 "required": ["maximum_percent"]
             },
             "fargate": {
                 "type": "object",
                 "properties": {
                     "cpu": {
                         "type": "number",
                         "minimum": 256,
                         "maximum": 4096
                     },
                     "memory": {
                         "type": "number",
                         "minimum": 512,
                         "maximum": 30720
                     }
                 }
             },
             "command": {
                 "oneOf": [
                     {"type": "string"},
                     {"type": "null"}
                 ]
             },
             "task_role_arn": {
                 "oneOf": [
                     {"type": "string"},
                     {"type": "null"}
                 ]
             },
             "task_execution_role_arn": {
                 "oneOf": [
                     {"type": "string"},
                     {"type": "null"}
                 ]
             },
             "stop_timeout": {
                 "type": "number"
             },
             "container_health_check": {
                 "type": "object",
                 "properties": {
                     "command": {
                         "type": "string"
                     },
                     "start_period": {
                         "type": "number"
                     },
                     "retries": {
                         "type": "number",
                         "minimum": 1,
                         "maximum": 10
                     },
                     "interval": {
                         "type": "number",
                         "minimum": 5,
                         "maximum": 300
                     },
                     "timeout": {
                         "type": "number",
                         "minimum": 2,
                         "maximum": 60
                     },
                 },
                 "required": ["command"]
             },
             "placement_constraints": {
                 "type": "array",
                 "items": {
                     "required": ["type"],
                     "type": "object",
                     "properties": {
                         "type": {
                             "type": "string",
                             "enum": ["memberOf", "distinctInstance"],
                         },
                         "expression": {
                             "type": "string"
                         }
                     }
                 },
             },
             "sidecars": {
                 "type": "array",
                 "items": {
                     "type": "object",
                     "properties": {
                         "name": {
                             "type": "string"
                         },
                         "image": {
                             "type": "string"
                         },
                         "command": {
                             "type": "array",
                             "items": {
                                 "type": "string"
                             }
                         },
                         "memory_reservation": {
                             "type": "number"
                         }
                     },
                     "required": ["name", "image", "memory_reservation"]
                 }
             },
             "system_controls": {
                 "type": "array",
                 "items": {
                     "type": "object",
                     "properties": {
                         "namespace": {
                             "type": "string"
                         },
                         "value": {
                             "type": "string"
                         }
                     }
                 },
             },
             "log_group": {
                 "type": "string",
             },
             "secrets_name": {
                 "type": "string",
             },
             "task_role_attached_managed_policy_arns": {
                 "type": "array",
                 "items": {
                     "type": "string"
                 }
             },
             "autoscaling": {
                 "type": "object",
                 "properties": {
                     "max_capacity": {
                         "type": "number"
                     },
                     "min_capacity": {
                         "type": "number"
                     },
                     "request_count_per_target": {
                         "type": "object",
                         "properties": {
                             "alb_arn": {
                                 "type": "string"
                             },
                             "target_value": {
                                 "type": "number"
                             },
                             "scale_in_cool_down_seconds": {
                                 "type": "number"
                             },
                             "scale_out_cool_down_seconds": {
                                 "type": "number"
                             }
                         },
                         "required": ['target_value', 'scale_in_cool_down_seconds', 'scale_out_cool_down_seconds']
                     },
                 },
                 "required": ['max_capacity', 'min_capacity', 'request_count_per_target']
             },
             "container_labels": {
                 "type": "object",
                 "patternProperties": {
                     ".*": {"type": "string"}
                 },
                 "additionalProperties": False
             },
         },
         "required": ["memory_reservation", "command"]
     }
     schema = {
         # "$schema": "http://json-schema.org/draft-04/schema#",
         "title": "configuration",
         "type": "object",
         "properties": {
             "notifications_arn": {
                 "type": "string"
             },
             "service_role_arn": {
                 "oneOf": [
                     {"type": "string"},
                     {"type": "null"}
                 ]
             },
             "services": {
                 "type": "object",
                 "patternProperties": {
                     "^[a-zA-Z]+$": service_schema
                 }
             },
             "ecr_repo": {
                 "type": "object",
                 "properties": {
                     "account_id": {"type": "string"},
                     "assume_role_arn": {"type": "string"},
                     "name": {"type": "string"},
                 },
                 "required": ["name"]
             },
             "cloudlift_version": {
                 "type": "string"
             }
         },
         "required": ["cloudlift_version", "services", "ecr_repo"]
     }
     try:
         validate(configuration, schema)
     except ValidationError as validation_error:
         errors = [str(i) for i in validation_error.relative_path]
         raise UnrecoverableException(validation_error.message + " in " +
                                      str(".".join(list(errors))))
     log_bold("Schema valid!")