From a79f7e575a9576f804007ed979aa6c1aa731dd2d Mon Sep 17 00:00:00 2001 From: Felix Fontein Date: Mon, 30 Sep 2019 06:09:00 +0200 Subject: [PATCH] docker_image and docker_login: move tests needing a registry into own target, add tests for docker_login (#62721) * Move tests with docker registry into own target. * Add docker_login tests. * Add step which makes sure hello-world:latest is around. * Make work inside docker container. * Add dependency. * Use plaintext password. * Forgot check_mode. * Add no_log to avoid double log output in verbose mode. --- .../targets/docker-registry/aliases | 7 + .../targets/docker-registry/files/Dockerfile | 3 + .../targets/docker-registry/files/nginx.conf | 46 ++++++ .../docker-registry/files/nginx.htpasswd | 1 + .../targets/docker-registry/meta/main.yml | 4 + .../targets/docker-registry/tasks/main.yml | 140 ++++++++++++++++ .../docker-registry/tasks/run-test.yml | 3 + .../tasks/tests/docker_image.yml | 152 ++++++++++++++++++ .../tasks/tests/docker_login.yml | 103 ++++++++++++ .../targets/docker_image/tasks/main.yml | 25 +-- .../docker_image/tasks/tests/basic.yml | 84 ---------- .../docker_image/tasks/tests/options.yml | 50 ------ 12 files changed, 462 insertions(+), 156 deletions(-) create mode 100644 test/integration/targets/docker-registry/aliases create mode 100644 test/integration/targets/docker-registry/files/Dockerfile create mode 100644 test/integration/targets/docker-registry/files/nginx.conf create mode 100644 test/integration/targets/docker-registry/files/nginx.htpasswd create mode 100644 test/integration/targets/docker-registry/meta/main.yml create mode 100644 test/integration/targets/docker-registry/tasks/main.yml create mode 100644 test/integration/targets/docker-registry/tasks/run-test.yml create mode 100644 test/integration/targets/docker-registry/tasks/tests/docker_image.yml create mode 100644 test/integration/targets/docker-registry/tasks/tests/docker_login.yml diff --git a/test/integration/targets/docker-registry/aliases b/test/integration/targets/docker-registry/aliases new file mode 100644 index 00000000000..1817f0dae46 --- /dev/null +++ b/test/integration/targets/docker-registry/aliases @@ -0,0 +1,7 @@ +shippable/posix/group3 +skip/osx +skip/freebsd +destructive +docker_image +docker_image_info +docker_login diff --git a/test/integration/targets/docker-registry/files/Dockerfile b/test/integration/targets/docker-registry/files/Dockerfile new file mode 100644 index 00000000000..c2951375fa1 --- /dev/null +++ b/test/integration/targets/docker-registry/files/Dockerfile @@ -0,0 +1,3 @@ +FROM busybox +ENV foo /bar +WORKDIR ${foo} diff --git a/test/integration/targets/docker-registry/files/nginx.conf b/test/integration/targets/docker-registry/files/nginx.conf new file mode 100644 index 00000000000..bfba52041a9 --- /dev/null +++ b/test/integration/targets/docker-registry/files/nginx.conf @@ -0,0 +1,46 @@ +events { + worker_connections 16; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + error_log /dev/stdout info; + access_log /dev/stdout; + + server { + listen *:5000 ssl; + server_name test-registry.ansible.com; + server_name_in_redirect on; + + ssl_protocols TLSv1.2; + ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-DSS-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256'; + ssl_ecdh_curve X25519:secp521r1:secp384r1; + ssl_prefer_server_ciphers on; + ssl_certificate /etc/nginx/cert.pem; + ssl_certificate_key /etc/nginx/cert.key; + + location / { + return 401; + } + + location /v2/ { + proxy_pass http://real-registry:5000; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header Host $http_host; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-For $remote_addr; + proxy_set_header X-Forwarded-Port $server_port; + proxy_set_header X-Request-Start $msec; + + client_max_body_size 0; + chunked_transfer_encoding on; + + auth_basic "Ansible Test Docker Registry"; + auth_basic_user_file /etc/nginx/nginx.htpasswd; + } + } +} diff --git a/test/integration/targets/docker-registry/files/nginx.htpasswd b/test/integration/targets/docker-registry/files/nginx.htpasswd new file mode 100644 index 00000000000..1291ae77ab9 --- /dev/null +++ b/test/integration/targets/docker-registry/files/nginx.htpasswd @@ -0,0 +1 @@ +testuser:{PLAIN}hunter2 diff --git a/test/integration/targets/docker-registry/meta/main.yml b/test/integration/targets/docker-registry/meta/main.yml new file mode 100644 index 00000000000..078660a8abb --- /dev/null +++ b/test/integration/targets/docker-registry/meta/main.yml @@ -0,0 +1,4 @@ +--- +dependencies: + - setup_docker + - setup_openssl diff --git a/test/integration/targets/docker-registry/tasks/main.yml b/test/integration/targets/docker-registry/tasks/main.yml new file mode 100644 index 00000000000..790c1bbd675 --- /dev/null +++ b/test/integration/targets/docker-registry/tasks/main.yml @@ -0,0 +1,140 @@ +--- +- name: Create random name prefix and test registry name + set_fact: + name_prefix: "{{ 'ansible-test-%0x' % ((2**32) | random) }}" + registry_name: "{{ 'ansible-test-registry-%0x' % ((2**32) | random) }}" + nginx_name: "{{ 'ansible-test-registry-frontend-%0x' % ((2**32) | random) }}" +- name: Create image and container list + set_fact: + inames: [] + cnames: + - "{{ registry_name }}" + - "{{ nginx_name }}" + vnames: + - "{{ nginx_name }}" + +- debug: + msg: "Using name prefix {{ name_prefix }} and test registry name {{ registry_name }}" + +- block: + - name: Start test registry + docker_container: + name: "{{ registry_name }}" + image: registry:2.6.1 + ports: 5000 + register: registry_container + + - name: Get registry URL + set_fact: + registry_address: "localhost:{{ registry_container.container.NetworkSettings.Ports['5000/tcp'].0.HostPort }}" + + - name: Start nginx frontend for registry + docker_volume: + name: "{{ nginx_name }}" + state: present + + - name: Create container for nginx frontend for registry + docker_container: + state: stopped + name: "{{ nginx_name }}" + image: nginx:alpine + ports: 5000 + links: + - "{{ registry_name }}:real-registry" + volumes: + - "{{ nginx_name }}:/etc/nginx/" + register: nginx_container + + - name: Copy static files into volume + command: docker cp {{ role_path }}/files/{{ item }} {{ nginx_name }}:/etc/nginx/{{ item }} + loop: + - "nginx.conf" + - "nginx.htpasswd" + + - name: Create private key for frontend certificate + openssl_privatekey: + path: "{{ output_dir }}/cert.key" + type: ECC + curve: secp256r1 + - name: Create CSR for frontend certificate + openssl_csr: + path: "{{ output_dir }}/cert.csr" + privatekey_path: "{{ output_dir }}/cert.key" + subject_alt_name: + - "DNS:test-registry.ansible.com" + - name: Create frontend certificate + openssl_certificate: + path: "{{ output_dir }}/cert.pem" + csr_path: "{{ output_dir }}/cert.csr" + privatekey_path: "{{ output_dir }}/cert.key" + provider: selfsigned + + - name: Copy dynamic files into volume + command: docker cp {{ output_dir }}/{{ item }} {{ nginx_name }}:/etc/nginx/{{ item }} + loop: + - "cert.pem" + - "cert.key" + + - name: Start nginx frontend for registry + docker_container: + name: "{{ nginx_name }}" + state: started + register: nginx_container + + - debug: var=nginx_container.container.NetworkSettings + + - name: Wait for registry frontend + uri: + url: "https://{{ nginx_container.container.NetworkSettings.IPAddress }}:5000/v2/" + url_username: testuser + url_password: hunter2 + validate_certs: no + register: result + until: result is success + retries: 5 + delay: 1 + + - name: Get registry URL + set_fact: + registry_frontend_address: "localhost:{{ nginx_container.container.NetworkSettings.Ports['5000/tcp'].0.HostPort }}" + + - debug: msg="Registry available under {{ registry_address }}, NGINX frontend available under {{ registry_frontend_address }}" + + - include_tasks: run-test.yml + with_fileglob: + - "tests/*.yml" + + always: + - name: "Make sure all images are removed" + docker_image: + name: "{{ item }}" + state: absent + with_items: "{{ inames }}" + - name: "Get registry logs" + command: "docker logs {{ registry_name }}" + register: registry_logs + no_log: yes + - name: "Printing registry logs" + debug: var=registry_logs.stdout_lines + - name: "Get nginx logs" + command: "docker logs {{ nginx_name }}" + register: nginx_logs + no_log: yes + - name: "Printing nginx logs" + debug: var=nginx_logs.stdout_lines + - name: "Make sure all containers are removed" + docker_container: + name: "{{ item }}" + state: absent + force_kill: yes + with_items: "{{ cnames }}" + - name: "Make sure all volumes are removed" + docker_volume: + name: "{{ item }}" + state: absent + with_items: "{{ vnames }}" + + when: docker_py_version is version('1.8.0', '>=') and docker_api_version is version('1.20', '>=') + +- fail: msg="Too old docker / docker-py version to run docker_image tests!" + when: not(docker_py_version is version('1.8.0', '>=') and docker_api_version is version('1.20', '>=')) and (ansible_distribution != 'CentOS' or ansible_distribution_major_version|int > 6) diff --git a/test/integration/targets/docker-registry/tasks/run-test.yml b/test/integration/targets/docker-registry/tasks/run-test.yml new file mode 100644 index 00000000000..a2999370142 --- /dev/null +++ b/test/integration/targets/docker-registry/tasks/run-test.yml @@ -0,0 +1,3 @@ +--- +- name: "Loading tasks from {{ item }}" + include_tasks: "{{ item }}" diff --git a/test/integration/targets/docker-registry/tasks/tests/docker_image.yml b/test/integration/targets/docker-registry/tasks/tests/docker_image.yml new file mode 100644 index 00000000000..1959446aa58 --- /dev/null +++ b/test/integration/targets/docker-registry/tasks/tests/docker_image.yml @@ -0,0 +1,152 @@ +--- +- name: Registering image name + set_fact: + iname: "{{ name_prefix ~ '-options' }}" + +- name: Determining pushed image names + set_fact: + hello_world_image_base: "{{ registry_address }}/test/hello-world" + test_image_base: "{{ registry_address }}/test/{{ iname }}" + +- name: Registering image name + set_fact: + inames: "{{ inames + [iname, test_image_base ~ ':latest', hello_world_image_base ~ ':latest'] }}" + +#################################################################### +## interact with test registry ##################################### +#################################################################### + +- name: Make sure image is not there + docker_image: + name: "{{ hello_world_image_base }}:latest" + state: absent + force_absent: yes + +- name: Make sure we have hello-world:latest + docker_image: + name: hello-world:latest + source: pull + +- name: Push image to test registry + docker_image: + name: "hello-world:latest" + repository: "{{ hello_world_image_base }}" + push: yes + source: local + register: push_1 + +- name: Push image to test registry (idempotent) + docker_image: + name: "hello-world:latest" + repository: "{{ hello_world_image_base }}" + push: yes + source: local + register: push_2 + +- name: Push image to test registry (force, still idempotent) + docker_image: + name: "hello-world:latest" + repository: "{{ hello_world_image_base }}" + push: yes + source: local + force_tag: yes + register: push_3 + +- assert: + that: + - push_1 is changed + - push_2 is not changed + - push_3 is not changed + +- name: Get facts of local image + docker_image_info: + name: "{{ hello_world_image_base }}:latest" + register: facts_1 + +- name: Make sure image is not there + docker_image: + name: "{{ hello_world_image_base }}:latest" + state: absent + force_absent: yes + +- name: Get facts of local image (absent) + docker_image_info: + name: "{{ hello_world_image_base }}:latest" + register: facts_2 + +- name: Pull image from test registry + docker_image: + name: "{{ hello_world_image_base }}:latest" + state: present + source: pull + register: pull_1 + +- name: Pull image from test registry (idempotency) + docker_image: + name: "{{ hello_world_image_base }}:latest" + state: present + source: pull + register: pull_2 + +- name: Get facts of local image (present) + docker_image_info: + name: "{{ hello_world_image_base }}:latest" + register: facts_3 + +- assert: + that: + - pull_1 is changed + - pull_2 is not changed + - facts_1.images | length == 1 + - facts_2.images | length == 0 + - facts_3.images | length == 1 + +#################################################################### +## repository ###################################################### +#################################################################### + +- name: Make sure image is not there + docker_image: + name: "{{ test_image_base }}:latest" + state: absent + force_absent: yes + +- name: repository + docker_image: + name: "{{ iname }}" + build: + path: "{{ role_path }}/files" + pull: no + repository: "{{ test_image_base }}" + source: build + register: repository_1 + +- name: repository (idempotent) + docker_image: + name: "{{ iname }}" + build: + path: "{{ role_path }}/files" + pull: no + repository: "{{ test_image_base }}" + source: build + register: repository_2 + +- assert: + that: + - repository_1 is changed + - repository_2 is not changed + +- name: Get facts of image + docker_image_info: + name: "{{ test_image_base }}:latest" + register: facts_1 + +- name: cleanup + docker_image: + name: "{{ test_image_base }}:latest" + state: absent + force_absent: yes + +- assert: + that: + - facts_1.images | length == 1 diff --git a/test/integration/targets/docker-registry/tasks/tests/docker_login.yml b/test/integration/targets/docker-registry/tasks/tests/docker_login.yml new file mode 100644 index 00000000000..3f931ec7778 --- /dev/null +++ b/test/integration/targets/docker-registry/tasks/tests/docker_login.yml @@ -0,0 +1,103 @@ +--- +- name: Log in with wrong password (check mode) + docker_login: + registry_url: "{{ registry_frontend_address }}" + username: testuser + password: "1234" + state: present + register: login_failed_check + ignore_errors: yes + check_mode: yes + +- name: Log in with wrong password + docker_login: + registry_url: "{{ registry_frontend_address }}" + username: testuser + password: "1234" + state: present + register: login_failed + ignore_errors: yes + +- name: Make sure that login failed + assert: + that: + - login_failed_check is failed + - "('login attempt to http://' ~ registry_frontend_address ~ '/v2/ failed') in login_failed_check.msg" + - login_failed is failed + - "('login attempt to http://' ~ registry_frontend_address ~ '/v2/ failed') in login_failed.msg" + +#- name: Log in (check mode) +# docker_login: +# registry_url: "{{ registry_frontend_address }}" +# username: testuser +# password: hunter2 +# state: present +# register: login_1 +# check_mode: yes + +- name: Log in + docker_login: + registry_url: "{{ registry_frontend_address }}" + username: testuser + password: hunter2 + state: present + register: login_2 + +- name: Log in (idempotent) + docker_login: + registry_url: "{{ registry_frontend_address }}" + username: testuser + password: hunter2 + state: present + register: login_3 + +- name: Log in (idempotent, check mode) + docker_login: + registry_url: "{{ registry_frontend_address }}" + username: testuser + password: hunter2 + state: present + register: login_4 + check_mode: yes + +- name: Make sure that login worked + assert: + that: + #- login_1 is changed + - login_2 is changed + - login_3 is not changed + - login_4 is not changed + +#- name: Log out (check mode) +# docker_login: +# registry_url: "{{ registry_frontend_address }}" +# state: absent +# register: logout_1 +# check_mode: yes + +- name: Log out + docker_login: + registry_url: "{{ registry_frontend_address }}" + state: absent + register: logout_2 + +- name: Log out (idempotent) + docker_login: + registry_url: "{{ registry_frontend_address }}" + state: absent + register: logout_3 + +- name: Log out (idempotent, check mode) + docker_login: + registry_url: "{{ registry_frontend_address }}" + state: absent + register: logout_4 + check_mode: yes + +- name: Make sure that login worked + assert: + that: + #- logout_1 is changed + - logout_2 is changed + - logout_3 is not changed + - logout_4 is not changed diff --git a/test/integration/targets/docker_image/tasks/main.yml b/test/integration/targets/docker_image/tasks/main.yml index 0b9174a55bf..47c629860bd 100644 --- a/test/integration/targets/docker_image/tasks/main.yml +++ b/test/integration/targets/docker_image/tasks/main.yml @@ -1,30 +1,16 @@ --- -- name: Create random name prefix and test registry name +- name: Create random name prefix set_fact: name_prefix: "{{ 'ansible-test-%0x' % ((2**32) | random) }}" - registry_name: "{{ 'ansible-test-registry-%0x' % ((2**32) | random) }}" - name: Create image and container list set_fact: inames: [] - cnames: - - "{{ registry_name }}" + cnames: [] - debug: - msg: "Using name prefix {{ name_prefix }} and test registry name {{ registry_name }}" + msg: "Using name prefix {{ name_prefix }}" - block: - - name: Start test registry - docker_container: - name: "{{ registry_name }}" - image: registry:2.6.1 - ports: 5000 - - - name: Get registry URL - set_fact: - registry_address: "localhost:{{ docker_container.NetworkSettings.Ports['5000/tcp'].0.HostPort }}" - - - debug: msg="Registry available under {{ registry_address }}" - - include_tasks: run-test.yml with_fileglob: - "tests/*.yml" @@ -35,11 +21,6 @@ name: "{{ item }}" state: absent with_items: "{{ inames }}" - - name: "Get registry logs" - command: "docker logs {{ registry_name }}" - register: registry_logs - - name: "Printing registry logs" - debug: var=registry_logs.stdout_lines - name: "Make sure all containers are removed" docker_container: name: "{{ item }}" diff --git a/test/integration/targets/docker_image/tasks/tests/basic.yml b/test/integration/targets/docker_image/tasks/tests/basic.yml index f87b4b2ffd1..6f0bdd69356 100644 --- a/test/integration/targets/docker_image/tasks/tests/basic.yml +++ b/test/integration/targets/docker_image/tasks/tests/basic.yml @@ -76,87 +76,3 @@ docker_image: name: "hello-world:alias" state: absent - -#################################################################### -## interact with test registry ##################################### -#################################################################### - -- name: Make sure image is not there - docker_image: - name: "{{ registry_address }}/test/hello-world:latest" - state: absent - force_absent: yes - -- name: Push image to test registry - docker_image: - name: "hello-world:latest" - repository: "{{ registry_address }}/test/hello-world" - push: yes - source: local - register: push_1 - -- name: Push image to test registry (idempotent) - docker_image: - name: "hello-world:latest" - repository: "{{ registry_address }}/test/hello-world" - push: yes - source: local - register: push_2 - -- name: Push image to test registry (force, still idempotent) - docker_image: - name: "hello-world:latest" - repository: "{{ registry_address }}/test/hello-world" - push: yes - source: local - force_tag: yes - register: push_3 - -- assert: - that: - - push_1 is changed - - push_2 is not changed - - push_3 is not changed - -- name: Get facts of local image - docker_image_info: - name: "{{ registry_address }}/test/hello-world:latest" - register: facts_1 - -- name: Make sure image is not there - docker_image: - name: "{{ registry_address }}/test/hello-world:latest" - state: absent - force_absent: yes - -- name: Get facts of local image (absent) - docker_image_info: - name: "{{ registry_address }}/test/hello-world:latest" - register: facts_2 - -- name: Pull image from test registry - docker_image: - name: "{{ registry_address }}/test/hello-world:latest" - state: present - source: pull - register: pull_1 - -- name: Pull image from test registry (idempotency) - docker_image: - name: "{{ registry_address }}/test/hello-world:latest" - state: present - source: pull - register: pull_2 - -- name: Get facts of local image (present) - docker_image_info: - name: "{{ registry_address }}/test/hello-world:latest" - register: facts_3 - -- assert: - that: - - pull_1 is changed - - pull_2 is not changed - - facts_1.images | length == 1 - - facts_2.images | length == 0 - - facts_3.images | length == 1 diff --git a/test/integration/targets/docker_image/tasks/tests/options.yml b/test/integration/targets/docker_image/tasks/tests/options.yml index bcea65f80f9..1ff729c6fae 100644 --- a/test/integration/targets/docker_image/tasks/tests/options.yml +++ b/test/integration/targets/docker_image/tasks/tests/options.yml @@ -126,56 +126,6 @@ - dockerfile_1 is changed - dockerfile_1['image']['Config']['WorkingDir'] == '/newdata' -#################################################################### -## repository ###################################################### -#################################################################### - -- name: Make sure image is not there - docker_image: - name: "{{ registry_address }}/test/{{ iname }}:latest" - state: absent - force_absent: yes - -- name: repository - docker_image: - name: "{{ iname }}" - build: - path: "{{ role_path }}/files" - pull: no - repository: "{{ registry_address }}/test/{{ iname }}" - source: build - register: repository_1 - -- name: repository (idempotent) - docker_image: - name: "{{ iname }}" - build: - path: "{{ role_path }}/files" - pull: no - repository: "{{ registry_address }}/test/{{ iname }}" - source: build - register: repository_2 - -- assert: - that: - - repository_1 is changed - - repository_2 is not changed - -- name: Get facts of image - docker_image_info: - name: "{{ registry_address }}/test/{{ iname }}:latest" - register: facts_1 - -- name: cleanup - docker_image: - name: "{{ registry_address }}/test/{{ iname }}:latest" - state: absent - force_absent: yes - -- assert: - that: - - facts_1.images | length == 1 - #################################################################### ## force ########################################################### ####################################################################