diff --git a/roles/migration_getfacts/defaults/main.yml b/roles/migration_getfacts/defaults/main.yml index 790ff69..cb79796 100644 --- a/roles/migration_getfacts/defaults/main.yml +++ b/roles/migration_getfacts/defaults/main.yml @@ -8,6 +8,9 @@ apis: rolebinding: default: "rbac.authorization.k8s.io/v1beta1" "3.7": "v1" + deployment: + default: "apps/v1" + "3.7": "extensions/v1beta1" daemonset: default: "apps/v1" "3.7": "extensions/v1beta1" diff --git a/roles/migration_getfacts/tasks/main.yml b/roles/migration_getfacts/tasks/main.yml index 0f63609..b4e466e 100644 --- a/roles/migration_getfacts/tasks/main.yml +++ b/roles/migration_getfacts/tasks/main.yml @@ -41,6 +41,7 @@ cronjob_api: "{{ apis.cronjob.get(oc_version, apis.cronjob.get('default')) }}" role_api: "{{ apis.role.get(oc_version, apis.role.get('default')) }}" rolebinding_api: "{{ apis.rolebinding.get(oc_version, apis.rolebinding.get('default')) }}" + deployment_api: "{{ apis.deployment.get(oc_version, apis.deployment.get('default')) }}" daemonset_api: "{{ apis.daemonset.get(oc_version, apis.daemonset.get('default')) }}" replicaset_api: "{{ apis.replicaset.get(oc_version, apis.replicaset.get('default')) }}" statefulset_api: "{{ apis.statefulset.get(oc_version, apis.statefulset.get('default')) }}" diff --git a/roles/ocp-29871-resquota/defaults/main.yml b/roles/ocp-29871-resquota/defaults/main.yml new file mode 100644 index 0000000..01b6cd7 --- /dev/null +++ b/roles/ocp-29871-resquota/defaults/main.yml @@ -0,0 +1,17 @@ +namespace: "ocp-29871-resquota" + +app_name: nginxapp +storage_size: 1Mi +html_accessmode: ReadWriteOnce +logs_accessmode: ReadWriteOnce + +migration_sample_name: "ocp-29871-resquota" +migration_plan_name: "{{ migration_sample_name }}-migplan-{{ ansible_date_time.epoch }}" +migration_name: "{{ migration_sample_name }}-mig-{{ ansible_date_time.epoch }}" + +with_deploy: true +with_migrate: true +with_cleanup: true +with_validate: true +pv: false +quiesce: false diff --git a/roles/ocp-29871-resquota/tasks/deploy.yml b/roles/ocp-29871-resquota/tasks/deploy.yml new file mode 100644 index 0000000..bc16e69 --- /dev/null +++ b/roles/ocp-29871-resquota/tasks/deploy.yml @@ -0,0 +1,51 @@ +- name: Check namespace + k8s_facts: + kind: Namespace + name: "{{ namespace }}" + register: ns + +- name: Create namespace + shell: "{{ oc_binary }} new-project {{ namespace }} --skip-config-write=true" + when: ns.resources | length == 0 + + +- name: Get LimitRange + k8s_info: + kind: LimitRange + namespace: "{{ namespace }}" + register: limitrange + until: limitrange.resources | length > 0 + ignore_errors: true + +- name: Remove LimitRanges so that default limit ranges dont interfere with the test + k8s: + kind: LimitRange + state : absent + namespace: "{{ namespace }}" + name: "{{ item.metadata.name }}" + loop: "{{ limitrange.resources | default([]) }}" + +- name: Create resource quota + k8s: + state : present + definition: "{{ lookup('template', 'resourcequota.yml.j2' )}}" + +- name: Create nginx deployment + k8s: + state : present + definition: "{{ lookup('template', 'nginx-deployment.yml.j2' )}}" + +- name: Wait unti nginx is up + k8s_facts: + kind: Pod + namespace: "{{ namespace }}" + label_selectors: "app={{ app_name }}" + field_selectors: + - status.phase=Running + register: pod + until: "true in (pod | json_query('resources[].status.containerStatuses[].ready'))" + retries: 30 + +- name: Upload an index html file + shell: "{{ oc_binary }} -n {{ namespace }} rsh $( {{oc_binary}} get pods -n {{ namespace }} -o jsonpath='{.items[0].metadata.name}') sh -c 'echo \"

HELLO WORLD

