diff --git a/ansible/roles/firstboot/files/ansible_payload/roles/metacluster/tasks/assets.yml b/ansible/roles/firstboot/files/ansible_payload/roles/metacluster/tasks/assets.yml new file mode 100644 index 0000000..d38d772 --- /dev/null +++ b/ansible/roles/firstboot/files/ansible_payload/roles/metacluster/tasks/assets.yml @@ -0,0 +1,12 @@ +- name: Extract container images + ansible.builtin.unarchive: + src: /opt/metacluster/container-images/image-tarballs.tgz + dest: /opt/metacluster/container-images + list_files: yes + register: imagetarballs + +- name: Import container images + ansible.builtin.command: + cmd: k3s ctr image import {{ item }} + chdir: /opt/metacluster/container-images + loop: "{{ imagetarballs.files }}" diff --git a/ansible/roles/firstboot/files/ansible_payload/roles/metacluster/tasks/certauthority.yml b/ansible/roles/firstboot/files/ansible_payload/roles/metacluster/tasks/certauthority.yml new file mode 100644 index 0000000..4dabfbc --- /dev/null +++ b/ansible/roles/firstboot/files/ansible_payload/roles/metacluster/tasks/certauthority.yml @@ -0,0 +1,95 @@ +- name: Install step-ca chart + kubernetes.core.helm: + name: step-certificates + chart_ref: /opt/metacluster/helm-charts/step-certificates + release_namespace: step-ca + create_namespace: yes + wait: yes + kubeconfig: "{{ kubeconfig.path }}" + values: "{{ components.stepcertificates.chart_values }}" + +- block: + + - name: Retrieve configmap w/ root certificate + kubernetes.core.k8s_info: + kind: ConfigMap + name: step-certificates-certs + namespace: step-ca + kubeconfig: "{{ kubeconfig.path }}" + register: stepca_cm_certs + + - name: Store root certificate in namespaced secrets + kubernetes.core.k8s: + state: present + definition: + apiVersion: v1 + kind: Secret + metadata: + name: step-certificates-certs + namespace: "{{ item }}" + data: + root_ca.crt: "{{ stepca_cm_certs.resources[0].data['root_ca.crt'] | b64encode }}" + kubeconfig: "{{ kubeconfig.path }}" + loop: + - argo-cd + - kube-system + +- name: Configure step-ca passthrough ingress + ansible.builtin.template: + src: ingressroutetcp.j2 + dest: /var/lib/rancher/k3s/server/manifests/{{ _template.name }}-manifest.yaml + owner: root + group: root + mode: 0600 + vars: + _template: + name: step-ca + namespace: step-ca + config: |2 + entryPoints: + - websecure + routes: + - match: HostSNI(`ca.{{ vapp['metacluster.fqdn'] }}`) + services: + - name: step-certificates + port: 443 + tls: + passthrough: true + notify: + - Apply manifests + +- name: Inject step-ca certificate into traefik container + ansible.builtin.blockinfile: + path: /var/lib/rancher/k3s/server/manifests/traefik-config.yaml + block: |2 + volumes: + - name: step-certificates-certs + mountPath: /step-ca + type: configMap + env: + - name: LEGO_CA_CERTIFICATES + value: /step-ca/root_ca.crt + marker: ' # {mark} ANSIBLE MANAGED BLOCK' + notify: + - Apply manifests + +- name: Trigger handlers + ansible.builtin.meta: flush_handlers + +- name: Retrieve step-ca configuration + kubernetes.core.k8s_info: + kind: ConfigMap + name: step-certificates-config + namespace: step-ca + kubeconfig: "{{ kubeconfig.path }}" + register: stepca_cm_config + +- name: Install root CA in system truststore + ansible.builtin.shell: + cmd: >- + step ca bootstrap \ + --ca-url=https://ca.{{ vapp['metacluster.fqdn'] }} \ + --fingerprint={{ stepca_cm_config.resources[0].data['defaults.json'] | from_json | json_query('fingerprint') }} \ + --install \ + --force + update-ca-certificates diff --git a/ansible/roles/firstboot/files/ansible_payload/roles/metacluster/tasks/cleanup.yml b/ansible/roles/firstboot/files/ansible_payload/roles/metacluster/tasks/cleanup.yml new file mode 100644 index 0000000..58bf9a9 --- /dev/null +++ b/ansible/roles/firstboot/files/ansible_payload/roles/metacluster/tasks/cleanup.yml @@ -0,0 +1,24 @@ +# - name: Create component entries in /etc/hosts +# ansible.builtin.lineinfile: +# path: /etc/hosts +# line: "{{ vapp['guestinfo.ipaddress'] }} {{ item + '.' + vapp['metacluster.fqdn'] }}" +# state: present +# loop: +# # TODO: Make this list dynamic +# - git +# - gitops +# - ingress +# - registry +# - storage + +- name: Delete container image tarballs/archives + ansible.builtin.file: + path: "{{ item }}" + state: absent + with_fileglob: /opt/metacluster/container-images/*.tar + +- name: Cleanup tempfile + ansible.builtin.file: + path: "{{ kubeconfig.path }}" + state: absent + when: kubeconfig.path is defined diff --git a/ansible/roles/firstboot/files/ansible_payload/roles/metacluster/tasks/git.yml b/ansible/roles/firstboot/files/ansible_payload/roles/metacluster/tasks/git.yml new file mode 100644 index 0000000..8a349f9 --- /dev/null +++ b/ansible/roles/firstboot/files/ansible_payload/roles/metacluster/tasks/git.yml @@ -0,0 +1,141 @@ +- block: + + - name: Install gitea chart + kubernetes.core.helm: + name: gitea + chart_ref: /opt/metacluster/helm-charts/gitea + release_namespace: gitea + create_namespace: yes + wait: yes + kubeconfig: "{{ kubeconfig.path }}" + values: "{{ components.gitea.chart_values }}" + + - name: Configure additional SSH ingress + ansible.builtin.template: + src: ingressroutetcp.j2 + dest: /var/lib/rancher/k3s/server/manifests/{{ _template.name }}-manifest.yaml + owner: root + group: root + mode: 0600 + vars: + _template: + name: gitea-ssh + namespace: gitea + config: |2 + entryPoints: + - ssh + routes: + - match: HostSNI(`*`) + services: + - name: gitea-ssh + port: 22 + notify: + - Apply manifests + + - name: Trigger handlers + ansible.builtin.meta: flush_handlers + + - name: Ensure gitea API availability + ansible.utils.cli_parse: + # Available from Gitea 1.17.x + # command: curl -k https://git.{{ vapp['metacluster.fqdn'] }}/api/healtz + command: curl -k https://git.{{ vapp['metacluster.fqdn'] }}/api/v1/version + parser: + name: ansible.utils.json + set_fact: api_readycheck + ignore_errors: + until: api_readycheck.version is defined + retries: 3 + delay: 30 + + - name: Generate gitea API token + ansible.builtin.uri: + url: https://git.{{ vapp['metacluster.fqdn'] }}/api/v1/users/administrator/tokens + method: POST + user: administrator + password: "{{ vapp['guestinfo.rootpw'] }}" + force_basic_auth: yes + body: + name: token_init_{{ lookup('password', '/dev/null length=5 chars=ascii_letters,digits') }} + register: gitea_api_token + + - name: Retrieve existing gitea configuration + ansible.builtin.uri: + url: https://git.{{ vapp['metacluster.fqdn'] }}/api/v1/repos/search + method: GET + register: gitea_existing_config + + - block: + + - name: Register SSH public key + ansible.builtin.uri: + url: https://git.{{ vapp['metacluster.fqdn'] }}/api/v1/user/keys + method: POST + headers: + Authorization: token {{ gitea_api_token.json.sha1 }} + body: + key: "{{ gitops_sshkey.public_key }}" + read_only: false + title: GitOps + + - name: Create organization(s) + ansible.builtin.uri: + url: https://git.{{ vapp['metacluster.fqdn'] }}/api/v1/orgs + method: POST + headers: + Authorization: token {{ gitea_api_token.json.sha1 }} + body: "{{ item }}" + loop: + - full_name: Meta-cluster + description: Meta-cluster configuration items + username: mc + website: https://git.{{ vapp['metacluster.fqdn'] }}/mc + location: '[...]' + visibility: public + - full_name: Workload-cluster + description: Workload-cluster configuration items + username: wl + website: https://git.{{ vapp['metacluster.fqdn'] }}/wl + location: '[...]' + visibility: public + loop_control: + label: "{{ item.full_name }}" + + - name: Create repositories + ansible.builtin.uri: + url: https://git.{{ vapp['metacluster.fqdn'] }}/api/v1/orgs/{{ item.organization }}/repos + method: POST + headers: + Authorization: token {{ gitea_api_token.json.sha1 }} + body: "{{ item.body }}" + loop: + - organization: mc + body: + name: GitOps.Config + # auto_init: true + # default_branch: main + description: GitOps manifests + - organization: wl + body: + name: Template.GitOps.Config + # auto_init: true + # default_branch: main + description: GitOps manifests + loop_control: + label: "{{ item.organization + '/' + item.body.name }}" + + - name: Rebase/Push source gitops repository + ansible.builtin.shell: + cmd: | + git config --local http.sslVerify false + git remote set-url origin https://administrator:{{ vapp['guestinfo.rootpw'] | urlencode }}@git.{{ vapp['metacluster.fqdn'] }}/mc/GitOps.Config.git + git push + chdir: /opt/metacluster/git-repositories/gitops + + when: (gitea_existing_config.json is undefined) or (gitea_existing_config.json.data | length == 0) + + module_defaults: + ansible.builtin.uri: + validate_certs: no + status_code: [200, 201] + body_format: json diff --git a/ansible/roles/firstboot/files/ansible_payload/roles/metacluster/tasks/gitops.yml b/ansible/roles/firstboot/files/ansible_payload/roles/metacluster/tasks/gitops.yml new file mode 100644 index 0000000..da6fecf --- /dev/null +++ b/ansible/roles/firstboot/files/ansible_payload/roles/metacluster/tasks/gitops.yml @@ -0,0 +1,60 @@ +- block: + + - name: Install argo-cd chart + kubernetes.core.helm: + name: argo-cd + chart_ref: /opt/metacluster/helm-charts/argo-cd + release_namespace: argo-cd + create_namespace: yes + wait: yes + kubeconfig: "{{ kubeconfig.path }}" + values: "{{ components.argocd.chart_values }}" + + - name: Ensure argo-cd API availability + ansible.utils.cli_parse: + command: curl -k https://gitops.{{ vapp['metacluster.fqdn'] }}/api/version + parser: + name: ansible.utils.json + set_fact: api_readycheck + ignore_errors: + until: api_readycheck.Version is defined + retries: 3 + delay: 30 + + - name: Generate argo-cd API token + ansible.builtin.uri: + url: https://gitops.{{ vapp['metacluster.fqdn'] }}/api/v1/session + method: POST + force_basic_auth: yes + body: + username: admin + password: "{{ vapp['guestinfo.rootpw'] }}" + register: argocd_api_token + + # - name: Create umbrella application + # ansible.builtin.template: + # + - name: Configure metacluster-gitops repository + ansible.builtin.template: + src: gitrepo.j2 + dest: /var/lib/rancher/k3s/server/manifests/{{ _template.name }}-manifest.yaml + owner: root + group: root + mode: 0600 + vars: + _template: + name: argocd-gitrepo-metacluster + namespace: argo-cd + uid: "{{ lookup('ansible.builtin.password', '/dev/null length=5 chars=ascii_lowercase,digits seed=inventory_hostname') }}" + privatekey: "{{ lookup('ansible.builtin.file', '~/.ssh/git_rsa_id') | indent(4, true) }}" + notify: + - Apply manifests + + - name: Trigger handlers + ansible.builtin.meta: flush_handlers + + module_defaults: + ansible.builtin.uri: + validate_certs: no + status_code: [200, 201] + body_format: json diff --git a/ansible/roles/firstboot/files/ansible_payload/roles/metacluster/tasks/ingress.yml b/ansible/roles/firstboot/files/ansible_payload/roles/metacluster/tasks/ingress.yml new file mode 100644 index 0000000..9e43122 --- /dev/null +++ b/ansible/roles/firstboot/files/ansible_payload/roles/metacluster/tasks/ingress.yml @@ -0,0 +1,26 @@ +- name: Configure traefik dashboard ingress + ansible.builtin.template: + src: ingressroute.j2 + dest: /var/lib/rancher/k3s/server/manifests/{{ _template.name }}-manifest.yaml + owner: root + group: root + mode: 0600 + vars: + _template: + name: traefik-dashboard + namespace: kube-system + config: |2 + entryPoints: + - web + - websecure + routes: + - kind: Rule + match: Host(`ingress.{{ vapp['metacluster.fqdn'] }}`) + services: + - kind: TraefikService + name: api@internal + notify: + - Apply manifests + +- name: Trigger handlers + ansible.builtin.meta: flush_handlers diff --git a/ansible/roles/firstboot/files/ansible_payload/roles/metacluster/tasks/k3s.yml b/ansible/roles/firstboot/files/ansible_payload/roles/metacluster/tasks/k3s.yml new file mode 100644 index 0000000..ae8ec25 --- /dev/null +++ b/ansible/roles/firstboot/files/ansible_payload/roles/metacluster/tasks/k3s.yml @@ -0,0 +1,44 @@ +- name: Gather service facts + ansible.builtin.service_facts: + # Module requires no attributes + +- name: Install K3s + ansible.builtin.command: + cmd: ./install.sh + chdir: /opt/metacluster/k3s + environment: + INSTALL_K3S_SKIP_DOWNLOAD: 'true' + INSTALL_K3S_EXEC: 'server --cluster-init --disable local-storage' + when: ansible_facts.services['k3s.service'] is undefined + +- name: Ensure API availability + ansible.utils.cli_parse: + command: curl -k https://{{ vapp['guestinfo.ipaddress'] }}:6443/livez?verbose + parser: + name: ansible.utils.json + set_fact: api_readycheck + ignore_errors: yes + until: api_readycheck.apiVersion is defined + retries: 3 + delay: 30 + +- name: Install kubectl tab-completion + ansible.builtin.shell: + cmd: kubectl completion bash | tee /etc/bash_completion.d/kubectl + +- name: Initialize tempfile + ansible.builtin.tempfile: + state: file + register: kubeconfig + +- name: Retrieve kubeconfig + ansible.builtin.command: + cmd: kubectl config view --raw + register: kubectl_config + +- name: Store kubeconfig in tempfile + ansible.builtin.copy: + dest: "{{ kubeconfig.path }}" + content: "{{ kubectl_config.stdout }}" + mode: 0600 + no_log: true diff --git a/ansible/roles/firstboot/files/ansible_payload/roles/metacluster/tasks/main.yml b/ansible/roles/firstboot/files/ansible_payload/roles/metacluster/tasks/main.yml index 254a61c..fb8ba07 100644 --- a/ansible/roles/firstboot/files/ansible_payload/roles/metacluster/tasks/main.yml +++ b/ansible/roles/firstboot/files/ansible_payload/roles/metacluster/tasks/main.yml @@ -1,442 +1,9 @@ -- name: Gather service facts - ansible.builtin.service_facts: - # Module requires no attributes - -- name: Install K3s - ansible.builtin.command: - cmd: ./install.sh - chdir: /opt/metacluster/k3s - environment: - INSTALL_K3S_SKIP_DOWNLOAD: 'true' - INSTALL_K3S_EXEC: 'server --cluster-init --disable local-storage' - when: ansible_facts.services['k3s.service'] is undefined - -- name: Ensure API availability - ansible.utils.cli_parse: - command: curl -k https://{{ vapp['guestinfo.ipaddress'] }}:6443/livez?verbose - parser: - name: ansible.utils.json - set_fact: api_readycheck - ignore_errors: yes - until: api_readycheck.apiVersion is defined - retries: 3 - delay: 30 - -- name: Install kubectl tab-completion - ansible.builtin.shell: - cmd: kubectl completion bash | tee /etc/bash_completion.d/kubectl - -- name: Extract container images - ansible.builtin.unarchive: - src: /opt/metacluster/container-images/image-tarballs.tgz - dest: /opt/metacluster/container-images - list_files: yes - register: imagetarballs - -- name: Import container images - ansible.builtin.command: - cmd: k3s ctr image import {{ item }} - chdir: /opt/metacluster/container-images - loop: "{{ imagetarballs.files }}" - -- name: Initialize tempfile - ansible.builtin.tempfile: - state: file - register: kubeconfig - -- name: Retrieve kubeconfig - ansible.builtin.command: - cmd: kubectl config view --raw - register: kubectl_config - -- name: Store kubeconfig in tempfile - ansible.builtin.copy: - dest: "{{ kubeconfig.path }}" - content: "{{ kubectl_config.stdout }}" - mode: 0600 - no_log: true - -- name: Configure traefik dashboard ingress - ansible.builtin.template: - src: ingressroute.j2 - dest: /var/lib/rancher/k3s/server/manifests/{{ _template.name }}-manifest.yaml - owner: root - group: root - mode: 0600 - vars: - _template: - name: traefik-dashboard - namespace: kube-system - config: |2 - entryPoints: - - web - - websecure - routes: - - kind: Rule - match: Host(`ingress.{{ vapp['metacluster.fqdn'] }}`) - services: - - kind: TraefikService - name: api@internal - notify: - - Apply manifests - -- name: Trigger handlers - ansible.builtin.meta: flush_handlers - -- name: Create component entries in /etc/hosts - ansible.builtin.lineinfile: - path: /etc/hosts - line: "{{ vapp['guestinfo.ipaddress'] }} {{ item + '.' + vapp['metacluster.fqdn'] }}" - state: present - loop: - # TODO: Make this list dynamic - - git - - gitops - - ingress - - registry - - storage - -- name: Install longhorn chart - kubernetes.core.helm: - name: longhorn - chart_ref: /opt/metacluster/helm-charts/longhorn - release_namespace: longhorn-system - create_namespace: yes - wait: yes - kubeconfig: "{{ kubeconfig.path }}" - values: "{{ components.longhorn.chart_values }}" - -- name: Install step-ca chart - kubernetes.core.helm: - name: step-certificates - chart_ref: /opt/metacluster/helm-charts/step-certificates - release_namespace: kube-system - create_namespace: yes - wait: yes - kubeconfig: "{{ kubeconfig.path }}" - values: "{{ components.stepcertificates.chart_values }}" - -- name: Configure step-ca passthrough ingress - ansible.builtin.template: - src: ingressroutetcp.j2 - dest: /var/lib/rancher/k3s/server/manifests/{{ _template.name }}-manifest.yaml - owner: root - group: root - mode: 0600 - vars: - _template: - name: step-ca - namespace: kube-system - config: |2 - entryPoints: - - websecure - routes: - - match: HostSNI(`ca.{{ vapp['metacluster.fqdn'] }}`) - services: - - name: step-certificates - port: 443 - tls: - passthrough: true - notify: - - Apply manifests - -- name: Inject step-ca certificate into traefik container - ansible.builtin.blockinfile: - path: /var/lib/rancher/k3s/server/manifests/traefik-config.yaml - block: |2 - volumes: - - name: step-certificates-certs - mountPath: /step-ca - type: configMap - env: - - name: LEGO_CA_CERTIFICATES - value: /step-ca/root_ca.crt - marker: ' # {mark} ANSIBLE MANAGED BLOCK' - notify: - - Apply manifests - -- name: Trigger handlers - ansible.builtin.meta: flush_handlers - -- name: Retrieve step-ca configuration - kubernetes.core.k8s_info: - kind: ConfigMap - name: step-certificates-config - namespace: kube-system - kubeconfig: "{{ kubeconfig.path }}" - register: stepca_configmap - -- name: Install root CA in system truststore - ansible.builtin.shell: - cmd: | - step ca bootstrap \ - --ca-url=https://ca.{{ vapp['metacluster.fqdn'] }} \ - --fingerprint={{ stepca_configmap.resources[0].data['defaults.json'] | from_json | json_query('fingerprint') }} \ - --install \ - --force - update-ca-certificates - -- name: Install harbor chart - kubernetes.core.helm: - name: harbor - chart_ref: /opt/metacluster/helm-charts/harbor - release_namespace: harbor - create_namespace: yes - wait: yes - kubeconfig: "{{ kubeconfig.path }}" - values: "{{ components.harbor.chart_values }}" - -- name: Push images to registry - ansible.builtin.shell: - cmd: >- - skopeo copy \ - --insecure-policy \ - --dest-tls-verify=false \ - --dest-creds admin:{{ vapp['guestinfo.rootpw'] }} \ - docker-archive:./{{ item }} \ - docker://registry.{{ vapp['metacluster.fqdn'] }}/library/$( \ - skopeo list-tags \ - --insecure-policy \ - docker-archive:./{{ item }} | \ - jq -r '.Tags[0]') - chdir: /opt/metacluster/container-images/ - loop: "{{ imagetarballs.files }}" - -- name: Configure K3s node for private registry - ansible.builtin.template: - dest: /etc/rancher/k3s/registries.yaml - src: registries.j2 - notify: - - Apply manifests - -- name: Trigger handlers - ansible.builtin.meta: flush_handlers - -- block: - - - name: Install gitea chart - kubernetes.core.helm: - name: gitea - chart_ref: /opt/metacluster/helm-charts/gitea - release_namespace: gitea - create_namespace: yes - wait: yes - kubeconfig: "{{ kubeconfig.path }}" - values: "{{ components.gitea.chart_values }}" - - - name: Configure additional SSH ingress - ansible.builtin.template: - src: ingressroutetcp.j2 - dest: /var/lib/rancher/k3s/server/manifests/{{ _template.name }}-manifest.yaml - owner: root - group: root - mode: 0600 - vars: - _template: - name: gitea-ssh - namespace: gitea - config: |2 - entryPoints: - - ssh - routes: - - match: HostSNI(`*`) - services: - - name: gitea-ssh - port: 22 - notify: - - Apply manifests - - - name: Trigger handlers - ansible.builtin.meta: flush_handlers - - - name: Ensure gitea API availability - ansible.utils.cli_parse: - # Available from Gitea 1.17.x - # command: curl -k https://git.{{ vapp['metacluster.fqdn'] }}/api/healtz - command: curl -k https://git.{{ vapp['metacluster.fqdn'] }}/api/v1/version - parser: - name: ansible.utils.json - set_fact: api_readycheck - ignore_errors: - until: api_readycheck.version is defined - retries: 3 - delay: 30 - - - name: Generate gitea API token - ansible.builtin.uri: - url: https://git.{{ vapp['metacluster.fqdn'] }}/api/v1/users/administrator/tokens - method: POST - user: administrator - password: "{{ vapp['guestinfo.rootpw'] }}" - force_basic_auth: yes - body: - name: token_init_{{ lookup('password', '/dev/null length=5 chars=ascii_letters,digits') }} - register: gitea_api_token - - - name: Retrieve existing gitea configuration - ansible.builtin.uri: - url: https://git.{{ vapp['metacluster.fqdn'] }}/api/v1/repos/search - method: GET - register: gitea_existing_config - - - ansible.builtin.uri: - url: "{{ item }}" - method: GET - headers: - Authorization: token {{ gitea_api_token.json.sha1 }} - register: gitea_existing_config_TST - loop: - # - https://git.{{ vapp['metacluster.fqdn'] }}/api/v1/orgs - - https://git.{{ vapp['metacluster.fqdn'] }}/api/v1/repos/search - - https://git.{{ vapp['metacluster.fqdn'] }}/api/v1/users/administrator/keys - ignore_errors: yes - - debug: - var: gitea_existing_config_TST.results[].json.data - - - block: - - - name: Register SSH public key - ansible.builtin.uri: - url: https://git.{{ vapp['metacluster.fqdn'] }}/api/v1/user/keys - method: POST - headers: - Authorization: token {{ gitea_api_token.json.sha1 }} - body: - key: "{{ gitops_sshkey.public_key }}" - read_only: false - title: GitOps - - - name: Create organization(s) - ansible.builtin.uri: - url: https://git.{{ vapp['metacluster.fqdn'] }}/api/v1/orgs - method: POST - headers: - Authorization: token {{ gitea_api_token.json.sha1 }} - body: "{{ item }}" - loop: - - full_name: Meta-cluster - description: Meta-cluster configuration items - username: mc - website: https://git.{{ vapp['metacluster.fqdn'] }}/mc - location: '[...]' - visibility: public - - full_name: Workload-cluster - description: Workload-cluster configuration items - username: wl - website: https://git.{{ vapp['metacluster.fqdn'] }}/wl - location: '[...]' - visibility: public - loop_control: - label: "{{ item.full_name }}" - - - name: Create repositories - ansible.builtin.uri: - url: https://git.{{ vapp['metacluster.fqdn'] }}/api/v1/orgs/{{ item.organization }}/repos - method: POST - headers: - Authorization: token {{ gitea_api_token.json.sha1 }} - body: "{{ item.body }}" - loop: - - organization: mc - body: - name: GitOps.Config - # auto_init: true - # default_branch: main - description: GitOps manifests - - organization: wl - body: - name: Template.GitOps.Config - # auto_init: true - # default_branch: main - description: GitOps manifests - loop_control: - label: "{{ item.organization + '/' + item.body.name }}" - - - name: Rebase/Push source gitops repository - ansible.builtin.shell: - cmd: | - git config --local http.sslVerify false - git remote set-url origin https://administrator:{{ vapp['guestinfo.rootpw'] | urlencode }}@git.{{ vapp['metacluster.fqdn'] }}/mc/GitOps.Config.git - git push - chdir: /opt/metacluster/git-repositories/gitops - - when: (gitea_existing_config.json is undefined) or (gitea_existing_config.json.data | length == 0) - - module_defaults: - ansible.builtin.uri: - validate_certs: no - status_code: [200, 201] - body_format: json - -- block: - - - name: Install argo-cd chart - kubernetes.core.helm: - name: argo-cd - chart_ref: /opt/metacluster/helm-charts/argo-cd - release_namespace: argo-cd - create_namespace: yes - wait: yes - kubeconfig: "{{ kubeconfig.path }}" - values: "{{ components.argocd.chart_values }}" - - - name: Ensure argo-cd API availability - ansible.utils.cli_parse: - command: curl -k https://gitops.{{ vapp['metacluster.fqdn'] }}/api/version - parser: - name: ansible.utils.json - set_fact: api_readycheck - ignore_errors: - until: api_readycheck.Version is defined - retries: 3 - delay: 30 - - - name: Generate argo-cd API token - ansible.builtin.uri: - url: https://gitops.{{ vapp['metacluster.fqdn'] }}/api/v1/session - method: POST - force_basic_auth: yes - body: - username: admin - password: "{{ vapp['guestinfo.rootpw'] }}" - register: argocd_api_token - - # - name: Create umbrella application - # ansible.builtin.template: - # - - name: Configure metacluster-gitops repository - ansible.builtin.template: - src: gitrepo.j2 - dest: /var/lib/rancher/k3s/server/manifests/{{ _template.name }}-manifest.yaml - owner: root - group: root - mode: 0600 - vars: - _template: - name: argocd-gitrepo-metacluster - namespace: argo-cd - uid: "{{ lookup('ansible.builtin.password', '/dev/null length=5 chars=ascii_lowercase,digits seed=inventory_hostname') }}" - privatekey: "{{ lookup('ansible.builtin.file', '~/.ssh/git_rsa_id') | indent(4, true) }}" - notify: - - Apply manifests - - - name: Trigger handlers - ansible.builtin.meta: flush_handlers - - module_defaults: - ansible.builtin.uri: - validate_certs: no - status_code: [200, 201] - body_format: json - -- name: Delete container image tarballs/archives - ansible.builtin.file: - path: "{{ item }}" - state: absent - with_fileglob: /opt/metacluster/container-images/*.tar - -- name: Cleanup tempfile - ansible.builtin.file: - path: "{{ kubeconfig.path }}" - state: absent - when: kubeconfig.path is defined +- import_tasks: k3s.yml +- import_tasks: assets.yml +- import_tasks: ingress.yml +- import_tasks: storage.yml +- import_tasks: certauthority.yml +- import_tasks: registry.yml +- import_tasks: git.yml +- import_tasks: gitops.yml +- import_tasks: cleanup.yml diff --git a/ansible/roles/firstboot/files/ansible_payload/roles/metacluster/tasks/registry.yml b/ansible/roles/firstboot/files/ansible_payload/roles/metacluster/tasks/registry.yml new file mode 100644 index 0000000..f82bf04 --- /dev/null +++ b/ansible/roles/firstboot/files/ansible_payload/roles/metacluster/tasks/registry.yml @@ -0,0 +1,35 @@ +- name: Install harbor chart + kubernetes.core.helm: + name: harbor + chart_ref: /opt/metacluster/helm-charts/harbor + release_namespace: harbor + create_namespace: yes + wait: yes + kubeconfig: "{{ kubeconfig.path }}" + values: "{{ components.harbor.chart_values }}" + +- name: Push images to registry + ansible.builtin.shell: + cmd: >- + skopeo copy \ + --insecure-policy \ + --dest-tls-verify=false \ + --dest-creds admin:{{ vapp['guestinfo.rootpw'] }} \ + docker-archive:./{{ item }} \ + docker://registry.{{ vapp['metacluster.fqdn'] }}/library/$( \ + skopeo list-tags \ + --insecure-policy \ + docker-archive:./{{ item }} | \ + jq -r '.Tags[0]') + chdir: /opt/metacluster/container-images/ + loop: "{{ imagetarballs.files }}" + +- name: Configure K3s node for private registry + ansible.builtin.template: + dest: /etc/rancher/k3s/registries.yaml + src: registries.j2 + notify: + - Apply manifests + +- name: Trigger handlers + ansible.builtin.meta: flush_handlers diff --git a/ansible/roles/firstboot/files/ansible_payload/roles/metacluster/tasks/storage.yml b/ansible/roles/firstboot/files/ansible_payload/roles/metacluster/tasks/storage.yml new file mode 100644 index 0000000..177dbd1 --- /dev/null +++ b/ansible/roles/firstboot/files/ansible_payload/roles/metacluster/tasks/storage.yml @@ -0,0 +1,9 @@ +- name: Install longhorn chart + kubernetes.core.helm: + name: longhorn + chart_ref: /opt/metacluster/helm-charts/longhorn + release_namespace: longhorn-system + create_namespace: yes + wait: yes + kubeconfig: "{{ kubeconfig.path }}" + values: "{{ components.longhorn.chart_values }}" diff --git a/ansible/vars/metacluster.yml b/ansible/vars/metacluster.yml index 2ac25fd..74925f3 100644 --- a/ansible/vars/metacluster.yml +++ b/ansible/vars/metacluster.yml @@ -14,7 +14,7 @@ platform: namespace: kube-system config: |2 additionalArguments: - - "--certificatesResolvers.stepca.acme.caserver=https://step-certificates.kube-system.svc.cluster.local/acme/acme/directory" + - "--certificatesResolvers.stepca.acme.caserver=https://step-certificates.step-ca.svc.cluster.local/acme/acme/directory" - "--certificatesResolvers.stepca.acme.email=admin" - "--certificatesResolvers.stepca.acme.storage=/data/acme.json" - "--certificatesResolvers.stepca.acme.tlsChallenge=true" @@ -79,7 +79,7 @@ components: --password-file=~/pwfile \ --force-cn rm ~/pwfile - dns: ca.{{ vapp['metacluster.fqdn'] }},step-certificates.kube-system.svc.cluster.local,127.0.0.1 + dns: ca.{{ vapp['metacluster.fqdn'] }},step-certificates.step-ca.svc.cluster.local,127.0.0.1 password: "{{ vapp['guestinfo.rootpw'] }}" provisioner: name: admin @@ -144,6 +144,29 @@ components: chart: argo/argo-cd parse_logic: helm template . | yq --no-doc eval '.. | .image? | select(.)' | sort -u | awk '!/ /' chart_values: !unsafe | + configs: + secret: + argocdServerAdminPassword: "{{ vapp['guestinfo.rootpw'] | password_hash('bcrypt') }}" + controller: + volumeMounts: + - name: custom-ca-certificates + mountPath: /etc/ssl/certs/root_ca.crt + subPath: root_ca.crt + volumes: + - name: custom-ca-certificates + secret: + defaultMode: 420 + secretName: step-certificates-certs + repoServer: + volumeMounts: + - name: custom-ca-certificates + mountPath: /etc/ssl/certs/root_ca.crt + subPath: root_ca.crt + volumes: + - name: custom-ca-certificates + secret: + defaultMode: 420 + secretName: step-certificates-certs server: extraArgs: - --insecure @@ -151,9 +174,15 @@ components: enabled: true hosts: - gitops.{{ vapp['metacluster.fqdn'] }} - configs: - secret: - argocdServerAdminPassword: "{{ vapp['guestinfo.rootpw'] | password_hash('bcrypt') }}" + volumeMounts: + - name: custom-ca-certificates + mountPath: /etc/ssl/certs/root_ca.crt + subPath: root_ca.crt + volumes: + - name: custom-ca-certificates + secret: + defaultMode: 420 + secretName: step-certificates-certs sealed-secrets: helm: