Jenkins Credentials Manager + Active Choices

Oct 07 2020

Jenkins資格情報にアクセスして、パイプラインスクリプトの前にGitで認証し、ActiveChoicesパラメーターを入力したいと思います。

パイプラインでは、単にwithCredentials。でそれを行います。ただし、パイプライン前の段階(つまり、Active Choicesパラメーター)では、withCredentialsまたはなどのハドソン拡張機能にsshAgentアクセスできません。現在git ls-remote -h [email protected]:repo.git、jenkins_homeディレクトリで暗号化されていないキーを使用してgroovyを実行しています(これはお勧めできません)。

Jenkinとそのプラグインのソースコードを読んでJavaオブジェクトをインポートして構築する可能性があることは知っていますが、これは無限のようです。

つまり、Hudson拡張機能を現在のGroovyスクリプトに簡単にインポートして適用することは可能ですか、それとも誰かがこれに対する準備ができている解決策を持っていますか(明らかにそのようなケースについては誰も公開していません)?

回答

jayhendren Oct 08 2020 at 03:41

あなたはすでにこれを知っているように私には聞こえますが、これを行うにはいくつかの簡単な方法と、これを行うためのいくつかの難しい方法があります。

簡単な方法:

  • SSHキーまたは認証資格情報なしでリポジトリをチェックアウトできるようにします。
  • JenkinsマスターノードのJenkinsユーザーにリポジトリチェックアウト用のSSHキーペアを手動で追加します(Active Choice Groovyスクリプトはマスターノードで実行されるため)(つまり、Credentialsストアを使用しないでください)。

難しい方法:

  • ActiveChoicesコードでJenkinsJava APIを使用して、認証情報ストアから認証情報を抽出します。APIドキュメントは、資格情報ストアとssh資格情報タイプで利用できます。これらのプラグインで使用可能なJavaクラスの使用方法に関するドキュメントはないため、自分で理解するのはあなた次第です。
  • セカンダリ「メタ」ジョブを作成して、元のJenkinsジョブ(Active Choicesスクリプトを含む)のパラメーターを更新します。これについては、以下で少し説明します。

セキュリティ上の理由で簡単な解決策を実行できない場合、gitリポジトリの内容に基づいてActiveChoiceパラメーターを更新する簡単で直接的な方法はありません。それの訳は:

  1. あなたが言ったように、「パイプライン前の段階(つまり、Active Choicesパラメーター)の間、withCredentialsやsshAgentなどのHudson拡張機能にはアクセスできません」。
  2. Active Choiceパラメーターを含むパラメーターの構成は、現在のパイプラインではなく、そのパイプラインの前回の実行から取得されます。つまり、パイプラインコード内からパラメーターを更新するには、パイプラインを実行する必要があります。これにより、やりたいことのポイントが無効になります。

1.の解決策があります。これは、宣言型の代わりにスクリプトパイプラインを使用することですが、2の問題を回避することはできません。

この問題が発生したときに思いついた解決策は、Active Choicesスクリプトを含む元のジョブのパラメーターを更新する2番目のJenkinsジョブ(「メタ」ジョブと呼びます)を作成することです。この2番目のメタジョ​​ブは、ActiveChoicesパラメーターのオプションを入力するために使用するリポジトリーのgitフックによってトリガーされます。これには、パラメータを更新するために元のジョブのパイプラインを実行する必要がないという利点があり、2の問題を解決します。また、パイプラインジョブであるため、通常のパイプラインステップを使用して、資格情報ストア。

私の仕事は(おおまかに)次のようになります-ここに収まるように大幅に簡略化したので、思いどおりに機能させるには、さまざまな調整を行う必要があります。これを作成するために既存のスクリプトを分解したため、これにはおそらく大量のエラーとバグがあります。注意すべきいくつかの事柄:

  • これはJenkinsAPIを多用するため、これを実行するには、スクリプトサンドボックスを無効にするか、Jenkinsセキュリティマネージャーで大量の関数をホワイトリストに登録する必要があります。
  • パイプラインファイルよりも通常のGroovyスクリプトに非常に似ていますが、これは技術的にはスクリプトパイプラインです。
  • これはパイプラインであるため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)