├── .applier ├── group_vars │ └── seed-hosts │ │ ├── auth.yml │ │ ├── aws.yml │ │ ├── main.yml │ │ └── openshift-machine-api.yml └── hosts ├── .gitignore ├── .yamllint ├── README.md ├── RHTE-LAB.adoc ├── manifests ├── apps │ ├── cakephp-namespace-config.yaml │ └── cakephp-namespaces.yaml ├── clusterautoscaler.yaml ├── namespace-config-operator-ns.yaml └── project-config │ ├── cluster-admins-myorg.yaml │ ├── default-project-template.yaml │ ├── gitops-config.yaml │ ├── multitenant-networkpolicy.yaml │ ├── project-config.yaml │ └── quota-tshirts.yaml ├── media ├── app-workflow.png └── ops-workflow.png ├── meta └── main.yml ├── requirements.yml ├── roles ├── discover │ ├── defaults │ │ └── main.yml │ ├── tasks │ │ └── main.yml │ └── tests │ │ ├── inventory │ │ └── hosts │ │ ├── roles │ │ └── test.yml ├── openshift_machine_api │ └── tasks │ │ ├── main.yml │ │ └── scale-up-group.yml └── roles └── templates ├── aws-ebs-storage-classes.yaml ├── cluster-gitops.yaml ├── custom-machinesets.j2 ├── default-machinesets.j2 ├── ldap-auth.yaml ├── ldap-cluster-secrets.yaml ├── ldap-group-sync.yaml └── scheduler.j2 /.applier/group_vars/seed-hosts/auth.yml: -------------------------------------------------------------------------------- 1 | # LDAP server URL 2 | ldap_url: "ldap://ipa.example.com" 3 | ldap_ca: "{{ lookup('file', '/path/to/ca.crt') }}" 4 | 5 | # LDAP BIND config for authentication and groups sync 6 | ldap_bind_dn: uid=admin,cn=users,cn=accounts,dc=example,dc=com 7 | 8 | # Do not store secrets in version control! 9 | ldap_bind_password: "{{ lookup('env', 'LDAP_BIND_PASSWORD') }}" 10 | 11 | # LDAP users group 12 | ldap_users_search_base: cn=users,cn=accounts,dc=example,dc=com 13 | 14 | # LDAP search URL used during authentication 15 | ldap_auth_search_filter: "(memberOf=cn=ocp-users,cn=groups,cn=accounts,dc=example,dc=com)" 16 | ldap_search_url: "{{ ldap_url }}/{{ ldap_users_search_base }}?uid?sub?{{ ldap_auth_search_filter }}" 17 | 18 | # LDAP group sync configuration 19 | ldap_cron_schedule: "@hourly" 20 | 21 | # Groups path for LDAP search 22 | ldap_groups_search_base: cn=groups,cn=accounts,dc=example,dc=com 23 | 24 | # Optional LDAP groups whitelist 25 | #ldap_groups_whitelist: | 26 | # cn=ocp-admins,cn=groups,cn=accounts,dc=example,dc=com 27 | # cn=ocp-users,cn=groups,cn=accounts,dc=example,dc=com 28 | -------------------------------------------------------------------------------- /.applier/group_vars/seed-hosts/aws.yml: -------------------------------------------------------------------------------- 1 | --- 2 | aws_sc_encrypt_storage: "true" 3 | -------------------------------------------------------------------------------- /.applier/group_vars/seed-hosts/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Exclude cluster-secrets when run from Eunomia GitOps operator 3 | exclude_tags: cluster-secrets 4 | 5 | openshift_cluster_content: 6 | - galaxy_requirements: "{{ inventory_dir }}/../requirements.yml" 7 | - object: Setup and Discovery 8 | pre_steps: 9 | - role: self/roles/discover 10 | content: 11 | - name: GitOps Config 12 | template: "{{ inventory_dir }}/../templates/cluster-gitops.yaml" 13 | tags: 14 | - gitops 15 | - object: OpenShift Machine API 16 | content: 17 | - name: Custom Machinesets 18 | file: "{{ inventory_dir }}/../templates/custom-machinesets.j2" 19 | tags: 20 | - openshift-machine-api 21 | - name: Default Machinesets 22 | file: "{{ inventory_dir }}/../templates/default-machinesets.j2" 23 | tags: 24 | - openshift-machine-api 25 | post_steps: 26 | - role: self/roles/openshift_machine_api 27 | - name: Cluster Autoscaler 28 | file: "{{ inventory_dir }}/../manifests/clusterautoscaler.yaml" 29 | - object: Scheduler 30 | content: 31 | - name: Cluster Autoscaler 32 | file: "{{ inventory_dir }}/../templates/scheduler.j2" 33 | - object: LDAP Cluster Secrets 34 | content: 35 | - name: LDAP Secrets 36 | template: "{{ inventory_dir }}/../templates/ldap-cluster-secrets.yaml" 37 | params_from_vars: 38 | LDAP_CA: "{{ ldap_ca }}" 39 | LDAP_BIND_PASSWORD: "{{ ldap_bind_password }}" 40 | namespace: openshift-config 41 | tags: 42 | - cluster-secrets 43 | - name: LDAP Authentication 44 | template: "{{ inventory_dir }}/../templates/ldap-auth.yaml" 45 | params_from_vars: 46 | LDAP_CA: "{{ ldap_ca }}" 47 | LDAP_BIND_PASSWORD: "{{ ldap_bind_password }}" 48 | LDAP_BIND_DN: "{{ ldap_bind_dn }}" 49 | LDAP_SEARCH_URL: "{{ ldap_search_url }}" 50 | namespace: openshift-config 51 | tags: 52 | - ldap_auth 53 | - object: LDAP Group Synchronization 54 | content: 55 | - name: LDAP Group Synchronization 56 | template: "{{ inventory_dir }}/../templates/ldap-group-sync.yaml" 57 | params_from_vars: 58 | LDAP_GROUPS_SEARCH_BASE: "{{ ldap_groups_search_base }}" 59 | LDAP_BIND_DN: "{{ ldap_bind_dn }}" 60 | LDAP_URL: "{{ ldap_url }}" 61 | LDAP_USERS_SEARCH_BASE: "{{ ldap_users_search_base }}" 62 | LDAP_GROUPS_WHITELIST: "{{ ldap_groups_whitelist | default('') }}" 63 | SCHEDULE: "{{ ldap_cron_schedule }}" 64 | namespace: openshift-config 65 | tags: 66 | - ldap_group_sync 67 | - object: Setup AWS StorageClasses 68 | content: 69 | - name: Setup AWS StorageClasses 70 | template: "{{ inventory_dir }}/../templates/aws-ebs-storage-classes.yaml" 71 | params_from_vars: 72 | ENCRYPT_STORAGE: "{{ aws_sc_encrypt_storage }}" 73 | namespace: openshift-config 74 | tags: 75 | - aws 76 | - aws-storage-classes 77 | - object: Configure Project Creation 78 | content: 79 | # Deploys the Namespace Configuration Operator 80 | # https://github.com/redhat-cop/namespace-configuration-operator/ 81 | - name: Namespace Config Operator Namespace 82 | file: "{{ inventory_dir }}/../manifests/namespace-config-operator-ns.yaml" 83 | tags: 84 | - project-config 85 | - name: Namespace Config Operator CRD 86 | file: https://raw.githubusercontent.com/redhat-cop/namespace-configuration-operator/v0.0.1/deploy/crds/redhatcop_v1alpha1_namespaceconfig_crd.yaml 87 | namespace: namespace-configuration-operator 88 | tags: 89 | - project-config 90 | - name: Namespace Config Operator Role 91 | file: https://raw.githubusercontent.com/redhat-cop/namespace-configuration-operator/v0.0.1/deploy/role.yaml 92 | namespace: namespace-configuration-operator 93 | tags: 94 | - project-config 95 | - name: Namespace Config Operator Role Binding 96 | file: https://raw.githubusercontent.com/redhat-cop/namespace-configuration-operator/v0.0.1/deploy/role_binding.yaml 97 | namespace: namespace-configuration-operator 98 | tags: 99 | - project-config 100 | - name: Namespace Config Operator Service Account 101 | file: https://raw.githubusercontent.com/redhat-cop/namespace-configuration-operator/v0.0.1/deploy/service_account.yaml 102 | namespace: namespace-configuration-operator 103 | tags: 104 | - project-config 105 | - name: Namespace Config Operator Deployment 106 | file: https://raw.githubusercontent.com/redhat-cop/namespace-configuration-operator/v0.0.1/deploy/operator.yaml 107 | namespace: namespace-configuration-operator 108 | tags: 109 | - project-config 110 | # Deploys the Onboarding Operator 111 | # https://github.com/redhat-cop/onboarding-manager 112 | - name: Project Configurations 113 | file: "{{ inventory_dir }}/../manifests/project-config/" 114 | tags: 115 | - project-config 116 | - name: Projects for Onboarded Applications 117 | file: "{{ inventory_dir }}/../manifests/apps/" 118 | tags: 119 | - project-config 120 | 121 | 122 | 123 | # Variables set dynamically by set_fact in discover role 124 | infraid: '' 125 | cloudregion: '' 126 | -------------------------------------------------------------------------------- /.applier/group_vars/seed-hosts/openshift-machine-api.yml: -------------------------------------------------------------------------------- 1 | --- 2 | machineset_disable_default_workers: True 3 | machineset_custom_groups: 4 | - name: compute 5 | autoscale: True 6 | max_replicas: 6 7 | min_replicas: 2 8 | roles: 9 | - compute 10 | - name: logging-es 11 | replicas: 1 12 | roles: 13 | - logging-es 14 | provider_spec_override: 15 | value: 16 | instanceType: m5.4xlarge 17 | 18 | scheduler_default_node_selector: node-role.kubernetes.io/compute= 19 | -------------------------------------------------------------------------------- /.applier/hosts: -------------------------------------------------------------------------------- 1 | [seed-hosts] 2 | localhost ansible_connection=local 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | galaxy/ 2 | *.swp 3 | -------------------------------------------------------------------------------- /.yamllint: -------------------------------------------------------------------------------- 1 | --- 2 | rules: 3 | braces: enable 4 | brackets: enable 5 | colons: enable 6 | commas: enable 7 | comments: 8 | level: warning 9 | comments-indentation: 10 | level: warning 11 | document-end: disable 12 | document-start: 13 | level: warning 14 | empty-lines: enable 15 | empty-values: enable 16 | hyphens: enable 17 | indentation: 18 | indent-sequences: false 19 | key-duplicates: enable 20 | key-ordering: disable 21 | line-length: 22 | max: 150 23 | new-line-at-end-of-file: enable 24 | new-lines: enable 25 | octal-values: enable 26 | quoted-strings: disable 27 | trailing-spaces: enable 28 | truthy: 29 | level: warning 30 | ignore: | 31 | galaxy/ 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # How to Apply Infrastructure as Code Principles to OpenShift 4 Operationalization 2 | 3 | ![GitOps Workflow](media/ops-workflow.png) 4 | 5 | In OpenShift 4, every cluster starts out the same way -- a bootstrapped control plane with 3 masters, and 3 compute nodes. But in order for this cluster to provide value to an organization, it needs further configuration. This repository is a reference implemntation that functions as a starting point for establishing an automated strategy for managing configuration of an operationalized cluster. 6 | 7 | We've included examples of: 8 | 9 | * Enabling Cluster Autoscaling 10 | * Configuring authentication and authorization 11 | * Managing namespace creation 12 | * Setting up quotas & limits for applications 13 | * Applying automated certificate management for applications 14 | * Deploying some initial workloads 15 | * Deploying some custom dashboards and setting up alerts 16 | 17 | This repository can be consumed in one of two ways: 18 | 19 | * [**As a reference implementation**](#quickstart) - Just follow the quickstart below on your own cluster 20 | * [**As an interactive lab**](RHTE-LAB.adoc) - The lab will walk through the setup and a few example use cases in more detail. 21 | 22 | ## Quickstart 23 | 24 | ### Setting up the Git Ops Operator 25 | 26 | _Git Ops_ is a form of Infrastructure as Code practice where all of the configurations that define a system are managed in a git repository, and automatically and idempotently applied to that system each time commits are made to the repository. For this lab, we are going to enable such a workflow using an Operator called [Eunomia](https://github.com/KohlsTechnology/eunomia) and [OpenShift Applier](https://github.com/redhat-cop/openshift-applier), and automation framework for OpenShift. Eunomia provides a workflow for watching git repos, triggering actions when new commits are detected. The action, in this case, will be a [Kubernetes Job](https://kubernetes.io/docs/tasks/job/) that executes Applier. 27 | 28 | Let's go ahead and deploy Eunomia. We can do that via provided helm charts. 29 | 30 | git clone https://github.com/KohlsTechnology/eunomia.git 31 | helm template eunomia/deploy/helm/prereqs/ | oc apply -f - 32 | helm template eunomia/deploy/helm/operator/ --set eunomia.operator.openshift.route.enabled=true | oc apply -f - 33 | 34 | ### Creating Cluster Secrets 35 | 36 | We are about to start managing clusters from configurations in a source code repository. However, we don't want to commit sensitive data to our repositories where someone might be able to see them. In an ideal enterprise scenario, we might integrate with some kind of Vault system to provide credentials for integrations that we would like to configure. In lieu of that, we are just going to feed our credentials to the cluster manually in the form of `Secrets`. 37 | 38 | Obtain the following values for the LDAP server: 39 | 40 | * LDAP URL 41 | * THe Address of the LDAP Server 42 | * Bind User (DN) 43 | * User to authenticate against the LDAP instance 44 | * Bind Password 45 | * The Password for the above user 46 | * User Search Base 47 | * Location for which to start looking for users in the LDAP structure 48 | * User Search Base 49 | * Location for which to start looking for groups in the LDAP structure 50 | * User White List 51 | * Contents of a file containing only the users that should be synchronized 52 | 53 | Apply the configurations to the cluster: 54 | 55 | ansible-playbook -i .applier/ galaxy/openshift-applier/playbooks/openshift-cluster-seed.yml \ 56 | -e include_tags=ldap_auth \ 57 | -e ldap_bind_dn="${ldap_bind_dn}" \ 58 | -e ldap_url="${ldap_url}" \ 59 | -e ldap_search_url="${ldap_search_url}" 60 | -e "ldap_groups_whitelist='$(cat whitelist)'" \ 61 | -e ldap_groups_search_base='${ldap_groups_search_base}' \ 62 | -e ldap_users_search_base="${ldap_users_search_base}" 63 | 64 | 65 | ### Initializing the GitOps Workflow 66 | 67 | Now we can set up Eunomia to monitor a repo of configs, which in turn will apply configs to set up our cluster. Eunomia provides a CR called a `GitOpsConfig` to set up a monitor on a repository. You can examine the one we're going to use at [templates/cluster-gitops.yaml](templates/cluster-gitops.yaml). Let's use Applier to roll out the config. 68 | 69 | git clone https://github.com/redhat-cop/operationalizing-openshift-lab 70 | cd operationalizing-openshift-lab 71 | ansible-galaxy install -r requirements.yml -p galaxy 72 | ansible-playbook -i .applier/ galaxy/openshift-applier/playbooks/openshift-cluster-seed.yml -e include_tags=gitops 73 | 74 | This run resulted in a namespace called `cluster-config` with our `GitOpsConfig` in it. From this, Eunomia spins up a job. Let's wait for that job to complete, and then see what we have. 75 | 76 | oc get jobs -n cluster-config 77 | 78 | You should see one job created in this namespace. To follow the progress of the job, we need to find the pod running the job. 79 | 80 | oc get pods -n cluster-config 81 | 82 | We should see that there is a pod running. Let's tail the logs of that pod. 83 | 84 | oc logs -f gitopsconfig-cluster-config-2bromv-wks8d -n cluster-config 85 | 86 | From this we should see that there is an OpenShift Applier playbook running. This will roll out the rest of the configurations stored in this repo. Once that job completes successfully, we can explore what has been set up for us. 87 | 88 | ## Cluster Exploration 89 | 90 | The above GitOps workflow will configure the following in your cluster, based on the configurations in this repo. 91 | 92 | ### MachineSets and AutoScalers 93 | 94 | As part of this automation, we've configured machinesets and machine autoscalers. You can see them with the following commands. 95 | 96 | oc get machineautoscalers -n openshift-machine-api 97 | oc get machinesets -n openshift-machine-api 98 | oc get nodes 99 | 100 | ### Authentication and Authorization 101 | 102 | #### LDAP Authentication 103 | 104 | OpenShift provides the capability to authenticate users stored in LDAP systems using the [LDAP identity provider](https://docs.openshift.com/container-platform/4.1/authentication/identity_providers/configuring-ldap-identity-provider.html). 105 | 106 | With the configurations applied, attempt to login with a user defined in the LDAP instance 107 | 108 | oc login -u -p 109 | 110 | To login to the web console, locate the URL of the OpenShift web console and navigate to the URL presented 111 | 112 | oc whoami --show-console 113 | 114 | Two login options are displayed. Select _LDAP_ and login with a valid username and password 115 | 116 | If you are authenticated using both methods, the configurations were LDAP authentication was successful. 117 | 118 | #### LDAP Group Synchronization 119 | 120 | Many organizations that make use of LDAP directory services arrange their users into Groups. This allows Administrators the ability to apply the same set of permissions across multiple users. A similar concept can be applied using OpenShift's Role Based Access (RBAC) facility where multiple users can be organized into groups and roles can be applied. Since OpenShift can make use of users defined in LDAP servers, [groups defined in LDAP can be synchronized into OpenShift](https://docs.openshift.com/container-platform/4.1/authentication/ldap-syncing.html) so that preexisting structures can also be applied. 121 | 122 | Since users, groups and their associated membership within the LDAP server changes frequently, it is important that the execution of the group synchronization process take place in a routine manner. While a job scheduler could be utilized, such as standalone [cron](https://en.wikipedia.org/wiki/Cron), OpenShift provides the facility to to execute tasks as _Jobs_ along with the repetitive execution of these jobs in a scheduled manner as a [CronJob](https://docs.openshift.com/container-platform/4.1/nodes/jobs/nodes-nodes-jobs.html). The process of executing the synchronization involves the use of the OpenShift command line tool which can triggered within a _CronJob_. A `LDAPSyncConfig` object defines how to connect to the LDAP instance from OpenShift along with the groups which should be synchronized. 123 | 124 | As part of the GitOps automation, we deployed a Group Sync config. You can check the status of the _Job_. 125 | 126 | oc get jobs 127 | 128 | The successful execution of the job will result in the _Completions_ field displaying `1/1`. 129 | 130 | Verify LDAP groups have been synchronized into OpenShift 131 | 132 | oc get groups 133 | 134 | The presence of groups and corresponding users indicate the successful completion of this iteration. 135 | 136 | ### Customized Project Creation 137 | 138 | When a user creates a project in this cluster, we use the Namespace Configuration Operator to apply custom rules to that project. 139 | 140 | The operator itself has been deployed to the cluster. You can examine the deployment with the following: 141 | 142 | oc describe deployment namespace-configuration-operator -n namespace-configuration-operator 143 | oc get pods -n namespace-configuration-operator 144 | oc logs -n namespace-configuration-operator $(oc get pod -n namespace-configuration-operator -o name) 145 | 146 | When a user creates a project, custom rules will be applied. We can see this through the following commands. 147 | 148 | $ oc new-project test-project-policy 149 | $ oc get resourcequota 150 | NAME CREATED AT 151 | large-size 2019-09-13T20:39:16Z 152 | 153 | $ oc get networkpolicy 154 | NAME POD-SELECTOR AGE 155 | allow-from-same-namespace 68s 156 | allow-network-policy-group-ingress 68s 157 | -------------------------------------------------------------------------------- /RHTE-LAB.adoc: -------------------------------------------------------------------------------- 1 | Operationalizing OpenShift Lab 2 | ============================== 3 | :toc: macro 4 | 5 | 6 | image::media/ops-workflow.png[] 7 | 8 | toc::[] 9 | 10 | In OpenShift 4, every cluster starts out the same way -- a bootstrapped control plane with master, and worker nodes. 11 | However, in order for this cluster to provide value to an organization, it needs further configuration. 12 | 13 | In this lab, we are going to develop a strategy for making these configurations and maintaining them over time. 14 | We are going to employ the concepts of 15 | link:https://openpracticelibrary.com/practice/everything-as-code/[Everything as Code^] and 16 | link:https://thenewstack.io/what-is-gitops-and-why-it-might-be-the-next-big-thing-for-devops/[GitOps^] 17 | to create self-managing OpenShift clusters, where the day 2 operations are done through 18 | link:https://www.weave.works/blog/gitops-operations-by-pull-request[Pull Requests^]. 19 | 20 | Note that the purpose of this lab is not to teach how each of the components works individually, but to show how one can employ a common automation strategy to a myriad of different configurations. 21 | 22 | Let's get started... 23 | 24 | Provisioning your Lab Environment 25 | --------------------------------- 26 | 27 | For Red Hat Tech Exchange, the GUID grabber link to get your lab environment is: 28 | 29 | link:https://www.opentlc.com/gg/gg.cgi?profile=generic_rhte[^] 30 | 31 | This lab code is "A2031", the activation code will be provided during the event. 32 | 33 | If you are running through the lab on your own then you may provision an "OpenShift 4 Configuration" lab environment from link:https://labs.opentlc.com/[^]. 34 | 35 | :numbered: 36 | 37 | GitOps Workflow for a Simple Use Case 38 | ------------------------------------- 39 | 40 | _GitOps_ is a form of Infrastructure as Code practice where all of the configurations that define a system are managed in a git repository, and automatically and idempotently applied to that system each time commits are made to the repository. 41 | For this lab, we are going to enable such a workflow using an Operator called link:https://github.com/KohlsTechnology/eunomia[Eunomia^] and link:https://github.com/redhat-cop/openshift-applier[OpenShift Applier^], an automation framework for OpenShift. 42 | Eunomia provides a workflow for watching git repos, triggering actions when new commits are detected. 43 | The action, in this case, will be a link:https://kubernetes.io/docs/tasks/job/[Kubernetes Job^] that executes Applier. 44 | 45 | You will begin by configuring a simple and common task for OpenShift clusters, exposing the cluster's integrated image registry. 46 | First you will configure a registry route manually and then build up to GitOps step by step. 47 | 48 | Applying a Configuration 49 | ~~~~~~~~~~~~~~~~~~~~~~~~ 50 | 51 | . Check the initial state of the container image registry. 52 | + 53 | In your newly installed OpenShift cluster you will find the container image registry running in the `openshift-image-registry` project namespace. 54 | There is an `image-registry` service, which is used for image access within the cluster, but there is no route for external access. 55 | + 56 | [subs=+quotes] 57 | -------------------------------------------------------------------------------- 58 | $ **oc get service -n openshift-image-registry** 59 | NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE 60 | image-registry ClusterIP 172.30.151.126 5000/TCP 37m 61 | $ **oc get route -n openshift-image-registry** 62 | No resources found. 63 | -------------------------------------------------------------------------------- 64 | 65 | . Use the `oc create route` command to create a route for the service: 66 | + 67 | It is a common practice to expose the OpenShift container image registry outside of the cluster so that CI/CD pipelines can 68 | link:http://v1.uncontained.io/playbooks/continuous_delivery/image_promotion.html[move images between clusters as a form of Promotion^]. 69 | We can do this manually using the oc expose command. 70 | + 71 | [subs=+quotes] 72 | -------------------------------------------------------------------------------- 73 | $ **oc create route reencrypt -n openshift-image-registry --service=image-registry** 74 | route.route.openshift.io/image-registry created 75 | -------------------------------------------------------------------------------- 76 | 77 | . Confirm the route has been created: 78 | + 79 | [subs=+quotes] 80 | -------------------------------------------------------------------------------- 81 | $ **oc get route -n openshift-image-registry** 82 | NAME HOST/PORT PATH SERVICES PORT TERMINATION WILDCARD 83 | image-registry image-registry-openshift-image-registry.apps.cluster-0b1e.sandbox506.opentlc.com image-registry 5000-tcp reencrypt None 84 | -------------------------------------------------------------------------------- 85 | 86 | . Attempt to reconfigure the route. 87 | + 88 | The hostname automatically assigned composed of the service name and the namespace. 89 | A friendlier name would be to change the leading `image-registry-openshift-image-registry` to just `image-registry`. 90 | + 91 | Attempt to adjust the route with `oc create route`: 92 | + 93 | [subs=+quotes] 94 | -------------------------------------------------------------------------------- 95 | $ **oc create route reencrypt -n openshift-image-registry --service=image-registry \ 96 | --hostname=image-registry.apps.cluster-d8f5.sandbox1349.opentlc.com** 97 | Error from server (AlreadyExists): routes.route.openshift.io "image-registry" already exists 98 | -------------------------------------------------------------------------------- 99 | + 100 | No luck, the `oc create route` command is not idempotent. 101 | Many of the subcommands provided by `oc` are helpful for getting things started, but are not suited for maintaining your configuration later. 102 | If we want to do it right we will need a workflow made from declarative configuration, not a series of commands. 103 | 104 | . On your bastion, create a directory for your GitOps lab files in your home directory: 105 | + 106 | [subs=+quotes] 107 | -------------------------------------------------------------------------------- 108 | $ **cd $HOME** 109 | $ **mkdir openshift-gitops-lab** 110 | $ **cd openshift-gitops-lab/** 111 | -------------------------------------------------------------------------------- 112 | 113 | . Dump the route definition created by `oc create route` to a YAML file called `image-registry-route.yaml`: 114 | + 115 | [subs=+quotes] 116 | -------------------------------------------------------------------------------- 117 | $ **oc get route -n openshift-image-registry -o yaml image-registry >image-registry-route.yaml** 118 | -------------------------------------------------------------------------------- 119 | + 120 | Then edit `image-registry-route.yaml`, removing everything except the bits you actually want to manage: 121 | + 122 | * Remove all content from the `metadata` section except `name` and `namespace` 123 | * Remove `spec.subdomain`, it is empty and so not needed 124 | * Remove `spec.to.weight`, the value 100 is just a default value and does not need to be managed 125 | * Remove `spec.wildcardPolicy`, it is not needed, `None` is the default 126 | * Remove the `status` section entirely 127 | + 128 | The result should look something like this, but with your own cluster domain for the host: 129 | + 130 | -------------------------------------------------------------------------------- 131 | apiVersion: route.openshift.io/v1 132 | kind: Route 133 | metadata: 134 | name: image-registry 135 | namespace: openshift-image-registry 136 | spec: 137 | host: image-registry-openshift-image-registry.apps.cluster-d8f5.sandbox1349.opentlc.com 138 | port: 139 | targetPort: 5000-tcp 140 | tls: 141 | termination: reencrypt 142 | to: 143 | kind: Service 144 | name: image-registry 145 | -------------------------------------------------------------------------------- 146 | 147 | . Apply your configuration to the cluster: 148 | + 149 | [subs=+quotes] 150 | -------------------------------------------------------------------------------- 151 | $ **oc apply -f image-registry-route.yaml** 152 | Warning: oc apply should be used on resource created by either oc create --save-config or oc apply 153 | route.route.openshift.io/image-registry configured 154 | -------------------------------------------------------------------------------- 155 | + 156 | NOTE: The first run of `oc apply` will issue a warning and add an annotation to the resource that is used later to track changes. 157 | 158 | . Now let's test that you can make our update to the short name. 159 | + 160 | Using a text editor, edit `image-registry-route.yaml` and shorten the hostname. 161 | + 162 | A `sed` command which makes this edit is: 163 | + 164 | [subs=+quotes] 165 | -------------------------------------------------------------------------------- 166 | $ **sed -i \'s/image-registry-openshift-image-registry/image-registry/' image-registry-route.yaml** 167 | -------------------------------------------------------------------------------- 168 | + 169 | Now use `oc apply` to reconfigure the route: 170 | + 171 | [subs=+quotes] 172 | -------------------------------------------------------------------------------- 173 | $ **oc apply -f image-registry-route.yaml** 174 | route.route.openshift.io/image-registry configured 175 | -------------------------------------------------------------------------------- 176 | + 177 | And confirm that the route hostname has been updated: 178 | + 179 | [subs=+quotes] 180 | -------------------------------------------------------------------------------- 181 | $ **oc get route -n openshift-image-registry** 182 | NAME HOST/PORT PATH SERVICES PORT TERMINATION WILDCARD 183 | image-registry image-registry.apps.cluster-0b1e.sandbox506.opentlc.com image-registry 5000-tcp reencrypt None 184 | -------------------------------------------------------------------------------- 185 | 186 | Ansible Automation 187 | ~~~~~~~~~~~~~~~~~~ 188 | 189 | In this next section you will have a look at how to automate an `oc apply` based workflow with Ansible. 190 | The configuration we put together so far is a bit brittle. 191 | For example, if this were managing real clusters you would want to dynamically determine the application domain for cluster ingress. 192 | Let's explore how that might look: 193 | 194 | . Create a `templates` directory and move your static route definition to have a `.j2` file extension, indicating Jinja2 template content: 195 | + 196 | [subs=+quotes] 197 | -------------------------------------------------------------------------------- 198 | $ **mkdir templates** 199 | $ **mv image-registry-route.yaml templates/image-registry-route.j2** 200 | -------------------------------------------------------------------------------- 201 | 202 | . Update the template content to use the variable `openshift_ingress_default_subdomain`: 203 | + 204 | [subs=+quotes] 205 | -------------------------------------------------------------------------------- 206 | $ **sed -i \'s/^ host: .*/ host: image-registry.{{ openshift_ingress_default_subdomain }}/' \ 207 | templates/image-registry-route.j2** 208 | $ **cat templates/image-registry-route.j2** 209 | apiVersion: route.openshift.io/v1 210 | kind: Route 211 | metadata: 212 | name: image-registry 213 | namespace: openshift-image-registry 214 | spec: 215 | host: image-registry.{{ openshift_ingress_default_subdomain }} 216 | port: 217 | targetPort: 5000-tcp 218 | tls: 219 | termination: reencrypt 220 | to: 221 | kind: Service 222 | name: image-registry 223 | -------------------------------------------------------------------------------- 224 | 225 | . Note that the `ingresscontroller` resource in the `openshift-ingress-operator` namespace has the cluster's domain: 226 | + 227 | [subs=+quotes] 228 | -------------------------------------------------------------------------------- 229 | $ **oc get ingresscontroller -n openshift-ingress-operator default \ 230 | -o custom-columns=DOMAIN:.status.domain** 231 | DOMAIN 232 | apps.cluster-742f.sandbox499.opentlc.com 233 | -------------------------------------------------------------------------------- 234 | 235 | . Write the following simple Ansible role named `discover` to fetch the IngressController definition from the cluster and save the domain to the `openshift_ingress_default_subdomain` fact. 236 | + 237 | [subs=+quotes] 238 | -------------------------------------------------------------------------------- 239 | $ **mkdir -p roles/discover/tasks** 240 | $ **cat >roles/discover/tasks/main.yaml < 252 | {{ r_default_ingress_controller.resources[0].status.domain }} 253 | EOF** 254 | -------------------------------------------------------------------------------- 255 | 256 | . Write a simple Ansible playbook to run the your `discover` role and then `oc apply` the resource definition from the template source: 257 | + 258 | [subs=+quotes] 259 | -------------------------------------------------------------------------------- 260 | $ **cat >playbook.yaml <requirements.yml <.applier/group_vars/seed-hosts/main.yml <.applier/hosts <meta/main.yml <> README.md** 413 | -------------------------------------------------------------------------------- 414 | 415 | . Commit all of your code and push to your GitHub repository 416 | + 417 | First, initialize your directory for `git`: 418 | + 419 | [subs=+quotes] 420 | -------------------------------------------------------------------------------- 421 | $ **git init** 422 | Initialized empty Git repository in /home/lab-user/openshift-gitops-lab/.git/ 423 | -------------------------------------------------------------------------------- 424 | + 425 | Add all required files for commit: 426 | + 427 | [subs=+quotes] 428 | -------------------------------------------------------------------------------- 429 | $ **git add README.md requirements.yml .applier/ meta roles/ templates/** 430 | -------------------------------------------------------------------------------- 431 | + 432 | Commit to your local repository: 433 | + 434 | [subs=+quotes] 435 | -------------------------------------------------------------------------------- 436 | $ **git commit -m "Initial commit"** 437 | [master 4b2e3e0] Initial commit 438 | 7 files changed, 47 insertions(+) 439 | create mode 100644 .applier/group_vars/seed-hosts/main.yml 440 | create mode 100644 .applier/hosts 441 | create mode 100644 README.md 442 | create mode 100644 meta/main.yml 443 | create mode 100644 requirements.yml 444 | create mode 100644 roles/discover/tasks/main.yaml 445 | create mode 100644 templates/image-registry-route.j2 446 | -------------------------------------------------------------------------------- 447 | + 448 | Add a remote to connect your local repository to your GitHub repository: 449 | + 450 | [subs=+quotes] 451 | -------------------------------------------------------------------------------- 452 | $ **git remote add origin https://github.com/${GITHUB_ACCOUNT}/openshift-gitops-lab.git** 453 | -------------------------------------------------------------------------------- 454 | + 455 | And finally push to GitHub: 456 | + 457 | [subs=+quotes] 458 | -------------------------------------------------------------------------------- 459 | $ **git push --set-upstream origin master** 460 | Username for \'https://github.com': **__GITHUB_ACCOUNT__** 461 | Password for \'https://__GITHUB_ACCOUNT__@github.com': 462 | Counting objects: 17, done. 463 | Delta compression using up to 2 threads. 464 | Compressing objects: 100% (9/9), done. 465 | Writing objects: 100% (17/17), 1.57 KiB | 0 bytes/s, done. 466 | Total 17 (delta 0), reused 0 (delta 0) 467 | To https://github.com/__GITHUB_ACCOUNT__/openshift-gitops-lab.git 468 | * [new branch] master -> master 469 | Branch master set up to track remote branch master from origin. 470 | -------------------------------------------------------------------------------- 471 | 472 | . Pull down a local copy of `openshift-applier` and test your `requirements.yml`: 473 | + 474 | [subs=+quotes] 475 | -------------------------------------------------------------------------------- 476 | $ **ansible-galaxy install -r requirements.yml -p galaxy** 477 | - extracting openshift-applier to /home/lab-user/openshift-gitops-lab/galaxy/openshift-applier 478 | - openshift-applier (master) was installed successfully 479 | - extracting self to /home/lab-user/openshift-gitops-lab/galaxy/self 480 | - self (master) was installed successfully 481 | -------------------------------------------------------------------------------- 482 | + 483 | NOTE: This step creates a `galaxy` subdirectory within your git repository. 484 | You should create a `.gitignore` configuration to exclude these files from later `git` activity. 485 | (link:https://git-scm.com/docs/gitignore[^]) 486 | 487 | . Run `openshift-applier` using the `openshift-cluster-seed.yml` playbook. 488 | + 489 | [subs=+quotes] 490 | -------------------------------------------------------------------------------- 491 | $ **ansible-playbook -i .applier/ galaxy/openshift-applier/playbooks/openshift-cluster-seed.yml** 492 | PLAY [seed-hosts[0]] 493 | 494 | ... OUTPUT OMITTED ... 495 | 496 | RUNNING HANDLER [openshift-applier : Clean up temporary dependency dir] 497 | changed: [localhost] => (item=/tmp/ansible.dO3wGX/) 498 | 499 | RUNNING HANDLER [openshift-applier : Clean up temporary Jinja directory] 500 | changed: [localhost] => (item=/tmp/ansible.aXO00w) 501 | 502 | PLAY RECAP 503 | localhost : ok=36 changed=9 unreachable=0 failed=0 504 | -------------------------------------------------------------------------------- 505 | + 506 | And now you're working with the OpenShift applier and ready to start contributing to the Red Hat containers community of practice! 507 | + 508 | Let's get on to GitOps now! 509 | 510 | Install Eunomia 511 | ~~~~~~~~~~~~~~ 512 | 513 | In this section of the lab we install Eunomia following the installation instructions provided in the 514 | link:https://github.com/KohlsTechnology/eunomia/blob/master/README.md[Eunomia README.md^]. 515 | 516 | . You will install the Eunomia operator using a provided link:https://helm.sh[Helm^] template. 517 | Install the `helm` command into your `~/bin` directory. 518 | + 519 | [subs=+quotes] 520 | -------------------------------------------------------------------------------- 521 | $ *mkdir ~/bin* 522 | $ *curl https://get.helm.sh/helm-v2.14.3-linux-amd64.tar.gz \ 523 | | tar xzf - -C ~/bin --strip-components=1 linux-amd64/helm* 524 | % Total % Received % Xferd Average Speed Time Time Time Current 525 | Dload Upload Total Spent Left Speed 526 | 100 23.0M 100 23.0M 0 0 18.8M 0 0:00:01 0:00:01 --:--:-- 12.0M 527 | $ *ls -l ~/bin/helm* 528 | -rwxr-xr-x. 1 lab-user users 41819776 Jul 30 16:35 /home/-redhat.com/bin/helm* 529 | -------------------------------------------------------------------------------- 530 | NOTE: This command only installs the `helm` executable from the release. 531 | The `tiller` component of Helm is not needed to install Eunomia which eliminates the security implications that would be required by making use of this component. 532 | 533 | . From your HOME directory, clone Eunomia from the KohlsTechnology git repository: 534 | + 535 | [subs=+quotes] 536 | -------------------------------------------------------------------------------- 537 | $ **cd $HOME** 538 | $ **git clone https://github.com/KohlsTechnology/eunomia.git** 539 | Cloning into 'eunomia'... 540 | remote: Enumerating objects: 888, done. 541 | remote: Total 888 (delta 0), reused 0 (delta 0), pack-reused 888 542 | Receiving objects: 100% (888/888), 1.45 MiB | 174.00 KiB/s, done. 543 | Resolving deltas: 100% (412/412), done. 544 | -------------------------------------------------------------------------------- 545 | 546 | . Process the Eunomia operator directory with `helm template` and pass the output resource definitions to `oc apply`: 547 | + 548 | [subs=+quotes] 549 | -------------------------------------------------------------------------------- 550 | $ **helm template deploy/helm/eunomia-operator/ \ 551 | --set eunomia.operator.openshift.route.enabled=true | oc apply -f -** 552 | namespace/eunomia-operator created 553 | configmap/job-templates created 554 | serviceaccount/eunomia-operator created 555 | rolebinding.rbac.authorization.k8s.io/eunomia-operator created 556 | customresourcedefinition.apiextensions.k8s.io/gitopsconfigs.eunomia.kohls.io created 557 | clusterrole.rbac.authorization.k8s.io/gitopsconfig-admin created 558 | clusterrole.rbac.authorization.k8s.io/gitopsconfig-viewer created 559 | clusterrole.rbac.authorization.k8s.io/eunomia-operator created 560 | clusterrolebinding.rbac.authorization.k8s.io/eunomia-operator created 561 | role.rbac.authorization.k8s.io/eunomia-operator created 562 | service/eunomia-operator created 563 | deployment.apps/eunomia-operator created 564 | -------------------------------------------------------------------------------- 565 | 566 | . Check Eunomia operator is running: 567 | + 568 | [subs=+quotes] 569 | -------------------------------------------------------------------------------- 570 | $ **oc get pod -n eunomia-operator** 571 | NAME READY STATUS RESTARTS AGE 572 | eunomia-operator-994b5d88d-ks6kw 1/1 Running 0 11m 573 | $ **oc logs -n eunomia-operator $(oc get pod -n eunomia-operator -o name)** 574 | {"level":"info","ts":1568215406.310659,"logger":"cmd","msg":"Go Version: go1.12.6"} 575 | {"level":"info","ts":1568215406.3106856,"logger":"cmd","msg":"Go OS/Arch: linux/amd64"} 576 | {"level":"info","ts":1568215406.3106892,"logger":"cmd","msg":"Version of operator-sdk: v0.8.1"} 577 | {"level":"info","ts":1568215406.3113315,"logger":"cmd","msg":"Templates initialized correctly"} 578 | {"level":"info","ts":1568215406.3116016,"logger":"leader","msg":"Trying to become the leader."} 579 | {"level":"info","ts":1568215406.4215379,"logger":"leader","msg":"No pre-existing lock was found."} 580 | {"level":"info","ts":1568215406.4294758,"logger":"leader","msg":"Became the leader."} 581 | {"level":"info","ts":1568215406.5258887,"logger":"cmd","msg":"Registering Components."} 582 | {"level":"info","ts":1568215406.526105,"logger":"kubebuilder.controller","msg":"Starting EventSource","controller":"gitopsconfig-controller","source":"kind source: /, Kind="} 583 | {"level":"info","ts":1568215406.5262187,"logger":"kubebuilder.controller","msg":"Starting EventSource","controller":"gitopsconfig-controller","source":"channel source: 0xc00096e7d0"} 584 | {"level":"info","ts":1568215406.5262578,"logger":"cmd","msg":"Starting the Web Server"} 585 | {"level":"info","ts":1568215406.5262656,"logger":"cmd","msg":"Starting the Cmd."} 586 | {"level":"info","ts":1568215406.6265268,"logger":"kubebuilder.controller","msg":"Starting Controller","controller":"gitopsconfig-controller"} 587 | {"level":"info","ts":1568215406.7266388,"logger":"kubebuilder.controller","msg":"Starting workers","controller":"gitopsconfig-controller","worker count":1} 588 | -------------------------------------------------------------------------------- 589 | + 590 | Eunomia is now running, but does not yet have any configuration for GitOps processing. 591 | 592 | Running Applier with Eunomia 593 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 594 | 595 | Now, you can set up Eunomia to leverage your `openshift-gitops-lab` repository to run `openshift-applier` for you. 596 | In this section of the lab you will setup the GitOps configuration manually. 597 | Later in the lab you will see how you can use `openshift-applier` to automatically configure or reconfigure the GitOps configuration. 598 | 599 | . Delete the route for the image-registry so that you can confirm that the Eunomia successfully reconfigures it: 600 | + 601 | [subs=+quotes] 602 | -------------------------------------------------------------------------------- 603 | $ **oc delete route -n openshift-image-registry image-registry** 604 | route.route.openshift.io "image-registry" deleted 605 | -------------------------------------------------------------------------------- 606 | 607 | . Configure Eunomia to configure your cluster with your `openshift-gitops-lab` repository: 608 | + 609 | Create the project namespace, `cluster-config`, for managing your GitOps cluster configuration: 610 | + 611 | [subs=+quotes] 612 | -------------------------------------------------------------------------------- 613 | $ **oc new-project cluster-config** 614 | Now using project "cluster-config" on server "https://api.cluster-742f.sandbox499.opentlc.com:6443". 615 | 616 | You can add applications to this project with the 'new-app' command. For example, try: 617 | 618 | oc new-app django-psql-example 619 | 620 | to build a new example application in Python. Or use kubectl to deploy a simple Kubernetes application: 621 | 622 | kubectl create deployment hello-node --image=gcr.io/hello-minikube-zero-install/hello-node 623 | 624 | -------------------------------------------------------------------------------- 625 | + 626 | Create a service account for the Eunomia template processor: 627 | + 628 | [subs=+quotes] 629 | -------------------------------------------------------------------------------- 630 | $ **oc create serviceaccount -n cluster-config gitops-runner** 631 | serviceaccount/gitops-runner created 632 | -------------------------------------------------------------------------------- 633 | + 634 | Grant the service account whatever rights it will need to configure resources: 635 | + 636 | [subs=+quotes] 637 | -------------------------------------------------------------------------------- 638 | $ **oc adm policy add-cluster-role-to-user cluster-admin system:serviceaccount:cluster-config:gitops-runner** 639 | clusterrole.rbac.authorization.k8s.io/cluster-admin added: "system:serviceaccount:cluster-config:gitops-runner" 640 | -------------------------------------------------------------------------------- 641 | + 642 | Create a `GitOpsConfig` for Eunomia that points to your `openshift-gitops-lab` repository and uses the `quay.io/kohlstechnology/eunomia-applier:v0.0.1` template processor: 643 | + 644 | [subs=+quotes] 645 | -------------------------------------------------------------------------------- 646 | $ **oc apply -f - < (item=/tmp/ansible.RFC9qm/) 704 | 705 | RUNNING HANDLER [openshift-applier : Clean up temporary Jinja directory] 706 | changed: [localhost] => (item=/tmp/ansible.reO3Mm) 707 | 708 | PLAY RECAP 709 | localhost : ok=36 changed=9 unreachable=0 failed=0 skipped=34 rescued=0 ignored=0 710 | 711 | CREATE_MODE and/or DELETE_MODE is set to None; This means that the template processor already applied the resources. Skipping the Manage Resources step. 712 | -------------------------------------------------------------------------------- 713 | 714 | . Finally, confirm that the route hostname has been recreated as expected: 715 | + 716 | [subs=+quotes] 717 | -------------------------------------------------------------------------------- 718 | $ **oc get route -n openshift-image-registry** 719 | NAME HOST/PORT PATH SERVICES PORT TERMINATION WILDCARD 720 | image-registry image-registry.apps.cluster-0b1e.sandbox506.opentlc.com image-registry 5000-tcp reencrypt None 721 | -------------------------------------------------------------------------------- 722 | 723 | GitOps Workflow for a complex use case as a Cluster Administration 724 | ------------------------------------------------------------------ 725 | 726 | In this next section of the lab you will begin working with a full-featured OpenShift GitOps repository. 727 | The link:https://github.com/redhat-cop/operationalizing-openshift-lab/[operationalizing-openshift-lab^] 728 | repository includes a collection of best practices examples developed by the Red Hat containers community of practice. 729 | 730 | Using this repository as a starting point we are going to establish an automated strategy for managing configuration of an operationalized cluster, including things like: 731 | 732 | * Enabling Cluster Autoscaling 733 | * Configuring authentication and authorization 734 | * Configuring namespace creation 735 | * Setting up quotas & limits for applications 736 | * Applying automated certificate management for applications 737 | * Deploying initial workloads 738 | * Deploying custom dashboards and setting up alerts 739 | 740 | Red Hat Containers Community of Practice GitOps Repository 741 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 742 | 743 | In this configuration you will see an `openshift-applier` inventory that is configured with the ability to bootstrap its own GitOps configuration. 744 | You can examine the one we're going to use at link:templates/cluster-gitops.yaml[templates/cluster-gitops.yaml]. 745 | Let's use Applier to roll out the config. 746 | 747 | . First, let's verify our `GITHUB_ACCOUNT` environment variable is still set. 748 | + 749 | [subs=+quotes] 750 | -------------------------------------------------------------------------------- 751 | $ **export GITHUB_ACCOUNT=__YOUR_GITHUB_ACCOUNT__** 752 | -------------------------------------------------------------------------------- 753 | 754 | . Fork the link:https://github.com/redhat-cop/operationalizing-openshift-lab/[operationalizing-openshift-lab^] repository into your own account by selecting the *Fork* button and then if necessary, select the account for which the repository should be forked to. 755 | 756 | . Clone your fork locally using the `GITHUB_ACCOUNT` variable you set previously: 757 | + 758 | [subs=+quotes] 759 | -------------------------------------------------------------------------------- 760 | $ **git clone https://github.com/${GITHUB_ACCOUNT}/operationalizing-openshift-lab** 761 | Cloning into \'operationalizing-openshift-lab'... 762 | remote: Enumerating objects: 77, done. 763 | remote: Counting objects: 100% (77/77), done. 764 | remote: Compressing objects: 100% (44/44), done. 765 | remote: Total 323 (delta 36), reused 62 (delta 29), pack-reused 246 766 | Receiving objects: 100% (323/323), 78.45 KiB | 0 bytes/s, done. 767 | Resolving deltas: 100% (129/129), done. 768 | -------------------------------------------------------------------------------- 769 | 770 | . Update repository references in source to make use of your GitHub account instead of the upstream repository. 771 | + 772 | There are references to the repository in `requirements.yml` and `templates/cluster-gitops.yaml`. 773 | The `templates/cluster-gitops.yaml` will be used to configure Eunomia to point to your repository. 774 | + 775 | [subs=+quotes] 776 | -------------------------------------------------------------------------------- 777 | $ *cd ~/operationalizing-openshift-lab* 778 | $ *sed -i "s|https://github.com/redhat-cop/operationalizing-openshift-lab|https://github.com/${GITHUB_ACCOUNT}/operationalizing-openshift-lab|" \ 779 | requirements.yml templates/cluster-gitops.yaml* 780 | -------------------------------------------------------------------------------- 781 | 782 | . The LDAP authentication and group sync configuration for this lab will communicate with an link:https://access.redhat.com/products/identity-management[IPA^] server in a secure fashion. 783 | Download the OpenTLC shared IPA TLS certificate authority that will be used to facilitate the communication: 784 | + 785 | [subs=+quotes] 786 | -------------------------------------------------------------------------------- 787 | $ *curl http://ipa.shared.example.opentlc.com/ipa/config/ca.crt -o ldap-ca.crt* 788 | % Total % Received % Xferd Average Speed Time Time Time Current 789 | Dload Upload Total Spent Left Speed 790 | 100 1350 100 1350 0 0 6597 0 --:--:-- --:--:-- --:--:-- 6617 791 | -------------------------------------------------------------------------------- 792 | 793 | . Set LDAP configuration variables for the lab. 794 | The `openshift-applier` Ansible role used for GitOps in this lab uses an Ansible inventory store in the `.ansible/` directory of your repository. 795 | The ansible host group, `seed-hosts`, is configured with group variables in YAML in the `.applier/group_vars/seed-hosts/` directory. 796 | Within this directory the variables are managed in separate YAML files, organized by the purpose of the variables. 797 | An example `auth.yml` file is provided with the lab repository for reference. 798 | The LDAP settings used for this lab can be set by running the command below. 799 | + 800 | [subs=+quotes] 801 | -------------------------------------------------------------------------------- 802 | $ **cat >.applier/group_vars/seed-hosts/auth.yml <- 808 | {{ lookup("file", inventory_dir ~ "/../ldap-ca.crt") }} 809 | 810 | # LDAP BIND config for user authentication and groups sync 811 | ldap_bind_dn: uid=admin,cn=users,cn=accounts,dc=shared,dc=example,dc=opentlc,dc=com 812 | 813 | # Do not store secrets in version control! 814 | # LDAP bind password configured by environment variable 815 | ldap_bind_password: >- 816 | {{ lookup("env", "LDAP_BIND_PASSWORD") }} 817 | 818 | # Location in LDAP for users and groups 819 | ldap_users_search_base: cn=users,cn=accounts,dc=shared,dc=example,dc=opentlc,dc=com 820 | ldap_groups_search_base: cn=groups,cn=accounts,dc=shared,dc=example,dc=opentlc,dc=com 821 | 822 | # LDAP search filter URL used during authentication 823 | ldap_search_url: "{{ ldap_url }}/{{ ldap_users_search_base }}?uid?sub?{{ ldap_auth_search_filter }}" 824 | 825 | # This search filter enforces that users must belong to the ocp-users group 826 | ldap_auth_search_filter: "(memberOf=cn=ocp-users,cn=groups,cn=accounts,dc=shared,dc=example,dc=opentlc,dc=com)" 827 | 828 | # LDAP group sync schedule configuration 829 | ldap_cron_schedule: "*/5 * * * *" 830 | 831 | # LDAP groups whitelist restricts groups synced from LDAP to OpenShift 832 | ldap_groups_whitelist: | 833 | cn=ocp-users,cn=groups,cn=accounts,dc=shared,dc=example,dc=opentlc,dc=com 834 | cn=ocp-platform,cn=groups,cn=accounts,dc=shared,dc=example,dc=opentlc,dc=com 835 | cn=ocp-production,cn=groups,cn=accounts,dc=shared,dc=example,dc=opentlc,dc=com 836 | cn=paymentapp,cn=groups,cn=accounts,dc=shared,dc=example,dc=opentlc,dc=com 837 | cn=portalapp,cn=groups,cn=accounts,dc=shared,dc=example,dc=opentlc,dc=com 838 | EOF** 839 | -------------------------------------------------------------------------------- 840 | 841 | . Commit and push changes to your Git repository: 842 | + 843 | Add files with changes to be included in next commit: 844 | + 845 | [subs=+quotes] 846 | -------------------------------------------------------------------------------- 847 | $ *git add .applier/group_vars/seed-hosts/auth.yml ldap-ca.crt \ 848 | requirements.yml templates/cluster-gitops.yaml* 849 | -------------------------------------------------------------------------------- 850 | + 851 | Commit changes to your git working directory: 852 | + 853 | [subs=+quotes] 854 | -------------------------------------------------------------------------------- 855 | $ *git commit -m "Update settings for initial lab run"* 856 | [master 9d96661] Update settings for initial lab run 857 | 4 files changed, 59 insertions(+), 29 deletions(-) 858 | rewrite .applier/group_vars/seed-hosts/auth.yml (76%) 859 | create mode 100644 ldap-ca.crt 860 | -------------------------------------------------------------------------------- 861 | + 862 | Push your commit to your GitHub repository, authenticating when prompted: 863 | + 864 | [subs=+quotes] 865 | -------------------------------------------------------------------------------- 866 | $ *git push origin master* 867 | Username for \'https://github.com': **__GITHUB_ACCOUNT__** 868 | Password for \'https://__GITHUB_ACCOUNT__@github.com': 869 | Counting objects: 18, done. 870 | Delta compression using up to 2 threads. 871 | Compressing objects: 100% (9/9), done. 872 | Writing objects: 100% (10/10), 2.10 KiB | 0 bytes/s, done. 873 | Total 10 (delta 5), reused 1 (delta 0) 874 | remote: Resolving deltas: 100% (5/5), completed with 5 local objects. 875 | To https://github.com/__GITHUB_ACCOUNT__/operationalizing-openshift-lab 876 | a46e90f..3fa37a4 master -> master 877 | -------------------------------------------------------------------------------- 878 | + 879 | NOTE: If your GitHub account is configured for two-factor authentication then you will need to configure a 880 | link:https://help.github.com/en/articles/creating-a-personal-access-token-for-the-command-line[GitHub personal access token^] to authenticate for the `git push` command. 881 | 882 | Configure Eunomia 883 | ~~~~~~~~~~~~~~~~ 884 | 885 | You will configure Eunomia using `openshift-applier` using the same repository that will be used by Eunomia. 886 | This means that once complete, Eunomia will be able to reconfigure itself on the running cluster. 887 | This also means that all configuration can be processed and validated using `openshift-applier` outside of Eunomia. 888 | 889 | . Install `openshift-applier` into the `galaxy` directory using the `ansible-galaxy` command line tool using the provided `requirements.yml`: 890 | + 891 | [subs=+quotes] 892 | -------------------------------------------------------------------------------- 893 | $ *ansible-galaxy install -r requirements.yml -p galaxy* 894 | - extracting openshift-applier to /home/lab-user/operationalizing-openshift-lab/galaxy/openshift-applier 895 | - openshift-applier (master) was installed successfully 896 | - extracting self to /home/lab-user/operationalizing-openshift-lab/galaxy/self 897 | - self (master) was installed successfully 898 | -------------------------------------------------------------------------------- 899 | + 900 | This step also validates that your GitOps repository `requirements.yml` is correctly configured. 901 | 902 | . Set and export the `LDAP_BIND_PASSWORD` environment variable as it will be added to the set of cluster _secrets_ required in later portions of the lab: 903 | + 904 | [subs=+quotes] 905 | -------------------------------------------------------------------------------- 906 | $ *export LDAP_BIND_PASSWORD=xxxxxx* 907 | -------------------------------------------------------------------------------- 908 | + 909 | NOTE: The LDAP bind password is included in the authentication unit of the "Red Hat OpenShift Container Platform 4 Configuration" course in the learning management system. 910 | 911 | . Configure cluster secrets by running `openshift-applier` with the objects tagged "cluster-secrets". 912 | + 913 | This step configures values that will not be available during Eunomia processing. 914 | In this lab this is just the LDAP bind password. 915 | + 916 | [subs=+quotes] 917 | -------------------------------------------------------------------------------- 918 | $ *ansible-playbook -i .applier/ galaxy/openshift-applier/playbooks/openshift-cluster-seed.yml \ 919 | -e include_tags=cluster-secrets -e exclude_tags=""* 920 | -------------------------------------------------------------------------------- 921 | + 922 | NOTE: We must override `exclude_tags` to set cluster secrets because this variable is set in `.applier/group_vars/seed-hosts/main.yml` to exclude the `cluster-secrets` tag. 923 | 924 | . Kick off Eunomia by running `openshift-applier` with the objects tagged "gitops": 925 | + 926 | [subs=+quotes] 927 | -------------------------------------------------------------------------------- 928 | $ *ansible-playbook -i .applier/ galaxy/openshift-applier/playbooks/openshift-cluster-seed.yml \ 929 | -e include_tags=gitops* 930 | -------------------------------------------------------------------------------- 931 | 932 | . *Wait for nodes to come back*. By running the above command, you've kicked off a Eunomia run. This run will create a Kubernetes `Job` that runs OpenShift Applier against your repo. One of the items this repo is configured to manage is your OpenShift `MachineSets`. By default, OpenShift nodes are designated either as a `master` (designated by the label `node-role.kubernetes.io/master=`) or a `worker` (designated by the label `node-role.kubernetes.io/master=`). For our cluster we would like to further configure a layer of isolation: designated `compute` and `logging` worker nodes. In order to do that, we replace the default `MachineSets`, which will re-provision all of the nodes, including the master. Let's watch this happen. First, we'll run `watch oc get machinesets` until the output looks something like this. 933 | + 934 | [subs=+quotes] 935 | ---- 936 | $ *watch oc get machinesets -n openshift-machine-api* 937 | Every 2.0s: oc get machinesets -n openshift-machine-api Wed Sep 25 08:03:13 2019 938 | 939 | NAME DESIRED CURRENT READY AVAILABLE AGE 940 | cluster-8fa0-9gfz9-compute-eu-west-1a 1 1 3m33s 941 | cluster-8fa0-9gfz9-compute-eu-west-1b 1 1 3m33s 942 | cluster-8fa0-9gfz9-compute-eu-west-1c 0 0 3m33s 943 | cluster-8fa0-9gfz9-logging-es-eu-west-1a 1 1 3m33s 944 | cluster-8fa0-9gfz9-logging-es-eu-west-1b 0 0 3m33s 945 | cluster-8fa0-9gfz9-logging-es-eu-west-1c 0 0 3m33s 946 | cluster-8fa0-9gfz9-worker-eu-west-1a 0 0 18h 947 | cluster-8fa0-9gfz9-worker-eu-west-1b 0 0 18h 948 | cluster-8fa0-9gfz9-worker-eu-west-1c 0 0 18h 949 | ---- 950 | 951 | . Next, we can `watch oc get nodes` until all of our new nodes are ready. 952 | + 953 | [subs=+quotes] 954 | ---- 955 | $ *watch oc get nodes* 956 | Every 2.0s: oc get nodes Wed Sep 25 08:03:13 2019 957 | 958 | NAME STATUS ROLES AGE VERSION 959 | ip-10-0-128-223.eu-west-1.compute.internal Ready master 18h v1.13.4+ab8449285 960 | ip-10-0-159-213.eu-west-1.compute.internal Ready master 18h v1.13.4+ab8449285 961 | ip-10-0-167-35.eu-west-1.compute.internal Ready,SchedulingDisabled worker 18h v1.13.4+ab8449285 962 | ip-10-0-168-183.eu-west-1.compute.internal Ready master 18h v1.13.4+ab8449285 963 | ---- 964 | 965 | . Check Eunomia Configuration created by `openshift-applier`. 966 | + 967 | You should find a GitOpsConfig resource has been created in the `cluster-config` namespace. 968 | Check the references to your GitHub repository in the GitOpsConfig: 969 | + 970 | [subs=+quotes] 971 | -------------------------------------------------------------------------------- 972 | $ *oc get gitopsconfig cluster-config -n cluster-config -o yaml* 973 | apiVersion: eunomia.kohls.io/v1alpha1 974 | kind: GitOpsConfig 975 | metadata: 976 | annotations: 977 | gitopsconfig.eunomia.kohls.io/initialized: "true" 978 | kubectl.kubernetes.io/last-applied-configuration: | 979 | {"apiVersion":"eunomia.kohls.io/v1alpha1","kind":"GitOpsConfig","metadata":{"annotations":{},"name":"cluster-config","namespace":"cluster-config"},"spec":{"resourceDeletionMode":"None","resourceHandlingMode":"None","serviceAccountRef":"eunomia-runner","templateProcessorImage":"quay.io/kohlstechnology/eunomia-applier:v0.0.1","templateSource":{"contextDir":"","ref":"master","uri":"https://github.com/__GITOPS_ACCOUNT__/operationalizing-openshift-lab"},"triggers":[{"type":"Change"}]}} 980 | creationTimestamp: "2019-09-11T19:22:09Z" 981 | finalizers: 982 | - eunomia-finalizer 983 | generation: 3 984 | name: cluster-config 985 | namespace: cluster-config 986 | resourceVersion: "2412063" 987 | selfLink: /apis/eunomia.kohls.io/v1alpha1/namespaces/cluster-config/gitopsconfigs/cluster-config 988 | uid: 73008724-d4c9-11e9-8b21-0665501aae14 989 | spec: 990 | parameterSource: 991 | contextDir: . 992 | ref: master 993 | uri: https://github.com/__GITHUB_ACCOUNT__/operationalizing-openshift-lab 994 | resourceDeletionMode: None 995 | resourceHandlingMode: None 996 | serviceAccountRef: eunomia-runner 997 | templateProcessorImage: quay.io/kohlstechnology/eunomia-applier:v0.0.1 998 | templateSource: 999 | contextDir: "" 1000 | ref: master 1001 | uri: https://github.com/__GITHUB_ACCOUNT__/operationalizing-openshift-lab 1002 | triggers: 1003 | - type: Change 1004 | -------------------------------------------------------------------------------- 1005 | 1006 | . Check Eunomia job completion. 1007 | + 1008 | The change trigger on the GitOpsConfig will cause Eunomia to immediately process by creating a kubernetes job in the same directory as the GitOpsConfig. 1009 | + 1010 | [subs=+quotes] 1011 | -------------------------------------------------------------------------------- 1012 | $ *oc get job -n cluster-config* 1013 | NAME COMPLETIONS DURATION AGE 1014 | gitopsconfig-cluster-config-gtvzhi 1/1 52s 2m50s 1015 | -------------------------------------------------------------------------------- 1016 | + 1017 | The Eunomia job log shows the output of `openshift-applier` running the same Ansible playbook as we used to configure cluster secrets and gitops: 1018 | + 1019 | [subs=+quotes] 1020 | -------------------------------------------------------------------------------- 1021 | $ *oc logs -n cluster-config job/gitopsconfig-cluster-config-gtvzhi --tail=10* 1022 | 1023 | RUNNING HANDLER [openshift-applier : Clean up temporary Jinja directory] 1024 | changed: [localhost] => (item=/tmp/ansible.LY8psZ) 1025 | 1026 | PLAY RECAP 1027 | localhost : ok=162 changed=22 unreachable=0 failed=0 skipped=189 rescued=0 ignored=0 1028 | 1029 | Managing Resources 1030 | Context "current" modified. 1031 | Switched to context "current". 1032 | -------------------------------------------------------------------------------- 1033 | + 1034 | NOTE: The configuration performed by this first job run reconfigures the openshift-machine-api, resulting in the worker nodes being replaced. 1035 | It is possible that the node where the job ran will terminate before the logs can be retrieved. 1036 | It may take up to 6 minutes for the cluster to create the replacement nodes. 1037 | You may watch for the new nodes to become available with `oc get nodes -w`. 1038 | 1039 | === Exploring Your Newly Configured Cluster 1040 | 1041 | At this point we expect to have a working GitOps workflow for our shiny new cluster. Let's take a few moments to examine the state of our cluster in greater detail. 1042 | 1043 | For this part of the Lab, we are going to take on the persona of *David*, a cluster administrator. Let's log in as `david`. 1044 | 1045 | NOTE: To log in as `david`, use the LDAP_BIND_PASSWORD that is included in the authentication unit of the "Red Hat OpenShift Container Platform 4 Configuration" course in the learning management system. 1046 | 1047 | [subs=+quotes] 1048 | ---- 1049 | $ *oc whoami --show-server* 1050 | https://api.cluster-**.**.sandbox.opentlc.com:6443 1051 | 1052 | $ *oc login -u david $(oc whoami --show-server)* 1053 | Logged into "https://api.cluster-..sandbox.opentlc.com:6443" as "david" using existing credentials. 1054 | 1055 | You have access to 59 projects, the list has been suppressed. You can list all projects with 'oc projects' 1056 | 1057 | Using project "default". 1058 | 1059 | ---- 1060 | 1061 | ==== Authentication and Authorization 1062 | 1063 | One of the first things we've done in this cluster is configured multi-user authentication to an external LDAP provider. This will allow us to have multiple different users logged into our cluster with various roles and policies applied. 1064 | 1065 | Let's confirm that our workflow has reconfigured the cluster by testing login. 1066 | 1067 | First confirm that the OAuth configuration has been updated. 1068 | The oauth resource named "cluster" should have the `spec.identityProviders` now configured for LDAP authentication: 1069 | 1070 | [subs=+quotes] 1071 | -------------------------------------------------------------------------------- 1072 | $ **oc get oauth cluster -o yaml | grep identityProviders: -A22** 1073 | identityProviders: 1074 | - challenge: true 1075 | ldap: 1076 | attributes: 1077 | email: 1078 | - mail 1079 | id: 1080 | - dn 1081 | name: 1082 | - cn 1083 | preferredUsername: 1084 | - uid 1085 | bindDN: uid=admin,cn=users,cn=accounts,dc=shared,dc=example,dc=opentlc,dc=com 1086 | bindPassword: 1087 | name: ldap-bind-password 1088 | ca: 1089 | name: ldap-tls-ca 1090 | insecure: false 1091 | url: ldap://ipa.shared.example.opentlc.com/cn=users,cn=accounts,dc=shared,dc=example,dc=opentlc,dc=com?uid?sub?(memberOf=cn=ocp-users,cn=groups,cn=accounts,dc=shared,dc=example,dc=opentlc,dc=com) 1092 | login: true 1093 | mappingMethod: claim 1094 | name: LDAP 1095 | type: LDAP 1096 | -------------------------------------------------------------------------------- 1097 | 1098 | We can also observe that we have several `Groups` already synced in our cluster 1099 | 1100 | [subs=+quotes] 1101 | ---- 1102 | $ *oc get groups* 1103 | NAME USERS 1104 | ocp-platform david, admin1, admin2, admin 1105 | ocp-production karla, prod1, prod2, admin 1106 | ocp-users andrew, marina, karla, david, portal1, portal2, payment1, payment2, prod1, prod2, platform1, platform2, admin1, admin2, admin 1107 | paymentapp marina, payment1, payment2 1108 | portalapp andrew, portal1, portal2 1109 | ---- 1110 | 1111 | Now let's test the login capabilities. First we'll grab the cluster console URL: 1112 | 1113 | [subs=+quotes] 1114 | -------------------------------------------------------------------------------- 1115 | $ *oc whoami --show-console* 1116 | https://console-openshift-console.apps.example.com 1117 | -------------------------------------------------------------------------------- 1118 | 1119 | Now test login with user "karla", "david", or "andrew" using the same password as was used for LDAP bind. 1120 | 1121 | ==== MachineSets and AutoScalers 1122 | 1123 | A common ask in the enterprise is for administrators to create various customized types of nodes. 1124 | This could be done for various purposes such as: 1125 | 1126 | * To protect infrastructure components from application workloads 1127 | * To support workloads with varying resource needs (e.g. memory intensive vs. cpu intensive vs. GPU use cases) 1128 | 1129 | To simulate this we've configured a few machinesets which each manage a different flavor of node. You can see them with the following commands. 1130 | 1131 | [subs=+quotes] 1132 | ---- 1133 | $ *oc get machinesets -n openshift-machine-api* 1134 | NAME DESIRED CURRENT READY AVAILABLE AGE 1135 | cluster-1b5b-fxs69-compute-ap-northeast-1a 1 1 1 1 18h 1136 | cluster-1b5b-fxs69-compute-ap-northeast-1c 1 1 1 1 18h 1137 | cluster-1b5b-fxs69-compute-ap-northeast-1d 0 0 18h 1138 | cluster-1b5b-fxs69-logging-es-ap-northeast-1a 1 1 1 1 18h 1139 | cluster-1b5b-fxs69-logging-es-ap-northeast-1c 0 0 18h 1140 | cluster-1b5b-fxs69-logging-es-ap-northeast-1d 0 0 18h 1141 | cluster-1b5b-fxs69-worker-ap-northeast-1a 0 0 41h 1142 | cluster-1b5b-fxs69-worker-ap-northeast-1c 0 0 41h 1143 | cluster-1b5b-fxs69-worker-ap-northeast-1d 0 0 41h 1144 | 1145 | $ *oc get nodes* 1146 | NAME STATUS ROLES AGE VERSION 1147 | ip-10-0-137-115.ap-northeast-1.compute.internal Ready logging-es,worker 18h v1.13.4+ab8449285 1148 | ip-10-0-139-108.ap-northeast-1.compute.internal Ready compute,worker 18h v1.13.4+ab8449285 1149 | ip-10-0-141-142.ap-northeast-1.compute.internal Ready master 41h v1.13.4+ab8449285 1150 | ip-10-0-147-132.ap-northeast-1.compute.internal Ready compute,worker 18h v1.13.4+ab8449285 1151 | ---- 1152 | 1153 | You'll notice that all of our non-master nodes still have the role of `worker`. This is the functionality that comes out of the box. But by provisioning our own `machinesets`, we have created "sub roles" for our machines of `compute` and `logging-es`, creating a class of dedicated nodes to run our logging infrastructure. 1154 | 1155 | We have additionally enabled auto scaling of each machineset so that the cluster will automatically add new nodes when the schedule comes under pressure. 1156 | 1157 | [subs=+quotes] 1158 | ---- 1159 | $ *oc get machineautoscalers -n openshift-machine-api* 1160 | NAME REF KIND REF NAME MIN MAX AGE 1161 | cluster-1b5b-fxs69-compute-ap-northeast-1a MachineSet cluster-1b5b-fxs69-compute-ap-northeast-1a 1 2 18h 1162 | cluster-1b5b-fxs69-compute-ap-northeast-1c MachineSet cluster-1b5b-fxs69-compute-ap-northeast-1c 1 2 18h 1163 | cluster-1b5b-fxs69-compute-ap-northeast-1d MachineSet cluster-1b5b-fxs69-compute-ap-northeast-1d 0 2 18h 1164 | ---- 1165 | 1166 | Feel free to experiment with deploying some pods such that the machine autoscalers will trigger a scale-up. 1167 | 1168 | ==== Customized Project Creation 1169 | 1170 | Another common enterprise use case is to customize the project creation flow such that cluster administrators can maintain some control over the process. 1171 | 1172 | When a user creates a project in our cluster, we use the link:https://github.com/redhat-cop/namespace-configuration-operator[Namespace Configuration Operator^] to apply custom rules to that project. 1173 | 1174 | The operator itself has been deployed to the cluster. You can examine the deployment with the following: 1175 | 1176 | [subs=+quotes] 1177 | ---- 1178 | $ *oc describe deployment namespace-configuration-operator -n namespace-configuration-operator* 1179 | Name: namespace-configuration-operator 1180 | Namespace: namespace-configuration-operator 1181 | ... 1182 | 1183 | $ *oc get pods -n namespace-configuration-operator* 1184 | NAME READY STATUS RESTARTS AGE 1185 | namespace-configuration-operator-7f5d8dc966-db227 1/1 Running 0 18h 1186 | 1187 | $ *oc logs -n namespace-configuration-operator $(oc get pod -n namespace-configuration-operator -o name)* 1188 | ... 1189 | {"level":"info","ts":1568627802.596237,"logger":"cmd","msg":"Starting the Cmd."} 1190 | {"level":"info","ts":1568627802.6964204,"logger":"kubebuilder.controller","msg":"Starting Controller","controller":"namespaceconfig-controller"} 1191 | {"level":"info","ts":1568627802.796626,"logger":"kubebuilder.controller","msg":"Starting workers","controller":"namespaceconfig-controller","worker count":1} 1192 | {"level":"info","ts":1568627864.75009,"logger":"controller_namespaceconfig","msg":"Reconciling NamespaceConfig","Request.Namespace":"namespace-configuration-operator","Request.Name":"multitenant"} 1193 | {"level":"info","ts":1568627864.7545173,"logger":"controller_namespaceconfig","msg":"Reconciling NamespaceConfig","Request.Namespace":"namespace-configuration-operator","Request.Name":"multitenant"} 1194 | ... 1195 | $ *oc get namespaceconfig -n namespace-configuration-operator* 1196 | NAME AGE 1197 | cakephp-rolebindings 85m 1198 | gitops 50m 1199 | large-size 18h 1200 | multitenant 18h 1201 | small-size 18h 1202 | ---- 1203 | 1204 | When a user creates a project, custom rules will be applied based on labels placed on the project. We can see this through the following commands. 1205 | 1206 | [subs=+quotes] 1207 | ---- 1208 | $ *oc new-project test-project-policy* 1209 | $ *oc get resourcequota* 1210 | NAME CREATED AT 1211 | large-size 2019-09-13T20:39:16Z 1212 | 1213 | $ *oc get networkpolicy* 1214 | NAME POD-SELECTOR AGE 1215 | allow-from-same-namespace 68s 1216 | allow-network-policy-group-ingress 68s 1217 | 1218 | $ *oc delete project test-project-policy* 1219 | ---- 1220 | 1221 | Finally, log back into the web console as *andrew*. 1222 | 1223 | [subs=+quotes] 1224 | ---- 1225 | $ *oc whoami --show-console* 1226 | ---- 1227 | 1228 | You'll notice that some projects that have been created that only he has access to. This is to emulate the separation of concerns between Cluster Administrators and Application Developers. We'll use these projects later when we set up our *Application GitOps* 1229 | 1230 | === Enabling Webhook Triggering for Eunomia 1231 | 1232 | At this point we have used the combination of Eunomia and OpenShift Applier to roll out some complex cluster configurations, and our new OpenShift cluster is looking pretty snazzy. However, we'll want to make changes to our cluster over time, and we want those changes to be driven by the configurations we've pushed to Git. In order to enable a true GitOps workflow, we need the ability to trigger Eunomia to re-run our Applier job every time we make a change in our repository. To do this, we'll set up a webhook in our repo to trigger Eunomia. 1233 | 1234 | . When we deployed eunomia via `helm`, we created a `Route` for the operator. This route exposes a webhook in eunomia. We need to grab the hostname of that route. 1235 | + 1236 | [subs=+quotes] 1237 | ---- 1238 | $ **oc get route -n eunomia-operator** 1239 | NAME HOST/PORT PATH SERVICES PORT TERMINATION WILDCARD 1240 | eunomia-operator eunomia-operator-eunomia-operator.apps.cluster-a9b2.sandbox338.opentlc.com eunomia-operator webhook None 1241 | ---- 1242 | + 1243 | **Copy the route hostname value** for use in the next step. 1244 | 1245 | . **Create a webhook in your repository.** 1246 | + 1247 | Open your GitHub repository in your browser and navigate to the **Settings** tab. 1248 | 1249 | .. In the left sidebar menu, click on **Webhooks**, and then click the **Add webhook** button. 1250 | .. In the **Payload URL** field enter a value of the format `http:///webhook/`. For example: `http://eunomia-operator-eunomia-operator.apps.cluster-a9b2.sandbox338.opentlc.com/webhook/` 1251 | .. In the **Content Type** field, select `application/json`. 1252 | .. Under **Which events would you like to trigger this webhook?** ensure that `Just the push event` is selected 1253 | .. Ensure the **Active** box is checked. 1254 | .. Click **Add webhook**. 1255 | 1256 | If everything went properly, you should see a green checkbox next to your newly created webhook. 1257 | 1258 | === LDAP Group Sync Reconfiguration 1259 | 1260 | The initial parameters you used to configure LDAP group sync configured a cronjob to run every five minutes. 1261 | Now that you have given it a little time to run, you will now configure it to run hourly instead. 1262 | LDAP group sync was configured using an OpenShift template, so you will begin by exploring how the template was invoked and then reconfigure the parameter passed to the template to set the schedule. 1263 | 1264 | . Check the initial cronjob schedule for LDAP group sync: 1265 | + 1266 | [subs=+quotes] 1267 | -------------------------------------------------------------------------------- 1268 | $ **oc get cronjob -n openshift-config** 1269 | NAME SCHEDULE SUSPEND ACTIVE LAST SCHEDULE AGE 1270 | cronjob-ldap-group-sync */5 * * * * False 0 3m30s 91m 1271 | -------------------------------------------------------------------------------- 1272 | + 1273 | This cron time specification indicates the job should run every 5 minutes. 1274 | 1275 | . Examine the link:templates/ldap-group-sync.yaml[templates/ldap-group-sync.yaml] template and identify the parameter that controls the schedule. 1276 | + 1277 | The `oc process --parameters` command can be used to extract the description of available parameters: 1278 | + 1279 | [subs=+quotes] 1280 | -------------------------------------------------------------------------------- 1281 | $ *oc process --parameters -f ./templates/ldap-group-sync.yaml* 1282 | NAME DESCRIPTION VALUE 1283 | NAMESPACE Name of the Namespace where to deploy the Scheduled Job openshift-config 1284 | JOB_NAME Name of the Scheduled Job to Create. cronjob-ldap-group-sync 1285 | *SCHEDULE Cron Schedule to Execute the Job @hourly* 1286 | JOB_SERVICE_ACCOUNT Name of the Service Account To Exeucte the Job As. ldap-group-syncer 1287 | LDAP_CA_CONFIGMAP Name of the ConfigMap containing the LDAP Certificate Authority ldap-tls-ca 1288 | BIND_PASSWORD_SECRET Name of the Secret containing the LDAP bind password ldap-bind-password 1289 | LDAP_CONFIG_VOLUME_PATH Mount path of LDAP configuration files /ldap-sync 1290 | LDAP_CA_FILENAME Name of the LDAP CA file ca.crt 1291 | LDAP_BIND_PASSWORD_FILENAME Name of the LDAP bind password file bindPassword 1292 | LDAP_GROUPS_SEARCH_BASE Location in LDAP tree where you will find groups 1293 | LDAP_GROUPS_FILTER LDAP Filter to use when deciding which groups to sync into OpenShift (objectClass=groupofnames) 1294 | LDAP_GROUP_NAME_ATTRIBUTES The attribute list to use to discover the name for the group. ["cn"] 1295 | LDAP_GROUP_MEMBERSHIP_ATTRIBUTES ["member"] 1296 | LDAP_GROUP_UID_ATTRIBUTE The attribute that uniquely identifies a group on the LDAP server. dn 1297 | LDAP_GROUPS_WHITELIST File content for groups sync --whitelist option 1298 | LDAP_URL URL of you LDAP server 1299 | LDAP_BIND_DN The Full DN for the user you wish to use to authenticate to LDAP 1300 | LDAP_USERS_SEARCH_BASE Location in LDAP tree where you will find users 1301 | LDAP_SYNC_CONFIGMAP Name for the config map storing the group sync config ldap-group-sync 1302 | LDAP_USER_UID_ATTRIBUTE The attribute that uniquely identifies a user on the LDAP server. dn 1303 | LDAP_USER_NAME_ATTRIBUTES JSON list of attributes to use to discover the user name for group membership ["uid"] 1304 | LDAP_BIND_PASSWORD_SECRET The name for the secret in which to store the bind password ldap-bind-password 1305 | SUCCESS_JOBS_HISTORY_LIMIT The number of successful jobs that will be retained 5 1306 | FAILED_JOBS_HISTORY_LIMIT The number of failed jobs that will be retained 5 1307 | IMAGE Image to use for the container. registry.redhat.io/openshift4/ose-cli 1308 | IMAGE_TAG Image Tag to use for the container. 4.1 1309 | LDAP_SYNC_CONFIGMAP Name for the config map storing the group sync config ldap-group-sync 1310 | LDAP_CA_CONFIGMAP Name for the config map storing the TLS certificate authority ldap-tls-ca 1311 | -------------------------------------------------------------------------------- 1312 | + 1313 | The `SCHEDULE` template parameter is used to set the cron job schedule. 1314 | 1315 | . Identify the connection between the OpenShift template parameter, `SCHEDULE` and the Ansible inventory: 1316 | + 1317 | [subs=+quotes] 1318 | -------------------------------------------------------------------------------- 1319 | $ *grep SCHEDULE -C10 ./.applier/group_vars/seed-hosts/main.yml* 1320 | - object: LDAP Group Synchronization 1321 | content: 1322 | - name: LDAP Group Synchronization 1323 | template: "{{ inventory_dir }}/../templates/ldap-group-sync.yaml" 1324 | params_from_vars: 1325 | LDAP_GROUPS_SEARCH_BASE: "{{ ldap_groups_search_base }}" 1326 | LDAP_BIND_DN: "{{ ldap_bind_dn }}" 1327 | LDAP_URL: "{{ ldap_url }}" 1328 | LDAP_USERS_SEARCH_BASE: "{{ ldap_users_search_base }}" 1329 | LDAP_GROUPS_WHITELIST: "{{ ldap_groups_whitelist | default('') }}" 1330 | *SCHEDULE: "{{ ldap_cron_schedule }}"* 1331 | namespace: openshift-config 1332 | tags: 1333 | - ldap_group_sync 1334 | - object: Setup AWS StorageClasses 1335 | content: 1336 | - name: Setup AWS StorageClasses 1337 | template: "{{ inventory_dir }}/../templates/aws-ebs-storage-classes.yaml" 1338 | params_from_vars: 1339 | ENCRYPT_STORAGE: "{{ aws_sc_encrypt_storage }}" 1340 | namespace: openshift-config 1341 | -------------------------------------------------------------------------------- 1342 | + 1343 | The `params_from_vars` option under the `openshift_cluster_content` object content provides the `openshift-applier` ansible role the mapping from Ansible values to template parameters. 1344 | In this configuration, `SCHEDULE` is set based on the value of `ldap_cron_schedule`. 1345 | 1346 | . Identify where the `ldap_cron_schedule` variable is set. 1347 | As we saw when configuring authentication, the `seed-hosts` group in the `openshift-applier` inventory is configured in `.applier/group_vars/seed-hosts`. 1348 | Use `grep` to find where in the Ansible inventory `ldap_cron_schedule` is set: 1349 | + 1350 | [subs=+quotes] 1351 | -------------------------------------------------------------------------------- 1352 | $ **grep ^ldap_cron_schedule -B1 .applier/group_vars/seed-hosts/*** 1353 | .applier/group_vars/seed-hosts/auth.yml-# LDAP group sync configuration 1354 | .applier/group_vars/seed-hosts/auth.yml:ldap_cron_schedule: "*/5 * * * *" 1355 | -------------------------------------------------------------------------------- 1356 | + 1357 | NOTE: The `-B1` option to grep is shown here to include context of one line before the match. 1358 | 1359 | . Set `ldap_cron_schedule` to `@hourly` within `.applier/group_vars/seed-hosts/auth.yml`: 1360 | + 1361 | [subs=+quotes] 1362 | -------------------------------------------------------------------------------- 1363 | $ **sed -i \'s|^ldap_cron_schedule:.*|ldap_cron_schedule: "@hourly"|' \ 1364 | .applier/group_vars/seed-hosts/auth.yml** 1365 | -------------------------------------------------------------------------------- 1366 | + 1367 | NOTE: `@hourly` is a convenient shorthand for hourly cron jobs. It is equivalent to `0 * * * *`. 1368 | 1369 | . Git add/commit/push your changes to your GitHub repository: 1370 | + 1371 | Use `git add -p` to review changes and queue them for your next commit: 1372 | + 1373 | [subs=+quotes] 1374 | -------------------------------------------------------------------------------- 1375 | $ *git add -p .applier/* 1376 | diff --git a/.applier/group_vars/seed-hosts/auth.yml b/.applier/group_vars/seed-hosts/auth.yml 1377 | index 0015fe1..57e3461 100644 1378 | --- a/.applier/group_vars/seed-hosts/auth.yml 1379 | \+++ b/.applier/group_vars/seed-hosts/auth.yml 1380 | @@ -18,7 \+18,7 @@ ldap_auth_search_filter: "(memberOf=cn=ocp-users,cn=groups,cn=accounts,dc=shared 1381 | ldap_search_url: "{{ ldap_url }}/{{ ldap_users_search_base }}?uid?sub?{{ ldap_auth_search_filter }}" 1382 | 1383 | # LDAP group sync configuration 1384 | -ldap_cron_schedule: "\*/5 * * * 8" 1385 | +ldap_cron_schedule: "@hourly" 1386 | 1387 | # Groups path for LDAP search 1388 | ldap_groups_search_base: cn=groups,cn=accounts,dc=example,dc=com 1389 | Stage this hunk [y,n,q,a,d,/,j,J,g,e,?]? **y** 1390 | 1391 | -------------------------------------------------------------------------------- 1392 | + 1393 | Use `git commit` to commit changes to the "master" branch: 1394 | + 1395 | [subs=+quotes] 1396 | -------------------------------------------------------------------------------- 1397 | $ **git commit -m "Set ldap group sync to hourly"** 1398 | [master d4f7a8f] Set ldap group sync to hourly 1399 | 1 file changed, 1 insertions(+), 1 deletions(-) 1400 | -------------------------------------------------------------------------------- 1401 | + 1402 | Push your commit to your GitHub repository, authenticating when prompted: 1403 | + 1404 | [subs=+quotes] 1405 | -------------------------------------------------------------------------------- 1406 | $ **git push origin master** 1407 | Username for \'https://github.com': **__GITHUB_ACCOUNT__** 1408 | Password for \'https://__GITHUB_ACCOUNT__@github.com': 1409 | Counting objects: 11, done. 1410 | Delta compression using up to 2 threads. 1411 | Compressing objects: 100% (5/5), done. 1412 | Writing objects: 100% (6/6), 667 bytes | 0 bytes/s, done. 1413 | Total 6 (delta 2), reused 0 (delta 0) 1414 | remote: Resolving deltas: 100% (2/2), completed with 2 local objects. 1415 | To https://github.com/__GITHUB_ACCOUNT__/operationalizing-openshift-lab.git 1416 | 3a51662..d4f7a8f master -> master 1417 | -------------------------------------------------------------------------------- 1418 | 1419 | . Since we configured a webhook in the previous section, a new Eunomia job should have started automatically. Check that Eunomia has started a new job to apply the changes: 1420 | + 1421 | [subs=+quotes] 1422 | -------------------------------------------------------------------------------- 1423 | $ **oc get job -n cluster-config** 1424 | NAME COMPLETIONS DURATION AGE 1425 | gitopsconfig-cluster-config-7u9lld 1/1 58s 5m43s 1426 | gitopsconfig-cluster-config-8ziqr8 1/1 44s 47s 1427 | -------------------------------------------------------------------------------- 1428 | + 1429 | You may need to wait for job completion or inspect job logs as shown previously. 1430 | 1431 | . Verify the cronjob schedule has been updated after Eunomia job completion: 1432 | + 1433 | [subs=+quotes] 1434 | -------------------------------------------------------------------------------- 1435 | $ **oc get cronjob -n openshift-config** 1436 | NAME SCHEDULE SUSPEND ACTIVE LAST SCHEDULE AGE 1437 | cronjob-ldap-group-sync @hourly False 0 3m30s 91m 1438 | -------------------------------------------------------------------------------- 1439 | 1440 | Configure Eunomia to Run Periodically 1441 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1442 | 1443 | You have Eunomia running and processing changes from git. 1444 | Now, wouldn't it be great if it processed updates automatically? 1445 | Let's configure Eunomia to run periodically jobs. 1446 | 1447 | . Edit the template `cluster-gitops.yaml` template to add a `SCHEDULE` parameter: 1448 | + 1449 | Find the two commented lines in the provided template and enable periodic processing using the `"${SCHEDULE}"` parameter value for the `cron` value. 1450 | Once updated the triggers for the GitOpsConfig should read: 1451 | + 1452 | -------------------------------------------------------------------------------- 1453 | triggers: 1454 | - type: Change 1455 | - type: Periodic 1456 | cron: "${SCHEDULE}" 1457 | -------------------------------------------------------------------------------- 1458 | + 1459 | Next add the `SCHEDULE` parameter to the template definition with the default value of `@hourly`: 1460 | + 1461 | -------------------------------------------------------------------------------- 1462 | - name: SCHEDULE 1463 | description: Periodic schedule for gitops processing 1464 | value: "@hourly" 1465 | -------------------------------------------------------------------------------- 1466 | + 1467 | The following commands can be used for this change, though manually editing the file is recommended: 1468 | + 1469 | [subs=+quotes] 1470 | -------------------------------------------------------------------------------- 1471 | $ **sed -i \'s/#- type: Periodic/- type: Periodic/' templates/cluster-gitops.yaml** 1472 | $ **sed -i \'s/#cron: .*/cron: "${SCHEDULE}"/' templates/cluster-gitops.yaml** 1473 | $ **cat >>templates/cluster-gitops.yaml <.applier/group_vars/seed-hosts/gitops.yml < master 1574 | -------------------------------------------------------------------------------- 1575 | 1576 | . Wait for Eunomia applier processing to complete and then check gitopsconfig definition: 1577 | + 1578 | [subs=+quotes] 1579 | -------------------------------------------------------------------------------- 1580 | $ **oc get gitopsconfig -n cluster-config cluster-config -o yaml | grep \'^ triggers:' -A3** 1581 | triggers: 1582 | - type: Change 1583 | - cron: '30 7,19 * * ' 1584 | type: Periodic 1585 | -------------------------------------------------------------------------------- 1586 | 1587 | Cluster Autoscaler Reconfiguration 1588 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1589 | 1590 | The initial configuration of the link:https://docs.openshift.com/container-platform/4.1/machine_management/creating-machineset.html#machine-api-overview_creating-machineset[OpenShift Machine API^] provided in this lab has configured machine sets and the cluster autoscaler. 1591 | In this exercise we will explore this configuration and add new parameters to customize the cluster autoscaler configuration. 1592 | 1593 | . Inspect the cluster autoscaler configuration: 1594 | + 1595 | [subs=+quotes] 1596 | -------------------------------------------------------------------------------- 1597 | $ **oc get clusterautoscaler default -o yaml** 1598 | apiVersion: autoscaling.openshift.io/v1 1599 | kind: ClusterAutoscaler 1600 | metadata: 1601 | annotations: 1602 | kubectl.kubernetes.io/last-applied-configuration: | 1603 | {"apiVersion":"autoscaling.openshift.io/v1","kind":"ClusterAutoscaler","metadata":{"annotations":{},"name":"default"},"spec":{"podPriorityThreshold":-10,"resourceLimits":{"cores":{"max":128,"min":8},"maxNodesTotal":24,"memory":{"max":256,"min":4}},"scaleDown":{"delayAfterAdd":"30m","delayAfterDelete":"30m","delayAfterFailure":"5m","enabled":true,"unneededTime":"5m"}}} 1604 | creationTimestamp: "2019-09-12T14:12:21Z" 1605 | generation: 2 1606 | name: default 1607 | resourceVersion: "253315" 1608 | selfLink: /apis/autoscaling.openshift.io/v1/clusterautoscalers/default 1609 | uid: 55e18c3d-d567-11e9-9b22-0a6f47c8dc86 1610 | spec: 1611 | podPriorityThreshold: -10 1612 | resourceLimits: 1613 | cores: 1614 | max: 128 1615 | min: 8 1616 | maxNodesTotal: 24 1617 | memory: 1618 | max: 256 1619 | min: 4 1620 | scaleDown: 1621 | delayAfterAdd: 30m 1622 | delayAfterDelete: 30m 1623 | delayAfterFailure: 5m 1624 | enabled: true 1625 | unneededTime: 5m 1626 | -------------------------------------------------------------------------------- 1627 | + 1628 | For this exercise you will add variables for setting the `cores` and `memory` resource limits. 1629 | 1630 | . Identify the source of the autoscaler configuration: 1631 | + 1632 | [subs=+quotes] 1633 | -------------------------------------------------------------------------------- 1634 | $ **grep Autoscaler -r manifests/ templates/** 1635 | manifests/clusterautoscaler.yaml:kind: ClusterAutoscaler 1636 | templates/custom-machinesets.j2:kind: MachineAutoscaler 1637 | -------------------------------------------------------------------------------- 1638 | + 1639 | There is configuration for both MachineAutoscaler as well as the ClusterAutoscaler custom resources. 1640 | The MachineAutoscaler configuration is already handled by a Jinja2 template and configured with the `machineset_custom_groups` ansible variable. 1641 | We will focus on adding a Jinja2 template for the ClusterAutoscaler. 1642 | 1643 | . Rename `manifests/clusterautoscaler.yaml` to `templates/clusterautoscaler.j2`: 1644 | + 1645 | [subs=+quotes] 1646 | -------------------------------------------------------------------------------- 1647 | $ **git mv manifests/clusterautoscaler.yaml templates/clusterautoscaler.j2** 1648 | -------------------------------------------------------------------------------- 1649 | + 1650 | Using `git mv` renames the file while retaining version control history. 1651 | 1652 | . Update the reference the file path for the cluster autoscaler configuration in `openshift_cluster_content` in the file `.applier/group_vars/seed-hosts/main.yml`: 1653 | + 1654 | Within the definition of `openshift_cluster_content` and find the "OpenShift Machine API" object and the content item "ClusterAutoscaler" within it. 1655 | The current value fo this item is `file: "{{ inventory_dir }}/../manifests/clusterautoscaler.yaml"`. 1656 | Change this to `file: "{{ inventory_dir }}/../templates/clusterautoscaler.j2"`. 1657 | Note that we keep the `file` processing as the output of the template is a resource definition rather than an OpenShift template definition. 1658 | + 1659 | A single command to make this edit to `.applier/group_vars/seed-hosts/main.yml` is: 1660 | + 1661 | [subs=+quotes] 1662 | -------------------------------------------------------------------------------- 1663 | $ **sed -i "s|manifests/clusterautoscaler.yaml|templates/clusterautoscaler.j2|" \ 1664 | .applier/group_vars/seed-hosts/main.yml** 1665 | -------------------------------------------------------------------------------- 1666 | 1667 | . Update `templates/clusterautoscaler.j2` to add variables for max and min cpus and memory: 1668 | + 1669 | Change the resource limits `cores` and `memory` values for `min` and `max` to use variables `cluster_autoscaler_cores_min`, `cluster_autoscaler_cores_max`, `cluster_autoscaler_memory_min`, and `cluster_autoscaler_memory_max` using the current values as defaults. 1670 | + 1671 | A single command to overwrite `templates/clusterautoscaler.j2` making these changes is: 1672 | + 1673 | [subs=+quotes] 1674 | -------------------------------------------------------------------------------- 1675 | $ **cat >templates/clusterautoscaler.j2 <>.applier/group_vars/seed-hosts/openshift-machine-api.yml < templates/clusterautoscaler.j2} (56%) 1773 | $ **git push origin master** 1774 | Username for \'https://github.com': **__GITHUB_ACCOUNT__** 1775 | Password for \'https://__GITHUB_ACCOUNT__@github.com': 1776 | Counting objects: 16, done. 1777 | Delta compression using up to 2 threads. 1778 | Compressing objects: 100% (8/8), done. 1779 | Writing objects: 100% (9/9), 1.01 KiB | 0 bytes/s, done. 1780 | Total 9 (delta 5), reused 0 (delta 0) 1781 | remote: Resolving deltas: 100% (5/5), completed with 5 local objects. 1782 | To git@github.com:__GITHUB_ACCOUNT__/operationalizing-openshift-lab.git 1783 | 2fa5a16..5c02182 master -> master 1784 | -------------------------------------------------------------------------------- 1785 | 1786 | . Wait for Eunomia applier job completion and then check that the clusterautoscaler has been updated: 1787 | + 1788 | [subs=+quotes] 1789 | -------------------------------------------------------------------------------- 1790 | $ **oc get clusterautoscaler default -o yaml** 1791 | apiVersion: autoscaling.openshift.io/v1 1792 | kind: ClusterAutoscaler 1793 | metadata: 1794 | annotations: 1795 | kubectl.kubernetes.io/last-applied-configuration: | 1796 | {"apiVersion":"autoscaling.openshift.io/v1","kind":"ClusterAutoscaler","metadata":{"annotations":{},"name":"default"},"spec":{"podPriorityThreshold":-10,"resourceLimits":{"cores":{"max":256,"min":16},"maxNodesTotal":24,"memory":{"max":512,"min":8}},"scaleDown":{"delayAfterAdd":"30m","delayAfterDelete":"30m","delayAfterFailure":"5m","enabled":true,"unneededTime":"5m"}}} 1797 | creationTimestamp: "2019-09-12T14:12:21Z" 1798 | generation: 3 1799 | name: default 1800 | resourceVersion: "262082" 1801 | selfLink: /apis/autoscaling.openshift.io/v1/clusterautoscalers/default 1802 | uid: 55e18c3d-d567-11e9-9b22-0a6f47c8dc86 1803 | spec: 1804 | podPriorityThreshold: -10 1805 | resourceLimits: 1806 | cores: 1807 | max: 256 1808 | min: 16 1809 | maxNodesTotal: 24 1810 | memory: 1811 | max: 512 1812 | min: 8 1813 | scaleDown: 1814 | delayAfterAdd: 30m 1815 | delayAfterDelete: 30m 1816 | delayAfterFailure: 5m 1817 | enabled: true 1818 | unneededTime: 5m 1819 | -------------------------------------------------------------------------------- 1820 | + 1821 | Note the increased values for resource limits for `cores` and `memory`. 1822 | 1823 | Application GitOps 1824 | ------------------ 1825 | 1826 | image::media/app-workflow.png[] 1827 | 1828 | _Infrastructure as Code_ plays an equally important role for application developers as for cluster administrators. 1829 | The good news is that our GitOps workflow applies just as easily for applications as it does for clusters. 1830 | In this section, we are going to walk through an entirely separate GitOps workflow for managing an application on OpenShift. 1831 | We will use a very similar pattern as we have seen for platform administration. 1832 | A couple key differences are that we are dealing with application build and deployment resources and that application administrators usually do not have full cluster-admin access. 1833 | 1834 | Until now, we have bee acting as the persona of *David* the cluster administrator. For this section, we are going to take on a new persona of *Andrew*, a PHP Developer on the *Portal App* team. 1835 | 1836 | As you go through this portion of the lab, you will learn the following. 1837 | 1838 | * *Our GitOps tooling naturally supports multitenancy.* By this we mean that we can have multiple GitOps workflows owned by different users within the same cluster, and they do not conflict or depend on each other. 1839 | 1840 | Creating an App GitOps Repository 1841 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1842 | 1843 | . Fork the link:https://github.com/redhat-gpte-devopsautomation/cakephp-ex[redhat-gpte-devopsautomation cakephp-ex^] repository into your own account by selecting the *Fork* button and then if necessary, select the account for which the repository should be forked to. 1844 | 1845 | . Clone the fork locally in your home directory: 1846 | + 1847 | [subs=+quotes] 1848 | -------------------------------------------------------------------------------- 1849 | $ **cd $HOME** 1850 | $ **git clone https://github.com/${GITHUB_ACCOUNT}/cakephp-ex.git** 1851 | Cloning into 'cakephp-ex'... 1852 | remote: Enumerating objects: 13, done. 1853 | remote: Counting objects: 100% (13/13), done. 1854 | remote: Compressing objects: 100% (11/11), done. 1855 | remote: Total 3256 (delta 1), reused 9 (delta 0), pack-reused 3243 1856 | Receiving objects: 100% (3256/3256), 3.68 MiB | 2.73 MiB/s, done. 1857 | Resolving deltas: 100% (1073/1073), done. 1858 | -------------------------------------------------------------------------------- 1859 | 1860 | . Update repository references in the OpenShift templates: 1861 | + 1862 | [subs=+quotes] 1863 | -------------------------------------------------------------------------------- 1864 | $ **cd cakephp-ex** 1865 | $ **sed -r -i "s|https://github.com/redhat-gpte-devopsautomation/|https://github.com/${GITHUB_ACCOUNT}/|" \ 1866 | openshift/\*/*.yml** 1867 | -------------------------------------------------------------------------------- 1868 | 1869 | . Update repository references in `.applier/group_vars/seed-hosts/main.yml`: 1870 | + 1871 | [subs=+quotes] 1872 | -------------------------------------------------------------------------------- 1873 | $ **sed -r -i "s|https://raw.githubusercontent.com/redhat-gpte-devopsautomation/|https://raw.githubusercontent.com/${GITHUB_ACCOUNT}/|" \ 1874 | .applier/group_vars/seed-hosts/main.yml** 1875 | -------------------------------------------------------------------------------- 1876 | 1877 | . Add a NetworkPolicy definition to the database template: 1878 | + 1879 | [subs=+quotes] 1880 | -------------------------------------------------------------------------------- 1881 | $ **cat >>openshift/multi-project-templates/cakephp-mysql-persistent.yml < master (forced update) 1935 | -------------------------------------------------------------------------------- 1936 | 1937 | . Create and push git tag `v1.0-1`. 1938 | + 1939 | Application deployment will require tagged versions in your GitHub repository. 1940 | Create a git tag `v1.0-1` and push it to your repository: 1941 | + 1942 | [subs=+quotes] 1943 | -------------------------------------------------------------------------------- 1944 | $ **git tag v1.0-1** 1945 | $ **git push origin v1.0-1** 1946 | Username for \'https://github.com': **__GITHUB_ACCOUNT__** 1947 | Password for \'https://__GITHUB_ACCOUNT__@github.com': 1948 | Total 0 (delta 0), reused 0 (delta 0) 1949 | To https://github.com/__GITHUB_ACCOUNT__/cakephp-ex.git 1950 | * [new tag] v1.0-1 -> v1.0-1 1951 | -------------------------------------------------------------------------------- 1952 | 1953 | Configuring App GitOps with Eunomia 1954 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1955 | 1956 | The CakePHP example repository you are using is already configured for use with `openshift-applier`, including the `.applier` directory, `requirements.yml`, and GitOps OpenShift template. 1957 | We will now apply our Application GitOps configuration to the cluster. But first, we need to take on our new persona, *Andrew* the PHP Developer. 1958 | 1959 | [subs=+quota] 1960 | ---- 1961 | $ oc whoami --show-server 1962 | https://api.cluster-..sandbox.opentlc.com:6443 1963 | 1964 | $ oc login -u andrew $(oc whoami --show-server) 1965 | Username: andrew 1966 | Password: 1967 | Login successful. 1968 | 1969 | You have access to the following projects and can switch between them with 'oc project ': 1970 | 1971 | * cakephp-app-dev 1972 | cakephp-app-prod 1973 | cakephp-app-test 1974 | cakephp-build 1975 | cakephp-db-dev 1976 | cakephp-db-prod 1977 | cakephp-db-test 1978 | 1979 | Using project "cakephp-app-dev". 1980 | ---- 1981 | 1982 | Remember those projects we saw earlier in the lab? Now that we have taken on the *Andrew* persona, we are going to deploy our Cake PHP app to these projects. 1983 | Let's configure our GitOps workflow. 1984 | 1985 | . Configure application GitOps for the example CakePHP application by processing the `gitops.yml` template found in the `openshift/multi-project-templates/` directory. 1986 | + 1987 | [subs=+quotes] 1988 | -------------------------------------------------------------------------------- 1989 | $ **oc process -f openshift/multi-project-templates/gitops.yml | oc apply -f -** 1990 | gitopsconfig.eunomia.kohls.io/cakephp created 1991 | -------------------------------------------------------------------------------- 1992 | + 1993 | This has created a GitOpsConfig in the `cakephp-gitops` namespace for processing by Eunomia. 1994 | 1995 | . Eunomia should automatically respond to the creation of the GitOpsConfig by creating a GitOps job in the `cakephp-gitops` namespace: 1996 | + 1997 | [subs=+quotes] 1998 | -------------------------------------------------------------------------------- 1999 | $ **oc get job -n cakephp-gitops** 2000 | NAME COMPLETIONS DURATION AGE 2001 | gitopsconfig-cakephp-pd3pu1 1/1 56s 71s 2002 | -------------------------------------------------------------------------------- 2003 | + 2004 | Upon completion the GitOps resources and deployments will have been created. 2005 | Now we need to show how to use GitOps with application pipelines. 2006 | 2007 | . Verify application build and deployment: 2008 | + 2009 | Verify that the `openshift/multi-project-templates/cakephp-build.yml` template has created an OpenShift build config in the `cakephp-build` namespace: 2010 | + 2011 | [subs=+quotes] 2012 | -------------------------------------------------------------------------------- 2013 | $ **oc get buildconfig -n cakephp-build** 2014 | NAME TYPE FROM LATEST 2015 | cakephp Source Git 1 2016 | -------------------------------------------------------------------------------- 2017 | + 2018 | This build config is configured to create an output image with the version tag `v1.0-1`: 2019 | + 2020 | [subs=+quotes] 2021 | -------------------------------------------------------------------------------- 2022 | $ **oc get buildconfig -n cakephp-build cakephp -o yaml | grep -A3 output:** 2023 | output: 2024 | to: 2025 | kind: ImageStreamTag 2026 | name: cakephp:v1.0-1 2027 | -------------------------------------------------------------------------------- 2028 | + 2029 | And the build is triggered by a change to the build config: 2030 | + 2031 | [subs=+quotes] 2032 | -------------------------------------------------------------------------------- 2033 | $ **oc get buildconfig -n cakephp-build cakephp -o yaml | grep -A4 triggers:** 2034 | triggers: 2035 | - type: ConfigChange 2036 | - github: 2037 | secret: 1hvHmVgqn7KUG147X6uT0m7pvJtsylDuYtwHogW3 2038 | type: GitHub 2039 | -------------------------------------------------------------------------------- 2040 | + 2041 | Verify that the initial creation of the `cakephp` build config has triggered a build: 2042 | + 2043 | [subs=+quotes] 2044 | -------------------------------------------------------------------------------- 2045 | $ **oc get build -n cakephp-build** 2046 | NAME TYPE FROM STATUS STARTED DURATION 2047 | cakephp-1 Source Git@e580286 Complete 5 minutes ago 2m1s 2048 | -------------------------------------------------------------------------------- 2049 | + 2050 | And that this build has produced a version `v1.0-1` image: 2051 | + 2052 | [subs=+quotes] 2053 | -------------------------------------------------------------------------------- 2054 | $ **oc get is -n cakephp-build** 2055 | NAME IMAGE REPOSITORY TAGS UPDATED 2056 | cakephp image-registry.openshift-image-registry.svc:5000/cakephp-build/cakephp v1.0-1 5 minutes ago 2057 | -------------------------------------------------------------------------------- 2058 | + 2059 | Next check the deployment config in the `cakephp-app-dev` namespace 2060 | + 2061 | [subs=+quotes] 2062 | -------------------------------------------------------------------------------- 2063 | **$ oc get deploymentconfig -n cakephp-app-dev** 2064 | NAME REVISION DESIRED CURRENT TRIGGERED BY 2065 | cakephp 1 1 1 config,image(cakephp:v1.0-1) 2066 | -------------------------------------------------------------------------------- 2067 | + 2068 | And that this deployment config has been triggered by the creation of the image in the `cakephp-build` namespace with tag `v1.0-1`: 2069 | + 2070 | [subs=+quotes] 2071 | -------------------------------------------------------------------------------- 2072 | **$ oc get deploymentconfig -n cakephp-app-dev cakephp -o yaml | grep triggers: -A11** 2073 | triggers: 2074 | - imageChangeParams: 2075 | automatic: true 2076 | containerNames: 2077 | - cakephp-mysql-persistent 2078 | from: 2079 | kind: ImageStreamTag 2080 | name: cakephp:v1.0-1 2081 | namespace: cakephp-build 2082 | lastTriggeredImage: image-registry.openshift-image-registry.svc:5000/cakephp-build/cakephp@sha256:c354df0f026fdf5fda9f03635f51eb8d7aaed23e55cc8664b085a629f94bc0ce 2083 | type: ImageChange 2084 | - type: ConfigChange 2085 | -------------------------------------------------------------------------------- 2086 | + 2087 | Finally get the route for the `cakephp-app-dev` namespace... 2088 | + 2089 | [subs=+quotes] 2090 | -------------------------------------------------------------------------------- 2091 | $ **oc get route -n cakephp-app-dev** 2092 | NAME HOST/PORT PATH SERVICES PORT TERMINATION WILDCARD 2093 | cakephp cakephp-cakephp-app-dev.apps.cluster-1b5b.1b5b.sandbox1485.opentlc.com cakephp None 2094 | -------------------------------------------------------------------------------- 2095 | + 2096 | And verify that the hostname showed is serving content with protocol `http://`. 2097 | + 2098 | Similar verification can also be performed on the `cakephp-app-test` and `cakephp-app-prod` namespaces. 2099 | 2100 | Developer Workflow 2101 | ~~~~~~~~~~~~~~~~~~ 2102 | 2103 | The developer workflow with GitOps starts in the developer's sandbox project namespace. 2104 | The sandbox is where the application is first run containerized and is where most issues should be first caught, before changes are ever run through the pipeline or even committed to version control. 2105 | This is why it is so important that the GitOps process is able to process in the same manner that users work to deploy for initial testing. 2106 | 2107 | You will start by creating a developer sandbox project and deploying the full application into the sandbox. 2108 | You will then edit and validate changes in your sandbox. 2109 | Once changes are verified you will manually simulate an CI/CD deployment implemented by GitOps. 2110 | 2111 | . Create a `cakephp-sbx` project: 2112 | + 2113 | [subs=+quotes] 2114 | -------------------------------------------------------------------------------- 2115 | $ **oc new-project cakephp-sbx** 2116 | Now using project "cakephp-sbx" on server "https://api.cluster-1b5b.1b5b.sandbox1485.opentlc.com:6443". 2117 | 2118 | You can add applications to this project with the 'new-app' command. For example, try: 2119 | 2120 | oc new-app django-psql-example 2121 | 2122 | to build a new example application in Python. Or use kubectl to deploy a simple Kubernetes application: 2123 | 2124 | kubectl create deployment hello-node --image=gcr.io/hello-minikube-zero-install/hello-node 2125 | 2126 | -------------------------------------------------------------------------------- 2127 | 2128 | . Write a template parameters file, setting all namespaces to the sandbox: 2129 | + 2130 | [subs=+quotes] 2131 | -------------------------------------------------------------------------------- 2132 | $ **cat >sandbox-params.yml < None 2215 | -------------------------------------------------------------------------------- 2216 | + 2217 | NOTE: It may take a few minutes for the database to initialize and the application to start for the first time. 2218 | 2219 | . Add some missing documentation to the application home page template: 2220 | + 2221 | Add the following content in `src/Template/Pages/home.ctp` immediately before the line "# Within your project directory": 2222 | + 2223 | -------------------------------------------------------------------------------- 2224 | # Test build from local source before commit 2225 | $ oc start-build cakephp-example --from-dir=. 2226 | -------------------------------------------------------------------------------- 2227 | + 2228 | A `sed` command to make this edit is: 2229 | + 2230 | [subs=+quotes] 2231 | -------------------------------------------------------------------------------- 2232 | $ **sed -i \'/# Within your project directory/i # Test build from local source before commit\n$ oc start-build cakephp-example --from-dir=.\n' \ 2233 | ./src/Template/Pages/home.ctp** 2234 | -------------------------------------------------------------------------------- 2235 | 2236 | . Now test a local build yourself from your local source: 2237 | + 2238 | [subs=+quotes] 2239 | -------------------------------------------------------------------------------- 2240 | $ **oc start-build cakephp --from-dir=.** 2241 | Uploading directory "." as binary input for the build ... 2242 | .. 2243 | Uploading finished 2244 | build.build.openshift.io/cakephp-2 started 2245 | -------------------------------------------------------------------------------- 2246 | 2247 | . Wait for the build to finish then observe that a new deployment of the deployment config in your sandbox is triggered automatically. 2248 | When the build finishes, recheck the webpage and confirm that the new content appears. 2249 | + 2250 | You can follow the build logs with: 2251 | + 2252 | [subs=+quotes] 2253 | -------------------------------------------------------------------------------- 2254 | $ **oc logs -f build/cakephp-2** 2255 | -------------------------------------------------------------------------------- 2256 | + 2257 | After the build completes wait for the new deployment to trigger and complete then check the web page content by refreshing your browser. 2258 | You should see the instruction regarding "Test build from local source before commit" that you added. 2259 | 2260 | . Now that you have tested your new code running with an OpenShift build in a container you are ready to commit and push: 2261 | + 2262 | Add your change for commit: 2263 | + 2264 | [subs=+quotes] 2265 | -------------------------------------------------------------------------------- 2266 | $ **git add -p** 2267 | diff --git a/src/Template/Pages/home.ctp b/src/Template/Pages/home.ctp 2268 | index 3f304ce..3f468da 100644 2269 | --- a/src/Template/Pages/home.ctp 2270 | \+++ b/src/Template/Pages/home.ctp 2271 | @@ -28,6 +28,9 @@ 2272 | 2273 |
$ git clone <git_url> <directory_to_create>
2274 | 
2275 | +# Test build from local source before commit
2276 | +$ oc start-build cakephp-example --from-dir=.
2277 | +
2278 |  # Within your project directory
2279 |  # Commit your changes and push to OpenShift
2280 | 
2281 | Stage this hunk [y,n,q,a,d,/,e,?]? **y**
2282 | --------------------------------------------------------------------------------
2283 | +
2284 | And commit to git:
2285 | +
2286 | [subs=+quotes]
2287 | --------------------------------------------------------------------------------
2288 | $ **git commit -m \'Add home page content'**
2289 | [master ec4f9e6] Add home page content
2290 |  4 files changed, 32 insertions(+)
2291 | --------------------------------------------------------------------------------
2292 | 
2293 | . Now tag and update for a new release.
2294 | +
2295 | Check current git tags:
2296 | +
2297 | [subs=+quotes]
2298 | --------------------------------------------------------------------------------
2299 | $ **git tag**
2300 | v1.0
2301 | v1.0-1
2302 | --------------------------------------------------------------------------------
2303 | +
2304 | The next tagged release for build will be `v1.0-2`.
2305 | Update the `build_version` variable in `.applier/group_vars/seed-hosts/vars.yml`:
2306 | +
2307 | [subs=+quotes]
2308 | --------------------------------------------------------------------------------
2309 | $ **sed -i \'s|build_version:.*|build_version: v1.0-2|' .applier/group_vars/seed-hosts/vars.yml**
2310 | --------------------------------------------------------------------------------
2311 | +
2312 | Add this change and commit to git:
2313 | +
2314 | [subs=+quotes]
2315 | --------------------------------------------------------------------------------
2316 | $ **git add -p**
2317 | diff --git a/.applier/group_vars/seed-hosts/vars.yml b/.applier/group_vars/seed-hosts/vars.yml
2318 | index 8f2e2ad..1c0757d 100644
2319 | --- a/.applier/group_vars/seed-hosts/vars.yml
2320 | \+++ b/.applier/group_vars/seed-hosts/vars.yml
2321 | @@ -1,4 +1,4 @@
2322 | -build_version: v1.0-1
2323 | +build_version: v1.0-2
2324 |  dev_version: v1.0-1
2325 |  test_version: v1.0-1
2326 |  prod_version: v1.0-1
2327 | Stage this hunk [y,n,q,a,d,/,e,?]? **y**
2328 | 
2329 | $ **git commit -m \'Update build_version to v1.0-2'**
2330 | [master 87304ca] Update build_version to v1.0-2
2331 |  1 file changed, 1 insertion(+), 1 deletion(-)
2332 | --------------------------------------------------------------------------------
2333 | +
2334 | Create tag `v1.0-2`:
2335 | +
2336 | [subs=+quotes]
2337 | --------------------------------------------------------------------------------
2338 | $ **git tag v1.0-2**
2339 | --------------------------------------------------------------------------------
2340 | +
2341 | Push commits and the new tag to GitHub:
2342 | +
2343 | [subs=+quotes]
2344 | --------------------------------------------------------------------------------
2345 | $ **git push origin master v1.0-2**
2346 | Username for \'https://github.com': **__GITHUB_ACCOUNT__**
2347 | Password for \'https://__GITHUB_ACCOUNT__@github.com':
2348 | Counting objects: 31, done.
2349 | Delta compression using up to 2 threads.
2350 | Compressing objects: 100% (16/16), done.
2351 | Writing objects: 100% (17/17), 1.68 KiB | 0 bytes/s, done.
2352 | Total 17 (delta 9), reused 0 (delta 0)
2353 | remote: Resolving deltas: 100% (9/9), completed with 8 local objects.
2354 | To https://github.com/__GITHUB_ACCOUNT__/cakephp-ex.git
2355 |    270ee9b..87304ca  master -> master
2356 |  * [new tag]         v1.0-2 -> v1.0-2
2357 | --------------------------------------------------------------------------------
2358 | 
2359 | . Trigger eunomia to build in dev:
2360 | +
2361 | [subs=+quotes]
2362 | --------------------------------------------------------------------------------
2363 | $ **oc annotate --overwrite -n cakephp-gitops gitopsconfig cakephp trigger-update=$(date +"%FT%TZ")**
2364 | --------------------------------------------------------------------------------
2365 | +
2366 | Verify that a new build automatically starts:
2367 | +
2368 | [subs=+quotes]
2369 | --------------------------------------------------------------------------------
2370 | $ **oc get build -n cakephp-build**
2371 | NAME        TYPE     FROM          STATUS     STARTED          DURATION
2372 | cakephp-1   Source   Git@e580286   Complete   12 hours ago     2m1s
2373 | cakephp-2   Source   Git@v1.0-2    Running    10 seconds ago
2374 | --------------------------------------------------------------------------------
2375 | +
2376 | Once the build completes, confirm that a new image is available with tag `v1.0-2`:
2377 | +
2378 | [subs=+quotes]
2379 | --------------------------------------------------------------------------------
2380 | $ **oc get is -n cakephp-build**
2381 | NAME     IMAGE REPOSITORY                                                        TAGS           UPDATED
2382 | cakephp  image-registry.openshift-image-registry.svc:5000/cakephp-build/cakephp  v1.0-2,v1.0-1  43 seconds ago
2383 | --------------------------------------------------------------------------------
2384 | 
2385 | . Now let's get this version released to our dev namespaces:
2386 | +
2387 | Update the `dev_version` variable in `.applier/group_vars/seed-hosts/vars.yml`:
2388 | +
2389 | [subs=+quotes]
2390 | --------------------------------------------------------------------------------
2391 | $ **sed -i \'s|dev_version:.*|dev_version: v1.0-2|' .applier/group_vars/seed-hosts/vars.yml**
2392 | --------------------------------------------------------------------------------
2393 | +
2394 | [subs=+quotes]
2395 | --------------------------------------------------------------------------------
2396 | Add this change, commit to git, and push:
2397 | $ **git add -p**
2398 | diff --git a/.applier/group_vars/seed-hosts/vars.yml b/.applier/group_vars/seed-hosts/vars.yml
2399 | index 1c0757d..108b70c 100644
2400 | --- a/.applier/group_vars/seed-hosts/vars.yml
2401 | \+++ b/.applier/group_vars/seed-hosts/vars.yml
2402 | @@ -1,4 +1,4 @@
2403 |  build_version: v1.0-2
2404 | -dev_version: v1.0-1
2405 | +dev_version: v1.0-2
2406 |  test_version: v1.0-1
2407 |  prod_version: v1.0-1
2408 | Stage this hunk [y,n,q,a,d,/,e,?]? y
2409 | 
2410 | $ **git commit -m \'Release v1.0-2 to dev'**
2411 | [master 6fbc6b5] Release v1.0-2 to dev
2412 |  1 file changed, 1 insertion(+), 1 deletion(-)
2413 | $ **git push origin master**
2414 | Username for \'https://github.com': **__GITHUB_ACCOUNT__**
2415 | Password for \'https://__GITHUB_ACCOUNT__@github.com':
2416 | Counting objects: 11, done.
2417 | Delta compression using up to 2 threads.
2418 | Compressing objects: 100% (5/5), done.
2419 | Writing objects: 100% (6/6), 528 bytes | 0 bytes/s, done.
2420 | Total 6 (delta 1), reused 0 (delta 0)
2421 | remote: Resolving deltas: 100% (1/1), completed with 1 local object.
2422 | To https://github.com/__GITHUB_ACCOUNT__/cakephp-ex.git
2423 |    1391022..6fbc6b5  master -> master
2424 | --------------------------------------------------------------------------------
2425 | 
2426 | . Trigger Eunomia to release to dev:
2427 | +
2428 | [subs=+quotes]
2429 | --------------------------------------------------------------------------------
2430 | $ **oc annotate --overwrite -n cakephp-gitops gitopsconfig cakephp trigger-update=$(date +"%FT%TZ")**
2431 | --------------------------------------------------------------------------------
2432 | +
2433 | Wait for job completion and deployment to `cakephp-app-dev`.
2434 | +
2435 | When complete you should see a new deployment has run:
2436 | +
2437 | [subs=+quotes]
2438 | --------------------------------------------------------------------------------
2439 | $ **oc get pod -n cakephp-app-dev**
2440 | NAME                 READY   STATUS      RESTARTS   AGE
2441 | cakephp-1-deploy     0/1     Completed   0          25m
2442 | cakephp-1-hook-pre   0/1     Completed   0          25m
2443 | cakephp-2-deploy     0/1     Completed   0          77s
2444 | cakephp-2-gf2bs      1/1     Running     0          54s
2445 | cakephp-2-hook-pre   0/1     Completed   0          69s
2446 | --------------------------------------------------------------------------------
2447 | 
2448 | . Get the route for the application dev environment then check in your web browser for new content.
2449 | +
2450 | [subs=+quotes]
2451 | --------------------------------------------------------------------------------
2452 | $ **oc get route -n cakephp-app-dev**
2453 | NAME      HOST/PORT                                                                PATH   SERVICES   PORT    TERMINATION   WILDCARD
2454 | cakephp   cakephp-cakephp-app-dev.apps.cluster-1b5b.1b5b.sandbox1485.opentlc.com          cakephp                     None
2455 | --------------------------------------------------------------------------------
2456 | 
2457 | . Now let's test rollback:
2458 | +
2459 | Set the `dev_version` variable back to `v1.0-1`:
2460 | +
2461 | [subs=+quotes]
2462 | --------------------------------------------------------------------------------
2463 | $ **sed -i \'s|dev_version:.*|dev_version: v1.0-1|' .applier/group_vars/seed-hosts/vars.yml**
2464 | --------------------------------------------------------------------------------
2465 | +
2466 | With this simple change we reconfigure not just to run with the previous image but also with the previous templated configuration.
2467 | +
2468 | Add, commit and push to git:
2469 | +
2470 | [subs=+quotes]
2471 | --------------------------------------------------------------------------------
2472 | $ **git add -p**
2473 | diff --git a/.applier/group_vars/seed-hosts/vars.yml b/.applier/group_vars/seed-hosts/vars.yml
2474 | index 108b70c..1c0757d 100644
2475 | --- a/.applier/group_vars/seed-hosts/vars.yml
2476 | \+++ b/.applier/group_vars/seed-hosts/vars.yml
2477 | @@ -1,4 +1,4 @@
2478 |  build_version: v1.0-2
2479 | -dev_version: v1.0-2
2480 | +dev_version: v1.0-1
2481 |  test_version: v1.0-1
2482 |  prod_version: v1.0-1
2483 | Stage this hunk [y,n,q,a,d,/,e,?]? y
2484 | 
2485 | $ **git commit -m \'Rollback dev to v1.0-1'**
2486 | [master 6a88c09] Rollback dev to v1.0-1
2487 |  1 file changed, 1 insertion(+), 1 deletion(-)
2488 | $ **git push origin master**
2489 | Username for \'https://github.com': **__GITHUB_ACCOUNT__**
2490 | Password for \'https://__GITHUB_ACCOUNT__@github.com':
2491 | Counting objects: 11, done.
2492 | Delta compression using up to 2 threads.
2493 | Compressing objects: 100% (5/5), done.
2494 | Writing objects: 100% (6/6), 527 bytes | 0 bytes/s, done.
2495 | Total 6 (delta 1), reused 0 (delta 0)
2496 | remote: Resolving deltas: 100% (1/1), completed with 1 local object.
2497 | To https://github.com/__GITHUB_ACCOUNT__/cakephp-ex.git
2498 |    6fbc6b5..6a88c09  master -> master
2499 | --------------------------------------------------------------------------------
2500 | 
2501 | . Once again, trigger Eunomia processing, wait for job completion, and then check the web page to confirm that the previous content is shown:
2502 | +
2503 | [subs=+quotes]
2504 | --------------------------------------------------------------------------------
2505 | $ **oc annotate --overwrite -n cakephp-gitops gitopsconfig cakephp trigger-update=$(date +"%FT%TZ")**
2506 | --------------------------------------------------------------------------------
2507 | 
2508 | What Next?
2509 | ----------
2510 | 
2511 | The Red Hat Community of Practice for containers is looking for help developing this repository and populate it with many examples of common use cases.
2512 | 


