Example #1
0
    def _serialize_(self, commands, tmp_dir, app_name, docker_links, config):
        if not self.has_value():
            return

        for port in config.ports:
            if port.container_port != port.host_port:
                raise DMakeException(
                    "AWS Elastic Beanstalk only supports ports binding which are the same in the container and the host."
                )
        ports = [{
            "ContainerPort": ports.container_port
        } for ports in config.ports]
        volumes = [
            {
                "HostDirectory": volume.host_volume,
                "ContainerDirectory": volume.container_volume
            } for volume in config.volumes if volume.host_volume !=
            "/var/log/deepomatic"  # Cannot specify a volume both in logging and mounting
        ]

        if config.pre_deploy_script  != "" or \
           config.mid_deploy_script  != "" or \
           config.post_deploy_script != "":
            raise DMakeException(
                "Pre/Mid/Post-Deploy scripts for AWS is not supported yet.")
        if len(config.docker_opts) > 0:
            raise DMakeException(
                "Docker options for AWS is not supported yet.")

        # Generate Dockerrun.aws.json
        data = {
            "AWSEBDockerrunVersion": "1",
            # "Authentication": {
            #     "Bucket": "my-bucket",
            #     "Key": "mydockercfg"
            # },
            # "Image": {
            #     "Name": "quay.io/johndoe/private-image",
            #     "Update": "true"
            # },
            "Ports": ports,
            "Volumes": volumes,
            "Logging": "/var/log/deepomatic"
        }
        with open(os.path.join(tmp_dir, "Dockerrun.aws.json"),
                  'w') as dockerrun:
            json.dump(data, dockerrun)

        common.run_shell_command(
            'deepomake_replace_vars %s %s' %
            (self.options, os.path.join(tmp_dir, 'options.txt')))

        append_command(commands,
                       'sh',
                       shell='deepomake_deploy_aws_eb "%s" "%s" "%s" "%s"' %
                       (tmp_dir, app_name, self.region, self.stack))
Example #2
0
 def _generate_env_file_(self):
     if self.env_file is None:
         tmp_dir = common.run_shell_command('deepomake_make_tmp_dir')
         env_file = os.path.join(tmp_dir, 'env.txt')
         with open(env_file, 'w') as f:
             for key, value in self.env.items():
                 value = common.eval_str_in_env(value)
                 f.write('%s=%s\n' % (key, value))
         self.env_file = env_file
     return self.env_file
Example #3
0
    def _serialize_(self, commands, tmp_dir, app_name, docker_links, config):
        if not self.has_value():
            return

        opts = config.full_docker_opts(False)

        launch_links = ""
        for link in docker_links:
            launch_links += 'if [ \\`docker ps -f name=%s | wc -l\\` = "1" ]; then set +e; docker rm -f %s 2> /dev/null ; set -e; docker run -d --name %s %s -i %s; fi\n' % (
                link.link_name, link.link_name, link.link_name,
                link.deployed_options, link.image_name)
            opts += " --link %s" % link.link_name

        common.run_shell_command(
            'export APP_NAME="%s" && export DOCKER_OPTS="%s" && export LAUNCH_LINK="%s" && export PRE_DEPLOY_HOOKS="%s" && export MID_DEPLOY_HOOKS="%s" && export POST_DEPLOY_HOOKS="%s" && deepomake_copy_template deploy/deploy_ssh/start_app.sh %s && deepomake_copy_template deploy/deploy_ssh/start_cmd.sh %s'
            %
            (app_name + "-%s" % common.branch.lower(), opts, launch_links,
             config.pre_deploy_script, config.mid_deploy_script,
             config.post_deploy_script, os.path.join(tmp_dir, "start_app.sh"),
             os.path.join(tmp_dir, "start_cmd.sh")))

        cmd = 'deepomake_deploy_ssh "%s" "%s" "%s" "%s"' % (
            tmp_dir, self.user, self.host, self.port)
        append_command(commands, 'sh', shell=cmd)
