ansible / ansible-container

DEPRECATED -- Ansible Container was a tool to build Docker images and orchestrate containers using only Ansible playbooks.
GNU Lesser General Public License v3.0
2.19k stars 392 forks source link

Conductor roles path #793

Closed luther7 closed 6 years ago

luther7 commented 7 years ago
ISSUE TYPE
container.yml
version: "2"

settings:
  conductor:
    base: alpine:3.5
    roles_path: roles
  deployment_output_path: ./ansible_deployment
  vars_files:
    - vars.yml

services:
  nginx:
    from: nginx:1.13.6-alpine
    roles:
      - nginx_reverse_proxy
    command: [nginx, -g, daemon off;]
    ports:
      - "8080:80"
    links:
      - php-fpm

  php-fpm:
    from: php:7.0-fpm-alpine
    roles:
      - install_packages
      - install_php_extensions
      - purge_package_cache
      - postfix
    command: [php-fpm]
    volumes:
      - api:/srv
    dev_overrides:
      volumes:
        - /home/franz/workspace/php/test:/srv

volumes:
  api:
    docker:
      driver: local
OS / ENVIRONMENT
Ansible Container, version 0.9.2
Linux, frosty-fruit, 4.13.0-16-generic, #19-Ubuntu SMP Wed Oct 11 18:35:14 UTC 2017, x86_64
2.7.14 (default, Sep 23 2017, 22:06:14) 
[GCC 7.2.0] /usr/bin/python
{
  "ContainersPaused": 0, 
  "Labels": null, 
  "CgroupDriver": "cgroupfs", 
  "ContainersRunning": 0, 
  "ContainerdCommit": {
    "Expected": "aa8187dbd3b7ad67d8e5e3a15115d3eef43a7ed1", 
    "ID": "aa8187dbd3b7ad67d8e5e3a15115d3eef43a7ed1"
  }, 
  "InitBinary": "docker-init", 
  "NGoroutines": 48, 
  "Swarm": {
    "Managers": 0, 
    "ControlAvailable": false, 
    "NodeID": "", 
    "Cluster": {
      "Spec": {
        "TaskDefaults": {}, 
        "Orchestration": {}, 
        "EncryptionConfig": {
          "AutoLockManagers": false
        }, 
        "Raft": {
          "HeartbeatTick": 0, 
          "ElectionTick": 0
        }, 
        "CAConfig": {}, 
        "Dispatcher": {}
      }, 
      "Version": {}, 
      "ID": "", 
      "CreatedAt": "0001-01-01T00:00:00Z", 
      "UpdatedAt": "0001-01-01T00:00:00Z"
    }, 
    "Nodes": 0, 
    "Error": "", 
    "RemoteManagers": null, 
    "LocalNodeState": "inactive", 
    "NodeAddr": ""
  }, 
  "LoggingDriver": "json-file", 
  "OSType": "linux", 
  "HttpProxy": "", 
  "Runtimes": {
    "runc": {
      "path": "docker-runc"
    }
  }, 
  "DriverStatus": [
    [
      "Root Dir", 
      "/var/lib/docker/aufs"
    ], 
    [
      "Backing Filesystem", 
      "extfs"
    ], 
    [
      "Dirs", 
      "32"
    ], 
    [
      "Dirperm1 Supported", 
      "true"
    ]
  ], 
  "OperatingSystem": "Ubuntu 17.10", 
  "Containers": 1, 
  "HttpsProxy": "", 
  "BridgeNfIp6tables": true, 
  "MemTotal": 12325679104, 
  "SecurityOptions": [
    "name=apparmor", 
    "name=seccomp,profile=default"
  ], 
  "Driver": "aufs", 
  "IndexServerAddress": "https://index.docker.io/v1/", 
  "ClusterStore": "", 
  "InitCommit": {
    "Expected": "949e6facb77383876aeff8a6944dde66b3089574", 
    "ID": "N/A"
  }, 
  "Isolation": "", 
  "SystemStatus": null, 
  "OomKillDisable": true, 
  "ClusterAdvertise": "", 
  "SystemTime": "2017-11-16T16:03:00.298161774+11:00", 
  "Name": "frosty-fruit", 
  "CPUSet": true, 
  "RegistryConfig": {
    "InsecureRegistryCIDRs": [
      "127.0.0.0/8"
    ], 
    "IndexConfigs": {
      "docker.io": {
        "Official": true, 
        "Name": "docker.io", 
        "Secure": true, 
        "Mirrors": null
      }
    }, 
    "Mirrors": []
  }, 
  "DefaultRuntime": "runc", 
  "ContainersStopped": 1, 
  "NCPU": 4, 
  "NFd": 19, 
  "Architecture": "x86_64", 
  "KernelMemory": true, 
  "CpuCfsQuota": true, 
  "Debug": false, 
  "ID": "IUK5:C2ED:UVEU:VGXL:HYA3:QGYQ:CUX6:TNZ3:7DVF:U7PE:LWAG:AFWQ", 
  "IPv4Forwarding": true, 
  "KernelVersion": "4.13.0-16-generic", 
  "BridgeNfIptables": true, 
  "NoProxy": "", 
  "LiveRestoreEnabled": false, 
  "ServerVersion": "1.13.1", 
  "CpuCfsPeriod": true, 
  "ExperimentalBuild": false, 
  "MemoryLimit": true, 
  "SwapLimit": false, 
  "Plugins": {
    "Volume": [
      "local"
    ], 
    "Network": [
      "bridge", 
      "host", 
      "macvlan", 
      "null", 
      "overlay"
    ], 
    "Authorization": null
  }, 
  "Images": 16, 
  "DockerRootDir": "/var/lib/docker", 
  "NEventsListener": 0, 
  "CPUShares": true, 
  "RuncCommit": {
    "Expected": "9df8b306d01f59d3a8029be411de015b7304dd8f", 
    "ID": "9df8b306d01f59d3a8029be411de015b7304dd8f"
  }
}
{
  "KernelVersion": "4.13.0-16-generic", 
  "Arch": "amd64", 
  "BuildTime": "2017-10-12T22:34:44.081141927+00:00", 
  "ApiVersion": "1.26", 
  "Version": "1.13.1", 
  "MinAPIVersion": "1.12", 
  "GitCommit": "092cba3", 
  "Os": "linux", 
  "GoVersion": "go1.8.3"
}
SUMMARY