\" > /usr/share/nginx/html/index.html'" + diff --git a/roles/ocp-29871-resquota/tasks/main.yml b/roles/ocp-29871-resquota/tasks/main.yml new file mode 100644 index 0000000..08175e4 --- /dev/null +++ b/roles/ocp-29871-resquota/tasks/main.yml @@ -0,0 +1,24 @@ +- name: Get oc facts + include_role: + name: migration_getfacts + +- name: Cleanup resources + include_role: + name: migration_cleanup + when: with_cleanup|bool + +- name: Create resources + import_tasks: deploy.yml + when: with_deploy|bool + +- name: Start migration + import_tasks: migrate.yml + when: with_migrate|bool + +- name: Track migration + import_tasks: track.yml + when: with_migrate|bool + +- name: Validate migration + import_tasks: validate.yml + when: with_validate|bool diff --git a/roles/ocp-29871-resquota/tasks/migrate.yml b/roles/ocp-29871-resquota/tasks/migrate.yml new file mode 100644 index 0000000..ac543a0 --- /dev/null +++ b/roles/ocp-29871-resquota/tasks/migrate.yml @@ -0,0 +1,3 @@ +- name: Execute migration + include_role: + name: migration_run diff --git a/roles/ocp-29871-resquota/tasks/track.yml b/roles/ocp-29871-resquota/tasks/track.yml new file mode 100644 index 0000000..8a3cf7c --- /dev/null +++ b/roles/ocp-29871-resquota/tasks/track.yml @@ -0,0 +1,3 @@ +- name: Track migration + include_role: + name: migration_track diff --git a/roles/ocp-29871-resquota/tasks/validate.yml b/roles/ocp-29871-resquota/tasks/validate.yml new file mode 100644 index 0000000..91174b2 --- /dev/null +++ b/roles/ocp-29871-resquota/tasks/validate.yml @@ -0,0 +1,150 @@ +- name: Get Quota + k8s_info: + kind: ResourceQuota + namespace: "{{ namespace }}" + register: quota + retries: 20 + until: quota.resources | length > 0 + +- name: Get Pods + k8s_info: + kind: Pod + namespace: "{{ namespace }}" + label_selectors: "app={{ app_name }}" + register: pod + retries: 20 + until: pod.resources | length > 0 + +#- fail: +# msg: "There should be only one pod present, before and after the migration." +# when: pod.resources == 1 + +- name: Scale deployment and check that no more pods are allowed + k8s: + api_version: "{{ deployment_api }}" + kind: Deployment + name: "{{ app_name }}-deployment" + namespace: "{{ namespace }}" + definition: + spec: + replicas: 2 + register: scale + +- name: Get ReplicaSet + k8s_info: + api_version: "{{ deployment_api }}" + kind: ReplicaSet + namespace: "{{ namespace }}" + label_selectors: "app={{ app_name }}" + register: replica + until: replica.resources | length > 0 + +#- fail: +# msg: "ReplicaSet should not have created new pods, because resourcequota does not allow it" +# when: (replica.resources | first ).status.get('conditions', {}) | list | selectattr( 'type', 'equalto', 'ReplicaFailure') | list | length == 0 + +- name: Scale down again + k8s: + api_version: "{{ deployment_api }}" + kind: Deployment + name: "{{ app_name }}-deployment" + namespace: "{{ namespace }}" + definition: + spec: + replicas: 1 + +- name: Get Pods + k8s_info: + kind: Pod + namespace: "{{ namespace }}" + label_selectors: "app={{ app_name }}" + register: pod + retries: 20 + until: pod.resources | length == 1 + +- name: Check that no LoadBalancer can be created + k8s: + state : present + definition: "{{ lookup('template', 'service.yml.j2' ) }}" + vars: + service_type: 'LoadBalancer' + service_app: 'balancersvc' + register: lbalancer + ignore_errors: true + +- fail: + msg: "FAILURE. Resource quota should not allow to create more LoadBalancers" + when: lbalancer.failed != true or lbalancer.error != 403 + +- name: Check that no NodePort can be created + k8s: + state : present + definition: "{{ lookup('template', 'service.yml.j2' )}}" + vars: + service_type: 'NodePort' + service_app: 'podeportrsvc' + register: nodeport + ignore_errors: true + +- fail: + msg: "FAILURE. Resource quota should not allow to create more NodePort services" + when: nodeport.failed != true or nodeport.error != 403 + +- name: Check that a ClusterIP service can be created. No resourcequota limit reached. + k8s: + state : present + definition: "{{ lookup('template', 'service.yml.j2' )}}" + vars: + service_type: 'ClusterIP' + service_app: 'clustersvc' + register: clusterip + +- name: Remove the created ClusterIP service + k8s: + state : absent + definition: "{{ lookup('template', 'service.yml.j2' )}}" + vars: + service_type: 'ClusterIP' + service_app: 'clustersvc' + register: clusterip + +- name: Check that no more PVCs can be created + k8s: + state : present + definition: "{{ lookup('template', 'persistent-volume-claim.yml.j2' )}}" + register: pvc + ignore_errors: true + +- fail: + msg: "FAILURE. Resource quota should not allow to create pvcs" + when: pvc.failed != true or pvc.error != 403 + +- name: Get route + k8s_facts: + kind: Route + namespace: "{{ namespace }}" + label_selectors: "app={{ app_name }}" + register: nginx_route + until: nginx_route.resources | length > 0 + retries: 30 + +- name: Wait for pods running + k8s_info: + kind: Pod + namespace: "{{ namespace }}" + label_selectors: "app={{ app_name }}" + field_selectors: + - status.phase=Running + register: pod + retries: 20 + until: pod.resources | length == 1 + +- name: Acces the html file + uri: + url: http://{{ nginx_route.resources[0].spec.host }} + method: GET + status_code: 200 + retries: 10 + register: req + until: req.status == 200 + diff --git a/roles/ocp-29871-resquota/templates/nginx-deployment.yml.j2 b/roles/ocp-29871-resquota/templates/nginx-deployment.yml.j2 new file mode 100644 index 0000000..a4a5b1d --- /dev/null +++ b/roles/ocp-29871-resquota/templates/nginx-deployment.yml.j2 @@ -0,0 +1,104 @@ +apiVersion: v1 +items: +- apiVersion: v1 + kind: PersistentVolumeClaim + metadata: + labels: + app: {{ app_name }} + name: {{ app_name }}-logs + namespace: {{ namespace }} + spec: + accessModes: + - {{ logs_accessmode }} + resources: + requests: + storage: {{ storage_size }} +{% if storage_class is defined and storage_class != "default" %} + storageClassName: {{ storage_class | quote }} +{% endif %} +- apiVersion: v1 + kind: PersistentVolumeClaim + metadata: + labels: + app: {{ app_name }} + name: {{ app_name }}-html + namespace: {{ namespace }} + spec: + accessModes: + - {{ html_accessmode }} + resources: + requests: + storage: 50Mi +{% if storage_class is defined and storage_class != "default" %} + storageClassName: {{ storage_class | quote }} +{% endif %} +- apiVersion: {{ deployment_api }} + kind: Deployment + metadata: + labels: + app: {{ app_name }} + name: {{ app_name }}-deployment + namespace: {{ namespace }} + spec: + replicas: 1 + selector: + matchLabels: + app: {{ app_name }} + template: + metadata: + labels: + app: {{ app_name }} + spec: + containers: + - image: docker.io/twalter/openshift-nginx + resources: + limits: + cpu: "1" + memory: 128Mi + name: {{ app_name }} + ports: + - containerPort: 8081 + volumeMounts: + - mountPath: /var/log/nginx + name: {{ app_name }}-logs + readOnly: false + - mountPath: /usr/share/nginx/html + name: {{ app_name }}-html + readOnly: false + volumes: + - name: {{ app_name }}-logs + persistentVolumeClaim: + claimName: {{ app_name }}-logs + - name: {{ app_name }}-html + persistentVolumeClaim: + claimName: {{ app_name }}-html +- apiVersion: v1 + kind: Service + metadata: + labels: + app: {{ app_name }} + name: my-{{ app_name }} + namespace: {{ namespace }} + spec: + ports: + - port: 8081 + targetPort: 8081 + selector: + app: {{ app_name }} + type: ClusterIP +- apiVersion: route.openshift.io/v1 + kind: Route + metadata: + labels: + app: {{ app_name }} + service: my-{{ app_name }} + name: my-{{ app_name }} + namespace: {{ namespace }} + spec: + port: + targetPort: 8081 + to: + kind: Service + name: my-{{ app_name }} +kind: List +metadata: {} diff --git a/roles/ocp-29871-resquota/templates/persistent-volume-claim.yml.j2 b/roles/ocp-29871-resquota/templates/persistent-volume-claim.yml.j2 new file mode 100644 index 0000000..2e1860a --- /dev/null +++ b/roles/ocp-29871-resquota/templates/persistent-volume-claim.yml.j2 @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ namespace }}-test + namespace: {{ namespace }} +spec: + storageClassName: manual + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Mi diff --git a/roles/ocp-29871-resquota/templates/resourcequota.yml.j2 b/roles/ocp-29871-resquota/templates/resourcequota.yml.j2 new file mode 100644 index 0000000..9d3dd3b --- /dev/null +++ b/roles/ocp-29871-resquota/templates/resourcequota.yml.j2 @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: ResourceQuota +metadata: + name: object-quota + namespace: {{ namespace }} +spec: + hard: + persistentvolumeclaims: "2" + services.loadbalancers: "0" + services.nodeports: "0" + pods: "1" + replicationcontrollers: "1" + secrets: "6" + configmaps: "4" + services: "10" + limits.cpu: "20" + limits.memory: 20Gi + requests.cpu: "10" + requests.memory: 10Gi diff --git a/roles/ocp-29871-resquota/templates/service.yml.j2 b/roles/ocp-29871-resquota/templates/service.yml.j2 new file mode 100644 index 0000000..768005f --- /dev/null +++ b/roles/ocp-29871-resquota/templates/service.yml.j2 @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + app: {{ service_app }} + name: my-{{ service_app }} + namespace: {{ namespace }} +spec: + ports: + - port: 9999 + targetPort: 8888 + selector: + app: {{ service_app }} + type: {{ service_type }}