Installation

This document describes installation procedure of self-hosted Operator Service for Jenkins®.

Installation of a self-hosted Operator Service for Jenkins® happens with Helm Chart.

When purchasing the Operator Service for Jenkins, client is given a personalized token with a password and name of the registry to access the Operator’s image and helm chart.

First create namespaces of choice to deploy Operator and Jenkins in.

$ kubectl create ns <operator-namespace>
$ kubectl create ns <jenkins-namespace>

Second create a secret for downloading the Enterprise image by exchanging the placeholders in the below code:

$ kubectl create secret docker-registry token \
--namespace <operator-namespace> \
--docker-server=<registry-name> \
--docker-username=<token-name> \
--docker-password=<password>

This secret must reside in the same namespace as Operator and be named image-secret in order to fetch the image.

Third create a secret with the license information.

apiVersion: v1
kind: Secret
metadata:
  name: <custom-name>
stringData:
  clientName: "<your-name>"
  licenseKey: "<your-license-key>"

By default the Operator will look for a secret named “license”, but you can use custom name by specifying it in the flag in the Operator command:

-license-secret=<custom-name>

Next, prepare your two custom values.yaml files: values-operator.yaml and values-crs.yaml, and specify all of the desired configuration for the Operator and other available Custom Resources. Don’t forget to exchange the jenkins namespace and operator namespace with the one you have just created. You can copy and customize the default files:

# Operator Service for Jenkins Helm chart

# apiVersion is the version of the Custom Resources manifests
apiVersion: operator-service.com/v1beta1

# operator is section for configuring operator deployment
operator:
  # namespace is the namespace where the operator will be deployed
  # It's not recommended to use default namespace
  # Create new namespace for the Operator (called e.g. operator-service)
  namespace: default

  replicaCount: 1

  # image is the name (and tag) of the Operator Service for Jenkins image
  image: operatorservice.azurecr.io/op-svc-jenkins:0.8.1

  # imagePullPolicy defines policy for pulling images
  imagePullPolicy: IfNotPresent

  # imagePullSecrets is used to pull images from private repository
  imagePullSecrets:
    - name: token

  # command
  command:
    - /manager

  # args is used to specify flags
  args:
    - --leader-elect=true

  # nameOverride overrides the app name
  nameOverride: ""

  # fullnameOverride overrides the deployment name
  fullnameOverride: ""

  # Resource limit/request for Operator Service
  # See https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/ for details
  resources: { }

  # NodeSelector for Operator Service
  # For more details see https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/
  nodeSelector: { }

  # Tolerations are applied to pods, and allow the pods to schedule onto nodes with matching taints.
  # See https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/ fore more details
  tolerations: [ ]

  # Affinity allows you to constrain which nodes your pod is eligible to be scheduled on, based on labels on the node.
  # For more details see https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity
  affinity: { }

  # JenkinsNamespace is used by Operator Service to watch namespace where Jenkins will be deployed
  jenkinsNamespace: "default"

  # JenkinsName is name of Jenkins to watch by Operator Service
  jenkinsName: "jenkins"
# Operator Service for Jenkins Custom Resources Helm chart

# apiVersion is the version of the Custom Resources manifests
apiVersion: operator-service.com/v1beta1