Example #4
0
    def generate_build(self, commands, path_dir, app_name, service_name,
                       docker_base, env, config):
        tmp_dir = common.run_shell_command('deepomake_make_tmp_dir')

        dockerfile_template = os.path.join(tmp_dir, 'Dockerfile_template')
        with open(dockerfile_template, 'w') as f:
            f.write('FROM %s\n' % docker_base)
            f.write('\n')
            f.write('${ENV_VARS}\n')
            f.write('\n')
            f.write('WORKDIR %s\n' % self.workdir)
            f.write('\n')
            for port in config.ports:
                f.write('EXPOSE %s\n' % port.container_port)
            f.write('\n')
            for target in self.install_targets:
                f.write(target.docker_cmd(commands, path_dir, tmp_dir) + "\n")
            f.write('\n')
            if self.install_script is not None:
                generate_copy_command(commands, path_dir, tmp_dir,
                                      self.install_script)
                f.write('COPY %s %s\n' %
                        (self.install_script,
                         os.path.join(self.workdir, self.install_script)))
                f.write('RUN cd %s && ./%s\n' %
                        (self.workdir, self.install_script))
            f.write('\n')
            generate_copy_command(commands, path_dir, tmp_dir,
                                  self.start_script)
            f.write('COPY %s %s\n' %
                    (self.start_script,
                     os.path.join(self.workdir, self.start_script)))
            if self.entrypoint is not None:
                f.write('ENTRYPOINT ["./%s"]\n' % self.entrypoint)
            f.write('CMD ["./%s"]\n' % self.start_script)

        generate_dockerfile(commands, tmp_dir, env)

        image_name = self.get_image_name(app_name, service_name)
        append_command(commands,
                       'sh',
                       shell='deepomake_build_docker "%s" "%s"' %
                       (tmp_dir, image_name))
        return tmp_dir
Example #5
0
def look_for_changed_directories():
    if common.target is None:
        return None
    common.logger.info("Looking for changes between HEAD and %s" %
                       common.target)
    try:
        output = common.run_shell_command(
            "git diff --name-only origin/%s...HEAD" % common.target)
    except common.ShellError as e:
        common.logger.error("Error: " + str(e))
        return None

    #common.logger.info("Changed files:")
    #common.logger.info(output)

    changed_dirs = set()
    for file in [file.strip() for file in output.split('\n')]:
        if len(file) == 0:
            continue
        d = os.path.dirname(file)
        if d in changed_dirs:
            continue

        # Only keep bottom changed directories
        do_add = True
        to_remove = []
        for directory in changed_dirs:
            if directory.startswith(d):  # sub directory of d
                do_add = False
            elif d.startswith(directory):
                to_remove = directory

        changed_dirs.difference_update(to_remove)
        if do_add:
            changed_dirs.add(d)
    return list(changed_dirs)
Example #6
0
def load_deepomake_files_list():
    build_files = filter(
        lambda f: len(f.strip()) > 0,
        common.run_shell_command("find . -name deepomake.yml").split("\n"))
    build_files = [file[2:] for file in build_files]
    return build_files
