--- # Ensures a project exists and is configured in accordance with ICHP rules: # # - has correct RBAC (user role binding) # - has network policies # - has quota and limitranges # - has an EgressIP allocated # # Requires the following structure: # # role: # state: present or absent # requester: the user requesting the project # name: the name of the project # displayname: optional displayname (defaults to name) # rbac_level: cluster role to assign to requester # egress_ip: an available egress IP to allocate to the project # quota: compute resourcequotas # requests: compute reservation # cpu: max cpu reserved (1500m, 1.5 CPU) # memory: max memory reserved (2048Mi, 2Gi) # limits: compute limits # cpu: max cpu consumed (4000m, 4 CPUs) # memory: max memory consumed (4096Mi, 4Gi) # lrange: compute limitranges, for both container and pod # default: default limits and requests # limit: # cpu: role.lrange.min.cpu * role.lrange.ratio.cpu # memory: role.lrange.min.memory * role.lrange.ratio.memory # request: # cpu: defaults to whatever role.lrange.min.cpu is # memory: defaults to whatever role.lrange.min.memory is # max: maximum limits # cpu: maximum cpu limit (4000m, 4 cpus) # memory: maximum memory limit (4096Mi, 4Gi) # min: minimum requests # cpu: minimum requested cpu (50m, 5%) # memory: minimum requested memory (64Mi) # ratio: max limit-to-request ratio (x-to-1) # cpu: cpu lrr (4) # memory: memory lrr (4) # # IMPORTANT: XXX: ALL COMPUTE UNITS MUST BE IN milicores AND Mi! # - name: Show the values at verbosity 1+ ansible.builtin.debug: var: role verbosity: 1 # TODO: conditional block for state: present - name: Check the values and apply sanity if state=present. block: - name: Verify that the requesting user exists. kubernetes.core.k8s_info: kubeconfig: tmp/kubeconfig-ocp4 validate_certs: no api_version: user.openshift.io/v1 kind: user name: "{{ role.requester }}" register: requester - name: Fail if the user is missing. ansible.builtin.assert: that: - requester.resources is defined - requester.resources | length == 1 success_msg: "OK, requester exists as an OpenShift user." fail_msg: "FATAL: requester ({{ role.requester }}) does not exist as an OpenShift user." - name: Ensure that the project is not there yet. kubernetes.core.k8s_info: kubeconfig: tmp/kubeconfig-ocp4 validate_certs: no api_version: v1 kind: namespace name: "{{ role.name }}" register: namespace - name: Fail if the namespace exists. ansible.builtin.assert: that: - namespace.resources is defined - namespace.resources | length == 0 success_msg: "OK, project does not exist yet." fail_msg: "FATAL: project \"{{ role.name }}\" already exists; remove it using delete-project.yml and retry." - name: Ensure that the clusterrole exists. kubernetes.core.k8s_info: kubeconfig: tmp/kubeconfig-ocp4 validate_certs: no api_version: rbac.authorization.k8s.io/v1 kind: clusterrole name: "{{ role.rbac_level }}" register: clusterrole - name: Fail if the requested cluster role is missing. ansible.builtin.assert: that: - clusterrole.resources is defined - clusterrole.resources | length == 1 success_msg: "OK, clusterrole exists." fail_msg: "FATAL: clusterrole ({{ role.rbac_level }}) does not exist." - name: Get a full list of IPs from openshift.egress_range. ansible.builtin.set_fact: egressips_full: "{{ (openshift.egress_range | ansible.utils.usable_range)['usable_ips'] }}" - name: Get a list of allocated egress IPs in the cluster kubernetes.core.k8s_info: kubeconfig: tmp/kubeconfig-ocp4 validate_certs: no api_version: k8s.ovn.org/v1 kind: egressip register: egressips - name: Get the remaining available egress IPs from openshift.egress_range. ansible.builtin.set_fact: egressips_avail: "{{ egressips_full | difference(egressips | community.general.json_query('resources[*].status.items[*].egressIP') | flatten) }}" - name: Ensure that there are still available IPs. ansible.builtin.assert: that: - egressips_avail | length > 0 success_msg: "OK, {{ egressips_avail | length }} egress IP(s) still available" fail_msg: "FATAL: No egress IPs remain available. Please remove some projects and release their IPs, then retry." - name: Find an available egress IP from openshift.egress_range, or... ansible.builtin.set_fact: allocated_egressip: "{{ egressips_avail[0] }}" when: role.egress_ip is not defined - name: ...if egress IP was specified, ensure it is available and in openshift.egress_range. block: - name: Verify the requested IP is still available. ansible.builtin.assert: that: - role.egress_ip in egressips_avail success_msg: "OK, requested egress IP is still available." fail_msg: "FATAL: requested egress IP ({{ role.egress_ip }}) is not available or not from egress range ({{ openshift.egress_range }})." - name: If we survived up until here, that is an acceptable egress IP. ansible.builtin.set_fact: allocated_egressip: "{{ role.egress_ip }}" when: role.egress_ip is defined when: (role.state | default('present')) == 'present' - name: Verify that the project exists and is a valid ICHP namespace. block: - name: Ensure that the project is there. kubernetes.core.k8s_info: kubeconfig: tmp/kubeconfig-ocp4 validate_certs: no api_version: v1 kind: namespace name: "{{ role.name }}" register: namespace - name: Fail if the namespace exists. ansible.builtin.assert: that: - namespace.resources is defined - namespace.resources | length == 1 success_msg: "OK, project exists." fail_msg: "FATAL: project \"{{ role.name }}\" does not exist." - name: Fail if the namespace is not properly labeled. ansible.builtin.assert: that: - namespace.resources is defined - namespace.resources | length == 1 - namespace.resources[0].metadata.labels["ichp.ing.net/generated"] is defined success_msg: "OK, project looks like ICHP." fail_msg: "FATAL: project \"{{ role.name }}\" does not look like an ICHP project." when: (role.state | default('present')) == 'absent' - name: Apply the project template to the cluster with correct state set. kubernetes.core.k8s: kubeconfig: tmp/kubeconfig-ocp4 validate_certs: no template: templates/project-template.yml state: "{{ role.state | default('present') }}" ...