def test_purge(lxd_client_cls):
    # Application context
    app = get_test_application()

    job = {
        "_id": "test_job_id",
        "app_id": "test_app_id",
        "command": "buildimage",
        "instance_type": "test_instance_type",
        "options": [False]  # Do not skip bootstrap
    }

    test_config = get_test_config(ami_retention=3)

    # Mocks
    lxd_client = mock.MagicMock()
    lxd_client_cls.return_value = lxd_client
    lxd_image = mock.MagicMock()
    lxd_client.images.all.return_value = [lxd_image for i in range(0, 6)]

    # Purge images
    with mock.patch('ghost_tools.config', new=test_config):
        image_builder = LXDImageBuilder(app, job, None, LOG_FILE, test_config)
        image_builder.purge_old_images()

    # Test
    assert lxd_image.delete.call_count == 3
Exemple #2
0
def test_build_image_custom_envvars(packer_run_packer_cmd,
                                    provisioner_get_local_repo_path):
    # Application context
    app = get_test_application(env_vars=[{
        "var_value": u"hello world !",
        "var_key": "TESTVAR"
    }, {
        "var_value": u"ên français avec des accents! héhé Ã@¤.",
        "var_key": "C_USTom2"
    }])
    job = {
        "_id": "test_job_id",
        "app_id": "test_app_id",
        "command": "buildimage",
        "instance_type": "test_instance_type",
        "options": [False]  # Do not skip bootstrap
    }
    test_config = get_test_config()

    # Mocks
    packer_run_packer_cmd.return_value = (0, "something:test_ami_id")

    tmp_dir = tempfile.mkdtemp()
    provisioner_get_local_repo_path.return_value = tmp_dir

    # Build image
    with mock.patch('ghost_tools.config', new=test_config):
        image_builder = AWSImageBuilder(app, job, None, LOG_FILE, test_config)
        ami_id, ami_name = image_builder.start_builder()

    # Test
    assert ami_id == "test_ami_id"
    assert ami_name.startswith("ami.test.eu-west-1.webfront.test-app.")

    with open(os.path.join(PACKER_JSON_PATH, job['_id'], 'aws_builder.json'),
              'r') as f:
        # Verify packer config
        packer_config = json.load(f)
        packer_config_reference = {
            "provisioners": [{
                "type":
                "shell",
                "environment_vars": [
                    u"C_USTom2=\u00ean fran\u00e7ais avec des accents! h\u00e9h\u00e9 \u00c3@\u00a4.",
                    "GHOST_APP=test-app", "GHOST_ENV=test", "GHOST_ENV_COLOR=",
                    "GHOST_ROLE=webfront", "TESTVAR=hello world !"
                ],
                "script":
                "/ghost/test-app/test/webfront/hook-pre_buildimage"
            }, {
                "skip_bootstrap":
                False,
                "log_level":
                "info",
                "local_state_tree":
                os.path.join(tmp_dir, 'salt'),
                "local_pillar_roots":
                os.path.join(tmp_dir, 'pillar'),
                "type":
                "salt-masterless"
            }, {
                "type":
                "shell",
                "environment_vars": [
                    u"C_USTom2=\u00ean fran\u00e7ais avec des accents! h\u00e9h\u00e9 \u00c3@\u00a4.",
                    "GHOST_APP=test-app", "GHOST_ENV=test", "GHOST_ENV_COLOR=",
                    "GHOST_ROLE=webfront", "TESTVAR=hello world !"
                ],
                "script":
                "/ghost/test-app/test/webfront/hook-post_buildimage"
            }, {
                "inline": [
                    "sudo rm -rf /srv/salt || echo 'Salt - no cleanup salt'",
                    "sudo rm -rf /srv/pillar || echo 'Salt - no cleanup pillar'"
                ],
                "type":
                "shell"
            }],
            "builders": [{
                "ami_block_device_mappings": [],
                "launch_block_device_mappings": [],
                "source_ami": "ami-source",
                "tags": {
                    "Name": "ec2.name.test",
                    "tag-name": "tag-value",
                },
                "subnet_id": "subnet-test",
                "ssh_username": "******",
                "ssh_interface": "private_ip",
                "region": "eu-west-1",
                "security_group_ids": ["sg-test"],
                "ami_name": ami_name,
                "iam_instance_profile": "iam.profile.test",
                "instance_type": "test_instance_type",
                "associate_public_ip_address": True,
                "vpc_id": "vpc-test",
                "type": "amazon-ebs",
                "ssh_pty": True
            }]
        }
        assert packer_config == packer_config_reference