--------------------------------------------------------------------------------
/manifests/apps/cakephp-namespace-config.yaml:
--------------------------------------------------------------------------------
 1 | ---
 2 | apiVersion: redhatcop.redhat.io/v1alpha1
 3 | kind: NamespaceConfig
 4 | metadata:
 5 |   name: cakephp-rolebindings
 6 |   namespace: namespace-configuration-operator
 7 | spec:
 8 |   selector:
 9 |     matchLabels:
10 |       app: cakephp
11 |   resources:
12 |   - apiVersion: rbac.authorization.k8s.io/v1
13 |     kind: RoleBinding
14 |     metadata:
15 |       name: admin
16 |     roleRef:
17 |       apiGroup: rbac.authorization.k8s.io
18 |       kind: ClusterRole
19 |       name: admin
20 |     subjects:
21 |     - apiGroup: rbac.authorization.k8s.io
22 |       kind: Group
23 |       name: portalapp
24 |   - apiVersion: rbac.authorization.k8s.io/v1
25 |     kind: RoleBinding
26 |     metadata:
27 |       name: gitops-runner-binding
28 |     roleRef:
29 |       apiGroup: rbac.authorization.k8s.io
30 |       kind: ClusterRole
31 |       name: admin
32 |     subjects:
33 |     - kind: ServiceAccount
34 |       name: gitops-runner
35 |       namespace: cakephp-gitops
36 | 


