- block: # Below tasks circumvent usernames with `\` format, which causes CAPV to # incorrectly interpret the backslash (despite automatic escaping) as an escape sequence. # `vcenter_session.user` will instead contain the username in `@` format. - name: Generate vCenter API token ansible.builtin.uri: url: https://{{ vapp['hv.fqdn'] }}/api/session method: POST headers: Authorization: Basic {{ ( vapp['hv.username'] ~ ':' ~ vapp['hv.password'] ) | b64encode }} register: vcenterapi_token - name: Retrieve vCenter API session details ansible.builtin.uri: url: https://{{ vapp['hv.fqdn'] }}/api/session method: GET headers: vmware-api-session-id: "{{ vcenterapi_token.json }}" register: vcenter_session module_defaults: ansible.builtin.uri: validate_certs: no status_code: [200, 201] body_format: json - name: Configure clusterctl ansible.builtin.template: src: clusterctl.j2 dest: /opt/metacluster/cluster-api/clusterctl.yaml vars: _template: version: base: "{{ components.clusterapi.management.version.base }}" cert_manager: "{{ components.clusterapi.management.version.cert_manager }}" infrastructure_vsphere: "{{ components.clusterapi.management.version.infrastructure_vsphere }}" ipam_incluster: "{{ components.clusterapi.management.version.ipam_incluster }}" hv: fqdn: "{{ vapp['hv.fqdn'] }}" tlsthumbprint: "{{ tls_thumbprint.stdout }}" username: "{{ vcenter_session.json.user }}" password: "{{ vapp['hv.password'] }}" datacenter: "{{ vcenter_info.datacenter }}" datastore: "{{ vcenter_info.datastore }}" network: "{{ vcenter_info.network }}" resourcepool: "{{ vcenter_info.resourcepool }}" folder: "{{ vcenter_info.folder }}" cluster: nodetemplate: "{{ (components.clusterapi.workload.node_template.url | basename | split('.'))[:-1] | join('.') }}" publickey: "{{ vapp['guestinfo.rootsshkey'] }}" version: "{{ components.clusterapi.workload.version.k8s }}" vip: "{{ vapp['workloadcluster.vip'] }}" - name: Update image references to use local registry ansible.builtin.replace: dest: "{{ item.root ~ '/' ~ item.path }}" regexp: '([ ]+image:[ "]+)(?!({{ _template.pattern }}|"{{ _template.pattern }}))' replace: '\1{{ _template.pattern }}' vars: _template: pattern: registry.{{ vapp['metacluster.fqdn'] }}/library/ loop: "{{ lookup('community.general.filetree', '/opt/metacluster/cluster-api') }}" loop_control: label: "{{ item.path }}" when: - item.path is search('.yaml') - item.path is not search("clusterctl.yaml|metadata.yaml") - name: Generate kustomization template ansible.builtin.template: src: kustomization.cluster-template.j2 dest: /opt/metacluster/cluster-api/infrastructure-vsphere/{{ components.clusterapi.management.version.infrastructure_vsphere }}/kustomization.yaml vars: _template: fqdn: "{{ vapp['metacluster.fqdn'] }}" rootca: "{{ stepca_cm_certs.resources[0].data['root_ca.crt'] }}" script: # Base64 encoded; to avoid variable substitution when clusterctl parses the cluster-template.yml encoded: IyEvYmluL2Jhc2gKdm10b29sc2QgLS1jbWQgJ2luZm8tZ2V0IGd1ZXN0aW5mby5vdmZFbnYnID4gL3RtcC9vdmZlbnYKCklQQWRkcmVzcz0kKHNlZCAtbiAncy8uKlByb3BlcnR5IG9lOmtleT0iZ3Vlc3RpbmZvLmludGVyZmFjZS4wLmlwLjAuYWRkcmVzcyIgb2U6dmFsdWU9IlwoW14iXSpcKS4qL1wxL3AnIC90bXAvb3ZmZW52KQpTdWJuZXRNYXNrPSQoc2VkIC1uICdzLy4qUHJvcGVydHkgb2U6a2V5PSJndWVzdGluZm8uaW50ZXJmYWNlLjAuaXAuMC5uZXRtYXNrIiBvZTp2YWx1ZT0iXChbXiJdKlwpLiovXDEvcCcgL3RtcC9vdmZlbnYpCkdhdGV3YXk9JChzZWQgLW4gJ3MvLipQcm9wZXJ0eSBvZTprZXk9Imd1ZXN0aW5mby5pbnRlcmZhY2UuMC5yb3V0ZS4wLmdhdGV3YXkiIG9lOnZhbHVlPSJcKFteIl0qXCkuKi9cMS9wJyAvdG1wL292ZmVudikKRE5TPSQoc2VkIC1uICdzLy4qUHJvcGVydHkgb2U6a2V5PSJndWVzdGluZm8uZG5zLnNlcnZlcnMiIG9lOnZhbHVlPSJcKFteIl0qXCkuKi9cMS9wJyAvdG1wL292ZmVudikKTUFDQWRkcmVzcz0kKHNlZCAtbiAncy8uKnZlOkFkYXB0ZXIgdmU6bWFjPSJcKFteIl0qXCkuKi9cMS9wJyAvdG1wL292ZmVudikKCm1hc2syY2lkcigpIHsKICBjPTAKICB4PTAkKCBwcmludGYgJyVvJyAkezEvLy4vIH0gKQoKICB3aGlsZSBbICR4IC1ndCAwIF07IGRvCiAgICBsZXQgYys9JCgoeCUyKSkgJ3g+Pj0xJwogIGRvbmUKCiAgZWNobyAkYwp9CgpQcmVmaXg9JChtYXNrMmNpZHIgJFN1Ym5ldE1hc2spCgpjYXQgPiAvZXRjL25ldHBsYW4vMDEtbmV0Y2ZnLnlhbWwgPDxFT0YKbmV0d29yazoKICB2ZXJzaW9uOiAyCiAgcmVuZGVyZXI6IG5ldHdvcmtkCiAgZXRoZXJuZXRzOgogICAgaWQwOgogICAgICBzZXQtbmFtZTogZXRoMAogICAgICBtYXRjaDoKICAgICAgICBtYWNhZGRyZXNzOiAkTUFDQWRkcmVzcwogICAgICBhZGRyZXNzZXM6CiAgICAgICAgLSAkSVBBZGRyZXNzLyRQcmVmaXgKICAgICAgZ2F0ZXdheTQ6ICRHYXRld2F5CiAgICAgIG5hbWVzZXJ2ZXJzOgogICAgICAgIGFkZHJlc3NlcyA6IFskRE5TXQpFT0YKcm0gL2V0Yy9uZXRwbGFuLzUwKi55YW1sIC1mCgpzdWRvIG5ldHBsYW4gYXBwbHk= runcmds: - update-ca-certificates - bash /root/network.sh - name: Store custom cluster-template ansible.builtin.copy: dest: /opt/metacluster/cluster-api/custom-cluster-template.yaml content: "{{ lookup('kubernetes.core.kustomize', dir='/opt/metacluster/cluster-api/infrastructure-vsphere/' ~ components.clusterapi.management.version.infrastructure_vsphere ) }}" - name: Initialize Cluster API management cluster ansible.builtin.shell: cmd: >- clusterctl init \ -v5 \ --infrastructure vsphere:{{ components.clusterapi.management.version.infrastructure_vsphere }} \ --ipam in-cluster:{{ components.clusterapi.management.version.ipam_incluster }} \ --config ./clusterctl.yaml \ --kubeconfig {{ kubeconfig.path }} chdir: /opt/metacluster/cluster-api - name: Ensure CAPI/CAPV controller availability kubernetes.core.k8s_info: kind: Deployment name: "{{ item.name }}" namespace: "{{ item.namespace }}" wait: true kubeconfig: "{{ kubeconfig.path }}" loop: - name: capi-controller-manager namespace: capi-system - name: capv-controller-manager namespace: capv-system loop_control: label: "{{ item.name }}" - name: Parse vApp for workload cluster sizing ansible.builtin.set_fact: clustersize: >- {{ { 'controlplane': vapp['deployment.type'] | regex_findall('^cp(\d)+') | first, 'workers': vapp['deployment.type'] | regex_findall('w(\d)+$') | first } }} - name: Generate workload cluster manifest ansible.builtin.shell: cmd: >- clusterctl generate cluster \ {{ vapp['workloadcluster.name'] | lower }} \ --control-plane-machine-count {{ clustersize.controlplane }} \ --worker-machine-count {{ clustersize.workers }} \ --from ./custom-cluster-template.yaml \ --config ./clusterctl.yaml \ --kubeconfig {{ kubeconfig.path }} chdir: /opt/metacluster/cluster-api register: clusterctl_newcluster # TODO: move to git repo - name: Save workload cluster manifest ansible.builtin.copy: dest: /opt/metacluster/cluster-api/new-cluster.yaml content: "{{ clusterctl_newcluster.stdout }}" - name: Apply workload cluster manifest kubernetes.core.k8s: definition: >- {{ clusterctl_newcluster.stdout }} wait: yes kubeconfig: "{{ kubeconfig.path }}" # TODO: move to git repo - name: Wait for cluster to be available ansible.builtin.shell: cmd: >- kubectl wait clusters.cluster.x-k8s.io/{{ vapp['workloadcluster.name'] | lower }} \ --for=condition=Ready \ --timeout 0s register: cluster_readycheck until: cluster_readycheck is succeeded retries: "{{ playbook.retries }}" delay: "{{ playbook.delays.long }}" - name: Initialize tempfile ansible.builtin.tempfile: state: file register: capi_kubeconfig - name: Retrieve kubeconfig ansible.builtin.shell: cmd: >- clusterctl get kubeconfig \ {{ vapp['workloadcluster.name'] | lower }} \ --kubeconfig {{ kubeconfig.path }} register: capi_kubectl_config - name: Store kubeconfig in tempfile ansible.builtin.copy: dest: "{{ capi_kubeconfig.path }}" content: "{{ capi_kubectl_config.stdout }}" mode: 0600 no_log: true # TODO: move to git repo - name: Apply cni plugin manifest kubernetes.core.k8s: src: /opt/metacluster/cluster-api/cni-calico/{{ components.clusterapi.workload.version.calico }}/calico.yaml state: present wait: yes kubeconfig: "{{ capi_kubeconfig.path }}" # TODO: move to git repo