Example #7
0
def make(root_dir, sub_dir, dmake_command, app, options):
    if 'DMAKE_TMP_DIR' in os.environ:
        del os.environ['DMAKE_TMP_DIR']
    common.init(dmake_command, root_dir, options)

    if dmake_command == "stop":
        common.run_shell_command(
            "docker rm -f `docker ps -q -f name=%s.%s.%s`" %
            (app, common.branch, common.build_id))
        return

    if dmake_command == "deploy" and common.is_local:
        r = common.read_input(
            "Carefull ! Are you sure you want to do that ? [Y/n] ")
        if r.lower() != 'y' and r != "":
            print('Aborting')
            sys.exit(0)

    # Format args
    auto_complete = False
    auto_completed_app = None
    if app is not None:
        n = len(app.split('/'))
        if n > 2:
            raise DMakeException(
                'Cannot have more than one slash in the app name')
        auto_complete = n == 1
        if auto_complete:
            auto_completed_app = None
            auto_complete_is_app = None
        else:
            auto_completed_app = app

    # Load build files
    build_files = load_deepomake_files_list()
    if len(build_files) == 0:
        raise DMakeException('No deepomake.yml file found !')

    # Sort by increasing length to make sure we load parent files first
    sorted(build_files, key=lambda file: len(file))

    # Load all deepomake.yml files (except those blacklisted)
    blacklist = []
    loaded_files = {}
    service_providers = {}
    service_dependencies = {}
    for file in build_files:
        load_deepomake_file(loaded_files, blacklist, service_providers,
                            service_dependencies, file)

    # Remove black listed files
    for f in blacklist:
        if f in loaded_files:
            del loaded_files[f]

    # Register all apps and services in the repo
    docker_links = {}
    services = {}
    for file, deepomake in loaded_files.items():
        app_name = deepomake.get_app_name()
        if app_name not in docker_links:
            docker_links[app_name] = {}
        if app_name not in services:
            services[app_name] = {}

        if auto_complete and app_name == app:
            if auto_completed_app is None:
                auto_completed_app = app

        app_services = services[app_name]
        for service in deepomake.get_services():
            needs = [
                "%s/%s" % (app_name, sa) for sa in service.needed_services
            ]
            full_service_name = "%s/%s" % (app_name, service.service_name)
            if service.service_name in app_services:
                raise DMakeException("Duplicated sub-app name: '%s'" %
                                     full_service_name)
            add_service_provider(service_providers, full_service_name, file,
                                 needs)
            app_services[service.service_name] = service

            if auto_complete and service.service_name == app:
                if auto_completed_app is None:
                    auto_completed_app = full_service_name
                    auto_complete_is_app = False
                else:
                    raise DMakeException(
                        "Ambigous app name '%s' is matching sub-app '%s' and %sapp '%s'"
                        % (app, full_service_name, "" if auto_complete_is_app
                           else "sub-", auto_completed_app))

        app_links = docker_links[app_name]
        for link in deepomake.get_docker_links():
            if link.link_name in app_links:
                raise DMakeException(
                    "Duplicate link name '%s' for application '%s'. Link names must be unique inside each app."
                    % (link.link_name, app_name))
            app_links[link.link_name] = link

    if auto_complete and auto_completed_app is None:
        raise DMakeException(
            "Could not find any app or sub-app matching '%s'" % app)

    # Filter base images which are not provided
    for deps in service_dependencies.values():
        to_delete = []
        for i, dep in enumerate(deps):
            if dep[1] not in service_providers:
                to_delete.append(i)
        to_delete.reverse()
        for i in to_delete:
            del deps[i]

    is_app_only = auto_completed_app is None or auto_completed_app.find(
        '/') < 0
    if dmake_command == "run" and is_app_only:
        common.options.dependencies = True

    if auto_completed_app is None:
        # Find file where changes have happened
        find_active_files(loaded_files, service_providers,
                          service_dependencies, sub_dir, dmake_command)
    else:
        if is_app_only:  # app only
            if dmake_command == 'shell':
                raise DMakeException("Could not find sub-app '%s'" % app)
            active_file = set()
            app_services = services[auto_completed_app]
            for service in app_services.values():
                full_service_name = "%s/%s" % (auto_completed_app,
                                               service.service_name)
                file, _ = service_providers[full_service_name]
                active_file.add(file)
            for file in active_file:
                activate_file(loaded_files, service_providers,
                              service_dependencies, dmake_command, file)
        else:
            activate_service(loaded_files, service_providers,
                             service_dependencies, dmake_command,
                             auto_completed_app)

    # check services circularity
    sorted_leaves = check_no_circular_dependencies(service_dependencies)
    sorted_leaves = filter(lambda a_b__c: a_b__c[0][0] == dmake_command,
                           sorted_leaves)
    build_files_order = order_dependencies(service_dependencies, sorted_leaves)

    # Sort by order
    ordered_build_files = sorted(build_files_order.items(),
                                 key=lambda file_order: file_order[1])

    # Separate into base / build / tests / deploy
    n = len(ordered_build_files)
    base = list(
        filter(lambda a_b__c: a_b__c[0][0] in ['base'], ordered_build_files))
    build = list(
        filter(
            lambda a_b__c: a_b__c[0][0] in ['build_service', 'build_docker'],
            ordered_build_files))
    test = list(
        filter(
            lambda a_b__c: a_b__c[0][
                0] in ['build_tests', 'test', 'shell', 'run_link', 'run'],
            ordered_build_files))
    deploy = list(
        filter(lambda a_b__c: a_b__c[0][0] in ['deploy'], ordered_build_files))
    if len(base) + len(build) + len(test) + len(deploy) != len(
            ordered_build_files):
        raise Exception(
            'Something went wrong when reorganizing build steps. One of the commands is probably missing.'
        )

    ordered_build_files = [('Building Docker', base), ('Building App', build),
                           ('Running and Testing App', test)]
    if not common.is_pr:
        ordered_build_files.append(
            ('Deploying', list(deploy))
        )  # build_service needs to be before others otherwise it may mess-up with tests

    # Display commands
    common.logger.info("Okay, here is the plan:")
    for stage, commands in ordered_build_files:
        if len(commands) > 0:
            common.logger.info("## %s ##" % (stage))
        for (command, service), order in commands:
            # Sanity check
            sub_task_orders = [
                build_files_order[a]
                for a in service_dependencies[(command, service)]
            ]
            if any(map(lambda o: order <= o, sub_task_orders)):
                raise DMakeException('Bad ordering')
            common.logger.info("- %s @ %s" % (command, service))

    # Generate the list of command to run
    all_commands = []
    append_command(all_commands, 'env', var="REPO", value=common.repo)
    append_command(all_commands, 'env', var="COMMIT", value=common.commit_id)
    append_command(all_commands, 'env', var="BUILD", value=common.build_id)
    append_command(all_commands, 'env', var="BRANCH", value=common.branch)
    append_command(all_commands, 'env', var="ENV_TYPE", value=common.env_type)
    append_command(all_commands,
                   'env',
                   var="DMAKE_TMP_DIR",
                   value=common.tmp_dir)

    for stage, commands in ordered_build_files:
        if len(commands) > 0:
            append_command(all_commands,
                           'stage',
                           name=stage,
                           concurrency=1 if stage == "Deploying" else None)
        for (command, service), order in commands:
            append_command(all_commands,
                           'sh',
                           shell='echo "Running %s @ %s"' % (command, service))
            if command in ['build_tests', 'build_service']:
                deepomake = loaded_files[service]
            else:
                file, _ = service_providers[service]
                deepomake = loaded_files[file]
            app_name = deepomake.get_app_name()
            links = docker_links[app_name]
            app_services = services[app_name]

            #try:
            if True:  # HACK
                if command == "base":
                    deepomake.generate_base(all_commands)
                elif command == "shell":
                    deepomake.generate_shell(all_commands, service,
                                             app_services, links)
                elif command == "test":
                    deepomake.generate_test(all_commands, service,
                                            app_services, links)
                elif command == "run":
                    deepomake.generate_run(all_commands, service, links)
                elif command == "run_link":
                    deepomake.generate_run_link(all_commands, service, links)
                elif command == "build_tests":
                    deepomake.generate_build_tests(all_commands)
                elif command == "build_service":
                    deepomake.generate_build_services(all_commands)
                elif command == "build_docker":
                    deepomake.generate_build(all_commands, service)
                elif command == "deploy":
                    deepomake.generate_deploy(all_commands, service, links)
                else:
                    raise Exception("Unkown command '%s'" % command)
            # except DMakeException as e:
            #     print(('ERROR in file %s:\n' % file) + str(e))
            #     sys.exit(1)

    # Check stages do not appear twice (otherwise it may block Jenkins)
    stage_names = set()
    for cmd, kwargs in all_commands:
        if cmd == "stage":
            name = kwargs['name']
            if name in stage_names:
                raise DMakeException('Duplicate stage name: %s' % name)
            else:
                stage_names.add(name)

    # If not on Pull Request, tag the commit as deployed
    if dmake_command == "deploy" and not common.is_pr:
        append_command(all_commands,
                       'git_tag',
                       tag='deployed_version_%s' % common.branch)

    # Generate output
    if common.is_local:
        file_to_generate = os.path.join(common.tmp_dir, "DMakefile")
    else:
        file_to_generate = "DMakefile"
    generate_command(file_to_generate, all_commands)
    common.logger.info("Output has been written to %s" % file_to_generate)

    # If on local, run the commands
    if common.is_local:
        result = os.system('bash %s' % file_to_generate)
        if result != 0 or dmake_command != 'run':
            os.system('deepomake_clean %s' % common.tmp_dir)