--------------------------------------------------------------------------------
/manifests/apps/cakephp-namespaces.yaml:
--------------------------------------------------------------------------------
  1 | ---
  2 | apiVersion: v1
  3 | kind: Namespace
  4 | metadata:
  5 |   annotations:
  6 |     openshift.io/requester: andrew
  7 |   labels:
  8 |     app: cakephp
  9 |     flavor: gitops
 10 |     multitenant: "true"
 11 |     size: small
 12 |   name: cakephp-gitops
 13 | ---
 14 | apiVersion: v1
 15 | kind: Namespace
 16 | metadata:
 17 |   annotations:
 18 |     openshift.io/requester: andrew
 19 |   labels:
 20 |     app: cakephp
 21 |     flavor: default
 22 |     multitenant: "true"
 23 |     size: large
 24 |   name: cakephp-build
 25 | ---
 26 | apiVersion: v1
 27 | kind: Namespace
 28 | metadata:
 29 |   annotations:
 30 |     openshift.io/requester: andrew
 31 |   labels:
 32 |     app: cakephp
 33 |     env: dev
 34 |     flavor: default
 35 |     multitenant: "true"
 36 |     size: large
 37 |   name: cakephp-app-dev
 38 | ---
 39 | apiVersion: v1
 40 | kind: Namespace
 41 | metadata:
 42 |   annotations:
 43 |     openshift.io/requester: andrew
 44 |   labels:
 45 |     app: cakephp
 46 |     env: test
 47 |     flavor: default
 48 |     multitenant: "true"
 49 |     size: large
 50 |   name: cakephp-app-test
 51 | ---
 52 | apiVersion: v1
 53 | kind: Namespace
 54 | metadata:
 55 |   annotations:
 56 |     openshift.io/requester: andrew
 57 |   labels:
 58 |     app: cakephp
 59 |     env: prod
 60 |     flavor: default
 61 |     multitenant: "true"
 62 |     size: large
 63 |   name: cakephp-app-prod
 64 | ---
 65 | apiVersion: v1
 66 | kind: Namespace
 67 | metadata:
 68 |   annotations:
 69 |     openshift.io/requester: andrew
 70 |   labels:
 71 |     app: cakephp
 72 |     env: dev
 73 |     flavor: default
 74 |     multitenant: "true"
 75 |     size: large
 76 |   name: cakephp-db-dev
 77 | ---
 78 | apiVersion: v1
 79 | kind: Namespace
 80 | metadata:
 81 |   annotations:
 82 |     openshift.io/requester: andrew
 83 |   labels:
 84 |     app: cakephp
 85 |     env: test
 86 |     flavor: default
 87 |     multitenant: "true"
 88 |     size: large
 89 |   name: cakephp-db-test
 90 | ---
 91 | apiVersion: v1
 92 | kind: Namespace
 93 | metadata:
 94 |   annotations:
 95 |     openshift.io/requester: andrew
 96 |   labels:
 97 |     app: cakephp
 98 |     env: prod
 99 |     flavor: default
