Jenkins Credentials Manager + Active Choices

Oct 07 2020

ฉันต้องการเข้าถึง Jenkins Credentials เพื่อพิสูจน์ตัวตนกับ Git ก่อนสคริปต์ไปป์ไลน์ - เพื่อเติมพารามิเตอร์ Active Choices

withCredentialsในท่อฉันก็ทำมันด้วย แต่ในระหว่างขั้นตอนก่อนไปป์ไลน์ (เช่นพารามิเตอร์ Active Choices) ส่วนขยายของฮัดสันเช่นwithCredentialsหรือsshAgentไม่สามารถเข้าถึงได้ ขณะนี้ฉันดำเนินการ groovy git ls-remote -h [email protected]:repo.gitด้วยคีย์ที่ไม่ได้เข้ารหัสในไดเร็กทอรี jenkins_home (ซึ่งไม่ใช่ความคิดที่ดี)

ฉันรู้ว่ามีความเป็นไปได้ที่จะนำเข้าและสร้างวัตถุ Java โดยการอ่านซอร์สโค้ดของ Jenkin และปลั๊กอิน - แต่ดูเหมือนจะไม่มีที่สิ้นสุด

ดังนั้น: เป็นไปได้หรือไม่ที่จะนำเข้าและใช้ส่วนขยายของฮัดสันกับสคริปต์ Groovy ปัจจุบันได้อย่างง่ายดายหรือใครมีโซลูชันที่พร้อมสำหรับสิ่งนี้ (ดูเหมือนไม่มีใครเผยแพร่อะไรเกี่ยวกับกรณีดังกล่าว)

คำตอบ

jayhendren Oct 08 2020 at 03:41

สำหรับฉันดูเหมือนว่าคุณจะรู้เรื่องนี้แล้ว แต่มีวิธีง่ายๆสองสามวิธีในการทำเช่นนี้และสองวิธีที่ยากในการทำเช่นนี้

วิธีง่ายๆ:

  • ทำให้ repo ของคุณพร้อมสำหรับการชำระเงินโดยไม่มีคีย์ SSH หรือข้อมูลรับรองการตรวจสอบสิทธิ์
  • เพิ่มคีย์คู่ SSH ด้วยตนเองสำหรับการชำระเงิน repo ให้กับผู้ใช้ Jenkins บนโหนดหลักของ Jenkins ของคุณ (เนื่องจากสคริปต์ Active Choice Groovy ทำงานบนโหนดหลัก) (เช่นอย่าใช้ที่เก็บข้อมูลรับรอง)

วิธีที่ยาก:

  • ใช้ Jenkins Java API ในโค้ด Active Choices ของคุณเพื่อแยกข้อมูลรับรองจากที่เก็บหนังสือรับรอง เอกสาร API ที่มีอยู่สำหรับการจัดเก็บข้อมูลประจำตัวและประเภทข้อมูลประจำตัวของ SSH ไม่มีเอกสารเกี่ยวกับวิธีใช้คลาส Java ที่มีอยู่ในปลั๊กอินเหล่านี้ดังนั้นคุณจึงต้องคิดออกด้วยตัวเอง
  • สร้างงาน "เมตา" รองเพื่ออัปเดตพารามิเตอร์ในงาน Jenkins ดั้งเดิม (รวมถึงสคริปต์ Active Choices) - ฉันอธิบายสิ่งนี้ไว้ด้านล่างเล็กน้อย

หากคุณไม่สามารถทำวิธีแก้ปัญหาง่ายๆด้วยเหตุผลด้านความปลอดภัยไม่มีวิธีที่ง่ายและตรงไปตรงมาในการอัปเดตพารามิเตอร์ Active Choice ตามเนื้อหาของ git repo นี้เป็นเพราะ:

  1. เช่นเดียวกับที่คุณกล่าวว่า "ในช่วงก่อนไปป์ไลน์ (เช่นพารามิเตอร์ Active Choices) ส่วนขยายของฮัดสันเช่น withCredentials หรือ sshAgent ไม่สามารถเข้าถึงได้"
  2. การกำหนดค่าสำหรับพารามิเตอร์รวมถึงพารามิเตอร์ Active Choice นำมาจากการเรียกใช้ Pipeline ก่อนหน้านั้นไม่ใช่พารามิเตอร์ปัจจุบัน หมายความว่าในการอัปเดตพารามิเตอร์ของคุณจากภายในรหัสไปป์ไลน์คุณจะต้องเรียกใช้ไปป์ไลน์ซึ่งเอาชนะจุดที่คุณต้องการทำ

มีวิธีแก้ไขสำหรับ 1. ซึ่งก็คือการใช้ Scripted Pipelines แทน Declarative แต่จะไม่สามารถแก้ไขปัญหาของ 2 ได้

