Jenkins Credentials Manager + Active Choices

Oct 07 2020

Tôi muốn truy cập Jenkins Credentials để xác thực bằng Git trước tập lệnh đường dẫn - để điền thông số Active Choices.

Trong đường ống, tôi chỉ đơn giản làm điều đó với withCredentials. Nhưng trong giai đoạn tiền đường dẫn (tức là tham số Active Choices), các phần mở rộng Hudson như withCredentialshoặc sshAgentkhông thể truy cập được. Hiện tại tôi thực thi groovy git ls-remote -h [email protected]:repo.gitvới khóa không được mã hóa trong thư mục jenkins_home (đó không phải là một ý kiến ​​hay).

Tôi biết rằng có một số khả năng nhập và xây dựng các đối tượng Java bằng cách đọc mã nguồn của Jenkin và các plugin của nó - nhưng điều này dường như vô tận.

Vì vậy: bằng cách nào đó có thể dễ dàng nhập và áp dụng các phần mở rộng Hudson cho tập lệnh Groovy hiện tại hoặc có ai có giải pháp sẵn sàng cho việc này không (dường như không ai công bố bất kỳ điều gì về trường hợp như vậy)?

Trả lời

jayhendren Oct 08 2020 at 03:41

Tôi nghe có vẻ như bạn đã biết về điều này, nhưng có một số cách dễ dàng để làm điều này và một số cách khó để làm điều này.

Những cách dễ dàng:

  • Cung cấp repo của bạn để thanh toán mà không cần khóa SSH hoặc thông tin xác thực.
  • Thêm một cặp khóa SSH theo cách thủ công để kiểm tra lại cho người dùng Jenkins trên nút chính Jenkins của bạn (vì các tập lệnh Active Choice Groovy chạy trên nút chính) (tức là không sử dụng kho lưu trữ Thông tin xác thực).

Những cách khó:

  • Sử dụng Jenkins Java API trong mã Active Choices của bạn để trích xuất thông tin xác thực từ kho lưu trữ thông tin xác thực. Tài liệu API có sẵn cho kho thông tin xác thực và loại thông tin xác thực ssh . Không có tài liệu nào về cách sử dụng các lớp Java có sẵn trong các plugin này, vì vậy bạn có thể tự tìm hiểu.
  • Tạo một công việc "meta" phụ để cập nhật các thông số trong công việc Jenkins ban đầu (bao gồm các tập lệnh Active Choices) - Tôi mô tả điều này một chút bên dưới.

Nếu bạn không thể thực hiện bất kỳ giải pháp dễ dàng nào vì lý do bảo mật, sẽ không có cách dễ dàng, đơn giản để cập nhật các tham số Active Choice dựa trên nội dung của git repo. Điều này là do:

  1. Giống như bạn đã nói, "trong giai đoạn tiền đường dẫn (tức là tham số Active Choices) các phần mở rộng Hudson như withCredentials hoặc sshAgent không thể truy cập được".
  2. Cấu hình cho các tham số, bao gồm các tham số Active Choice, được lấy từ lần chạy trước của Đường ống đó, không phải ở lần chạy hiện tại. Có nghĩa là, để cập nhật các thông số của bạn từ bên trong mã Pipeline, bạn sẽ cần chạy Pipeline của mình, điều này đánh bại điểm những gì bạn muốn làm.

Có một giải pháp cho 1., đó là sử dụng Đường ống được mã hóa thay vì So sánh giữa các trang, nhưng điều đó sẽ không giải quyết được các vấn đề của 2.

Giải pháp mà tôi nghĩ ra khi gặp sự cố này là tạo công việc Jenkins thứ hai (tôi gọi đó là công việc "meta") cập nhật các thông số của công việc ban đầu, bao gồm các tập lệnh Active Choices. Công việc meta thứ hai này được kích hoạt bởi một git hook trên kho lưu trữ mà tôi sử dụng để điền các tùy chọn trong thông số Active Choices của mình. Điều này có lợi thế là bạn không cần phải chạy Pipeline của công việc ban đầu để cập nhật các thông số của nó, giải quyết các vấn đề trong 2. Ngoài ra, vì nó vẫn là một công việc Pipeline, bạn có thể sử dụng các bước Pipeline bình thường, do đó cho phép bạn truy cập kho thông tin xác thực.

Công việc của tôi trông (đại khái) như thế này - Tôi đã đơn giản hóa nó rất nhiều để làm cho nó phù hợp ở đây, vì vậy bạn sẽ cần thực hiện một loạt các chỉnh sửa để nó hoạt động theo cách bạn muốn. Có lẽ có rất nhiều lỗi và lỗi trong việc này bởi vì tôi đã tách một tập lệnh hiện có để thực hiện điều này. Một số điều cần lưu ý:

  • Điều này làm cho việc sử dụng nhiều API Jenkins, vì vậy bạn sẽ cần phải vô hiệu hóa hộp cát tập lệnh hoặc đưa vào danh sách trắng hàng loạt chức năng trong trình quản lý bảo mật Jenkins để làm cho điều này chạy
  • Về mặt kỹ thuật, đây vẫn là một Pipeline được Tập lệnh mặc dù nó trông giống một tập lệnh Groovy bình thường hơn là một tệp Pipeline
  • Vì đây là một Pipeline, bạn có thể sử dụng các bước như withCredentialsđể kiểm tra kho lưu trữ bằng cách sử dụng cửa hàng thông tin xác thực.
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)