100 |     multitenant: "true"
101 |     size: large
102 |   name: cakephp-db-prod
103 | 


--------------------------------------------------------------------------------
/manifests/clusterautoscaler.yaml:
--------------------------------------------------------------------------------
 1 | ---
 2 | apiVersion: autoscaling.openshift.io/v1
 3 | kind: ClusterAutoscaler
 4 | metadata:
 5 |   name: default
 6 | spec:
 7 |   podPriorityThreshold: -10
 8 |   resourceLimits:
 9 |     maxNodesTotal: 24
10 |     cores:
11 |       min: 8
12 |       max: 128
13 |     memory:
14 |       min: 4
15 |       max: 256
16 |   scaleDown:
17 |     enabled: true
18 |     delayAfterAdd: 30m
19 |     delayAfterDelete: 30m
20 |     delayAfterFailure: 5m
21 |     unneededTime: 5m
22 | 


--------------------------------------------------------------------------------
/manifests/namespace-config-operator-ns.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: v1
3 | kind: Namespace
4 | metadata:
5 |   annotations:
6 |   name: namespace-configuration-operator
7 | 


--------------------------------------------------------------------------------
/manifests/project-config/cluster-admins-myorg.yaml:
--------------------------------------------------------------------------------
 1 | ---
 2 | apiVersion: rbac.authorization.k8s.io/v1
 3 | kind: ClusterRoleBinding
 4 | metadata:
 5 |   name: cluster-admins-myorg
 6 | roleRef:
 7 |   apiGroup: rbac.authorization.k8s.io
 8 |   kind: ClusterRole
 9 |   name: cluster-admin
