diff --git a/roles/startup-infrastructure/tasks/configure-portainer.yml b/roles/startup-infrastructure/tasks/configure-portainer.yml new file mode 100644 index 0000000..3f0da4c --- /dev/null +++ b/roles/startup-infrastructure/tasks/configure-portainer.yml @@ -0,0 +1,14 @@ +--- +#https://app.swaggerhub.com/apis/deviantony/Portainer/1.19.2/#/users/ +- name: Check for portainer admin account + shell: > + curl --silent -I -X GET "http://portainer.{{ root_domain }}/api/users/admin/check" -H "accept: application/json" + register: admin_account_check + delegate_to: localhost + +- name: Init admin account if it hasn't already + shell: > + curl -X POST "http://portainer.{{ root_domain }}/api/users/admin/init" -H "accept: application/json" -H \ + "Content-Type: application/json" -d "{ \"Username\": \"admin\", \"Password\": \"admin-password\"}" + when: not admin_account_check.stdout | search("204") + delegate_to: localhost \ No newline at end of file diff --git a/roles/startup-infrastructure/tasks/main.yml b/roles/startup-infrastructure/tasks/main.yml index 3346c76..9a060e6 100644 --- a/roles/startup-infrastructure/tasks/main.yml +++ b/roles/startup-infrastructure/tasks/main.yml @@ -16,5 +16,12 @@ become: true - name: Run stack deploy - shell: cd /data && docker stack deploy -c startup-infrastructure.yml startup-infrastructure - become: true \ No newline at end of file + shell: > + docker stack deploy -c /data/startup-infrastructure.yml startup-infrastructure + become: true + +- name: Give containers time to spin up + wait_for: + timeout: 120 + +- include_tasks: configure-portainer.yml \ No newline at end of file diff --git a/roles/startup-infrastructure/templates/docker-compose.yml.j2 b/roles/startup-infrastructure/templates/docker-compose.yml.j2 index 7cecc48..f7719e7 100644 --- a/roles/startup-infrastructure/templates/docker-compose.yml.j2 +++ b/roles/startup-infrastructure/templates/docker-compose.yml.j2 @@ -1,9 +1,12 @@ +#jinja2: lstrip_blocks: True +# ^that fixes tab in compose files when jinja2 compiles them +{% set docker_volumes = ['portainer_data','wekan-db','wekan-db-dump'] %} version: '3.1' networks: appnet: external: true - wekan: - driver: bridge + portainer: + driver: overlay services: traefik: @@ -16,11 +19,65 @@ services: - appnet volumes: - /var/run/docker.sock:/var/run/docker.sock:ro - command: --docker --docker.swarmMode --docker.domain=traefik --docker.watch --api --ping + command: --docker --docker.swarmMode --docker.domain={{ root_domain }} --docker.watch --api --ping # --acme --acme.email='test@test.com' --acme.storage='acme.json' --acme.entrypoint='https' deploy: mode: replicated replicas: 1 placement: constraints: - - node.role == manager \ No newline at end of file + - node.role == manager + + portainer-agent: + image: portainer/agent + environment: + # REQUIRED: Should be equal to the service name prefixed by "tasks." when + # deployed inside an overlay network + AGENT_CLUSTER_ADDR: tasks.portainer-agent + # AGENT_PORT: 9001 + # LOG_LEVEL: debug + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - /var/lib/docker/volumes:/var/lib/docker/volumes + networks: + - portainer + deploy: + mode: global + placement: + constraints: [node.platform.os == linux] + + portainer: + image: portainer/portainer + command: -H tcp://tasks.portainer-agent:9001 --tlsskipverify + volumes: + - portainer_data:/data + networks: + - portainer + - appnet + deploy: + labels: + - "traefik.frontend.entryPoints=http" + - "traefik.protocol=http" + - "traefik.backend=portainer" + - "traefik.port=9000" + - "traefik.docker.network=appnet" + - "traefik.frontend.rule=Host:portainer.{{ root_domain }}" + mode: replicated + replicas: 1 + placement: + constraints: + - node.role == manager + +volumes: +{% for volume in docker_volumes %} + {{ volume }}: + {% if storage_type == 'nfs' %} + driver: local + driver_opts: + type: nfs + o: "addr={{ nfs_address }},soft,nolock,rw" + device: ":{{ nfs_root_path }}/{{ volume }}" + {% elif storage_type == 'local' %} + driver: local + {% endif %} +{% endfor %} \ No newline at end of file diff --git a/roles/startup-infrastructure/templates/old-code.yml b/roles/startup-infrastructure/templates/old-code.yml index 17b12fc..bd01ba0 100644 --- a/roles/startup-infrastructure/templates/old-code.yml +++ b/roles/startup-infrastructure/templates/old-code.yml @@ -1,154 +1,116 @@ - portainer: - image: portainer/portainer - networks: - - appnet - volumes: - - portainer_data:/data - - /var/run/docker.sock:/var/run/docker.sock:ro - deploy: - labels: - - "traefik.frontend.entryPoints=http" - - "traefik.protocol=http" - - "traefik.backend=portainer" - - "traefik.port=9000" - - "traefik.docker.network=appnet" - - "traefik.frontend.rule=Host:portainer.{{ root_domain }}" - mode: replicated - replicas: 1 - {% if {{ groups['workers'] | length }} > 0 %} - placement: - constraints: - - node.role == worker - {% endif %} - - bitwarden: - image: mprasil/bitwarden - networks: - - appnet - volumes: - - bitwarden_data:/data - deploy: - labels: - - "traefik.frontend.entryPoints=http" - - "traefik.protocol=http" - - "traefik.backend=bitwarden" - - "traefik.port=80" - - "traefik.docker.network=appnet" - - "traefik.frontend.rule=Host:bitwarden.{{ root_domain }}" - mode: replicated - replicas: 1 - {% if {{ groups['workers'] | length }} > 0 %} - placement: - constraints: - - node.role == worker - {% endif %} - - gitea: - image: gitea/gitea:latest - environment: - - USER_UID=1000 - - USER_GID=1000 - networks: - - appnet - volumes: - - gitea_data:/data - ports: - - "2222:22" - deploy: - labels: - - "traefik.frontend.entryPoints=http" - - "traefik.protocol=http" - - "traefik.backend=git" - - "traefik.port=3000" - - "traefik.docker.network=appnet" - - "traefik.frontend.rule=Host:git.{{ root_domain }}" - mode: replicated - replicas: 1 - {% if {{ groups['workers'] | length }} > 0 %} - placement: - constraints: - - node.role == worker - {% endif %} - - dokuwiki: - image: mprasil/dokuwiki - networks: - - appnet - volumes: - - dokuwiki_data:/dokuwiki - deploy: - labels: - - "traefik.frontend.entryPoints=http" - - "traefik.protocol=http" - - "traefik.backend=dokuwiki" - - "traefik.port=80" - - "traefik.docker.network=appnet" - - "traefik.frontend.rule=Host:dokuwiki.{{ root_domain }}" - mode: replicated - replicas: 1 - {% if {{ groups['workers'] | length }} > 0 %} - placement: - constraints: - - node.role == worker - {% endif %} - - wekandb: - # All Wekan data is stored in MongoDB. For backup and restore, see: - # https://github.com/wekan/wekan/wiki/Export-Docker-Mongo-Data - image: mongo:3.2.21 - command: mongod --smallfiles --oplogSize 128 - networks: - - wekan - volumes: - - wekan-db:/data/db - - wekan-db-dump:/dump - deploy: - mode: replicated - replicas: 1 - {% if {{ groups['workers'] | length }} > 0 %} - placement: - constraints: - - node.role == worker - {% endif %} - - wekan: - image: quay.io/wekan/wekan - networks: - - wekan - - appnet - environment: - - ROOT_URL=http://{{ root_domain }} - - MONGO_URL=mongodb://wekandb:27017/wekan - #- MAIL_URL=smtp://user:pass@mailserver.example.com:25/ - #- MAIL_FROM='Example Wekan Support ' - - WITH_API=true - deploy: - labels: - - "traefik.frontend.entryPoints=http" - - "traefik.protocol=http" - - "traefik.backend=wekan" - - "traefik.port=8080" - - "traefik.docker.network=appnet" - - "traefik.frontend.rule=Host:wekan.{{ root_domain }}" - mode: replicated - replicas: 1 - {% if {{ groups['workers'] | length }} > 0 %} - placement: - constraints: - - node.role == worker - {% endif %} - -{% set docker_volumes = ['portainer_data','bitwarden_data','gitea_data','dokuwiki_data','wekan-db','wekan-db-dump'] %} -volumes: -{% for volume in docker_volumes %} - {{ volume }}: - {% if storage_type == 'nfs' %} - driver: local - driver_opts: - type: nfs - o: "addr={{ nfs_address }},soft,nolock,rw" - device: ":{{ nfs_root_path }}/{{ volume }}" - {% elif storage_type == 'local' %} - driver: local - {% endif %} -{% endfor %} \ No newline at end of file + wekandb: + # All Wekan data is stored in MongoDB. For backup and restore, see: + # https://github.com/wekan/wekan/wiki/Export-Docker-Mongo-Data + image: mongo:3.2.21 + command: mongod --smallfiles --oplogSize 128 + networks: + - wekan + volumes: + - wekan-db:/data/db + - wekan-db-dump:/dump + deploy: + mode: replicated + replicas: 1 + {% if (groups['workers'] | length) > 0 %} + placement: + constraints: + - node.role == worker + {% endif %} + + wekan: + image: quay.io/wekan/wekan + networks: + - wekan + - appnet + environment: + - ROOT_URL=http://{{ root_domain }} + - MONGO_URL=mongodb://wekandb:27017/wekan + #- MAIL_URL=smtp://user:pass@mailserver.example.com:25/ + #- MAIL_FROM='Example Wekan Support ' + - WITH_API=true + deploy: + labels: + - "traefik.frontend.entryPoints=http" + - "traefik.protocol=http" + - "traefik.backend=wekan" + - "traefik.port=8080" + - "traefik.docker.network=appnet" + - "traefik.frontend.rule=Host:wekan.{{ root_domain }}" + mode: replicated + replicas: 1 + {% if (groups['workers'] | length) > 0 %} + placement: + constraints: + - node.role == worker + {% endif %} + + bitwarden: + image: mprasil/bitwarden + networks: + - appnet + volumes: + - bitwarden_data:/data + deploy: + labels: + - "traefik.frontend.entryPoints=http" + - "traefik.protocol=http" + - "traefik.backend=bitwarden" + - "traefik.port=80" + - "traefik.docker.network=appnet" + - "traefik.frontend.rule=Host:bitwarden.{{ root_domain }}" + mode: replicated + replicas: 1 + {% if (groups['workers'] | length) > 0 %} + placement: + constraints: + - node.role == worker + {% endif %} + + gitea: + image: gitea/gitea:latest + environment: + - USER_UID=1000 + - USER_GID=1000 + networks: + - appnet + volumes: + - gitea_data:/data + ports: + - "2222:22" + deploy: + labels: + - "traefik.frontend.entryPoints=http" + - "traefik.protocol=http" + - "traefik.backend=git" + - "traefik.port=3000" + - "traefik.docker.network=appnet" + - "traefik.frontend.rule=Host:git.{{ root_domain }}" + mode: replicated + replicas: 1 + {% if (groups['workers'] | length) > 0 %} + placement: + constraints: + - node.role == worker + {% endif %} + + dokuwiki: + image: mprasil/dokuwiki + networks: + - appnet + volumes: + - dokuwiki_data:/dokuwiki + deploy: + labels: + - "traefik.frontend.entryPoints=http" + - "traefik.protocol=http" + - "traefik.backend=dokuwiki" + - "traefik.port=80" + - "traefik.docker.network=appnet" + - "traefik.frontend.rule=Host:dokuwiki.{{ root_domain }}" + mode: replicated + replicas: 1 + {% if (groups['workers'] | length) > 0 %} + placement: + constraints: + - node.role == worker + {% endif %} \ No newline at end of file diff --git a/tests/files/group_vars_all b/tests/files/group_vars_all index 336cc02..97ff688 100644 --- a/tests/files/group_vars_all +++ b/tests/files/group_vars_all @@ -11,6 +11,7 @@ chosen_timezone: "America/New_York" # root domain for all services. You should have an A record for *.root_domain. For example, if your domain is test.com you should have an A record for *.test.com pointing to your node. # this will allow automatic dns for for things like dokuwiki.test.com and portainer.test.com root_domain: test.com +portainer_admin_password: "admin-password" # interface for the swarm network swarm_network_interface: enp0s8 diff --git a/tests/files/provision-script.sh b/tests/files/provision-script.sh index 7646878..54de01e 100644 --- a/tests/files/provision-script.sh +++ b/tests/files/provision-script.sh @@ -6,6 +6,7 @@ echo "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDYa9zstumlg7XkKoNrJMlIN/zteqMA9J4Gju # Setting A record echo "192.168.254.2 swarm.test.com" >> /etc/hosts +echo "192.168.254.2 portainer.test.com" >> /etc/hosts cp /vagrant/tests/files/test_rsa /home/vagrant/test_rsa chmod 600 /home/vagrant/test_rsa diff --git a/tests/vagrant-tests.sh b/tests/vagrant-tests.sh index d11c875..d61596e 100644 --- a/tests/vagrant-tests.sh +++ b/tests/vagrant-tests.sh @@ -47,6 +47,9 @@ function run-tests { testbash "Traefik got deployed" \ "vagrant ssh client -c 'curl --silent http://swarm.test.com:8081/ping | grep OK > /dev/null'" + testbash "Portainer was deployed and admin account was initialized" \ + "vagrant ssh client -c 'curl --silent -I \ + -X GET \"http://portainer.test.com/api/users/admin/check\" -H \"accept: application/json\"' | grep 204" } function destroy-infrastructure {