Merge "Import install-docker role"
This commit is contained in:
commit
686651c75d
18
.zuul.yaml
18
.zuul.yaml
@ -215,6 +215,22 @@
|
|||||||
- playbooks/templates/clouds/
|
- playbooks/templates/clouds/
|
||||||
- testinfra/test_nodepool.py
|
- testinfra/test_nodepool.py
|
||||||
|
|
||||||
|
- job:
|
||||||
|
name: system-config-run-docker
|
||||||
|
parent: system-config-run
|
||||||
|
description: |
|
||||||
|
Test docker installation and setup
|
||||||
|
nodeset:
|
||||||
|
nodes:
|
||||||
|
- name: bridge.openstack.org
|
||||||
|
label: ubuntu-bionic
|
||||||
|
- name: bionic-docker
|
||||||
|
label: ubuntu-bionic
|
||||||
|
files:
|
||||||
|
- .zuul.yaml
|
||||||
|
- playbooks/roles/install-docker
|
||||||
|
- testinfra/test_docker.py
|
||||||
|
|
||||||
- job:
|
- job:
|
||||||
name: system-config-run-dns
|
name: system-config-run-dns
|
||||||
parent: system-config-run
|
parent: system-config-run
|
||||||
@ -287,6 +303,7 @@
|
|||||||
- system-config-run-dns
|
- system-config-run-dns
|
||||||
- system-config-run-eavesdrop
|
- system-config-run-eavesdrop
|
||||||
- system-config-run-nodepool
|
- system-config-run-nodepool
|
||||||
|
- system-config-run-docker
|
||||||
gate:
|
gate:
|
||||||
jobs:
|
jobs:
|
||||||
- tox-linters
|
- tox-linters
|
||||||
@ -299,3 +316,4 @@
|
|||||||
- system-config-run-dns
|
- system-config-run-dns
|
||||||
- system-config-run-eavesdrop
|
- system-config-run-eavesdrop
|
||||||
- system-config-run-nodepool
|
- system-config-run-nodepool
|
||||||
|
- system-config-run-docker
|
||||||
|
@ -52,3 +52,8 @@
|
|||||||
name: "Base: configure authoritative nameservers"
|
name: "Base: configure authoritative nameservers"
|
||||||
roles:
|
roles:
|
||||||
- nameserver
|
- nameserver
|
||||||
|
|
||||||
|
- hosts: "docker:!disabled"
|
||||||
|
name: "Base: install and configure docker on docker hosts"
|
||||||
|
roles:
|
||||||
|
- install-docker
|
||||||
|
16
playbooks/roles/install-docker/README.rst
Normal file
16
playbooks/roles/install-docker/README.rst
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
An ansible role to install docker in the OpenStack infra production environment
|
||||||
|
|
||||||
|
**Role Variables**
|
||||||
|
|
||||||
|
.. zuul:rolevar:: use_upstream_docker
|
||||||
|
:default: True
|
||||||
|
|
||||||
|
By default this role adds repositories to install docker from upstream
|
||||||
|
docker. Set this to False to use the docker that comes with the distro.
|
||||||
|
|
||||||
|
.. zuul:rolevar:: docker_update_channel
|
||||||
|
:default: stable
|
||||||
|
|
||||||
|
Which update channel to use for upstream docker. The two choices are
|
||||||
|
``stable``, which is the default and updates quarterly, and ``edge``
|
||||||
|
which updates monthly.
|
65
playbooks/roles/install-docker/defaults/main.yaml
Normal file
65
playbooks/roles/install-docker/defaults/main.yaml
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
use_upstream_docker: True
|
||||||
|
docker_update_channel: stable
|
||||||
|
ubuntu_gpg_key: |
|
||||||
|
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||||
|
|
||||||
|
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
|
||||||
|
YT90qFF93M3v01BbxP+EIY2/9tiIPbrd
|
||||||
|
=0YYh
|
||||||
|
-----END PGP PUBLIC KEY BLOCK-----
|
5
playbooks/roles/install-docker/tasks/distro.yaml
Normal file
5
playbooks/roles/install-docker/tasks/distro.yaml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
- name: Install docker
|
||||||
|
become: yes
|
||||||
|
package:
|
||||||
|
name: docker.io
|
||||||
|
state: present
|
25
playbooks/roles/install-docker/tasks/main.yaml
Normal file
25
playbooks/roles/install-docker/tasks/main.yaml
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
- name: Create docker directory
|
||||||
|
become: yes
|
||||||
|
file:
|
||||||
|
state: directory
|
||||||
|
path: /etc/docker
|
||||||
|
|
||||||
|
- name: Install docker configuration
|
||||||
|
become: yes
|
||||||
|
template:
|
||||||
|
dest: /etc/docker/daemon.json
|
||||||
|
group: root
|
||||||
|
mode: 0644
|
||||||
|
owner: root
|
||||||
|
src: daemon.json.j2
|
||||||
|
|
||||||
|
- name: Install docker-ce from upstream
|
||||||
|
include: upstream.yaml
|
||||||
|
when: use_upstream_docker
|
||||||
|
|
||||||
|
- name: Install docker-engine from distro
|
||||||
|
include: distro.yaml
|
||||||
|
when: not use_upstream_docker
|
||||||
|
|
||||||
|
- name: reset ssh connection to pick up docker group
|
||||||
|
meta: reset_connection
|
32
playbooks/roles/install-docker/tasks/upstream.yaml
Normal file
32
playbooks/roles/install-docker/tasks/upstream.yaml
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
- name: Install pre-reqs
|
||||||
|
package:
|
||||||
|
name: "{{ item }}"
|
||||||
|
state: present
|
||||||
|
with_items:
|
||||||
|
- apt-transport-https
|
||||||
|
- ca-certificates
|
||||||
|
- curl
|
||||||
|
- software-properties-common
|
||||||
|
become: yes
|
||||||
|
|
||||||
|
- name: Add docker GPG key
|
||||||
|
become: yes
|
||||||
|
apt_key:
|
||||||
|
data: "{{ ubuntu_gpg_key }}"
|
||||||
|
|
||||||
|
# TODO(mordred) We should add a proxy cache mirror for this
|
||||||
|
- name: Add docker apt repo
|
||||||
|
become: yes
|
||||||
|
template:
|
||||||
|
dest: /etc/apt/sources.list.d/docker.list
|
||||||
|
group: root
|
||||||
|
mode: 0644
|
||||||
|
owner: root
|
||||||
|
src: sources.list.j2
|
||||||
|
|
||||||
|
- name: Install docker
|
||||||
|
become: yes
|
||||||
|
apt:
|
||||||
|
name: docker-ce
|
||||||
|
state: present
|
||||||
|
update_cache: yes
|
2
playbooks/roles/install-docker/templates/daemon.json.j2
Normal file
2
playbooks/roles/install-docker/templates/daemon.json.j2
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
{
|
||||||
|
}
|
1
playbooks/roles/install-docker/templates/sources.list.j2
Normal file
1
playbooks/roles/install-docker/templates/sources.list.j2
Normal file
@ -0,0 +1 @@
|
|||||||
|
deb [arch=amd64] https://download.docker.com/linux/ubuntu {{ ansible_lsb.codename }} {{ docker_update_channel }}
|
@ -7,3 +7,6 @@ groups:
|
|||||||
- xenial
|
- xenial
|
||||||
- centos7
|
- centos7
|
||||||
# note: bionic currently isn't puppeted
|
# note: bionic currently isn't puppeted
|
||||||
|
|
||||||
|
docker:
|
||||||
|
- bionic-docker
|
||||||
|
@ -62,7 +62,7 @@ def test_iptables(host):
|
|||||||
rules = host.iptables.rules()
|
rules = host.iptables.rules()
|
||||||
rules = [x.strip() for x in rules]
|
rules = [x.strip() for x in rules]
|
||||||
|
|
||||||
start = [
|
needed_rules = [
|
||||||
'-P INPUT ACCEPT',
|
'-P INPUT ACCEPT',
|
||||||
'-P FORWARD DROP',
|
'-P FORWARD DROP',
|
||||||
'-P OUTPUT ACCEPT',
|
'-P OUTPUT ACCEPT',
|
||||||
@ -72,11 +72,10 @@ def test_iptables(host):
|
|||||||
'-A openstack-INPUT -p icmp -m icmp --icmp-type any -j ACCEPT',
|
'-A openstack-INPUT -p icmp -m icmp --icmp-type any -j ACCEPT',
|
||||||
'-A openstack-INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT',
|
'-A openstack-INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT',
|
||||||
'-A openstack-INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT',
|
'-A openstack-INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT',
|
||||||
|
'-A openstack-INPUT -j REJECT --reject-with icmp-host-prohibited'
|
||||||
]
|
]
|
||||||
assert rules[:len(start)] == start
|
for rule in needed_rules:
|
||||||
|
assert rule in rules
|
||||||
reject = '-A openstack-INPUT -j REJECT --reject-with icmp-host-prohibited'
|
|
||||||
assert reject in rules
|
|
||||||
|
|
||||||
# Make sure that the zuul console stream rule is still present
|
# Make sure that the zuul console stream rule is still present
|
||||||
zuul = ('-A openstack-INPUT -p tcp -m state --state NEW'
|
zuul = ('-A openstack-INPUT -p tcp -m state --state NEW'
|
||||||
|
26
testinfra/test_docker.py
Normal file
26
testinfra/test_docker.py
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
# Copyright 2018 Red Hat, 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.
|
||||||
|
|
||||||
|
|
||||||
|
testinfra_hosts = ['bionic-docker']
|
||||||
|
|
||||||
|
|
||||||
|
def test_docker_config(host):
|
||||||
|
daemon_json = host.file('/etc/docker/daemon.json')
|
||||||
|
assert daemon_json.exists
|
||||||
|
|
||||||
|
|
||||||
|
def test_docker_service(host):
|
||||||
|
docker = host.service('docker')
|
||||||
|
assert docker.is_running
|
69
testinfra/test_firewall.py
Normal file
69
testinfra/test_firewall.py
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
# Copyright 2018 Red Hat, 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.
|
||||||
|
|
||||||
|
import socket
|
||||||
|
|
||||||
|
# TODO(ianw): docker fiddles the firewall rules; update these to
|
||||||
|
# handle docker too.
|
||||||
|
testinfra_hosts = ['all:!bionic-docker']
|
||||||
|
|
||||||
|
|
||||||
|
def get_ips(value, family=None):
|
||||||
|
ret = set()
|
||||||
|
try:
|
||||||
|
addr_info = socket.getaddrinfo(value, None, family)
|
||||||
|
except socket.gaierror:
|
||||||
|
return ret
|
||||||
|
for addr in addr_info:
|
||||||
|
ret.add(addr[4][0])
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
def test_iptables(host):
|
||||||
|
rules = host.iptables.rules()
|
||||||
|
rules = [x.strip() for x in rules]
|
||||||
|
|
||||||
|
start = [
|
||||||
|
'-P INPUT ACCEPT',
|
||||||
|
'-P FORWARD DROP',
|
||||||
|
'-P OUTPUT ACCEPT',
|
||||||
|
'-N openstack-INPUT',
|
||||||
|
'-A INPUT -j openstack-INPUT',
|
||||||
|
'-A openstack-INPUT -i lo -j ACCEPT',
|
||||||
|
'-A openstack-INPUT -p icmp -m icmp --icmp-type any -j ACCEPT',
|
||||||
|
'-A openstack-INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT',
|
||||||
|
'-A openstack-INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT',
|
||||||
|
]
|
||||||
|
assert rules[:len(start)] == start
|
||||||
|
|
||||||
|
reject = '-A openstack-INPUT -j REJECT --reject-with icmp-host-prohibited'
|
||||||
|
assert reject in rules
|
||||||
|
|
||||||
|
# Make sure that the zuul console stream rule is still present
|
||||||
|
zuul = ('-A openstack-INPUT -p tcp -m state --state NEW'
|
||||||
|
' -m tcp --dport 19885 -j ACCEPT')
|
||||||
|
assert zuul in rules
|
||||||
|
|
||||||
|
# Ensure all IPv4+6 addresses for cacti are allowed
|
||||||
|
for ip in get_ips('cacti.openstack.org', socket.AF_INET):
|
||||||
|
snmp = ('-A openstack-INPUT -s %s/32 -p udp -m udp'
|
||||||
|
' --dport 161 -j ACCEPT' % ip)
|
||||||
|
assert snmp in rules
|
||||||
|
|
||||||
|
# TODO(ianw) add ip6tables support to testinfra iptables module
|
||||||
|
ip6rules = host.check_output('ip6tables -S')
|
||||||
|
for ip in get_ips('cacti.openstack.org', socket.AF_INET6):
|
||||||
|
snmp = ('-A openstack-INPUT -s %s/128 -p udp -m udp'
|
||||||
|
' --dport 161 -j ACCEPT' % ip)
|
||||||
|
assert snmp in ip6rules
|
Loading…
x
Reference in New Issue
Block a user