10 | subjects:
11 | - apiGroup: rbac.authorization.k8s.io
12 |   kind: Group
13 |   name: ocp-platform
14 | 


--------------------------------------------------------------------------------
/manifests/project-config/default-project-template.yaml:
--------------------------------------------------------------------------------
 1 | ---
 2 | apiVersion: template.openshift.io/v1
 3 | kind: Template
 4 | metadata:
 5 |   creationTimestamp: null
 6 |   name: project-request
 7 |   namespace: openshift-config
 8 | objects:
 9 | - apiVersion: project.openshift.io/v1
10 |   kind: Project
11 |   metadata:
12 |     annotations:
13 |       openshift.io/description: ${PROJECT_DESCRIPTION}
14 |       openshift.io/display-name: ${PROJECT_DISPLAYNAME}
15 |       openshift.io/requester: ${PROJECT_REQUESTING_USER}
16 |     creationTimestamp: null
17 |     labels:
18 |       flavor: default
19 |       size: large
20 |       multitenant: "true"
21 |     name: ${PROJECT_NAME}
22 |   spec: {}
23 |   status: {}
24 | - apiVersion: rbac.authorization.k8s.io/v1
25 |   kind: RoleBinding
26 |   metadata:
27 |     annotations:
28 |       openshift.io/description: Allows all pods in this namespace to pull images from
29 |         this namespace.  It is auto-managed by a controller; remove subjects to disable.
30 |     creationTimestamp: null
31 |     name: system:image-pullers
32 |     namespace: ${PROJECT_NAME}
33 |   roleRef:
34 |     apiGroup: rbac.authorization.k8s.io
35 |     kind: ClusterRole
36 |     name: system:image-puller
37 |   subjects:
38 |   - apiGroup: rbac.authorization.k8s.io
39 |     kind: Group
40 |     name: system:serviceaccounts:${PROJECT_NAME}
41 | - apiVersion: rbac.authorization.k8s.io/v1
42 |   kind: RoleBinding
43 |   metadata:
44 |     annotations:
45 |       openshift.io/description: Allows builds in this namespace to push images to
46 |         this namespace.  It is auto-managed by a controller; remove subjects to disable.
47 |     creationTimestamp: null
48 |     name: system:image-builders
49 |     namespace: ${PROJECT_NAME}
50 |   roleRef:
51 |     apiGroup: rbac.authorization.k8s.io
52 |     kind: ClusterRole
53 |     name: system:image-builder
54 |   subjects:
55 |   - kind: ServiceAccount
56 |     name: builder
57 |     namespace: ${PROJECT_NAME}
58 | - apiVersion: rbac.authorization.k8s.io/v1
59 |   kind: RoleBinding
60 |   metadata:
61 |     annotations:
62 |       openshift.io/description: Allows deploymentconfigs in this namespace to rollout
63 |         pods in this namespace.  It is auto-managed by a controller; remove subjects
64 |         to disable.
65 |     creationTimestamp: null
66 |     name: system:deployers
67 |     namespace: ${PROJECT_NAME}
68 |   roleRef:
69 |     apiGroup: rbac.authorization.k8s.io
70 |     kind: ClusterRole
71 |     name: system:deployer
72 |   subjects:
73 |   - kind: ServiceAccount
74 |     name: deployer
75 |     namespace: ${PROJECT_NAME}
76 | - apiVersion: rbac.authorization.k8s.io/v1
77 |   kind: RoleBinding
78 |   metadata:
79 |     creationTimestamp: null
80 |     name: admin
81 |     namespace: ${PROJECT_NAME}
82 |   roleRef:
83 |     apiGroup: rbac.authorization.k8s.io
84 |     kind: ClusterRole
85 |     name: admin
86 |   subjects:
87 |   - apiGroup: rbac.authorization.k8s.io
88 |     kind: User
89 |     name: ${PROJECT_ADMIN_USER}
90 | parameters:
91 | - name: PROJECT_NAME
92 | - name: PROJECT_DISPLAYNAME
93 | - name: PROJECT_DESCRIPTION
94 | - name: PROJECT_ADMIN_USER
95 | - name: PROJECT_REQUESTING_USER
96 | 


--------------------------------------------------------------------------------
/manifests/project-config/gitops-config.yaml:
--------------------------------------------------------------------------------
 1 | ---
 2 | apiVersion: redhatcop.redhat.io/v1alpha1
 3 | kind: NamespaceConfig
 4 | metadata:
 5 |   name: gitops
 6 |   namespace: namespace-configuration-operator
 7 | spec:
 8 |   selector:
 9 |     matchLabels:
10 |       flavor: gitops
11 |   resources:
12 |     - apiVersion: v1
13 |       kind: ServiceAccount
14 |       metadata:
15 |         name: gitops-runner
16 | 


--------------------------------------------------------------------------------
/manifests/project-config/multitenant-networkpolicy.yaml:
--------------------------------------------------------------------------------
 1 | ---
 2 | apiVersion: redhatcop.redhat.io/v1alpha1
 3 | kind: NamespaceConfig
 4 | metadata:
 5 |   name: multitenant
 6 |   namespace: namespace-configuration-operator
 7 | spec:
 8 |   selector:
 9 |     matchLabels:
10 |       multitenant: "true"
11 |   resources:
12 |   - apiVersion: networking.k8s.io/v1
13 |     kind: NetworkPolicy
14 |     metadata:
15 |       name: allow-from-same-namespace
16 |     spec:
17 |       podSelector: {}
18 |       ingress:
19 |       - from:
20 |         - podSelector: {}
21 |   - apiVersion: networking.k8s.io/v1
22 |     kind: NetworkPolicy
23 |     metadata:
24 |       name: allow-network-policy-group-ingress
25 |     spec:
26 |       podSelector: {}
27 |       ingress:
28 |       - from:
29 |         - namespaceSelector:
30 |             matchLabels:
31 |               network.openshift.io/policy-group: ingress
32 | 


--------------------------------------------------------------------------------
/manifests/project-config/project-config.yaml:
--------------------------------------------------------------------------------
 1 | ---
 2 | apiVersion: config.openshift.io/v1
 3 | kind: Project
 4 | metadata:
 5 |   annotations:
 6 |   name: cluster
 7 | spec:
 8 |   projectRequestTemplate:
 9 |     name: project-request
10 | 


--------------------------------------------------------------------------------
/manifests/project-config/quota-tshirts.yaml:
--------------------------------------------------------------------------------
 1 | ---
 2 | apiVersion: redhatcop.redhat.io/v1alpha1
 3 | kind: NamespaceConfig
 4 | metadata:
 5 |   name: small-size
 6 |   namespace: namespace-configuration-operator
 7 | spec:
 8 |   selector:
 9 |     matchLabels:
10 |       size: small
11 |   resources:
12 |   - apiVersion: v1
13 |     kind: ResourceQuota
14 |     metadata:
15 |       name: small-size
16 |     spec:
17 |       hard:
18 |         requests.cpu: "4"
19 |         requests.memory: "2Gi"
20 |   - apiVersion: v1
21 |     kind: LimitRange
22 |     metadata:
23 |       annotations:
24 |         openshift.io/quota-tier: small
25 |       labels:
26 |         quota-tier: small
27 |       name: limits
28 |     spec:
29 |       limits:
30 |       - defaultRequest:
31 |           cpu: 100m
32 |           memory: 512Mi
33 |         type: Container
34 | ---
35 | apiVersion: redhatcop.redhat.io/v1alpha1
36 | kind: NamespaceConfig
37 | metadata:
38 |   name: large-size
39 |   namespace: namespace-configuration-operator
40 | spec:
41 |   selector:
42 |     matchLabels:
43 |       size: large
44 |   resources:
45 |   - apiVersion: v1
46 |     kind: ResourceQuota
47 |     metadata:
48 |       name: large-size
49 |     spec:
50 |       hard:
51 |         requests.cpu: "8"
52 |         requests.memory: "4Gi"
53 |   - apiVersion: v1
54 |     kind: LimitRange
55 |     metadata:
56 |       annotations:
57 |         openshift.io/quota-tier: large
58 |       labels:
59 |         quota-tier: large
60 |       name: limits
61 |     spec:
62 |       limits:
63 |       - defaultRequest:
64 |           cpu: 100m
65 |           memory: 512Mi
66 |         type: Container
67 | 


--------------------------------------------------------------------------------
/media/app-workflow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redhat-cop/operationalizing-openshift-lab/d00533a9eb425d2e7a5ad5e2f8ca7363506b0ecc/media/app-workflow.png


--------------------------------------------------------------------------------
/media/ops-workflow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redhat-cop/operationalizing-openshift-lab/d00533a9eb425d2e7a5ad5e2f8ca7363506b0ecc/media/ops-workflow.png


--------------------------------------------------------------------------------
/meta/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | galaxy_info:
3 | 


--------------------------------------------------------------------------------
/requirements.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - src: https://github.com/redhat-cop/openshift-applier
3 |   version: master
4 | 
5 | - src: https://github.com/redhat-cop/operationalizing-openshift-lab
6 |   name: self
7 |   version: master
8 | 


--------------------------------------------------------------------------------
/roles/discover/defaults/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # Custom label used to identify custom machinesets
3 | custom_machineset_label: machine.openshift.io/cluster-api-machineset-group
4 | 


--------------------------------------------------------------------------------
/roles/discover/tasks/main.yml:
--------------------------------------------------------------------------------
 1 | ---
 2 | - name: Get Cluster Info
 3 |   k8s_facts:
 4 |     api_version: config.openshift.io/v1
 5 |     kind: Infrastructure
 6 |     name: cluster
 7 |   register: cluster_info
 8 | 
 9 | - name: Get machinesets
10 |   k8s_facts:
11 |     api_version: machine.openshift.io/v1beta1
12 |     kind: MachineSet
13 |     namespace: openshift-machine-api
14 |   register: r_get_machinesets
15 | 
16 | - name: Set default_worker_machinesets
17 |   set_fact:
18 |     current_machinesets: >-
19 |       {{ r_get_machinesets.resources }}
20 |     current_machineset_names: >-
21 |       {{ r_get_machinesets.resources
22 |        | json_query('[].metadata.name')
23 |       }}
24 |     default_worker_machinesets: >-
25 |       {{ r_get_machinesets.resources
26 |        | json_query(default_worker_machineset_json_query)
27 |       }}
28 |   vars:
29 |     # Base worker machinesets will lack machineset group label
30 |     default_worker_machineset_json_query: >-
31 |       [?!contains(keys(metadata.labels), '{{custom_machineset_label}}')]
32 | 
33 | - name: Set Cluster Info Facts
34 |   set_fact:
35 |     infraid: "{{ cluster_info.resources[0].status.infrastructureName }}"
36 |     cloudregion: "{{ reference_machineset.spec.template.spec.providerSpec.value.placement.region }}"
37 |   vars:
38 |     reference_machineset: >-
39 |       {{ default_worker_machinesets[0] | default({}) }}
40 |     reference_provider_spec_value: >-
41 |       {{ reference_machineset
42 |        | json_query('spec.template.spec.providerSpec.value')
43 |       }}
44 |   when: default_worker_machinesets
45 | 
46 | - name: Print variables
47 |   debug:
48 |     msg: >-
49 |       Infra: {{ infraid }}
50 |       Region: {{ cloudregion }}
51 |     verbosity: 2
52 | 


--------------------------------------------------------------------------------
/roles/discover/tests/inventory/hosts:
--------------------------------------------------------------------------------
1 | [localhost]
2 | localhost ansible_connection=local
3 | 


--------------------------------------------------------------------------------
/roles/discover/tests/roles:
--------------------------------------------------------------------------------
1 | ../../../roles/


--------------------------------------------------------------------------------
/roles/discover/tests/test.yml:
--------------------------------------------------------------------------------
1 | ---
2 | 
3 | - hosts: localhost
4 |   roles:
5 |     - roles/discover
6 | 


--------------------------------------------------------------------------------
/roles/openshift_machine_api/tasks/main.yml:
--------------------------------------------------------------------------------
 1 | ---
 2 | - name: Scale up autoscaling custom machinesets
 3 |   include_tasks: scale-up-group.yml
 4 |   loop: "{{ machineset_custom_groups | default([]) }}"
 5 |   loop_control:
 6 |     loop_var: machineset_group
 7 |     label: "{{ machineset_group.name }}"
 8 |   vars:
 9 |     availability_zone_count: "{{ default_worker_machinesets | length }}"
10 |   when: machineset_group.autoscale | default(False) | bool
11 | 


--------------------------------------------------------------------------------
/roles/openshift_machine_api/tasks/scale-up-group.yml:
--------------------------------------------------------------------------------
 1 | ---
 2 | - name: Scale up machinesets to minimum
 3 |   k8s:
 4 |     api_version: machine.openshift.io/v1beta1
 5 |     kind: MachineSet
 6 |     name: "{{ machineset_name }}"
 7 |     namespace: openshift-machine-api
 8 |     merge_type: merge
 9 |     definition: "{{ resource_definition | from_yaml }}"
10 |   loop: "{{ default_worker_machinesets | default([]) }}"
11 |   loop_control:
12 |     index_var: i
13 |     loop_var: reference_machineset
14 |     label: machineset_name
15 |   vars:
16 |     availability_zone: >-
17 |       {{ reference_machineset.spec.template.spec.providerSpec.value.placement.availabilityZone }}
18 |     machineset_name: >-
19 |       {{ [infraid, machineset_group.name, availability_zone] | join('-') }}
20 |     current_machineset: >-
21 |       {{ current_machinesets | json_query(current_machineset_query) }}
22 |     current_machineset_query: >-
23 |       [?@.metadata.name == '{{ machineset_name }}' ]|[0]
24 |     current_replicas: >-
25 |       {{ current_machineset.spec.replicas|default(0) if current_machineset else 0 }}
26 |     min_replicas: >-
27 |       {{ ((machineset_group.min_replicas + availability_zone_count|int - i - 1) / availability_zone_count|int)|int }}
28 |     resource_definition: |
29 |       spec:
30 |         replicas: {{ min_replicas }}
31 |   # Set replicas to minimum when machineset is new or current is less than min
32 |   when: >-
33 |     not current_machineset or
34 |     current_replicas|int < min_replicas|int
35 | 


--------------------------------------------------------------------------------
/roles/roles:
--------------------------------------------------------------------------------
1 | roles


--------------------------------------------------------------------------------
/templates/aws-ebs-storage-classes.yaml:
--------------------------------------------------------------------------------
 1 | ---
 2 | apiVersion: v1
 3 | kind: Template
 4 | objects:
 5 |   - kind: StorageClass
 6 |     apiVersion: storage.k8s.io/v1
 7 |     metadata:
 8 |       annotations:
 9 |         storageclass.beta.kubernetes.io/is-default-class: "false"
10 |       name: fast-block
11 |     parameters:
12 |       encrypted: "${ENCRYPT_STORAGE}"
13 |       type: io1
14 |     provisioner: kubernetes.io/aws-ebs
15 |     reclaimPolicy: Delete
16 |     volumeBindingMode: Immediate
17 |   - kind: StorageClass
18 |     apiVersion: storage.k8s.io/v1
19 |     metadata:
20 |       annotations:
21 |         storageclass.beta.kubernetes.io/is-default-class: "false"
22 |       name: medium-block
23 |     parameters:
24 |       encrypted: "${ENCRYPT_STORAGE}"
25 |       type: gp2
26 |     provisioner: kubernetes.io/aws-ebs
27 |     reclaimPolicy: Delete
28 |     volumeBindingMode: Immediate
29 |   - kind: StorageClass
30 |     apiVersion: storage.k8s.io/v1
31 |     metadata:
32 |       annotations:
33 |         storageclass.beta.kubernetes.io/is-default-class: "false"
34 |       name: slow-block
35 |     parameters:
36 |       encrypted: "${ENCRYPT_STORAGE}"
37 |       type: st1
38 |     provisioner: kubernetes.io/aws-ebs
39 |     reclaimPolicy: Delete
40 |     volumeBindingMode: Immediate
41 | 
42 | parameters:
43 | - name: "ENCRYPT_STORAGE"
44 |   displayName: "Encrypt Storage"
45 |   description: "Denotes whether to encrypt the EBS volume."
46 |   value: "true"
47 |   required: true
48 | 


--------------------------------------------------------------------------------
/templates/cluster-gitops.yaml:
--------------------------------------------------------------------------------
 1 | ---
 2 | apiVersion: v1
 3 | kind: Template
 4 | objects:
 5 | - apiVersion: v1
 6 |   kind: Namespace
 7 |   metadata:
 8 |     name: cluster-config
 9 | - apiVersion: v1
10 |   kind: ServiceAccount
11 |   metadata:
12 |     name: eunomia-runner
13 |     namespace: ${CLUSTER_CONFIG_NAMESPACE}
14 | - apiVersion: rbac.authorization.k8s.io/v1
15 |   kind: ClusterRoleBinding
16 |   metadata:
17 |     name: eunomia-runner-binding
18 |   roleRef:
19 |     apiGroup: rbac.authorization.k8s.io
20 |     kind: ClusterRole
21 |     name: cluster-admin
22 |   subjects:
23 |   - kind: ServiceAccount
24 |     name: eunomia-runner
25 |     namespace: ${CLUSTER_CONFIG_NAMESPACE}
26 | - apiVersion: eunomia.kohls.io/v1alpha1
27 |   kind: GitOpsConfig
28 |   metadata:
29 |     name: cluster-config
30 |     namespace: ${CLUSTER_CONFIG_NAMESPACE}
31 |   spec:
32 |     templateSource:
33 |       uri: ${CLUSTER_CONFIG_REPO_URL}
34 |       ref: ${CLUSTER_CONFIG_REPO_REF}
35 |       contextDir: ${CLUSTER_CONFIG_REPO_DIR}
36 |     triggers:
37 |     - type: Change
38 |     - type: Webhook
39 |     #- type: Periodic
40 |       #cron: '*/1 * * * *'
41 |     serviceAccountRef: eunomia-runner
42 |     templateProcessorImage: ${TEMPLATE_PROCESSOR_IMAGE}
43 |     resourceHandlingMode: None
44 |     resourceDeletionMode: None
45 | parameters:
46 | - name: CLUSTER_CONFIG_NAMESPACE
47 |   value: cluster-config
48 | - name: CLUSTER_CONFIG_REPO_URL
49 |   value: https://github.com/redhat-cop/operationalizing-openshift-lab
50 | - name: CLUSTER_CONFIG_REPO_REF
51 |   value: master
52 | - name: CLUSTER_CONFIG_REPO_DIR
53 |   value: ''
54 | - name: TEMPLATE_PROCESSOR_IMAGE
55 |   value: quay.io/kohlstechnology/eunomia-applier:v0.0.1
56 | 


--------------------------------------------------------------------------------
/templates/custom-machinesets.j2:
--------------------------------------------------------------------------------
 1 | {% set availability_zone_count = default_worker_machinesets | length %}
 2 | {% for machineset_group in machineset_custom_groups %}
 3 | {%   for reference_machineset in default_worker_machinesets %}
 4 | {%     set availability_zone = reference_machineset.spec.template.spec.providerSpec.value.placement.availabilityZone %}
 5 | {%     set machineset_name = [infraid, machineset_group.name, availability_zone] | join('-') %}
 6 | ---
 7 | apiVersion: machine.openshift.io/v1beta1
 8 | kind: MachineSet
 9 | metadata:
10 |   labels:
11 |     machine.openshift.io/cluster-api-cluster: {{ infraid | to_json }}
12 |     machine.openshift.io/cluster-api-machineset-group: {{ machineset_group.name | to_json }}
13 |   name: {{ machineset_name | to_json }}
14 |   namespace: openshift-machine-api
15 | spec:
16 | {%     if not machineset_group.autoscale | default(False) | bool %}
17 |   replicas: {{
18 |     ( (machineset_group.replicas + availability_zone_count - loop.index) / availability_zone_count) | int
19 |     }}
20 | {%     endif %}
21 |   selector:
22 |     matchLabels:
23 |       machine.openshift.io/cluster-api-cluster: {{ infraid | to_json }}
24 |       machine.openshift.io/cluster-api-machine-role: {{ machineset_group.name | to_json }}
25 |       machine.openshift.io/cluster-api-machine-type: worker
26 |       machine.openshift.io/cluster-api-machineset: {{ machineset_group.name | to_json }}
27 |   template:
28 |     metadata:
29 |       labels:
30 |         machine.openshift.io/cluster-api-cluster: {{ infraid | to_json }}
31 |         machine.openshift.io/cluster-api-machine-role: {{ machineset_group.name | to_json }}
32 |         machine.openshift.io/cluster-api-machine-type: worker
33 |         machine.openshift.io/cluster-api-machineset: {{ machineset_group.name | to_json }}
34 |     spec:
35 |      metadata:
36 |        labels:
37 | {%     for role in machineset_group.roles %}
38 |          node-role.kubernetes.io/{{ role }}: ""
39 | {%     endfor %}
40 |      providerSpec: {{
41 |        reference_machineset.spec.template.spec.providerSpec
42 |        | combine(machineset_group.provider_spec_override | default({}), recursive=True)
43 |        | to_json
44 |      }}
45 | {%     if machineset_group.autoscale | default(False) | bool %}
46 | ---
47 | apiVersion: autoscaling.openshift.io/v1beta1
48 | kind: MachineAutoscaler
49 | metadata:
50 |   name: {{ machineset_name | to_json }}
51 |   namespace: openshift-machine-api
52 | spec:
53 |   maxReplicas: {{
54 |     ( (machineset_group.max_replicas + availability_zone_count - loop.index) / availability_zone_count) | int
55 |     }}
56 |   minReplicas: {{
57 |     ( (machineset_group.min_replicas + availability_zone_count - loop.index) / availability_zone_count) | int
58 |     }}
59 |   scaleTargetRef:
60 |     apiVersion: machine.openshift.io/v1beta1
61 |     kind: MachineSet
62 |     name: {{ machineset_name | to_json }}
63 | {%     endif %}
64 | {%   endfor %}
65 | {% endfor %}
66 | 


--------------------------------------------------------------------------------
/templates/default-machinesets.j2:
--------------------------------------------------------------------------------
 1 | {% for machineset in default_worker_machinesets %}
 2 | ---
 3 | apiVersion: machine.openshift.io/v1beta1
 4 | kind: MachineSet
 5 | metadata:
 6 |   labels:
 7 |     machine.openshift.io/cluster-api-cluster: {{ infraid | to_json }}
 8 |   name: {{ machineset.metadata.name | to_json }}
 9 |   namespace: openshift-machine-api
10 | spec:
11 | {%   if machineset_disable_default_workers | default(False) | bool %}
12 |   replicas: 0
13 | {%   else %}
14 |   replicas: {{ machineset.spec.replicas | to_json }}
15 | {%   endif %}
16 |   selector: {{ machineset.spec.selector | to_json }}
17 |   template: {{ machineset.spec.template | to_json }}
18 | {% endfor %}
19 | 


--------------------------------------------------------------------------------
/templates/ldap-auth.yaml:
--------------------------------------------------------------------------------
 1 | ---
 2 | apiVersion: v1
 3 | kind: Template
 4 | objects:
 5 | - apiVersion: config.openshift.io/v1
 6 |   kind: OAuth
 7 |   metadata:
 8 |     name: cluster
 9 |   spec:
10 |     identityProviders:
11 |     - name: LDAP
12 |       challenge: true
13 |       login: true
14 |       mappingMethod: claim
15 |       type: LDAP
16 |       ldap:
17 |         attributes:
18 |           email: ["mail"]
19 |           id: ["dn"]
20 |           name: ["cn"]
21 |           preferredUsername: ["uid"]
22 |         bindDN: "${LDAP_BIND_DN}"
23 |         bindPassword:
24 |           name: "${BIND_PASSWORD_SECRET}"
25 |         insecure: false
26 |         ca:
27 |           name: "${LDAP_CA_CONFIGMAP}"
28 |         url: "${LDAP_SEARCH_URL}"
29 | parameters:
30 | - name: LDAP_CA_CONFIGMAP
31 |   description: Name of the ConfigMap containing the LDAP Certificate Authority
32 |   value: ldap-tls-ca
33 |   required: true
34 | - name: BIND_PASSWORD_SECRET
35 |   description: Name of the Secret containing the LDAP bind password
36 |   value: ldap-bind-password
37 |   required: true
38 | - name: LDAP_BIND_DN
39 |   description: LDAP Bind Distinguished Name
40 |   required: true
41 | - name: LDAP_SEARCH_URL
42 |   description: LDAP Search URL
43 |   required: true
44 | 


--------------------------------------------------------------------------------
/templates/ldap-cluster-secrets.yaml:
--------------------------------------------------------------------------------
 1 | ---
 2 | apiVersion: v1
 3 | kind: Template
 4 | objects:
 5 | - apiVersion: v1
 6 |   kind: ConfigMap
 7 |   metadata:
 8 |     name: "${LDAP_CA_CONFIGMAP}"
 9 |     namespace: openshift-config
10 |   data:
11 |     ca.crt: "${LDAP_CA}"
12 | - apiVersion: v1
13 |   kind: Secret
14 |   metadata:
15 |     name: "${BIND_PASSWORD_SECRET}"
16 |     namespace: openshift-config
17 |   type: Opaque
18 |   stringData:
19 |     bindPassword: "${LDAP_BIND_PASSWORD}"
20 | parameters:
21 | - name: LDAP_CA
22 |   description: "Value of the LDAP Certificate"
23 |   required: true
24 | - name: LDAP_BIND_PASSWORD
25 |   description: LDAP Bind password
26 |   required: true
27 | - name: LDAP_CA_CONFIGMAP
28 |   description: Name of the ConfigMap containing the LDAP Certificate Authority
29 |   value: ldap-tls-ca
30 | - name: BIND_PASSWORD_SECRET
31 |   description: Name of the Secret containing the LDAP bind password
32 |   value: ldap-bind-password
33 |   required: true
34 | 


--------------------------------------------------------------------------------
/templates/ldap-group-sync.yaml:
--------------------------------------------------------------------------------
  1 | ---
  2 | apiVersion: v1
  3 | kind: Template
  4 | objects:
  5 | - kind: "ServiceAccount"
  6 |   apiVersion: "v1"
  7 |   metadata:
  8 |     name: "${JOB_SERVICE_ACCOUNT}"
  9 |     namespace: "${NAMESPACE}"
 10 | - kind: "ClusterRole"
 11 |   apiVersion: "rbac.authorization.k8s.io/v1"
 12 |   metadata:
 13 |     name: "ldap-group-syncer"
 14 |     namespace: "${NAMESPACE}"
 15 |     labels:
 16 |       template: "cronjob-ldap-group-sync"
 17 |   rules:
 18 |   - apiGroups:
 19 |     - ""
 20 |     - "user.openshift.io"
 21 |     resources:
 22 |     - "groups"
 23 |     verbs:
 24 |     - "get"
 25 |     - "list"
 26 |     - "create"
 27 |     - "update"
 28 | - kind: "ClusterRoleBinding"
 29 |   apiVersion: "rbac.authorization.k8s.io/v1"
 30 |   groupNames: null
 31 |   metadata:
 32 |     name: "system:ldap-group-syncers"
 33 |   roleRef:
 34 |     kind: "ClusterRole"
 35 |     name: "ldap-group-syncer"
 36 |   subjects:
 37 |   - kind: "ServiceAccount"
 38 |     name: "${JOB_SERVICE_ACCOUNT}"
 39 |     namespace: "${NAMESPACE}"
 40 |   userNames:
 41 |   - "system:serviceaccount:${NAMESPACE}:${JOB_SERVICE_ACCOUNT}"
 42 | - kind: "ConfigMap"
 43 |   apiVersion: "v1"
 44 |   metadata:
 45 |     name: "${LDAP_SYNC_CONFIGMAP}"
 46 |     namespace: "${NAMESPACE}"
 47 |   data:
 48 |     ldap-group-sync.yaml: |
 49 |       kind: "LDAPSyncConfig"
 50 |       apiVersion: "v1"
 51 |       url: "${LDAP_URL}"
 52 |       insecure: false
 53 |       bindDN: "${LDAP_BIND_DN}"
 54 |       bindPassword:
 55 |         file: "${LDAP_CONFIG_VOLUME_PATH}/${LDAP_BIND_PASSWORD_FILENAME}"
 56 |       ca: "${LDAP_CONFIG_VOLUME_PATH}/${LDAP_CA_FILENAME}"
 57 |       rfc2307:
 58 |         groupsQuery:
 59 |           baseDN: "${LDAP_GROUPS_SEARCH_BASE}"
 60 |           scope: "sub"
 61 |           derefAliases: "never"
 62 |           filter: "${LDAP_GROUPS_FILTER}"
 63 |           pageSize: 0
 64 |         groupUIDAttribute: "${LDAP_GROUP_UID_ATTRIBUTE}"
 65 |         groupNameAttributes: ${LDAP_GROUP_NAME_ATTRIBUTES}
 66 |         groupMembershipAttributes: ${LDAP_GROUP_MEMBERSHIP_ATTRIBUTES}
 67 |         usersQuery:
 68 |           baseDN: "${LDAP_USERS_SEARCH_BASE}"
 69 |           scope: "sub"
 70 |           derefAliases: "never"
 71 |           pageSize: 0
 72 |         userNameAttributes: ${LDAP_USER_NAME_ATTRIBUTES}
 73 |         userUIDAttribute: "${LDAP_USER_UID_ATTRIBUTE}"
 74 |         tolerateMemberNotFoundErrors: true
 75 |         tolerateMemberOutOfScopeErrors: true
 76 |     whitelist.txt: "${LDAP_GROUPS_WHITELIST}"
 77 | - kind: "CronJob"
 78 |   apiVersion: "batch/v1beta1"
 79 |   metadata:
 80 |     name: "${JOB_NAME}"
 81 |     namespace: "${NAMESPACE}"
 82 |     labels:
 83 |       template: "cronjob-ldap-group-sync"
 84 |   spec:
 85 |     schedule: "${SCHEDULE}"
 86 |     concurrencyPolicy: "Forbid"
 87 |     successfulJobsHistoryLimit: "${{SUCCESS_JOBS_HISTORY_LIMIT}}"
 88 |     failedJobsHistoryLimit: "${{FAILED_JOBS_HISTORY_LIMIT}}"
 89 |     jobTemplate:
 90 |       spec:
 91 |         backoffLimit: 0
 92 |         template:
 93 |           spec:
 94 |             containers:
 95 |             - name: "${JOB_NAME}"
 96 |               image: "${IMAGE}:${IMAGE_TAG}"
 97 |               command:
 98 |               - "/bin/bash"
 99 |               - "-c"
100 |               - oc adm groups sync --confirm
101 |                 --sync-config=${LDAP_CONFIG_VOLUME_PATH}/ldap-group-sync.yaml
102 |                 $([ -s ${LDAP_CONFIG_VOLUME_PATH}/whitelist.txt ] && echo --whitelist=${LDAP_CONFIG_VOLUME_PATH}/whitelist.txt)
103 |               volumeMounts:
104 |               - mountPath: "${LDAP_CONFIG_VOLUME_PATH}/ldap-group-sync.yaml"
105 |                 name: "ldap-sync-config"
106 |                 subPath: "ldap-group-sync.yaml"
107 |               - mountPath: "${LDAP_CONFIG_VOLUME_PATH}/whitelist.txt"
108 |                 name: "ldap-sync-config"
109 |                 subPath: "whitelist.txt"
110 |               - mountPath: "${LDAP_CONFIG_VOLUME_PATH}/${LDAP_CA_FILENAME}"
111 |                 name: "ldap-sync-ca"
112 |                 subPath: "${LDAP_CA_FILENAME}"
113 |               - mountPath: "${LDAP_CONFIG_VOLUME_PATH}/${LDAP_BIND_PASSWORD_FILENAME}"
114 |                 name: "ldap-bind-password"
115 |                 subPath: "${LDAP_BIND_PASSWORD_FILENAME}"
116 |             volumes:
117 |             - name: "ldap-sync-config"
118 |               configMap:
119 |                 name: "${LDAP_SYNC_CONFIGMAP}"
120 |             - name: "ldap-sync-ca"
121 |               configMap:
122 |                 name: "${LDAP_CA_CONFIGMAP}"
123 |             - name: "ldap-bind-password"
124 |               secret:
125 |                 secretName: "${LDAP_BIND_PASSWORD_SECRET}"
126 |             restartPolicy: "Never"
127 |             terminationGracePeriodSeconds: 30
128 |             activeDeadlineSeconds: 500
129 |             dnsPolicy: "ClusterFirst"
130 |             serviceAccountName: "${JOB_SERVICE_ACCOUNT}"
131 |             serviceAccount: "${JOB_SERVICE_ACCOUNT}"
132 | parameters:
133 | - name: "NAMESPACE"
134 |   displayName: "Namespace"
135 |   description: "Name of the Namespace where to deploy the Scheduled Job"
136 |   value: "openshift-config"
137 |   required: true
138 | - name: "JOB_NAME"
139 |   displayName: "Job Name"
140 |   description: "Name of the Scheduled Job to Create."
141 |   value: "cronjob-ldap-group-sync"
142 |   required: true
143 | - name: "SCHEDULE"
144 |   displayName: "Cron Schedule"
145 |   description: "Cron Schedule to Execute the Job"
146 |   value: "@hourly"
147 |   required: true
148 | - name: "JOB_SERVICE_ACCOUNT"
149 |   displayName: "Service Account Name"
150 |   description: "Name of the Service Account To Exeucte the Job As."
151 |   value: "ldap-group-syncer"
152 |   required: true
153 | - name: LDAP_CA_CONFIGMAP
154 |   description: Name of the ConfigMap containing the LDAP Certificate Authority
155 |   value: ldap-tls-ca
156 |   required: true
157 | - name: BIND_PASSWORD_SECRET
158 |   description: Name of the Secret containing the LDAP bind password
159 |   value: ldap-bind-password
160 |   required: true
161 | - name: LDAP_CONFIG_VOLUME_PATH
162 |   description: Mount path of LDAP configuration files
163 |   value: "/ldap-sync"
164 |   required: true
165 | - name: LDAP_CA_FILENAME
166 |   description: Name of the LDAP CA file
167 |   value: ca.crt
168 |   required: true
169 | - name: LDAP_BIND_PASSWORD_FILENAME
170 |   description: Name of the LDAP bind password file
171 |   value: bindPassword
172 |   required: true
173 | - name: "LDAP_GROUPS_SEARCH_BASE"
174 |   displayName: "Group search query"
175 |   description: "Location in LDAP tree where you will find groups"
176 |   required: true
177 | - name: "LDAP_GROUPS_FILTER"
178 |   displayName: "Group Filter"
179 |   description: "LDAP Filter to use when deciding which groups to sync into OpenShift"
180 |   required: true
181 |   value: "(objectClass=groupofnames)"
182 | - name: "LDAP_GROUP_NAME_ATTRIBUTES"
183 |   displayName: "Group name attributes"
184 |   description: "The attribute list to use to discover the name for the group."
185 |   required: true
186 |   value: >-
187 |     ["cn"]
188 | - name: "LDAP_GROUP_MEMBERSHIP_ATTRIBUTES"
189 |   displayName: "Group membership attributes"
190 |   required: true
191 |   value: >-
192 |     ["member"]
193 | - name: "LDAP_GROUP_UID_ATTRIBUTE"
194 |   displayName: "Group UID Attribute"
195 |   description: "The attribute that uniquely identifies a group on the LDAP server."
196 |   required: true
197 |   value: "dn"
198 | - name: "LDAP_GROUPS_WHITELIST"
199 |   displayName: "LDAP sync whitelist"
200 |   description: "File content for groups sync --whitelist option"
201 | - name: "LDAP_URL"
202 |   displayName: "LDAP Server URL"
203 |   description: "URL of you LDAP server"
204 |   required: true
205 | - name: "LDAP_BIND_DN"
206 |   displayName: "LDAP Bind User's DN"
207 |   description: "The Full DN for the user you wish to use to authenticate to LDAP"
208 |   required: true
209 | - name: "LDAP_USERS_SEARCH_BASE"
210 |   displayName: "User search query"
211 |   description: "Location in LDAP tree where you will find users"
212 |   required: true
213 | - name: "LDAP_SYNC_CONFIGMAP"
214 |   displayName: "LDAP sync config map"
215 |   description: "Name for the config map storing the group sync config"
216 |   value: "ldap-group-sync"
217 | - name: "LDAP_USER_UID_ATTRIBUTE"
218 |   displayName: "User UID attribute"
219 |   description: "The attribute that uniquely identifies a user on the LDAP server."
220 |   required: true
221 |   value: "dn"
222 | - name: "LDAP_USER_NAME_ATTRIBUTES"
223 |   displayName: "User name attributes"
224 |   description: "JSON list of attributes to use to discover the user name for group membership"
225 |   required: true
226 |   value: >-
227 |     ["uid"]
228 | - name: "LDAP_BIND_PASSWORD_SECRET"
229 |   displayName: "LDAP Bind Password Secret"
230 |   description: "The name for the secret in which to store the bind password"
231 |   value: "ldap-bind-password"
232 |   required: true
233 | - name: "SUCCESS_JOBS_HISTORY_LIMIT"
234 |   displayName: "Successful Job History Limit"
235 |   description: "The number of successful jobs that will be retained"
236 |   value: "5"
237 |   required: true
238 | - name: "FAILED_JOBS_HISTORY_LIMIT"
239 |   displayName: "Failed Job History Limit"
240 |   description: "The number of failed jobs that will be retained"
241 |   value: "5"
242 |   required: true
243 | - name: "IMAGE"
244 |   displayName: "Image"
245 |   description: "Image to use for the container."
246 |   required: true
247 |   value: "registry.redhat.io/openshift4/ose-cli"
248 | - name: "IMAGE_TAG"
249 |   displayName: "Image Tag"
250 |   description: "Image Tag to use for the container."
251 |   required: true
252 |   value: "4.1"
253 | - name: "LDAP_SYNC_CONFIGMAP"
254 |   displayName: "LDAP sync config map"
255 |   description: "Name for the config map storing the group sync config"
256 |   value: "ldap-group-sync"
257 | - name: "LDAP_CA_CONFIGMAP"
258 |   displayName: "LDAP sync certificate authority config map"
259 |   description: "Name for the config map storing the TLS certificate authority"
260 |   value: "ldap-tls-ca"
261 | 


--------------------------------------------------------------------------------
/templates/scheduler.j2:
--------------------------------------------------------------------------------
 1 | ---
 2 | apiVersion: config.openshift.io/v1
 3 | kind: Scheduler
 4 | metadata:
 5 |   annotations:
 6 |     release.openshift.io/create-only: "true"
 7 |   name: cluster
 8 | spec:
 9 |   defaultNodeSelector: {{ scheduler_default_node_selector }}
10 | 


--------------------------------------------------------------------------------