ansible/molecule

Environment variables in the `image` param to a platform are not properly interpolated while creating the instance container when `pre_build_image` is `false`

natefoo opened this issue · 1 comments

Issue Type

  • Bug report

Molecule and Ansible details

ansible [core 2.14.0]
  config file = None
  configured module search path = ['/home/nate/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /home/nate/.virtualenvs/moleculedev/lib/python3.10/site-packages/ansible
  ansible collection location = /home/nate/.ansible/collections:/usr/share/ansible/collections
  executable location = /home/nate/.virtualenvs/moleculedev/bin/ansible
  python version = 3.10.6 (main, Nov  2 2022, 18:53:38) [GCC 11.3.0] (/home/nate/.virtualenvs/moleculedev/bin/python)
  jinja version = 3.1.2
  libyaml = True
molecule 4.0.4.dev16 using python 3.10
    ansible:2.14.0
    delegated:4.0.4.dev16 from molecule
    docker:2.1.0 from molecule_docker requiring collections: community.docker>=3.0.2 ansible.posix>=1.4.0

Molecule installation method (one of):

  • pip (pip install -e '.[docker]')

Ansible installation method (one of):

  • pip

Desired Behavior

I'd like to use an environment variable in the image param to a platform (using the docker driver) for a Github action matrix to test on different images. Jeff Geerling does this in many roles, for example geerlingguy.postgresql workflow and molecule.yml.

I prefer to build the image at runtime (pre_build_image: false) rather than prebuilding as Jeff does.

Actual Behaviour

Environment variables in the image param to a platform are not properly interpolated while creating the instance container when pre_build_image is false.

molecule.yml:

dependency:
  name: galaxy
driver:
  name: docker
platforms:
  - name: molecule-test
    image: ${FROM_IMAGE:-centos:7}
    command: ""
    volumes:
      - /sys/fs/cgroup:/sys/fs/cgroup:rw
    privileged: true
    dockerfile: Dockerfile.j2
    pre_build_image: false
provisioner:
  name: ansible
  log: true
verifier:
  name: ansible

Dockerfile.j2:

FROM {{ item.image }}
CMD ["/usr/lib/systemd/systemd"]

molecule create:

$ ANSIBLE_STDOUT_CALLBACK=debug molecule --debug create
ANSIBLE_STDOUT_CALLBACK=debug molecule --debug create
DEBUG    Validating schema /home/nate/ansible/molecule-test/molecule/default/molecule.yml.
INFO     default scenario test matrix: dependency, create, prepare
INFO     Performing prerun with role_name_check=0...
INFO     Set ANSIBLE_LIBRARY=/home/nate/.cache/ansible-compat/435e6d/modules:/home/nate/.ansible/plugins/modules:/usr/share/ansible/plugins/modules
INFO     Set ANSIBLE_COLLECTIONS_PATH=/home/nate/.cache/ansible-compat/435e6d/collections:/home/nate/.ansible/collections:/usr/share/ansible/collections
INFO     Set ANSIBLE_ROLES_PATH=/home/nate/.cache/ansible-compat/435e6d/roles:/home/nate/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles
INFO     Running default > dependency
WARNING  Skipping, missing the requirements file.
WARNING  Skipping, missing the requirements file.
INFO     Running default > create
INFO     Sanity checks: 'docker'
DEBUG: ANSIBLE ENVIRONMENT:
ANSIBLE_COLLECTIONS_PATH: /home/nate/.cache/ansible-compat/435e6d/collections:/home/nate/.cache/molecule/molecule-test/default/collections:/home/nate/.ansible/collections:/usr/share/ansible/collections:/etc/ansible/collections
ANSIBLE_CONFIG: /home/nate/.cache/molecule/molecule-test/default/ansible.cfg
ANSIBLE_FILTER_PLUGINS: /home/nate/ansible/molecule/src/molecule/provisioner/ansible/plugins/filter:/home/nate/.cache/molecule/molecule-test/default/plugins/filter:/home/nate/ansible/molecule-test/plugins/filter:/home/nate/.ansible/plugins/filter:/usr/share/ansible/plugins/filter
ANSIBLE_FORCE_COLOR: '1'
ANSIBLE_LIBRARY: /home/nate/ansible/molecule/src/molecule/provisioner/ansible/plugins/modules:/home/nate/.cache/molecule/molecule-test/default/library:/home/nate/ansible/molecule-test/library:/home/nate/.ansible/plugins/modules:/usr/share/ansible/plugins/modules
ANSIBLE_ROLES_PATH: /home/nate/.cache/ansible-compat/435e6d/roles:/home/nate/.cache/molecule/molecule-test/default/roles:/home/nate/ansible:/home/nate/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles
ANSIBLE_STDOUT_CALLBACK: debug

DEBUG: MOLECULE ENVIRONMENT:
MOLECULE_DEBUG: 'True'
MOLECULE_DEPENDENCY_NAME: galaxy
MOLECULE_DRIVER_NAME: docker
MOLECULE_ENV_FILE: /home/nate/ansible/molecule-test/.env.yml
MOLECULE_EPHEMERAL_DIRECTORY: /home/nate/.cache/molecule/molecule-test/default
MOLECULE_FILE: /home/nate/.cache/molecule/molecule-test/default/molecule.yml
MOLECULE_INSTANCE_CONFIG: /home/nate/.cache/molecule/molecule-test/default/instance_config.yml
MOLECULE_INVENTORY_FILE: /home/nate/.cache/molecule/molecule-test/default/inventory/ansible_inventory.yml
MOLECULE_PROJECT_DIRECTORY: /home/nate/ansible/molecule-test
MOLECULE_PROVISIONER_NAME: ansible
MOLECULE_SCENARIO_DIRECTORY: /home/nate/ansible/molecule-test/molecule/default
MOLECULE_SCENARIO_NAME: default
MOLECULE_STATE_FILE: /home/nate/.cache/molecule/molecule-test/default/state.yml
MOLECULE_VERIFIER_NAME: ansible
MOLECULE_VERIFIER_TEST_DIRECTORY: /home/nate/ansible/molecule-test/molecule/default/tests

DEBUG: SHELL REPLAY:
ANSIBLE_COLLECTIONS_PATH=/home/nate/.cache/ansible-compat/435e6d/collections:/home/nate/.cache/molecule/molecule-test/default/collections:/home/nate/.ansible/collections:/usr/share/ansible/collections:/etc/ansible/collections ANSIBLE_CONFIG=/home/nate/.cache/molecule/molecule-test/default/ansible.cfg ANSIBLE_FILTER_PLUGINS=/home/nate/ansible/molecule/src/molecule/provisioner/ansible/plugins/filter:/home/nate/.cache/molecule/molecule-test/default/plugins/filter:/home/nate/ansible/molecule-test/plugins/filter:/home/nate/.ansible/plugins/filter:/usr/share/ansible/plugins/filter ANSIBLE_FORCE_COLOR=1 ANSIBLE_LIBRARY=/home/nate/ansible/molecule/src/molecule/provisioner/ansible/plugins/modules:/home/nate/.cache/molecule/molecule-test/default/library:/home/nate/ansible/molecule-test/library:/home/nate/.ansible/plugins/modules:/usr/share/ansible/plugins/modules ANSIBLE_ROLES_PATH=/home/nate/.cache/ansible-compat/435e6d/roles:/home/nate/.cache/molecule/molecule-test/default/roles:/home/nate/ansible:/home/nate/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles ANSIBLE_STDOUT_CALLBACK=debug MOLECULE_DEBUG=True MOLECULE_DEPENDENCY_NAME=galaxy MOLECULE_DRIVER_NAME=docker MOLECULE_ENV_FILE=/home/nate/ansible/molecule-test/.env.yml MOLECULE_EPHEMERAL_DIRECTORY=/home/nate/.cache/molecule/molecule-test/default MOLECULE_FILE=/home/nate/.cache/molecule/molecule-test/default/molecule.yml MOLECULE_INSTANCE_CONFIG=/home/nate/.cache/molecule/molecule-test/default/instance_config.yml MOLECULE_INVENTORY_FILE=/home/nate/.cache/molecule/molecule-test/default/inventory/ansible_inventory.yml MOLECULE_PROJECT_DIRECTORY=/home/nate/ansible/molecule-test MOLECULE_PROVISIONER_NAME=ansible MOLECULE_SCENARIO_DIRECTORY=/home/nate/ansible/molecule-test/molecule/default MOLECULE_SCENARIO_NAME=default MOLECULE_STATE_FILE=/home/nate/.cache/molecule/molecule-test/default/state.yml MOLECULE_VERIFIER_NAME=ansible MOLECULE_VERIFIER_TEST_DIRECTORY=/home/nate/ansible/molecule-test/molecule/default/tests


PLAY [Create] ******************************************************************

TASK [Set async_dir for HOME env] **********************************************
ok: [localhost]

TASK [Log into a Docker registry] **********************************************
skipping: [localhost] => (item=None)
skipping: [localhost]

TASK [Check presence of custom Dockerfiles] ************************************
ok: [localhost] => (item={'command': '', 'dockerfile': 'Dockerfile.j2', 'image': 'centos:7', 'name': 'molecule-test', 'pre_build_image': False, 'privileged': True, 'volumes': ['/sys/fs/cgroup:/sys/fs/cgroup:rw']})

TASK [Create Dockerfiles from image names] *************************************
--- before
+++ after: /home/nate/.ansible/tmp/ansible-local-688129puqlvf0v/tmpalentp1a/Dockerfile.j2
@@ -0,0 +1,2 @@
+FROM centos:7
+CMD ["/usr/lib/systemd/systemd"]

changed: [localhost] => (item={'command': '', 'dockerfile': 'Dockerfile.j2', 'image': 'centos:7', 'name': 'molecule-test', 'pre_build_image': False, 'privileged': True, 'volumes': ['/sys/fs/cgroup:/sys/fs/cgroup:rw']})

TASK [Synchronization the context] *********************************************
.d..t...... ./
>f+++++++++ Dockerfile.j2
>f.st...... molecule.yml
changed: [localhost] => (item={'command': '', 'dockerfile': 'Dockerfile.j2', 'image': 'centos:7', 'name': 'molecule-test', 'pre_build_image': False, 'privileged': True, 'volumes': ['/sys/fs/cgroup:/sys/fs/cgroup:rw']})

TASK [Discover local Docker images] ********************************************
ok: [localhost] => (item={'diff': [{'before': '', 'after_header': '/home/nate/.ansible/tmp/ansible-local-688129puqlvf0v/tmpalentp1a/Dockerfile.j2', 'after': 'FROM centos:7\nCMD ["/usr/lib/systemd/systemd"]\n'}], 'dest': '/home/nate/.cache/molecule/molecule-test/default/Dockerfile_centos_7', 'src': '/home/nate/.ansible/tmp/ansible-tmp-1668112848.056438-688180-166340275842266/source', 'md5sum': '742914a9cf3a9bf93f36ce7686525f2d', 'checksum': '024826b29182fd8e68f1577cac33f8c39710295c', 'changed': True, 'uid': 1000, 'gid': 1000, 'owner': 'nate', 'group': 'nate', 'mode': '0600', 'state': 'file', 'size': 47, 'invocation': {'module_args': {'src': '/home/nate/.ansible/tmp/ansible-tmp-1668112848.056438-688180-166340275842266/source', 'dest': '/home/nate/.cache/molecule/molecule-test/default/Dockerfile_centos_7', 'mode': '0600', 'follow': False, '_original_basename': 'Dockerfile.j2', 'checksum': '024826b29182fd8e68f1577cac33f8c39710295c', 'backup': False, 'force': True, 'unsafe_writes': False, 'content': None, 'validate': None, 'directory_mode': None, 'remote_src': None, 'local_follow': None, 'owner': None, 'group': None, 'seuser': None, 'serole': None, 'selevel': None, 'setype': None, 'attributes': None}}, 'failed': False, 'item': {'command': '', 'dockerfile': 'Dockerfile.j2', 'image': 'centos:7', 'name': 'molecule-test', 'pre_build_image': False, 'privileged': True, 'volumes': ['/sys/fs/cgroup:/sys/fs/cgroup:rw']}, 'ansible_loop_var': 'item', 'i': 0, 'ansible_index_var': 'i'})

TASK [Build an Ansible compatible image (new)] *********************************
changed: [localhost] => (item=molecule_local/centos:7)

TASK [Create docker network(s)] ************************************************
skipping: [localhost]

TASK [Determine the CMD directives] ********************************************
ok: [localhost] => (item={'name': 'molecule-test', 'image': '${FROM_IMAGE:-centos:7}', 'command': '', 'volumes': ['/sys/fs/cgroup:/sys/fs/cgroup:rw'], 'privileged': True, 'dockerfile': 'Dockerfile.j2', 'pre_build_image': False})

TASK [Create molecule instance(s)] *********************************************
changed: [localhost] => (item=molecule-test)

TASK [Wait for instance(s) creation to complete] *******************************
failed: [localhost] (item={'failed': 0, 'started': 1, 'finished': 0, 'ansible_job_id': '805836826211.688378', 'results_file': '/home/nate/.ansible_async/805836826211.688378', 'changed': True, 'item': {'name': 'molecule-test', 'image': '${FROM_IMAGE:-centos:7}', 'command': '', 'volumes': ['/sys/fs/cgroup:/sys/fs/cgroup:rw'], 'privileged': True, 'dockerfile': 'Dockerfile.j2', 'pre_build_image': False}, 'ansible_loop_var': 'item'}) => {
    "ansible_job_id": "805836826211.688378",
    "ansible_loop_var": "item",
    "attempts": 2,
    "changed": false,
    "finished": 1,
    "item": {
        "ansible_job_id": "805836826211.688378",
        "ansible_loop_var": "item",
        "changed": true,
        "failed": 0,
        "finished": 0,
        "item": {
            "command": "",
            "dockerfile": "Dockerfile.j2",
            "image": "${FROM_IMAGE:-centos:7}",
            "name": "molecule-test",
            "pre_build_image": false,
            "privileged": true,
            "volumes": [
                "/sys/fs/cgroup:/sys/fs/cgroup:rw"
            ]
        },
        "results_file": "/home/nate/.ansible_async/805836826211.688378",
        "started": 1
    },
    "results_file": "/home/nate/.ansible_async/805836826211.688378",
    "started": 1
}

MSG:

Error pulling image molecule_local/${FROM_IMAGE:-centos:7} - 400 Client Error for http+docker://localhost/v1.41/images/create?tag=7%7D&fromImage=molecule_local%2F%24%7BFROM_IMAGE: Bad Request ("invalid reference format: repository name must be lowercase")
FAILED - RETRYING: [localhost]: Wait for instance(s) creation to complete (300 retries left).

PLAY RECAP *********************************************************************
localhost                  : ok=8    changed=4    unreachable=0    failed=1    skipped=2    rescued=0    ignored=0

WARNING  Retrying execution failure 2 of: ansible-playbook --diff --inventory /home/nate/.cache/molecule/molecule-test/default/inventory --skip-tags molecule-notest,notest /home/nate/.virtualenvs/moleculedev/lib/python3.10/site-packages/molecule_docker/playbooks/create.yml
CRITICAL Ansible return code was 2, command was: ['ansible-playbook', '--diff', '--inventory', '/home/nate/.cache/molecule/molecule-test/default/inventory', '--skip-tags', 'molecule-notest,notest', '/home/nate/.virtualenvs/moleculedev/lib/python3.10/site-packages/molecule_docker/playbooks/create.yml']

Molecule successfully created the image:

$ docker images molecule_local/centos:7
REPOSITORY              TAG       IMAGE ID       CREATED              SIZE
molecule_local/centos   7         7863c9c5566b   About a minute ago   204MB

And if I update my platform to reference the local image and set pre_built_image to true:

platforms:
  - name: molecule-test
    image: molecule_local/${FROM_IMAGE:-centos:7}
    command: ""
    volumes:
      - /sys/fs/cgroup:/sys/fs/cgroup:rw
    privileged: true
    dockerfile: Dockerfile.j2
    pre_build_image: true

It properly interpolates the variable and creates the instance container:

$ molecule create
WARNING  The scenario config file ('/home/nate/ansible/molecule-test/molecule/default/molecule.yml') has been modified since the scenario was created. If recent changes are important, reset the scenario with 'molecule destroy' to clean up created items or 'molecule reset' to clear current configuration.
INFO     default scenario test matrix: dependency, create, prepare
INFO     Performing prerun with role_name_check=0...
INFO     Set ANSIBLE_LIBRARY=/home/nate/.cache/ansible-compat/435e6d/modules:/home/nate/.ansible/plugins/modules:/usr/share/ansible/plugins/modules
INFO     Set ANSIBLE_COLLECTIONS_PATH=/home/nate/.cache/ansible-compat/435e6d/collections:/home/nate/.ansible/collections:/usr/share/ansible/collections
INFO     Set ANSIBLE_ROLES_PATH=/home/nate/.cache/ansible-compat/435e6d/roles:/home/nate/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles
INFO     Running default > dependency
WARNING  Skipping, missing the requirements file.
WARNING  Skipping, missing the requirements file.
INFO     Running default > create
INFO     Sanity checks: 'docker'

PLAY [Create] ******************************************************************

TASK [Set async_dir for HOME env] **********************************************
ok: [localhost]

TASK [Log into a Docker registry] **********************************************
skipping: [localhost] => (item=None)
skipping: [localhost]

TASK [Check presence of custom Dockerfiles] ************************************
ok: [localhost] => (item={'command': '', 'dockerfile': 'Dockerfile.j2', 'image': 'molecule_local/centos:7', 'name': 'molecule-test', 'pre_build_image': True, 'privileged': True, 'volumes': ['/sys/fs/cgroup:/sys/fs/cgroup:rw']})

TASK [Create Dockerfiles from image names] *************************************
skipping: [localhost] => (item={'command': '', 'dockerfile': 'Dockerfile.j2', 'image': 'molecule_local/centos:7', 'name': 'molecule-test', 'pre_build_image': True, 'privileged': True, 'volumes': ['/sys/fs/cgroup:/sys/fs/cgroup:rw']})
skipping: [localhost]

TASK [Synchronization the context] *********************************************
skipping: [localhost] => (item={'command': '', 'dockerfile': 'Dockerfile.j2', 'image': 'molecule_local/centos:7', 'name': 'molecule-test', 'pre_build_image': True, 'privileged': True, 'volumes': ['/sys/fs/cgroup:/sys/fs/cgroup:rw']})
skipping: [localhost]

TASK [Discover local Docker images] ********************************************
ok: [localhost] => (item={'changed': False, 'skipped': True, 'skip_reason': 'Conditional result was False', 'item': {'command': '', 'dockerfile': 'Dockerfile.j2', 'image': 'molecule_local/centos:7', 'name': 'molecule-test', 'pre_build_image': True, 'privileged': True, 'volumes': ['/sys/fs/cgroup:/sys/fs/cgroup:rw']}, 'ansible_loop_var': 'item', 'i': 0, 'ansible_index_var': 'i'})

TASK [Build an Ansible compatible image (new)] *********************************
skipping: [localhost] => (item=molecule_local/molecule_local/centos:7)
skipping: [localhost]

TASK [Create docker network(s)] ************************************************
skipping: [localhost]

TASK [Determine the CMD directives] ********************************************
ok: [localhost] => (item={'command': '', 'dockerfile': 'Dockerfile.j2', 'image': 'molecule_local/centos:7', 'name': 'molecule-test', 'pre_build_image': True, 'privileged': True, 'volumes': ['/sys/fs/cgroup:/sys/fs/cgroup:rw']})

TASK [Create molecule instance(s)] *********************************************
changed: [localhost] => (item=molecule-test)

TASK [Wait for instance(s) creation to complete] *******************************
FAILED - RETRYING: [localhost]: Wait for instance(s) creation to complete (300 retries left).
changed: [localhost] => (item={'failed': 0, 'started': 1, 'finished': 0, 'ansible_job_id': '523555483669.688684', 'results_file': '/home/nate/.ansible_async/523555483669.688684', 'changed': True, 'item': {'command': '', 'dockerfile': 'Dockerfile.j2', 'image': 'molecule_local/centos:7', 'name': 'molecule-test', 'pre_build_image': True, 'privileged': True, 'volumes': ['/sys/fs/cgroup:/sys/fs/cgroup:rw']}, 'ansible_loop_var': 'item'})

PLAY RECAP *********************************************************************
localhost                  : ok=6    changed=2    unreachable=0    failed=0    skipped=5    rescued=0    ignored=0

INFO     Running default > prepare
WARNING  Skipping, prepare playbook not configured.

Nevermind, I see as per ansible-community/molecule-plugins#22 that this bug is in molecule-docker and is already fixed (although as of now, not yet released).