# Jenkins instance configuration
jenkins:
  # enabled can enable or disable the Jenkins instance
  # Set to false if you have configured CR already and/or you want to deploy an operator only
  enabled: true

  # name of resource
  # The pod name will be jenkins-<name> (name will be set as suffix)
  name: jenkins

  # namespace is the namespace where the resources will be deployed
  # It's not recommended to use default namespace
  # Create new namespace for jenkins (called e.g. jenkins)
  namespace: default

  # labels are injected into metadata labels field
  #labels: {}

  # annotations are injected into metadata annotations field
  annotations: {}

  # podLabels are injected into Jenkins Controller Pod's metadata labels field
  podLabels: {}

  # podSpec
  podSpec:
    initContainers: []
    sidecars: []
    jenkinsController:
      name: jenkins-controller
      image: jenkins/jenkins:2.277.4-lts-alpine
      imagePullPolicy: IfNotPresent
      imagePullSecrets: []
      livenessProbe: {}
      readinessProbe: {}
      # Resource limit/request for Jenkins
      # See https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/ for details
      resources:
        limits:
          cpu: 1500m
          memory: 3Gi
        requests:
          cpu: 1
          memory: 500Mi
      env: []
      volumeMounts: []
    volumes: []
    restartPolicy: ""
    nodeSelector: []
    imagePullSecrets: []
    hostAliases: []
    podSecurityContext: ""
    affinity: ""
    priorityClassName: ""

  # homePVC allows to set PersistentVolumeClaim properties for Jenkins home
  homePVC: {}

  # accessMode specifies the way the plugins cache can be mounted
  # accessMode: ""

  # resourceStorage is the plugins cache volume size
  # resourceStorage: ""

  # storageClassName is the name of the StorageClass required by the claim
  # storageClassName: ""
  # storageClassName: ""

  # pluginsCache allows to set plugins cache specific pvc properties
  pluginsCache: {}

  # accessMode specifies the way the plugins cache can be mounted
  # accessMode: ""

  # resourceStorage is the plugins cache volume size
  # resourceStorage: ""

  # storageClassName is the name of the StorageClass required by the claim
  # storageClassName: ""

  # plugins are plugins required by the user
  # You can define plugins here
  #
  # Example:
  #
  # plugins:
  # - name: jacoco
  #   version: "3.1.1"
  plugins: []

  # roles defines list of extra RBAC roles for the Jenkins Master pod service account
  roles: []

  # services allows to configure Jenkins services
  services: {}

  # defines authorization strategy of the operator for the Jenkins API
  authorizationStrategy: createUser

  # disableCSRFProtection can enable or disable operator built-in CSRF protection
  # Set it to true if you are using OpenShift Jenkins Plugin
  # See https://github.com/jenkinsci/kubernetes-operator/pull/193 for more info
  disableCSRFProtection: false

# jenkinsGroovyScriptsEnabled can enable or disable the JenkinsGroovyScript instances
# Set to false if you have configured CR(s) already and/or you want to deploy an operator only
jenkinsGroovyScriptsEnabled: true

# JenkinsGroovyScripts instances configuration
# JenkinsGroovyScriptSpec defines the desired state of JenkinsGroovyScript. It allows to write Groovy Scripts to modify the Jenkins.
jenkinsGroovyScripts:
  # name of resource
  - name: groovy

    # namespace is the namespace where the resources will be deployed
    namespace: default

    # labels are injected into metadata labels field. Each JenkinsSeedJob Custom Resource need to reference corresponding
    # Jenkins Custom Resource via labels field
    labels:
      operator-service.com/jenkins: jenkins

    # annotations are injected into metadata annotations field
    annotations: {}

    # data is inline Groovy script
    data: |
      import jenkins.model.Jenkins

      def systemMessage = "Hello from v1beta1.JenkinsGroovyScript!"

      Jenkins jenkins = Jenkins.getInstance()
      jenkins.setSystemMessage(systemMessage)
      jenkins.save()

      println "Hello world!"

      # secretRef is secret reference which allows to inject the secrets into Groovy script code
      #secretRef:
      #name: ""
      #namespace: ""

      # dependsOn is JenkinsGroovyScript reference which determines the order of the Groovy scripts
      #dependsOn:
      #name: ""      
    #namespace: ""

# jenkinsSeedJobsEnabled can enable or disable the JenkinsSeedJobs instances
# Set to false if you have configured CR(s) already and/or you want to deploy an operator only
jenkinsSeedJobsEnabled: true