Exemple #3
0
def test_build_image_ansible(packer_run_packer_cmd, gcall,
                             provisioner_get_local_repo_path):
    # Application context
    app = get_test_application()
    job = {
        "_id": "test_job_id",
        "app_id": "test_app_id",
        "command": "buildimage",
        "instance_type": "test_instance_type",
        "options": [False]  # Do not skip bootstrap
    }

    test_config = get_test_config(
        features_provisioners={
            'ansible': {
                'git_revision':
                'master',
                'git_repo':
                'my_ansible_repo',
                'base_playbook_file':
                'tests/provisioners_data/base_playbook.yml',
                'base_playbook_requirements_file':
                'tests/provisioners_data/base_requirements.yml',
            }
        })
    del test_config['features_provisioners']['salt']

    # Mocks
    packer_run_packer_cmd.return_value = (0, "something:test_ami_id")

    tmp_dir = tempfile.mkdtemp()
    venv_dir = os.path.join(os.path.dirname(os.path.dirname(__file__)),
                            '.tox/py27/bin/')
    shutil.copyfile(
        os.path.join(os.path.dirname(__file__), 'provisioners_data',
                     'requirements.yml'),
        os.path.join(tmp_dir, 'requirements.yml'))
    provisioner_get_local_repo_path.return_value = tmp_dir

    # Build image
    with mock.patch('ghost_tools.config', new=test_config):
        image_builder = AWSImageBuilder(app, job, None, LOG_FILE, test_config)
        ami_id, ami_name = image_builder.start_builder()

    # Test
    assert ami_id == "test_ami_id"
    assert ami_name.startswith("ami.test.eu-west-1.webfront.test-app.")

    gcall.assert_called_once_with(
        "{0}ansible-galaxy install -r {1}/requirement_app.yml -p {1}/roles".
        format(venv_dir,
               tmp_dir), 'Ansible -  ansible-galaxy command', LOG_FILE)

    with open(os.path.join(PACKER_JSON_PATH, job['_id'], 'aws_builder.json'),
              'r') as f:
        # Verify generated ansible files
        with open(os.path.join(tmp_dir, 'requirement_app.yml'), 'r') as f2:
            requirement_app = yaml.load(f2)
            assert requirement_app == [{
                "src": "base-role-src",
                "version": "base-role-version"
            }, {
                "name": "feature-ansible",
                "scm": "test-scm",
                "src": "test-src",
                "version": "test-version"
            }]

        with open(os.path.join(tmp_dir, 'main.yml'), 'r') as f3:
            playbook = yaml.load(f3)
            assert playbook == [{
                "name": "Base playbook",
                "hosts": "all",
                "roles": ['ansible-base-role']
            }, {
                "name":
                "Ghost application features",
                "hosts":
                "all",
                "roles": [{
                    "role": "feature-ansible",
                    "feature-property": "property"
                }]
            }]

        # Verify packer config
        packer_config = json.load(f)
        packer_config_reference = {
            "provisioners": [{
                "type":
                "shell",
                "environment_vars": [
                    "EMPTY_ENV=",
                    "GHOST_APP=test-app",
                    "GHOST_ENV=test",
                    "GHOST_ENV_COLOR=",
                    "GHOST_ROLE=webfront",
                ],
                "script":
                "/ghost/test-app/test/webfront/hook-pre_buildimage"
            }, {
                "type":
                "ansible",
                "playbook_file":
                os.path.join(tmp_dir, "main.yml"),
                "ansible_env_vars": [
                    "ANSIBLE_HOST_KEY_CHECKING=False", "ANSIBLE_FORCE_COLOR=1",
                    "PYTHONUNBUFFERED=1",
                    "ANSIBLE_ROLES_PATH={}".format(tmp_dir)
                ],
                "user":
                "******",
                "command":
                os.path.join(venv_dir, "ansible-playbook"),
                "extra_arguments": ['-v'],
            }, {
                "type":
                "shell",
                "environment_vars": [
                    "EMPTY_ENV=",
                    "GHOST_APP=test-app",
                    "GHOST_ENV=test",
                    "GHOST_ENV_COLOR=",
                    "GHOST_ROLE=webfront",
                ],
                "script":
                "/ghost/test-app/test/webfront/hook-post_buildimage"
            }],
            "builders": [{
                "ami_block_device_mappings": [],
                "launch_block_device_mappings": [],
                "source_ami": "ami-source",
                "tags": {
                    "Name": "ec2.name.test",
                    "tag-name": "tag-value",
                },
                "subnet_id": "subnet-test",
                "ssh_username": "******",
                "ssh_interface": "private_ip",
                "region": "eu-west-1",
                "security_group_ids": ["sg-test"],
                "ami_name": ami_name,
                "iam_instance_profile": "iam.profile.test",
                "instance_type": "test_instance_type",
                "associate_public_ip_address": True,
                "vpc_id": "vpc-test",
                "type": "amazon-ebs",
                "ssh_pty": True
            }]
        }
        assert packer_config == packer_config_reference
