diff --git a/README.md b/README.md index bfed9c0..381e08b 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,3 @@ -This repository contains OpenShift v3 projects -for Project Atomic related CI infrastructure, which -is currently just homu. - -The plan is to move this into -https://github.com/openshift/release/tree/master/cluster/ci - +This repository contains OpenShift v3 projects for Project +Atomic related CI infrastructure, which is currently homu +and PAPR. Together, we refer to these projects as PACI. diff --git a/papr/README.md b/papr/README.md new file mode 100644 index 0000000..46ac746 --- /dev/null +++ b/papr/README.md @@ -0,0 +1,54 @@ +# PAPR Jenkins + +This directory defines the infrastructure for a PAPR pipeline. + +The infrastructure is instantiated by the `papr-jenkins.yaml` OpenShift template +which will create a Jenkins instance customized for PAPR. This is heavily based +on the `jenkins-persistent` builtin OpenShift template, but with the following +enhancements: + +- the GHPRB plugin is installed by default +- anonymous users have read access +- a GitHub token is securely installed at runtime using OpenShift secrets + +The `jenkins/` directory is used by the S2I +[OpenShift Jenkins builder](https://github.com/openshift/jenkins/tree/8e58d88#installing-using-s2i-build) +to create the customized Jenkins image. + +# Usage + +Assuming you already have a cluster set up and running (e.g. `oc cluster up`): + +``` +$ oc new-project papr +$ echo "$GITHUB_TOKEN" > mytoken +$ oc secrets new github-token token=mytoken +$ oc new-app --file papr-jenkins.yaml +``` + +If your project already exists (e.g. you are not a cluster admin) and it is not +named `papr`, make sure to pass the `-p NAMESPACE=$project` argument to the +`new-app` command above. + +You can now start a build of the PAPR Jenkins image using: + +``` +$ oc start-build papr-jenkins +``` + +Once the image is built, it will be automatically deployed and available at: + +https://jenkins-papr.127.0.0.1.nip.io/ + +If you're working on your own fork, you can point OpenShift at it: + +``` +$ oc new-app --file papr-jenkins.yaml \ + -p REPO_URL=https://github.com/jlebon/projectatomic-ci-infra \ + -p REPO_REF=my-branch +``` + +Note that modifications to Jenkins configurations fed to the S2I builder will +probably require that you delete and recreate the PVC so that old configurations +don't override new ones (I find it easier to just `oc delete project papr` and +recreate it; the builder image is cached in the `openshift` namespace). diff --git a/papr/jenkins/configuration/config.xml.tpl b/papr/jenkins/configuration/config.xml.tpl new file mode 100644 index 0000000..0653fbd --- /dev/null +++ b/papr/jenkins/configuration/config.xml.tpl @@ -0,0 +1,66 @@ + + + + 2.89.2 + 5 + NORMAL + true + + hudson.model.Computer.Configure:admin + hudson.model.Computer.Delete:admin + hudson.model.Hudson.Administer:admin + hudson.model.Hudson.Read:admin + hudson.model.Item.Build:admin + hudson.model.Item.Configure:admin + hudson.model.Item.Create:admin + hudson.model.Item.Delete:admin + hudson.model.Item.Read:admin + hudson.model.Item.Workspace:admin + hudson.model.Run.Delete:admin + hudson.model.Run.Update:admin + hudson.model.View.Configure:admin + hudson.model.View.Create:admin + hudson.model.View.Delete:admin + + hudson.model.Hudson.Read:anonymous + hudson.model.Item.Discover:anonymous + hudson.model.Item.Read:anonymous + hudson.model.View.Read:anonymous + hudson.scm.SCM.Tag:admin + + + true + false + + false + ${ITEM_ROOTDIR}/workspace + ${ITEM_ROOTDIR}/builds + + + + + + ${KUBERNETES_CONFIG} + + 1 + 0 + + + + All + false + false + + + + All + ${JNLP_PORT} + + JNLP-connect + JNLP2-connect + + + + + true + diff --git a/papr/jenkins/configuration/credentials.xml.tpl b/papr/jenkins/configuration/credentials.xml.tpl new file mode 100644 index 0000000..d0b9497 --- /dev/null +++ b/papr/jenkins/configuration/credentials.xml.tpl @@ -0,0 +1,19 @@ + + + + ${KUBERNETES_CREDENTIALS} + + + + + + + GLOBAL + GitHub token + + SOOPERSEKRIT + + + + + diff --git a/papr/jenkins/configuration/init.groovy b/papr/jenkins/configuration/init.groovy new file mode 100644 index 0000000..dca92ad --- /dev/null +++ b/papr/jenkins/configuration/init.groovy @@ -0,0 +1,41 @@ +/* This init script injects the real GitHub token mounted into the container + * into the stored credentials. + */ + +import jenkins.model.* +import com.cloudbees.plugins.credentials.* +import com.cloudbees.plugins.credentials.domains.* +import org.jenkinsci.plugins.plaincredentials.* +import org.jenkinsci.plugins.plaincredentials.impl.* + +StringCredentials findGitHubTokenCreds() { + def creds = CredentialsProvider.lookupCredentials( + StringCredentials.class, + Jenkins.instance, + null, + null) + for (c in creds) { + if (c.description.equals("GitHub token")) + return c + } +} + +github_creds = findGitHubTokenCreds() +if (!github_creds) { + println("Didn't find GitHub token credentials, exiting...") + return +} + +def real_token_file = "/etc/github-token/token" +println("Reading token value from $real_token_file") +String real_token = new File(real_token_file).text.trim() + +Credentials new_github_creds = (Credentials) new StringCredentialsImpl( + CredentialsScope.GLOBAL, + github_creds.id, /* crucially; we copy the ID here to make it a clean swap */ + github_creds.description, + hudson.util.Secret.fromString(real_token)) + +store = SystemCredentialsProvider.instance.store +store.updateCredentials(Domain.global(), github_creds, new_github_creds) +println("Successfully updated credential token!") diff --git a/papr/jenkins/configuration/jobs/README.md b/papr/jenkins/configuration/jobs/README.md new file mode 100644 index 0000000..011692b --- /dev/null +++ b/papr/jenkins/configuration/jobs/README.md @@ -0,0 +1,4 @@ +This directory exists just so we override the default job +that gets created for us. See: + +https://github.com/openshift/jenkins/blob/8e58d888cc10e21db40ff5dbd40efe8ee8c77f93/2/contrib/s2i/assemble#L29 diff --git a/papr/jenkins/configuration/org.jenkinsci.plugins.ghprb.GhprbTrigger.xml.tpl b/papr/jenkins/configuration/org.jenkinsci.plugins.ghprb.GhprbTrigger.xml.tpl new file mode 100644 index 0000000..7014865 --- /dev/null +++ b/papr/jenkins/configuration/org.jenkinsci.plugins.ghprb.GhprbTrigger.xml.tpl @@ -0,0 +1,38 @@ + + + 1 + + (?m)^\s*bot,\s+add\s+author\s+to\s+whitelist\s*\.?$ + (?m)^\s*bot,\s+test\s+pull\s+request\s*\.?$ + (?m)^\s*bot,\s+test\s+pull\s+request\s+once\s*\.?$ + .*\[skip\W+ci\].* + + + H/10 * * * * + false + false + false + FAILURE + false + false + + + + + https://api.github.com + + Authenticated connection + + + + Can one of the admins verify this patch? + + + + false + false + + + + diff --git a/papr/jenkins/plugins.txt b/papr/jenkins/plugins.txt new file mode 100644 index 0000000..230d8f7 --- /dev/null +++ b/papr/jenkins/plugins.txt @@ -0,0 +1,4 @@ +# This file is read by +# https://github.com/openshift/jenkins/blob/8e58d88/2/contrib/jenkins/install-plugins.sh +# TODO: freeze all plugins +ghprb:1.39.0 diff --git a/papr/papr-jenkins.yaml b/papr/papr-jenkins.yaml new file mode 100644 index 0000000..820420a --- /dev/null +++ b/papr/papr-jenkins.yaml @@ -0,0 +1,261 @@ +apiVersion: v1 +kind: Template +labels: + app: papr-jenkins + template: papr-jenkins-template +metadata: + annotations: + description: |- + Jenkins service for PAPR. This template is heavily based on the built-in + jenkins-persistent OpenShift template. It adds an S2I buildconfig to + create Jenkins images tailored for PAPR usage. + iconClass: icon-jenkins + openshift.io/display-name: PAPR Jenkins + openshift.io/documentation-url: https://github.com/projectatomic/projectatomic-ci-infra + openshift.io/support-url: https://github.com/projectatomic/projectatomic-ci-infra + openshift.io/provider-display-name: Project Atomic CI + tags: papr,jenkins + name: papr-jenkins +objects: +- apiVersion: v1 + kind: ImageStream + metadata: + name: papr-jenkins + namespace: ${NAMESPACE} +- apiVersion: v1 + kind: BuildConfig + metadata: + name: papr-jenkins + triggers: + - type: ImageChange + imageChange: {} + - type: ConfigChange + # Notably missing here is a GitHub webhook; we don't want to restart Jenkins + # everytime we push to the PACI repo. + spec: + source: + type: Git + git: + uri: ${REPO_URL} + ref: ${REPO_REF} + contextDir: papr/jenkins/ + strategy: + type: Source + sourceStrategy: + from: + kind: ImageStreamTag + name: jenkins:latest + namespace: openshift + forcePull: true + output: + to: + kind: ImageStreamTag + name: papr-jenkins:latest + namespace: ${NAMESPACE} +- apiVersion: v1 + kind: Route + metadata: + annotations: + template.openshift.io/expose-uri: http://{.spec.host}{.spec.path} + name: ${JENKINS_SERVICE_NAME} + spec: + tls: + insecureEdgeTerminationPolicy: Redirect + termination: edge + to: + kind: Service + name: ${JENKINS_SERVICE_NAME} +- apiVersion: v1 + kind: PersistentVolumeClaim + metadata: + name: ${JENKINS_SERVICE_NAME} + spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: ${VOLUME_CAPACITY} +- apiVersion: v1 + kind: DeploymentConfig + metadata: + annotations: + template.alpha.openshift.io/wait-for-ready: "true" + name: ${JENKINS_SERVICE_NAME} + spec: + replicas: 1 + selector: + name: ${JENKINS_SERVICE_NAME} + strategy: + type: Recreate + template: + metadata: + labels: + name: ${JENKINS_SERVICE_NAME} + spec: + containers: + - capabilities: {} + env: + - name: OPENSHIFT_ENABLE_OAUTH + value: ${ENABLE_OAUTH} + - name: OPENSHIFT_ENABLE_REDIRECT_PROMPT + value: "true" + - name: KUBERNETES_MASTER + value: https://kubernetes.default:443 + - name: KUBERNETES_TRUST_CERTIFICATES + value: "true" + - name: JENKINS_SERVICE_NAME + value: ${JENKINS_SERVICE_NAME} + - name: JNLP_SERVICE_NAME + value: ${JNLP_SERVICE_NAME} + image: ' ' + imagePullPolicy: IfNotPresent + livenessProbe: + failureThreshold: 30 + httpGet: + path: /login + port: 8080 + initialDelaySeconds: 420 + timeoutSeconds: 3 + name: jenkins + readinessProbe: + httpGet: + path: /login + port: 8080 + initialDelaySeconds: 3 + timeoutSeconds: 3 + resources: + limits: + memory: ${MEMORY_LIMIT} + securityContext: + capabilities: {} + privileged: false + terminationMessagePath: /dev/termination-log + volumeMounts: + - name: ${JENKINS_SERVICE_NAME}-data + mountPath: /var/lib/jenkins + - name: github-token + mountPath: /etc/github-token + readOnly: true + dnsPolicy: ClusterFirst + restartPolicy: Always + serviceAccountName: ${JENKINS_SERVICE_NAME} + volumes: + - name: ${JENKINS_SERVICE_NAME}-data + persistentVolumeClaim: + claimName: ${JENKINS_SERVICE_NAME} + - name: github-token + secret: + secretName: ${GITHUB_TOKEN_SECRET} + triggers: + - imageChangeParams: + automatic: true + containerNames: + - jenkins + from: + kind: ImageStreamTag + name: ${JENKINS_IMAGE_STREAM_TAG} + namespace: ${NAMESPACE} + lastTriggeredImage: "" + type: ImageChange + - type: ConfigChange +- apiVersion: v1 + kind: ServiceAccount + metadata: + annotations: + serviceaccounts.openshift.io/oauth-redirectreference.jenkins: '{"kind":"OAuthRedirectReference","apiVersion":"v1","reference":{"kind":"Route","name":"${JENKINS_SERVICE_NAME}"}}' + name: ${JENKINS_SERVICE_NAME} +- apiVersion: v1 + groupNames: null + kind: RoleBinding + metadata: + name: ${JENKINS_SERVICE_NAME}_edit + roleRef: + name: edit + subjects: + - kind: ServiceAccount + name: ${JENKINS_SERVICE_NAME} +- apiVersion: v1 + kind: Service + metadata: + name: ${JNLP_SERVICE_NAME} + spec: + ports: + - name: agent + nodePort: 0 + port: 50000 + protocol: TCP + targetPort: 50000 + selector: + name: ${JENKINS_SERVICE_NAME} + sessionAffinity: None + type: ClusterIP +- apiVersion: v1 + kind: Service + metadata: + annotations: + service.alpha.openshift.io/dependencies: '[{"name": "${JNLP_SERVICE_NAME}", "namespace": "", "kind": "Service"}]' + service.openshift.io/infrastructure: "true" + name: ${JENKINS_SERVICE_NAME} + spec: + ports: + - name: web + nodePort: 0 + port: 80 + protocol: TCP + targetPort: 8080 + selector: + name: ${JENKINS_SERVICE_NAME} + sessionAffinity: None + type: ClusterIP +parameters: +- description: The name of the OpenShift Service exposed for the Jenkins container. + displayName: Jenkins Service Name + name: JENKINS_SERVICE_NAME + value: jenkins +- description: The name of the service used for master/slave communication. + displayName: Jenkins JNLP Service Name + name: JNLP_SERVICE_NAME + value: jenkins-jnlp +- description: Whether to enable OAuth OpenShift integration. If false, the static + account 'admin' will be initialized with the password 'password'. + displayName: Enable OAuth in Jenkins + name: ENABLE_OAUTH + value: "true" +- description: Maximum amount of memory the container can use. + displayName: Memory Limit + name: MEMORY_LIMIT + value: 512Mi +- description: Volume space available for data, e.g. 512Mi, 2Gi. + displayName: Volume Capacity + name: VOLUME_CAPACITY + required: true + value: 1Gi +- description: Name of the ImageStreamTag to be used for the Jenkins image. + displayName: Jenkins ImageStreamTag + name: JENKINS_IMAGE_STREAM_TAG + value: papr-jenkins:latest +# Templates are not able to pick up the namespace we're currently in, so let's +# allow users to specify a different one if they didn't name the project "papr" +# https://github.com/openshift/origin/issues/13934 +- description: > + The OpenShift Namespace where the Jenkins ImageStream resides. This is + usually the same namespace as the project. + displayName: Jenkins ImageStream Namespace + name: NAMESPACE + value: papr + required: true +# This is similar to the ci-pipeline template. It allows developers to specify +# their own repos when iterating. +- description: Git source URI for Jenkins S2I + name: REPO_URL + value: https://github.com/projectatomic/projectatomic-ci-infra +- description: Git branch/tag reference + name: REPO_REF + value: master +- description: > + GitHub token secret. This is *not* the token itself. It is the name of the + OpenShift secret containing the token, which must be created beforehand. The + secret is expected to define a key "token" containing the token. + name: GITHUB_TOKEN_SECRET + value: github-token + required: true