Conductor roles_path option does not work.

STEPS TO REPRODUCE

Set the roles_path option and attempt to build.

EXPECTED RESULTS

Roles to be found at roles_path

ACTUAL RESULTS
➜  api git:(master) ✗ ansible-container build        
Building Docker Engine context...   
Starting Docker build of Ansible Container Conductor image (please be patient)...   
Parsing conductor CLI args.
Traceback (most recent call last):
  File "/usr/bin/conductor", line 11, in <module>
    load_entry_point('ansible-container', 'console_scripts', 'conductor')()
  File "/_ansible/container/__init__.py", line 19, in __wrapped__
    return fn(*args, **kwargs)
  File "/_ansible/container/cli.py", line 389, in conductor_commandline
    conductor_config = AnsibleContainerConductorConfig(list_to_ordereddict(containers_config))
  File "/_ansible/container/__init__.py", line 19, in __wrapped__
    return fn(*args, **kwargs)
  File "/_ansible/container/config.py", line 297, in __init__
    self._process_services()
  File "/_ansible/container/config.py", line 357, in _process_services
    role_metadata = get_metadata_from_role(role_name)
  File "/_ansible/container/__init__.py", line 19, in __wrapped__
    return fn(*args, **kwargs)
  File "/_ansible/container/utils/__init__.py", line 275, in get_metadata_from_role
    return get_content_from_role(role_name, os.path.join('meta', 'container.yml'))
  File "/_ansible/container/__init__.py", line 19, in __wrapped__
    return fn(*args, **kwargs)
  File "/_ansible/container/utils/__init__.py", line 264, in get_content_from_role
    role_path = resolve_role_to_path(role_name)
  File "/_ansible/container/__init__.py", line 19, in __wrapped__
    return fn(*args, **kwargs)
  File "/_ansible/container/utils/__init__.py", line 210, in resolve_role_to_path
    loader=loader)
  File "/usr/lib/python2.7/site-packages/ansible/playbook/role/include.py", line 59, in load
    return ri.load_data(data, variable_manager=variable_manager, loader=loader)
  File "/usr/lib/python2.7/site-packages/ansible/playbook/base.py", line 244, in load_data
    ds = self.preprocess_data(ds)
  File "/usr/lib/python2.7/site-packages/ansible/playbook/role/definition.py", line 94, in preprocess_data
    (role_name, role_path) = self._load_role_path(role_name)
  File "/usr/lib/python2.7/site-packages/ansible/playbook/role/definition.py", line 187, in _load_role_path
    raise AnsibleError("the role '%s' was not found in %s" % (role_name, ":".join(role_search_paths)), obj=self._ds)