# JenkinsSeedJobs instances configuration
# See https://virtuslabrnd.github.io/operator-service-docs/operator-service-for-jenkins/latest/getting-started/seedjob-configuration/ for additional info
jenkinsSeedJobs:
  # name of resource
  - name: example

    # namespace is the namespace where the resources will be deployed
    namespace: default

    # labels are injected into metadata labels field. Each JenkinsSeedJob Custom Resource need to reference corresponding
    # Jenkins Custom Resource via labels field
    labels:
      operator-service.com/jenkins: jenkins

    # annotations are injected into metadata annotations field
    annotations: {}

    # Repository is VCS repository settings
    repository:

      # branch is the repository branch where seed job definitions are stored
      branch: master

      # URL is the repository access URL. Can be SSH or HTTPS.
      url: https://github.com/jenkinsci/kubernetes-operator.git

      # targets is the path from repository root where seed job definitions are stored
      targets: "cicd/jobs/*.jenkins"

      # credentialID is the Kubernetes secret name which stores repository access credentials
      credentialID: ""

      # credentialType is the https://jenkinsci.github.io/kubernetes-credentials-provider-plugin/ credential type (optional)
      # allowed types:
      # "" define none Jenkins credential type
      # "basicSSHUserPrivateKey" basic SSH Jenkins credential type
      # "usernamePassword" define username & password Jenkins credential type
      # "external" defines other credential type
      credentialType: ""

      # agentRef represents a Jenkins Kubernetes Agent Reference. It has enough information to retrieve agent
      # in any namespace
      #agentRef:

      # name is unique within a namespace to reference a secret resource.
      #name:

      # namespace defines the space within which the secret name must be unique.
      #namespace:

    # triggers define circumstances that execute jobs
    triggers:

      # bitbucketTrigger is used for Bitbucket web hooks (optional)
      bitbucketTrigger:
        push: false

      # gitHubTrigger is used for GitHub web hooks (optional)
      gitHubTrigger:
        push: false

      # buildPeriodically is used for scheduled timer trigger (optional)
      buildPeriodically: ""

      # pollSCM is setting for polling changes in SCM (optional)
      pollSCM: ""

    # settings configures the job build options
    settings:

      # ignoreMissingFiles is setting for Job DSL API plugin to ignore files that are missing (optional)
      ignoreMissingFiles: false

      # additionalClasspath is setting for Job DSL API plugin to set Additional Classpath (optional)
      additionalClasspath: ""

      # failOnMissingPlugin is setting for Job DSL API plugin that fails job if required plugin is missing (optional)
      failOnMissingPlugin: false

      # unstableOnDeprecation is setting for Job DSL API plugin that sets build status as unstable if build using deprecated features (optional)
      unstableOnDeprecation: false

# jenkinsConfigurationsAsCodeEnabled can enable or disable the JenkinsConfigurationAsCode instances
# Set to false if you have configured CR(s) already and/or you want to deploy an operator only
jenkinsConfigurationsAsCodeEnabled: true

# JenkinsConfigurationAsCode instances configuration
# For configuration as code creation tutorial, check https://virtuslabrnd.github.io/operator-service-docs/operator-service-for-jenkins/latest/getting-started/customization/
jenkinsConfigurationsAsCode:
  # name of resource
  - name: casc

    # namespace is the namespace where the resources will be deployed
    namespace: default

    # labels are injected into metadata labels field. Each JenkinsConfigurationAsCode Custom Resource need to reference corresponding
    # Jenkins Custom Resource via labels field
    labels:
      operator-service.com/jenkins: jenkins

    # annotations are injected into metadata annotations field
    annotations: {}

    # data is inline Groovy script
    data: |
      jenkins:
        systemMessage: |
          Welcome to our build server.

          This Jenkins is 100% configured and managed 'as code'.
          Config is now mostly handled by the 'Jenkins Configuration as Code' (JCasC) plugin.
          JCasC config can be found in the jenkins.yaml file in the ${JENKINS_HOME}/casc/ folder.

          Some settings are still injected from init.groovy.d scripts,
          but these settings will be ported over to JCasC as support becomes available.
        numExecutors: 1 #

      # secretRef is secret reference which allows to inject the secrets into Configuration As Code script
      #secretRef:
      #name: ""      
    #namespace: ""

# jenkinsKubernetesAgentEnabled can enable or disable the JenkinsKubernetesAgent instances
# Set to false if you have configured CR(s) already and/or you want to deploy an operator only
jenkinsKubernetesAgentEnabled: true