def test_build_image(lxd_client_cls):
    # Application context
    app = get_test_application()
    job = {
        "_id": "test_job_id",
        "app_id": "test_app_id",
        "command": "buildimage",
        "instance_type": "test_instance_type",
        "options": [False]  # Do not skip bootstrap
    }

    test_config = get_test_config(
        features_provisioners={
            'ansible': {
                'git_revision':
                'master',
                'git_repo':
                'my_ansible_repo',
                'base_playbook_file':
                'tests/provisioners_data/base_playbook.yml',
                'base_playbook_requirements_file':
                'tests/provisioners_data/base_requirements.yml',
            }
        })

    # Mocks
    lxd_client = mock.MagicMock()
    lxd_client_cls.return_value = lxd_client
    lxd_containers_mock = lxd_client.containers
    lxd_profiles_mock = lxd_client.profiles

    lxd_container_mock = mock.MagicMock()
    lxd_client.containers.create.return_value = lxd_container_mock
    valid_execution_mock = mock.MagicMock()
    valid_execution_mock.exit_code = 0
    valid_execution_mock.stdout = 'STDOUT'
    valid_execution_mock.stderr = 'STDERR'
    lxd_container_mock.execute.return_value = valid_execution_mock

    lxd_client.images.all.return_value = []

    venv_dir = os.path.join(os.path.dirname(os.path.dirname(__file__)),
                            '.tox/py27')
    venv_bin_dir = os.path.join(venv_dir, 'bin')

    # Build image
    with mock.patch('ghost_tools.config', new=test_config):
        image_builder = LXDImageBuilder(app, job, None, LOG_FILE, test_config)
        image_builder.set_source_hooks('/source-hook-path')
        image_builder.start_builder()

    # Test
    container_name = image_builder._container_name
    assert container_name.startswith("ami-test-eu-west-1-webfront-test-app-")
    expected_container_config = {
        'source': {
            "type": "image",
            "protocol": "lxd",
            "mode": "pull",
            "fingerprint": "lxd-container-image-test",
            "server": "http://lxd-image-endpoint:1234",
        },
        'config': {
            "security.privileged": 'True'
        },
        'ephemeral': False,
        'name': container_name,
        'profiles': ["default", container_name],
    }

    lxd_containers_mock.create.assert_called_once_with(
        expected_container_config, wait=True)

    lxd_container_mock.start.assert_called_once_with(wait=True)
    lxd_container_mock.stop.assert_called_once_with(wait=True)

    lxd_container_mock.execute.assert_any_call(
        ["sh", "/ghost/hook-pre_buildimage"],
        image_builder._get_ghost_env_vars())
    lxd_container_mock.execute.assert_any_call(
        ["sh", "/ghost/hook-post_buildimage"],
        image_builder._get_ghost_env_vars())

    lxd_container_mock.execute.assert_any_call([
        os.path.join(venv_bin_dir, "ansible-playbook"), "-i", "localhost,",
        "--connection=local", "/srv/ansible/main.yml", "-v"
    ])
    lxd_container_mock.execute.assert_any_call([
        "salt-call", "state.highstate", "--file-root=/srv/salt/salt",
        "--pillar-root=/srv/salt/pillar", "--local", "-l", "info"
    ])

    expected_devices_config = {
        'venv': {
            'path': venv_dir,
            'source': venv_dir,
            'type': 'disk',
        },
        'salt': {
            'path': '/srv/salt',
            'source': "/tmp/ghost-features-provisioner/salt-test_job_id",
            'type': 'disk'
        },
        'ansible': {
            'path': '/srv/ansible',
            'source': "/tmp/ghost-features-provisioner/ansible-test_job_id",
            'type': 'disk'
        },
        'hooks': {
            'path': "/ghost",
            'source': "/source-hook-path",
            'type': 'disk'
        },
    }
    lxd_profiles_mock.create.assert_called_once_with(
        container_name, devices=expected_devices_config)
    lxd_container_mock.publish.assert_called_once_with(wait=True)