ansible.errors.AnsibleError: the role 'nginx_reverse_proxy' was not found in ./roles:/home/franz/workspace/ansible/container/api/r:/home/franz/workspace/ansible/container/api/o:/home/franz/workspace/ansible/container/api/l:/home/franz/workspace/ansible/container/api/e:/home/franz/workspace/ansible/container/api/s:/src/roles:/etc/ansible/roles:.
Conductor terminated. Cleaning up.  command_rc=1 conductor_id=b749c55e40ad6645201ea7fc08897b7de6b7a815ee2b8951ccc721a1aefafa0c save_container=False
ERROR   Conductor exited with status 1  

Additionally

➜  api git:(master) ✗ l
total 72K
drwxr-xr-x 11 franz franz 4.0K Nov 16 16:02 .
drwxr-xr-x  4 franz franz 4.0K Nov 16 11:14 ..
-rw-rw-r--  1 franz franz  205 Nov 16 15:52 ansible.cfg
drwxr-xr-x  2 franz franz 4.0K Nov 16 15:42 ansible_deployment
-rw-rw-r--  1 franz franz  129 Nov  9 16:31 ansible-requirements.txt
-rw-rw-r--  1 franz franz  677 Nov 16 16:02 container.yml
drwxr-xr-x  2 root  root  4.0K Nov 16 16:02 e
drwxr-xr-x  8 franz franz 4.0K Nov 16 16:07 .git
-rw-rw-r--  1 franz franz    5 Nov 14 16:58 .gitignore
drwxr-xr-x  2 root  root  4.0K Nov 16 16:02 l
-rw-rw-r--  1 franz franz 1.2K Nov  9 16:31 meta.yml
drwxr-xr-x  2 root  root  4.0K Nov 16 16:02 o
drwxr-xr-x  2 root  root  4.0K Nov 16 16:02 r
-rw-rw-r--  1 franz franz  651 Nov 16 13:49 requirements.yml
drwxrwxr-x  2 franz franz 4.0K Nov 16 16:02 roles
drwxr-xr-x  2 root  root  4.0K Nov 16 16:02 s
-rw-rw-r--  1 franz franz  426 Nov 16 14:17 vars.yml
drwxrwxr-x  7 franz franz 4.0K Nov 14 14:03 venv
luther7 commented 7 years ago

Bug does not occur when roles_path is an array like so:

settings:
  conductor:
    base: alpine:3.5
    roles_path: 
      - roles
  deployment_output_path: ./ansible_deployment
  vars_files:
    - vars.yml
chouseknecht commented 6 years ago

The CLI --roles-path option returns and array. It appears that config.py returns an array, and expects conductor.roles_path to be an array. However, our docs indicate that container.yml expects contuctor.roles_path to be a string.

We need to fix the docs, and make it clear that roles_path is an array.

luther7 commented 6 years ago

Also note that if roles_path is a string, it creates empty directories - one for each character in roles_path.
Example:
container.yml

