From 0a57ebb96e095a1633248bc99b6fcbdf27c0375d Mon Sep 17 00:00:00 2001 From: Akram Ben Aissi Date: Wed, 29 Apr 2020 12:37:24 +0200 Subject: [PATCH] Adding OpenShift doc --- config.openshift.env | 8 ++ ...openshift_jenkins_v1alpha2_jenkins_cr.yaml | 113 +++++++++--------- .../configuration/base/reconcile_test.go | 28 ++++- .../configuration/base/resources/rbac.go | 52 ++++---- .../configuration/base/serviceaccount.go | 11 +- .../docs/Getting Started/latest/openshift.md | 104 ++++++++++++++++ 6 files changed, 229 insertions(+), 87 deletions(-) create mode 100644 config.openshift.env create mode 100644 website/content/en/docs/Getting Started/latest/openshift.md diff --git a/config.openshift.env b/config.openshift.env new file mode 100644 index 00000000..70ce76d7 --- /dev/null +++ b/config.openshift.env @@ -0,0 +1,8 @@ +KUBERNETES_PROVIDER=crc +DOCKER_ORGANIZATION=image-registry.openshift-image-registry.svc:5000/jenkins-operator +DOCKER_REGISTRY=kubernetes-operator +IMAGE_PULL_MODE=remote +JENKINS_API_PORT=0 +JENKINS_API_USE_NODEPORT=false +NAMESPACE=$(oc project -q) + diff --git a/deploy/crds/openshift_jenkins_v1alpha2_jenkins_cr.yaml b/deploy/crds/openshift_jenkins_v1alpha2_jenkins_cr.yaml index ae20efc5..9942813a 100644 --- a/deploy/crds/openshift_jenkins_v1alpha2_jenkins_cr.yaml +++ b/deploy/crds/openshift_jenkins_v1alpha2_jenkins_cr.yaml @@ -7,64 +7,67 @@ metadata: spec: master: containers: - - name: jenkins-master - command: - - /usr/bin/go-init - - '-main' - - /usr/libexec/s2i/run - env: - - name: OPENSHIFT_ENABLE_OAUTH - value: 'true' - - name: OPENSHIFT_ENABLE_REDIRECT_PROMPT - value: 'true' - - name: DISABLE_ADMINISTRATIVE_MONITORS - value: 'false' - - name: KUBERNETES_MASTER - value: 'https://kubernetes.default:443' - - name: KUBERNETES_TRUST_CERTIFICATES - value: 'true' - - name: JENKINS_SERVICE_NAME - value: jenkins-operator-http-example - - name: JNLP_SERVICE_NAME - value: jenkins-operator-slave-example - - name: JENKINS_UC_INSECURE - value: 'false' - - name: JENKINS_HOME - value: /var/lib/jenkins - - name: JAVA_OPTS - value: >- - -XX:+UnlockExperimentalVMOptions -XX:+UnlockExperimentalVMOptions - -XX:+UseCGroupMemoryLimitForHeap -XX:MaxRAMFraction=1 - -Djenkins.install.runSetupWizard=false -Djava.awt.headless=true - image: 'quay.io/openshift/origin-jenkins:latest' - imagePullPolicy: Always - livenessProbe: - httpGet: - path: /login - port: 8080 - scheme: HTTP - initialDelaySeconds: 420 - periodSeconds: 360 - timeoutSeconds: 240 - readinessProbe: - httpGet: - path: /login - port: 8080 - scheme: HTTP - initialDelaySeconds: 3 - periodSeconds: 0 - timeoutSeconds: 240 - resources: - limits: - cpu: 600m - memory: 4Gi - requests: - cpu: 500m - memory: 3Gi + - name: jenkins-master + command: + - /usr/bin/go-init + - '-main' + - /usr/libexec/s2i/run + env: + - name: OPENSHIFT_ENABLE_OAUTH + value: 'true' + - name: OPENSHIFT_ENABLE_REDIRECT_PROMPT + value: 'true' + - name: DISABLE_ADMINISTRATIVE_MONITORS + value: 'false' + - name: KUBERNETES_MASTER + value: 'https://kubernetes.default:443' + - name: KUBERNETES_TRUST_CERTIFICATES + value: 'true' + - name: JENKINS_SERVICE_NAME + value: jenkins-operator-http-jenkins + - name: JNLP_SERVICE_NAME + value: jenkins-operator-slave-jenkins + - name: JENKINS_UC_INSECURE + value: 'false' + - name: JENKINS_HOME + value: /var/lib/jenkins + - name: JAVA_OPTS + value: >- + -XX:+UnlockExperimentalVMOptions -XX:+UnlockExperimentalVMOptions + -XX:+UseCGroupMemoryLimitForHeap -XX:MaxRAMFraction=1 + -Djenkins.install.runSetupWizard=false -Djava.awt.headless=true + image: 'quay.io/openshift/origin-jenkins:latest' + imagePullPolicy: Always + livenessProbe: + httpGet: + path: /login + port: 8080 + scheme: HTTP + initialDelaySeconds: 420 + periodSeconds: 360 + timeoutSeconds: 240 + readinessProbe: + httpGet: + path: /login + port: 8080 + scheme: HTTP + initialDelaySeconds: 3 + periodSeconds: 0 + timeoutSeconds: 240 + resources: + limits: + cpu: 600m + memory: 4Gi + requests: + cpu: 500m + memory: 3Gi service: port: 8080 type: ClusterIP slaveService: port: 50000 type: ClusterIP - + serviceAccount: + annotations: + serviceaccounts.openshift.io/oauth-redirectreference.jenkins: '{"kind":"OAuthRedirectReference","apiVersion":"v1","reference":{"kind":"Route","name":"jenkins-operator"}}' + diff --git a/pkg/controller/jenkins/configuration/base/reconcile_test.go b/pkg/controller/jenkins/configuration/base/reconcile_test.go index 330d843b..0d03a151 100644 --- a/pkg/controller/jenkins/configuration/base/reconcile_test.go +++ b/pkg/controller/jenkins/configuration/base/reconcile_test.go @@ -767,7 +767,12 @@ func TestEnsureExtraRBAC(t *testing.T) { Roles: []rbacv1.RoleRef{}, }, } - reconciler := New(configuration.Configuration{Client: fakeClient, Jenkins: jenkins, Scheme: scheme.Scheme}, nil, client.JenkinsAPIConnectionSettings{}) + config := configuration.Configuration{ + Client: fakeClient, + Jenkins: jenkins, + Scheme: scheme.Scheme, + } + reconciler := New(config, log.Log, client.JenkinsAPIConnectionSettings{}) metaObject := resources.NewResourceObjectMeta(jenkins) // when @@ -803,7 +808,12 @@ func TestEnsureExtraRBAC(t *testing.T) { }, }, } - reconciler := New(configuration.Configuration{Client: fakeClient, Jenkins: jenkins, Scheme: scheme.Scheme}, nil, client.JenkinsAPIConnectionSettings{}) + config := configuration.Configuration{ + Client: fakeClient, + Jenkins: jenkins, + Scheme: scheme.Scheme, + } + reconciler := New(config, log.Log, client.JenkinsAPIConnectionSettings{}) metaObject := resources.NewResourceObjectMeta(jenkins) // when @@ -845,7 +855,12 @@ func TestEnsureExtraRBAC(t *testing.T) { }, }, } - reconciler := New(configuration.Configuration{Client: fakeClient, Jenkins: jenkins, Scheme: scheme.Scheme}, nil, client.JenkinsAPIConnectionSettings{}) + config := configuration.Configuration{ + Client: fakeClient, + Jenkins: jenkins, + Scheme: scheme.Scheme, + } + reconciler := New(config, log.Log, client.JenkinsAPIConnectionSettings{}) metaObject := resources.NewResourceObjectMeta(jenkins) // when @@ -888,7 +903,12 @@ func TestEnsureExtraRBAC(t *testing.T) { }, }, } - reconciler := New(configuration.Configuration{Client: fakeClient, Jenkins: jenkins, Scheme: scheme.Scheme}, log.Log, client.JenkinsAPIConnectionSettings{}) + config := configuration.Configuration{ + Client: fakeClient, + Jenkins: jenkins, + Scheme: scheme.Scheme, + } + reconciler := New(config, log.Log, client.JenkinsAPIConnectionSettings{}) metaObject := resources.NewResourceObjectMeta(jenkins) // when diff --git a/pkg/controller/jenkins/configuration/base/resources/rbac.go b/pkg/controller/jenkins/configuration/base/resources/rbac.go index cd4e6457..f8061836 100644 --- a/pkg/controller/jenkins/configuration/base/resources/rbac.go +++ b/pkg/controller/jenkins/configuration/base/resources/rbac.go @@ -6,17 +6,19 @@ import ( ) const ( - createVerb = "create" - deleteVerb = "delete" - getVerb = "get" - listVerb = "list" - watchVerb = "watch" - patchVerb = "patch" - updateVerb = "update" - EmptyApiGroups = "" - OpenshiftApiGroup = "image.openshift.io" - BuildApiGroup = "build.openshift.io" - + createVerb = "create" + deleteVerb = "delete" + getVerb = "get" + listVerb = "list" + watchVerb = "watch" + patchVerb = "patch" + updateVerb = "update" + //EmptyAPIGroup short hand for the empty API group while defining policies + EmptyAPIGroup = "" + //OpenshiftAPIGroup the openshift api group name + OpenshiftAPIGroup = "image.openshift.io" + //BuildAPIGroup the openshift api group name for builds + BuildAPIGroup = "build.openshift.io" ) // NewRole returns rbac role for jenkins master @@ -54,22 +56,23 @@ func NewRoleBinding(name, namespace, serviceAccountName string, roleRef v1.RoleR } } +// NewDefaultPolicyRules sets the default policy rules func NewDefaultPolicyRules() []v1.PolicyRule { var rules []v1.PolicyRule ReadOnly := []string{getVerb, listVerb, watchVerb} - Default := []string{createVerb, deleteVerb, getVerb, listVerb, patchVerb, updateVerb, watchVerb} - Create := []string{createVerb} + Default := []string{createVerb, deleteVerb, getVerb, listVerb, patchVerb, updateVerb, watchVerb} + Create := []string{createVerb} - rules = append(rules, NewPolicyRule(EmptyApiGroups, "pods/portforward", Create)) - rules = append(rules, NewPolicyRule(EmptyApiGroups, "pods", Default)) - rules = append(rules, NewPolicyRule(EmptyApiGroups, "pods/exec", Default)) - rules = append(rules, NewPolicyRule(EmptyApiGroups, "configmaps", ReadOnly)) - rules = append(rules, NewPolicyRule(EmptyApiGroups, "pods/log", ReadOnly)) - rules = append(rules, NewPolicyRule(EmptyApiGroups, "secrets", ReadOnly)) + rules = append(rules, NewPolicyRule(EmptyAPIGroup, "pods/portforward", Create)) + rules = append(rules, NewPolicyRule(EmptyAPIGroup, "pods", Default)) + rules = append(rules, NewPolicyRule(EmptyAPIGroup, "pods/exec", Default)) + rules = append(rules, NewPolicyRule(EmptyAPIGroup, "configmaps", ReadOnly)) + rules = append(rules, NewPolicyRule(EmptyAPIGroup, "pods/log", ReadOnly)) + rules = append(rules, NewPolicyRule(EmptyAPIGroup, "secrets", ReadOnly)) - rules = append(rules, NewOpenShiftPolicyRule(OpenshiftApiGroup, "imagestreams", ReadOnly)) - rules = append(rules, NewOpenShiftPolicyRule(BuildApiGroup, "buildconfigs", ReadOnly)) - rules = append(rules, NewOpenShiftPolicyRule(BuildApiGroup, "builds", ReadOnly)) + rules = append(rules, NewOpenShiftPolicyRule(OpenshiftAPIGroup, "imagestreams", ReadOnly)) + rules = append(rules, NewOpenShiftPolicyRule(BuildAPIGroup, "buildconfigs", ReadOnly)) + rules = append(rules, NewOpenShiftPolicyRule(BuildAPIGroup, "builds", ReadOnly)) return rules } @@ -84,8 +87,7 @@ func NewPolicyRule(apiGroup string, resource string, verbs []string) v1.PolicyRu return rule } -// NewPolicyRule returns a policyRule allowing verbs on resources +// NewOpenShiftPolicyRule returns a policyRule allowing verbs on resources func NewOpenShiftPolicyRule(apiGroup string, resource string, verbs []string) v1.PolicyRule { - return NewPolicyRule(apiGroup,resource,verbs) + return NewPolicyRule(apiGroup, resource, verbs) } - diff --git a/pkg/controller/jenkins/configuration/base/serviceaccount.go b/pkg/controller/jenkins/configuration/base/serviceaccount.go index 70a09289..40cc9200 100644 --- a/pkg/controller/jenkins/configuration/base/serviceaccount.go +++ b/pkg/controller/jenkins/configuration/base/serviceaccount.go @@ -2,8 +2,10 @@ package base import ( "context" + "fmt" "github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/configuration/base/resources" + "github.com/jenkinsci/kubernetes-operator/pkg/log" stackerr "github.com/pkg/errors" corev1 "k8s.io/api/core/v1" @@ -15,8 +17,11 @@ import ( func (r *ReconcileJenkinsBaseConfiguration) createServiceAccount(meta metav1.ObjectMeta) error { serviceAccount := &corev1.ServiceAccount{} err := r.Client.Get(context.TODO(), types.NamespacedName{Name: meta.Name, Namespace: meta.Namespace}, serviceAccount) + annotations := r.Configuration.Jenkins.Spec.ServiceAccount.Annotations + msg := fmt.Sprintf("createServiceAccount with annotations %v", annotations) + r.logger.V(log.VDebug).Info(msg) if err != nil && apierrors.IsNotFound(err) { - serviceAccount = resources.NewServiceAccount(meta, r.Configuration.Jenkins.Spec.ServiceAccount.Annotations) + serviceAccount = resources.NewServiceAccount(meta, annotations) if err = r.CreateResource(serviceAccount); err != nil { return stackerr.WithStack(err) } @@ -24,11 +29,11 @@ func (r *ReconcileJenkinsBaseConfiguration) createServiceAccount(meta metav1.Obj return stackerr.WithStack(err) } - if !compareMap(r.Configuration.Jenkins.Spec.ServiceAccount.Annotations, serviceAccount.Annotations) { + if !compareMap(annotations, serviceAccount.Annotations) { if serviceAccount.Annotations == nil { serviceAccount.Annotations = map[string]string{} } - for key, value := range r.Configuration.Jenkins.Spec.ServiceAccount.Annotations { + for key, value := range annotations { serviceAccount.Annotations[key] = value } if err = r.UpdateResource(serviceAccount); err != nil { diff --git a/website/content/en/docs/Getting Started/latest/openshift.md b/website/content/en/docs/Getting Started/latest/openshift.md new file mode 100644 index 00000000..7af146ac --- /dev/null +++ b/website/content/en/docs/Getting Started/latest/openshift.md @@ -0,0 +1,104 @@ +--- +title: "OpenShift" +linkTitle: "OpenShift" +weight: 20 +date: 2020-04-29 +description: > + Additional configuration for OpenShift +--- + +## SecurityContext + +OpenShift enforces Security Constraints Context (scc) when deploying an image. +By default, container images run in restricted scc which prevents from setting +a fixed user id to run with. You need to have ensure that you do not provide a +securityContext with a runAsUser and that your image does not use a hardcoded user. + +```yaml +securityContext: {} +``` + +## OpenShift Jenkins image + +OpenShift provides a pre-configured Jenkins image containing 3 openshift plugins for +jenkins (openshift-login-plugin, openshift-sync-plugin and openshift-client-plugin) +which allows better jenkins integration with kubernetes and OpenShift. + +The OpenShift Jenkins image requires additional configuration to be fully enabled. + +### Sample OpenShift CR +The following Custom Resource can be used to create a Jenkins instance using the +OpenShift Jenkins image and sets values for: +- `image: 'quay.io/openshift/origin-jenkins:latest' : This is the OpenShift Jenkins image. + +- serviceAccount: to allow oauth authentication to work, the service account needs +a specific annotation pointing to the route exposing the jenkins service. Here, +the route is named `jenkins-route` + +- `OPENSHIFT_ENABLE_OAUTH` environment variable for the master container is set to true. + +Here is a complete Jenkins CR allowing the deployment of the Jenkins OpenShift image. +```yaml +apiVersion: jenkins.io/v1alpha2 +kind: Jenkins +metadata: + annotations: + jenkins.io/openshift-mode: 'true' + name: jenkins +spec: + serviceAccount: + annotations: + serviceaccounts.openshift.io/oauth-redirectreference.jenkins: '{"kind":"OAuthRedirectReference","apiVersion":"v1","reference":{"kind":"Route","name":"jenkins-route"}}' + master: + containers: + - name: jenkins-master + image: 'quay.io/openshift/origin-jenkins:latest' + command: + - /usr/bin/go-init + - '-main' + - /usr/libexec/s2i/run + env: + - name: OPENSHIFT_ENABLE_OAUTH + value: 'true' + - name: OPENSHIFT_ENABLE_REDIRECT_PROMPT + value: 'true' + - name: DISABLE_ADMINISTRATIVE_MONITORS + value: 'false' + - name: KUBERNETES_MASTER + value: 'https://kubernetes.default:443' + - name: KUBERNETES_TRUST_CERTIFICATES + value: 'true' + - name: JENKINS_SERVICE_NAME + value: jenkins-operator-http-jenkins + - name: JNLP_SERVICE_NAME + value: jenkins-operator-slave-jenkins + - name: JENKINS_UC_INSECURE + value: 'false' + - name: JENKINS_HOME + value: /var/lib/jenkins + - name: JAVA_OPTS + value: >- + -XX:+UnlockExperimentalVMOptions -XX:+UnlockExperimentalVMOptions + -XX:+UseCGroupMemoryLimitForHeap -XX:MaxRAMFraction=1 + -Djenkins.install.runSetupWizard=false -Djava.awt.headless=true + imagePullPolicy: Always + service: + port: 8080 + type: ClusterIP + slaveService: + port: 50000 + type: ClusterIP +``` + +### OpenShift OAuth integration +The creation of a Route is required for the integraiton of Jenkins with +OpenShift oauth authentication. By default, the jenkins http service is named +`jenkins-operator-http-${jenkins-cr-name}` + +```bash +oc create route edge jenkins-route --service=jenkins-operator-http-jenkins +``` +Note: the route name (jenkins-route) must match the pointed route on the serviceaccount annotation. + + +After the creation of the Route. It can be used to navigate to the Jenkins Login Page and login with your Openshift Credentials.