From f2646d36d89548bd7ed4154aa0ef75a86d94b7b5 Mon Sep 17 00:00:00 2001 From: Travis Truman Date: Fri, 22 Apr 2016 12:58:37 -0400 Subject: [PATCH] Template /etc/environment rather than use lineinfile The existing lineinfile implementation is buggy when changing and removing entries from the global_environment_variables dict Significant refactoring of the role tests was necessary in order to get usable testing on the change as well as to resolve a few problems with the previous implementation of the role testing. Partial-Bug: #1573131 Change-Id: I401763d277d179249973545e873796f9dd54049b --- tasks/container_create.yml | 18 ++--- templates/environment.j2 | 10 +++ tests/group_vars/all_containers.yml | 27 +++++++ tests/inventory | 6 ++ tests/test-containers-functional.yml | 49 ++++++++++++ tests/test-prepare-containers.yml | 33 ++++++++ tests/test-prepare-host.yml | 61 +++++++++++++++ tests/test-prepare-keys.yml | 33 ++++++++ tests/test.yml | 108 +++------------------------ 9 files changed, 234 insertions(+), 111 deletions(-) create mode 100644 templates/environment.j2 create mode 100644 tests/group_vars/all_containers.yml create mode 100644 tests/test-containers-functional.yml create mode 100644 tests/test-prepare-containers.yml create mode 100644 tests/test-prepare-host.yml create mode 100644 tests/test-prepare-keys.yml diff --git a/tasks/container_create.yml b/tasks/container_create.yml index b657cdc..612e3e9 100644 --- a/tasks/container_create.yml +++ b/tasks/container_create.yml @@ -201,17 +201,11 @@ tags: - lxc-container-user-password-regen -# Setup proxy configs, this is done here to ensure that we have our container proxy setup -# prior to running online commands. This is using lxc_container because python2.7 may not be -# installed at this point. -- name: Run proxy config - lxc_container: - name: "{{ inventory_hostname }}" - container_command: | - if ! grep '{{ item.key }}={{ item.value }}' /etc/environment; then - echo '{{ item.key }}={{ item.value }}' | tee -a /etc/environment - fi - with_dict: "{{ global_environment_variables | default({}) }}" - delegate_to: "{{ physical_host }}" +- name: Install container proxy settings + template: + dest: "/etc/environment" + src: "environment.j2" + mode: "0644" + remote_user: root tags: - lxc-container-proxy diff --git a/templates/environment.j2 b/templates/environment.j2 new file mode 100644 index 0000000..37f7bc7 --- /dev/null +++ b/templates/environment.j2 @@ -0,0 +1,10 @@ +# {{ ansible_managed }} + +PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games" +{% if global_environment_variables %} +{% for key, value in global_environment_variables.iteritems() %} +{% if value %} +{{ key }}={{ value }} +{% endif %} +{% endfor %} +{% endif %} \ No newline at end of file diff --git a/tests/group_vars/all_containers.yml b/tests/group_vars/all_containers.yml new file mode 100644 index 0000000..4cc9794 --- /dev/null +++ b/tests/group_vars/all_containers.yml @@ -0,0 +1,27 @@ +--- +# Copyright 2016, Rackspace US, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +ansible_ssh_host: "{{ ansible_host }}" +container_name: "{{ inventory_hostname }}" +container_networks: + management_address: + address: "{{ ansible_host }}" + bridge: "lxcbr0" + interface: "eth1" + netmask: "255.255.252.0" + type: "veth" +physical_host: localhost +properties: + service_name: "{{ inventory_hostname }}" diff --git a/tests/inventory b/tests/inventory index 185aaef..a16c745 100644 --- a/tests/inventory +++ b/tests/inventory @@ -1,2 +1,8 @@ [all] localhost ansible_connection=local physical_host=localhost ansible_become=True +container1 ansible_host=10.100.100.2 ansible_become=True ansible_user=root +container2 ansible_host=10.100.100.3 ansible_become=True ansible_user=root + +[all_containers] +container1 +container2 \ No newline at end of file diff --git a/tests/test-containers-functional.yml b/tests/test-containers-functional.yml new file mode 100644 index 0000000..1a1293b --- /dev/null +++ b/tests/test-containers-functional.yml @@ -0,0 +1,49 @@ +--- +# Copyright 2015, Rackspace US, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +- name: Test whether the role produced expected results + hosts: localhost + tasks: + - name: List the running LXC containers present on the host + command: lxc-ls -1 --fancy --fancy-format name,ipv4 --running + register: lxc_container_list + - name: Verify that the expected containers are present with the correct addresses + # Example stdout: + # NAME IPV4 + # --------------------------------------- + # container1 172.16.12.3, 10.100.100.2 + # container2 10.100.100.3, 172.16.12.4 + assert: + that: + - lxc_container_list.stdout | search("container1\s+(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3},\s+)*10.100.100.2(,\s+\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})*\s+") + - lxc_container_list.stdout | search("container2\s+(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3},\s+)*10.100.100.3(,\s+\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})*\s+") + +- name: Test the containers themselves + hosts: all_containers + remote_user: root + tasks: + - name: Open /etc/environment file + slurp: + src: /etc/environment + register: environment_file + - name: Set /etc/environment contents fact + set_fact: + environment_content: "{{ environment_file.content | b64decode }}" + - name: Check /etc/enviroment matches expectations + assert: + that: + - "'/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games' in environment_content" + - "'http_proxy=http://someproxy.example.com' in environment_content" + - "'https_proxy=https://someproxy.example.com' in environment_content" \ No newline at end of file diff --git a/tests/test-prepare-containers.yml b/tests/test-prepare-containers.yml new file mode 100644 index 0000000..e4bfc24 --- /dev/null +++ b/tests/test-prepare-containers.yml @@ -0,0 +1,33 @@ +--- +# Copyright 2015, Rackspace US, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +- name: Playbook for creating containers + hosts: all_containers + gather_facts: false + roles: + - role: "{{ rolename | basename }}" + lxc_container_release: trusty + lxc_container_backing_store: dir + global_environment_variables: + http_proxy: "http://someproxy.example.com" + https_proxy: "https://someproxy.example.com" + post_tasks: + - name: Wait for ssh to be available + local_action: + module: wait_for + port: "{{ ansible_ssh_port | default('22') }}" + host: "{{ ansible_ssh_host | default(inventory_hostname) }}" + search_regex: OpenSSH + delay: 1 diff --git a/tests/test-prepare-host.yml b/tests/test-prepare-host.yml new file mode 100644 index 0000000..56f9d9b --- /dev/null +++ b/tests/test-prepare-host.yml @@ -0,0 +1,61 @@ +--- +# Copyright 2015, Rackspace US, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +- name: Playbook for configuring LXC host + hosts: localhost + connection: local + pre_tasks: + # Make sure OS does not have a stale package cache. + - name: Update apt cache + apt: + update_cache: yes + when: ansible_os_family == 'Debian' + - name: Ensure root's new public ssh key is in authorized_keys + authorized_key: + user: root + key: "{{ hostvars['localhost']['lxc_container_ssh_key'] }}" + manage_dir: no + - set_fact: + lxc_container_ssh_key: "{{ hostvars['localhost']['lxc_container_ssh_key'] }}" + - name: Check if this is an OpenStack-CI nodepool instance + stat: + path: /etc/nodepool/provider + register: nodepool + - name: Set the files to copy into the container cache for OpenStack-CI instances + set_fact: + lxc_container_cache_files: + - { src: '/etc/pip.conf', dest: '/etc/pip.conf' } + - { src: '/etc/apt/apt.conf.d/99unauthenticated', dest: '/etc/apt/apt.conf.d/99unauthenticated' } + when: nodepool.stat.exists | bool + - name: Determine the existing Ubuntu repo configuration + shell: 'awk "/^deb .*ubuntu\/? {{ ansible_distribution_release }} main/ {print \$2; exit}" /etc/apt/sources.list' + register: ubuntu_repo + changed_when: false + - name: Set apt repo facts based on discovered information + set_fact: + lxc_container_template_main_apt_repo: "{{ ubuntu_repo.stdout }}" + lxc_container_template_security_apt_rep: "{{ ubuntu_repo.stdout }}" + roles: + - role: "lxc_hosts" + lxc_net_address: 10.100.100.1 + lxc_net_dhcp_range: 10.100.100.8,10.100.100.253 + lxc_net_bridge: lxcbr0 + lxc_kernel_options: + - { key: 'fs.inotify.max_user_instances', value: 1024 } + lxc_container_caches: + - url: "https://rpc-repo.rackspace.com/container_images/rpc-trusty-container.tgz" + name: "trusty.tgz" + sha256sum: "56c6a6e132ea7d10be2f3e8104f47136ccf408b30e362133f0dc4a0a9adb4d0c" + chroot_path: trusty/rootfs-amd64 diff --git a/tests/test-prepare-keys.yml b/tests/test-prepare-keys.yml new file mode 100644 index 0000000..a4fad4f --- /dev/null +++ b/tests/test-prepare-keys.yml @@ -0,0 +1,33 @@ +--- +# Copyright 2015, Rackspace US, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# NOTE: we use become_user because setting become: no or become: false +# doesn't seem to override the ansible_become=true in the +# inventory +- name: Playbook for establishing ssh keys + hosts: localhost + become_user: "{{ ansible_ssh_user }}" + pre_tasks: + - name: Create ssh key pair for root + user: + name: "{{ ansible_ssh_user }}" + generate_ssh_key: "yes" + ssh_key_bits: 2048 + ssh_key_file: ".ssh/id_rsa" + - name: Get the calling user's key + command: cat ~/.ssh/id_rsa.pub + register: key_get + - set_fact: + lxc_container_ssh_key: "{{ key_get.stdout }}" diff --git a/tests/test.yml b/tests/test.yml index 1345343..6061b65 100644 --- a/tests/test.yml +++ b/tests/test.yml @@ -13,104 +13,14 @@ # See the License for the specific language governing permissions and # limitations under the License. -- name: Playbook for pre-role testing - hosts: localhost - connection: local - pre_tasks: - - name: First ensure apt cache is always refreshed - apt: - update_cache: yes - - name: Ensure root ssh key - user: - name: "{{ ansible_env.USER | default('root') }}" - generate_ssh_key: "yes" - ssh_key_bits: 2048 - ssh_key_file: ".ssh/id_rsa" - - name: get the calling users key - command: cat ~/.ssh/id_rsa.pub - register: key_get - - set_fact: - lxc_container_ssh_key: key_get.stdout - - name: Check if this is an OpenStack-CI nodepool instance - stat: - path: /etc/nodepool/provider - register: nodepool - - name: Set the files to copy into the container cache for OpenStack-CI instances - set_fact: - lxc_container_cache_files: - - { src: '/etc/pip.conf', dest: '/etc/pip.conf' } - - { src: '/etc/apt/apt.conf.d/99unauthenticated', dest: '/etc/apt/apt.conf.d/99unauthenticated' } - when: nodepool.stat.exists | bool - - name: Determine the existing Ubuntu repo configuration - shell: 'awk "/^deb .*ubuntu\/? {{ ansible_distribution_release }} main/ {print \$2; exit}" /etc/apt/sources.list' - register: ubuntu_repo - changed_when: false - - name: Set apt repo facts based on discovered information - set_fact: - lxc_container_template_main_apt_repo: "{{ ubuntu_repo.stdout }}" - lxc_container_template_security_apt_rep: "{{ ubuntu_repo.stdout }}" - roles: - - role: "lxc_hosts" - lxc_net_address: 10.100.100.1 - lxc_net_dhcp_range: 10.100.100.2,10.100.100.253 - lxc_net_bridge: lxcbr0 - lxc_kernel_options: - - { key: 'fs.inotify.max_user_instances', value: 1024 } - lxc_container_caches: - - url: "https://rpc-repo.rackspace.com/container_images/rpc-trusty-container.tgz" - name: "trusty.tgz" - sha256sum: "56c6a6e132ea7d10be2f3e8104f47136ccf408b30e362133f0dc4a0a9adb4d0c" - chroot_path: trusty/rootfs-amd64 - post_tasks: - # Inventory is being pre-loaded using a post tasks instead of through a dynamic - # inventory system. While this is not a usual method for deployment it's being - # done for functional testing. - - name: Create container hosts - add_host: - groups: "all,all_containers" - hostname: "{{ item.name }}" - inventory_hostname: "{{ item.name }}" - ansible_ssh_host: "{{ item.address }}" - ansible_become: true - properties: - service_name: "{{ item.service }}" - container_networks: - management_address: - address: "{{ item.address }}" - bridge: "lxcbr0" - interface: "eth1" - netmask: "255.255.252.0" - type: "veth" - physical_host: localhost - container_name: "{{ item.name }}" - with_items: - - { name: "container1", service: "service1", address: "10.100.100.101" } - - { name: "container2", service: "service2", address: "10.100.100.102" } +# Prepare the user ssh keys +- include: test-prepare-keys.yml -- name: Playbook for role testing - hosts: all_containers - connection: local - gather_facts: false - roles: - - role: "{{ rolename | basename }}" - lxc_container_release: trusty - lxc_container_backing_store: dir - global_environment_variables: - PATH: "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" +# Prepare the host +- include: test-prepare-host.yml -- name: Test whether the role produced expected results - hosts: localhost - tasks: - - name: List the running LXC containers present on the host - command: lxc-ls -1 --fancy --fancy-format name,ipv4 --running - register: lxc_container_list - - name: Verify that the expected containers are present with the correct addresses - # Example stdout: - # NAME IPV4 - # --------------------------------------- - # container1 172.16.12.3, 10.100.100.101 - # container2 10.100.100.102, 172.16.12.4 - assert: - that: - - lxc_container_list.stdout | search("container1\s+(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3},\s+)*10.100.100.101(,\s+\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})*\s+") - - lxc_container_list.stdout | search("container2\s+(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3},\s+)*10.100.100.102(,\s+\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})*\s+") +# Prepare the containers +- include: test-prepare-containers.yml + +# Test container creation +- include: test-containers-functional.yml