version: "2"

settings:
  conductor:
    base: alpine:3.5
    roles_path: roles
  deployment_output_path: ./ansible_deployment
  vars_files:
    - vars.yml

services:
  nginx:
    from: nginx:1.13.6-alpine
    roles:
      - nginx_reverse_proxy
    command: [nginx, -g, daemon off;]
    ports:
      - "8080:80"
    links:
      - php-fpm

  php-fpm:
    from: php:7.0-fpm-alpine
    roles:
      - install_packages
      - install_php_extensions
      - purge_package_cache
      - postfix
    command: [php-fpm]
    volumes:
      - api:/srv
    dev_overrides:
      volumes:
        - /home/franz/workspace/php/test:/srv

volumes:
  api:
    docker:
      driver: local

After build results in:

➜  api git:(master) ✗ l
total 72K
drwxr-xr-x 11 franz franz 4.0K Nov 16 16:02 .
drwxr-xr-x  4 franz franz 4.0K Nov 16 11:14 ..
-rw-rw-r--  1 franz franz  205 Nov 16 15:52 ansible.cfg
drwxr-xr-x  2 franz franz 4.0K Nov 16 15:42 ansible_deployment
-rw-rw-r--  1 franz franz  129 Nov  9 16:31 ansible-requirements.txt
-rw-rw-r--  1 franz franz  677 Nov 16 16:02 container.yml
drwxr-xr-x  2 root  root  4.0K Nov 16 16:02 e
drwxr-xr-x  8 franz franz 4.0K Nov 16 16:07 .git
-rw-rw-r--  1 franz franz    5 Nov 14 16:58 .gitignore
drwxr-xr-x  2 root  root  4.0K Nov 16 16:02 l
-rw-rw-r--  1 franz franz 1.2K Nov  9 16:31 meta.yml
drwxr-xr-x  2 root  root  4.0K Nov 16 16:02 o
drwxr-xr-x  2 root  root  4.0K Nov 16 16:02 r
-rw-rw-r--  1 franz franz  651 Nov 16 13:49 requirements.yml
drwxrwxr-x  2 franz franz 4.0K Nov 16 16:02 roles
drwxr-xr-x  2 root  root  4.0K Nov 16 16:02 s
-rw-rw-r--  1 franz franz  426 Nov 16 14:17 vars.yml
drwxrwxr-x  7 franz franz 4.0K Nov 14 14:03 venv
j00bar commented 6 years ago

The empty directory part can likely be fixed by applying the same sort of fail-but-warn strategy we used in #810

brandonheller commented 6 years ago

FWIW, I came across what @rubberydub came across today, and it leads to a really unexpected error, until you add --debug and notice that there's one path created per character in the passed-in roles_path string. In hindsight it's kinda funny, but the workaround (until you know the real cause) of having to pass roles-path in for every command execution get annoying real fast. You see a stack trace that ends like this:

... APIError: 400 Client Error: Bad Request ("invalid volume spec "/": invalid volume specification: '/': invalid mount config for type "volume": invalid specification: destination can't be '/'") ..which is triggered because one of the "paths" is a slash character in the real path.

Happens with a container.yml like this: settings: conductor: roles_path: ../../../ansible/fwd-ubuntu/roles/ ...

On 0.9.2, the fix is just to put the roles_path value into an array.

mvk commented 6 years ago

@rubberydub listen, the roles_path variable is supposed to be a list (or tupple), not a str.

This means even if you have 1 path to add you do it like so:

version: "2"

settings:
  conductor:
    base: alpine:3.5
    roles_path:
    - 'roles'
  deployment_output_path: ./ansible_deployment
  vars_files:
    - vars.yml
j00bar commented 6 years ago

And on develop, the content of container.yml is checked against a jsonschema, which would prevent you running where roles_path was a string, not a list.

https://github.com/ansible/ansible-container/blob/2b9bbb9/container/schema.yml#L17-L20

So I'm marking this one fixed. HTH!