วิธีแก้ปัญหาที่ฉันคิดขึ้นเมื่อฉันพบปัญหานี้คือการสร้างงานเจนกินส์ที่สอง (ฉันเรียกว่างาน "เมตา") ซึ่งอัปเดตพารามิเตอร์ของงานดั้งเดิมรวมถึงสคริปต์ Active Choices งานเมตาที่สองนี้ถูกทริกเกอร์โดย git hook บนที่เก็บที่ฉันใช้เพื่อเติมตัวเลือกในพารามิเตอร์ Active Choices ของฉัน สิ่งนี้มีข้อดีที่คุณไม่จำเป็นต้องเรียกใช้ Pipeline ของงานเดิมเพื่ออัปเดตพารามิเตอร์แก้ปัญหาในข้อ 2 นอกจากนี้เนื่องจากยังคงเป็นงาน Pipeline คุณจึงสามารถใช้ขั้นตอน Pipeline ตามปกติได้จึงช่วยให้คุณเข้าถึง การจัดเก็บข้อมูลรับรอง

งานของฉันมีลักษณะ (โดยประมาณ) แบบนี้ - ฉันทำให้มันง่ายขึ้นมากเพื่อให้พอดีกับที่นี่ดังนั้นคุณจะต้องปรับแต่งหลายอย่างเพื่อให้มันทำงานได้ตามที่คุณต้องการ อาจมีข้อผิดพลาดและข้อบกพร่องมากมายในเรื่องนี้เนื่องจากฉันฉีกสคริปต์ที่มีอยู่ออกจากกันเพื่อสร้างสิ่งนี้ สิ่งที่ควรทราบ:

  • สิ่งนี้ใช้ประโยชน์จาก Jenkins APIs เป็นจำนวนมากดังนั้นคุณจะต้องปิดการใช้งานแซนด์บ็อกซ์ของสคริปต์หรือรายการฟังก์ชันที่อนุญาตพิเศษมากมายในตัวจัดการความปลอดภัยของเจนกินส์เพื่อให้สิ่งนี้ทำงานได้
  • นี่ยังคงเป็นไปในทางเทคนิคของ Scripted Pipeline แม้ว่าจะดูเหมือนสคริปต์ Groovy ปกติมากกว่าไฟล์ Pipeline ก็ตาม
  • เนื่องจากนี่คือไปป์ไลน์คุณสามารถใช้ขั้นตอนต่างๆเช่นwithCredentialsการตรวจสอบที่เก็บโดยใช้ที่เก็บหนังสือรับรอง
import groovy.json.JsonOutput

import org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SecureGroovyScript 

import org.biouno.unochoice.*
import org.biouno.unochoice.model.*

def metadataUrl = 'ssh://git@**************/metadata.git'
def metadataBranch = 'master'

def metadata = [:]

node('master') {
  git(
    url: metadataUrl,
    branch: metadataBranch,
    credentialsId: 'jenkins',
    changelog: false,
  )

  def dataDir = "${pwd()}/myJsonFiles" // can't use File.eachFileMatch() due to: // https://wiki.jenkins.io/display/JENKINS/Pipeline+CPS+method+mismatches def files = new File(dataDir).list().findAll() { f-> FilenameUtils.getExtension(f) == 'json' } files.each() { f-> // TODO: nice error messages if files are misformatted def name = FilenameUtils.removeExtension(f) def data = readJson(file: "${dataDir}/${f}") metadata[name] = data } } // ChoiceParameterDefinition is the class name for the built-in static choice parameter. // CascadeChoiceParameter is the class name for the Active Choices dynamic choice parameter. // begin first parameter def fileParam = new ChoiceParameterDefinition( 'file', 'The JSON file to use', ) fileParam.setChoices(metadata.keySet()) // end first parameter // begin second parameter def metadataJson = JsonOutput.toJson(metadata) def scriptText = """ import groovy.json.JsonSlurper def slurper = new JsonSlurper() def metadata = slurper.parseText('${metadataJson}')

return metadata[file]['versions']
"""
def mainScript = new SecureGroovyScript(scriptText, true)
def fallbackScript = new SecureGroovyScript('return ["Error encountered - see logs"]', true)
def activeChoiceScript = new GroovyScript(mainScript, fallbackScript)

def versionParam = new CascadeChoiceParameter(
  'version', 
  'The version to use',
  activeChoiceScript,
  'PT_SINGLE_SELECT',
  'file',
  false,
)
// end second parameter


def paramProp = new ParametersDefinitionProperty(fileParam, versionParam)


// this is the full name of the actual job which needs dynamic parameters
def job = Jenkins.instance.getItemByFullName('UNIX/deploy_to_environment/master')


// There is no "setProperty" - we need to remove and replace.
// removeProperty is safe to run even if no matching properties are set on the job.
// However, this method only removes one property per method call.
// Theoretically, this means the job could accumulate multiple conflicting
// ParametersDefinitionProperty properties.
// TODO: run removeProperty in a while loop to eliminate all matching properties for extra safety.
job.removeProperty(ParametersDefinitionProperty)

job.addProperty(paramProp)