Issue
So I'm constructing a Jenkins pipeline to provision (Terraform), configure (Ansible) and deploy a set of "latest"-tagged containers from our container registry.
The first two stages are working perfectly. The host is provisioned and configured to run Docker containers. I have the CA cert, client cert and client key I need to create a DockerServerCredential, but am stuck on how to create that credential and use it in the pipeline. In other words, I don't want to end the pipeline after Ansible runs, manually add the credential to Jenkins, and then kick off another pipeline to deploy the containers using the new credential. I would like to use the CA, cert and key values to create a DockerServerCredential in my pipeline and then pass the name of that credential to docker.withServer().
So I know I COULD add this to my Jenkinsfile:
stage('Deploy') {
steps {
script {
def credName = "Docker-Cert-${env.OUT_VM_NAME}"
domain = com.cloudbees.plugins.credentials.domains.Domain.global()
store = Jenkins.instance.getExtensionList('com.cloudbees.plugins.credentials.SystemCredentialsProvider')[0].getStore()
dockerCertCred = new DockerServerCredentials(
CredentialsScope.GLOBAL,
credName,
'',
env.OUT_DCKR_CRED_KEY,
env.OUT_DCKR_CRED_CERT,
env.OUT_DCKR_CRED_CA
)
store.addCredentials(domain, dockerCertCred)
docker.withServer("tcp://${env.OUT_VM_NAME}:2376", credName) {
// things!
}
}
}
}
but I'm aware the best practice doco says to avoid using Jenkins.getInstance in a pipeline. And it would require me to add "staticMethod com.cloudbees.plugins.credentials.domains.Domain global" and "staticMethod jenkins.model.Jenkins getInstance" to Script Approvals, which I'm rather :/// on.
So my question is: HOW WOULD YOU DO IT? What's the best practice here?
P.S. I don't even need to persist the credential, so it'd be super-ideal if there was a wrapper that created a temporary credential, but I haven't been able to find one.
P.P.S. Haven't created a plugin before and am hoping against hope I can avoid having to do that.
Solution
Ended up creating the credential via API calls in the Ansible playbook, which is where the keys and certificates are being created.
Still using a crumb rather than API token to avoid the manual creation of the API token. Installed the Strict Crumb Issuer plugin to disable session ID check and set a 1-hour expiry on the crumbs.
- name: Get Jenkins Crumb
uri:
url: "{{ jenkins_host }}/crumbIssuer/api/json"
user: "{{ jenkins_user }}"
password: "{{ jenkins_password }}"
force_basic_auth: yes
return_content: yes
tags:
- always
register: crumb
when: add_credential | bool
- name: Fix newlines
set_fact:
ca_cert_value: "{{ ca_csr_content['content'] | b64decode | replace('\n', '\\n') | replace('+', '%2B') }}"
client_cert_value: "{{ client_cert_content['content'] | b64decode | replace('\n', '\\n') | replace('+', '%2B') }}"
client_key_value: "{{ client_key_content['content'] | b64decode | replace('\n', '\\n') | replace('+', '%2B') }}"
- name: Add Jenkins Credential
uri:
method: POST
url: "{{ jenkins_host }}/credentials/store/system/domain/_/createCredentials"
user: "{{ jenkins_user }}"
password: "{{ jenkins_password }}"
force_basic_auth: yes
body_format: form-urlencoded
follow_redirects: all
headers:
Jenkins-Crumb: "{{ crumb.json | json_query('crumb') }}"
body: |
json={
"": "0",
"credentials": {
"scope": "GLOBAL",
"id": "Docker-Cert-{{ ansible_hostname }}",
"description": "",
"clientKeySecret": "{{ client_key_value }}",
"clientCertificate": "{{ client_cert_value }}",
"serverCaCertificate": "{{ ca_cert_value }}",
"$class": "org.jenkinsci.plugins.docker.commons.credentials.DockerServerCredentials"
}
}
when: add_credential | bool
Answered By - Nathan C
Answer Checked By - Clifford M. (JavaFixing Volunteer)