Example #8
0
    def _serialize_(self, commands, path_dir):
        if self.base_image.has_value():
            # Make the temporary directory
            tmp_dir = common.run_shell_command('deepomake_make_tmp_dir')

            # Copy file and compute their md5
            files_to_copy = []
            for file in self.base_image.copy_files + self.base_image.install_scripts:
                files_to_copy.append(file)
            if self.base_image.python_requirements:
                files_to_copy.append(self.base_image.python_requirements)
            if self.base_image.python3_requirements:
                files_to_copy.append(self.base_image.python3_requirements)

            # Copy file and keep their md5
            md5s = {}
            for file in files_to_copy:
                md5s[file] = common.run_shell_command(
                    'deepomake_copy_file %s %s' % (os.path.join(
                        path_dir, file), os.path.join(tmp_dir, 'user', file)))

            # Set RUN command
            run_cmd = "cd user"
            for file in self.base_image.install_scripts:
                run_cmd += " && ./%s" % file

            # Install pip if needed
            if self.base_image.python_requirements:
                run_cmd += " && bash ../install_pip.sh && pip install --process-dependency-links -r " + self.base_image.python_requirements
            if self.base_image.python3_requirements:
                run_cmd += " && bash ../install_pip3.sh && pip3 install --process-dependency-links -r " + self.base_image.python3_requirements

            # Save the command in a bash file
            file = 'run_cmd.sh'
            with open(os.path.join(tmp_dir, file), 'w') as f:
                f.write(run_cmd)
            md5s[file] = common.run_shell_command('deepomake_md5 %s' %
                                                  os.path.join(tmp_dir, file))

            # HACK: copy key while #493 is not closed: https://github.com/docker/for-mac/issues/483
            if common.key_file is not None:
                common.run_shell_command(
                    'cp %s %s' %
                    (common.key_file, os.path.join(tmp_dir, 'key')))

            # Local environment for temmplates
            local_env = []
            local_env.append("export ROOT_IMAGE=%s" % self.root_image)
            local_env = ' && '.join(local_env)
            if len(local_env) > 0:
                local_env += ' && '

            # Copy templates
            for file in [
                    "make_base.sh", "config.logrotate", "load_credentials.sh",
                    "install_pip.sh", "install_pip3.sh"
            ]:
                md5s[file] = common.run_shell_command(
                    '%s deepomake_copy_template docker-base/%s %s' %
                    (local_env, file, os.path.join(tmp_dir, file)))

            # Output md5s for comparison
            with open(os.path.join(tmp_dir, 'md5s'), 'w') as f:
                for md5 in md5s.items():
                    f.write('%s %s\n' % md5)

            # Append Docker Base build command
            append_command(
                commands,
                'sh',
                shell='deepomake_build_base_docker "%s" "%s" "%s" "%s" "%s"' %
                (tmp_dir, self.root_image, self.base_image.name,
                 self._get_tag_(), self.base_image.version))