# JenkinsKubernetesAgent instances configuration
# For configuration as code creation tutorial, check https://virtuslabrnd.github.io/operator-service-docs/operator-service-for-jenkins/latest/getting-started/seedjob-configuration/
jenkinsKubernetesAgents:
  # name of resource
  - name: first-agent

    # namespace is the namespace where the resources will be deployed
    namespace: default

    # labels are injected into metadata labels field. Each JenkinsKubernetesAgent Custom Resource need to reference corresponding
    # Jenkins Custom Resource via labels field
    labels:
      operator-service.com/jenkins: jenkins

    # annotations are injected into metadata annotations field
    annotations: {}

    # PodSpec allows to set Jenkins Kubernetes Agent specific pod properties
    podSpec:
      containers:
        - name: jnlp
          image: quay.io/openshift/origin-jenkins-agent-base:4.9.0
          imagePullPolicy: IfNotPresent

    properties:
      # Description of Jenkins Agent
      description: ""

      # Executors defines the maximum number of concurrent builds that Jenkins may perform on this node.
      executors:

      # RemoteRootDirectory is directory dedicated to Jenkins for temporary files storage purposes.
      remoteRootDirectory: ""

      # Usage controls how Jenkins schedules builds on this node.
      # Available usage options are defined by AgentUsage enum
      usage: ""

      # LaunchMethod controls how Jenkins starts this agent.
      # AgentLaunchMethod is enum that defines the available launch methods
      launchMethod: ""

      # DisableWorkDir allows disabling Remoting Work Directory for the agent.
      disableWorkDir: false

      # CustomWorkDirPath is a custom Remoting work directory will be used instead of the Agent Root Directory
      customWorkDirPath: ""

      # InternalDataDirectory defines a storage directory for the internal data
      # By default it's "remoting"
      internalDataDirectory: ""

      # FailIfWorkspaceIsMissing if true, remoting will fail at startup if the target work directory is missing
      failIfWorkplaceIsMissing: false

      # UseWebSocket uses websocket to connect to the Jenkins Controller rather than the TCP port
      useWebSocket: false

      # TunnelConnectionThrough allows to route connection to another host
      # Field allowed values: "HOST:PORT", ":PORT" and "HOST:"
      tunnelConnectionThrough: ""

      # JVMOptions are additional startup arguments for Java Virtual Machine which runs agent
      jvmOptions: ""

      # Availability controls when Jenkins starts and stops this agent.
      availability: ""

      # ToolLocations is list of tools location
      toolLocations:
        - name: ""
          home: ""

    # RoleRef defines list of extra RBAC roles for the Jenkins Kubernetes Agent pod service account
    roleRef: []

Login to registry to download Helm charts:

$ export HELM_EXPERIMENTAL_OCI=1
$ helm registry login operatorservice.azurecr.io -u <token-name> -p <token-password>

Download Helm charts from registry:

$ helm chart pull operatorservice.azurecr.io/helm/op-svc-jenkins:latest
$ helm chart pull operatorservice.azurecr.io/helm/op-svc-jenkins-crs:latest

Export Helm charts from cache to change values and configure operator and Custom Resources:

$ helm chart export operatorservice.azurecr.io/helm/op-svc-jenkins:latest
$ helm chart export operatorservice.azurecr.io/helm/op-svc-jenkins-crs:latest
$ helm pull oci://operatorservice.azurecr.io/charts/op-svc-jenkins --version 0.1.3
$ helm pull oci://operatorservice.azurecr.io/charts/op-svc-jenkins-crs --version 0.1.3

In this step, replace default values.yaml with your own. Deploy Operator Service for Jenkins® in the first one running Helm command:

$ helm install operator-service -f values-operator.yaml ./op-svc-jenkins -n <operator-namespace>
$ helm install operator-service-crs -f values-crs.yaml ./op-svc-jenkins-crs -n <jenkins-namespace>

At that moment, the Operator and Jenkins pods will start to appear. You can watch over the process by running:

$ kubectl get po --all-namespaces -w

Operator will keep on printing logs about reaching consecutive phases and announce itself Ready. Then you can start to use it freely.

To connect to the UI you can use:

$ kubectl -n <jenkins-namespace> port-forward <jenkins-pod> 8080:8080

At http://localhost:8080 (or with customized suffix) you will find the Jenkins UI.

Login credentials can be obtained using commands below:

$ kubectl -n <jenkins-namespace> get secret <jenkins-name>-credentials -o 'jsonpath={.data.user}' | base64 -d
$ kubectl -n <jenkins-namespace> get secret <jenkins-name>-credentials -o 'jsonpath={.data.password}' | base64 -d

Jenkins UI

What’s next?

For further information about customization and configuration of Jenkins please refer to the Getting Started section.