Solved: 回答の下 S.Richmondのおかげです。変数envServers
とobject
を無効化することを意味するgroovy.json.internal.LazyMap
タイプの保存されたマップを設定解除allする必要がありましたつかいます。
Additional:このエラーを検索する人は、代わりにJenkinsパイプラインステップreadJSON
を使用することに興味があるかもしれません-詳細情報を見つける こちら 。
Jenkins Pipelineを使用して、json文字列としてジョブに渡されるユーザーからの入力を取得しようとしています。その後、パイプラインはこれをslurperを使用して解析し、重要な情報を抽出します。次に、その情報を使用して、1つのジョブを異なるジョブパラメーターと並行して複数回実行します。
以下のコードを追加するまで"## Error when below here is added"
スクリプトは問題なく実行されます。そのポイントより下のコードでさえ、単独で実行されます。しかし、結合すると、次のエラーが発生します。
トリガーされたジョブが呼び出され、正常に実行されますが、以下のエラーが発生してメインジョブが失敗することに注意してください。このため、メインジョブは、トリガーされたジョブの復帰を待機しません。私はcouldbuild job:
を試行/キャッチしましたが、トリガーされたジョブが終了するまでメインジョブを待機させます。
誰でもここで支援できますか?さらに情報が必要な場合はお知らせください。
乾杯
def slurpJSON() {
return new groovy.json.JsonSlurper().parseText(BUILD_CHOICES);
}
node {
stage 'Prepare';
echo 'Loading choices as build properties';
def object = slurpJSON();
def serverChoices = [];
def serverChoicesStr = '';
for (env in object) {
envName = env.name;
envServers = env.servers;
for (server in envServers) {
if (server.Select) {
serverChoicesStr += server.Server;
serverChoicesStr += ',';
}
}
}
serverChoicesStr = serverChoicesStr[0..-2];
println("Server choices: " + serverChoicesStr);
## Error when below here is added
stage 'Jobs'
build job: 'Dummy Start App', parameters: [[$class: 'StringParameterValue', name: 'SERVER_NAME', value: 'TestServer'], [$class: 'StringParameterValue', name: 'SERVER_DOMAIN', value: 'domain.uk'], [$class: 'StringParameterValue', name: 'APP', value: 'application1']]
}
エラー:
Java.io.NotSerializableException: groovy.json.internal.LazyMap
at org.jboss.marshalling.river.RiverMarshaller.doWriteObject(RiverMarshaller.Java:860)
at org.jboss.marshalling.river.RiverMarshaller.doWriteObject(RiverMarshaller.Java:569)
at org.jboss.marshalling.river.BlockMarshaller.doWriteObject(BlockMarshaller.Java:65)
at org.jboss.marshalling.river.BlockMarshaller.writeObject(BlockMarshaller.Java:56)
at org.jboss.marshalling.MarshallerObjectOutputStream.writeObjectOverride(MarshallerObjectOutputStream.Java:50)
at org.jboss.marshalling.river.RiverObjectOutputStream.writeObjectOverride(RiverObjectOutputStream.Java:179)
at Java.io.ObjectOutputStream.writeObject(Unknown Source)
at Java.util.LinkedHashMap.internalWriteEntries(Unknown Source)
at Java.util.HashMap.writeObject(Unknown Source)
...
...
Caused by: an exception which occurred:
in field delegate
in field closures
in object org.jenkinsci.plugins.workflow.cps.CpsThreadGroup@5288c
今日私は自分でこれに遭遇しましたが、いくつかのブルートフォースを通じて、それを解決する方法と潜在的な理由の両方を見つけました。
おそらくその理由から始めるのが最善でしょう:
Jenkinsには、サーバーの再起動によってすべてのジョブを中断、一時停止、および再開できるパラダイムがあります。これを実現するには、パイプラインとそのデータを完全にシリアル化できる必要があります-IEは、すべての状態を保存できる必要があります。同様に、ビルド内のノードとサブジョブ間でグローバル変数の状態をシリアル化できる必要があります。これはあなたと私にとって起こっていることであり、その追加のビルドステップを追加した場合にのみ発生します。
何らかの理由で、JSONObjectはデフォルトでシリアル化できません。私はJava devではないので、悲しいことにこのトピックについてこれ以上語ることはできません。 GroovyとJenkinsにどのように適用できるかはわかりませんが、これを適切に修正する方法についてはたくさんの答えがあります。 この投稿を参照 もう少し情報が必要です。
修正方法:
方法がわかっていれば、JSONObjectを何らかの方法でシリアライズ可能にすることができます。それ以外の場合は、そのタイプのグローバル変数がないことを確認することで解決できます。
object
変数の設定を解除するか、メソッドでラップして、スコープがノードグローバルにならないようにしてください。
代わりにJsonSlurperClassic
を使用してください。
Groovy 2.3以降(注:Jenkins 2.7.1はGroovy 2.4.7を使用します)JsonSlurper
は、LazyMap
ではなくHashMap
を返します。これにより、JsonSlurper
notスレッドセーフおよびnotシリアル化可能。これにより、パイプラインDSLスクリプトの@NonDSL関数以外では使用できなくなります。
ただし、古い behavior をサポートし、パイプラインスクリプト内で安全に使用できるgroovy.json.JsonSlurperClassic
にフォールバックできます。
import groovy.json.JsonSlurperClassic
@NonCPS
def jsonParse(def json) {
new groovy.json.JsonSlurperClassic().parseText(json)
}
node('master') {
def config = jsonParse(readFile("config.json"))
def db = config["database"]["address"]
...
}
追伸JsonSlurperClassic
を呼び出す前に承認する必要があります。
EDIT:コメントの @ Sunvic で指摘されているように、以下のソリューションはJSONアレイではそのままでは機能しません。
これに対処するには、JsonSlurper
を使用し、遅延結果から新しいHashMap
を作成しました。 HashMap
はSerializable
です。
これには、new HashMap(Map)
とJsonSlurper
の両方のホワイトリスト登録が必要だと思います。
@NonCPS
def parseJsonText(String jsonText) {
final slurper = new JsonSlurper()
return new HashMap<>(slurper.parseText(jsonText))
}
全体として、 Pipeline Utility Stepsプラグイン を使用することをお勧めします。これには readJSON
step ワークスペース内のファイルまたはテキストをサポートできます。
これは、求められた詳細な回答です。
設定解除は私のために働いた:
String res = sh(script: "curl --header 'X-Vault-Token: ${token}' --request POST --data '${payload}' ${url}", returnStdout: true)
def response = new JsonSlurper().parseText(res)
String value1 = response.data.value1
String value2 = response.data.value2
// unset response because it's not serializable and Jenkins throws NotSerializableException.
response = null
解析された応答から値を読み取り、オブジェクトが不要になったら設定を解除します。
配列とマップのデコードを可能にする@mkobitからの回答のもう少し一般化された形式は次のとおりです。
import groovy.json.JsonSlurper
@NonCPS
def parseJsonText(String json) {
def object = new JsonSlurper().parseText(json)
if(object instanceof groovy.json.internal.LazyMap) {
return new HashMap<>(object)
}
return object
}
注:これはトップレベルのLazyMapオブジェクトのみをHashMapに変換することに注意してください。ネストされたLazyMapオブジェクトは引き続き存在し、Jenkinsで引き続き問題が発生します。
私は答えの1つに賛成したいと思います:ワークスペース内のファイルまたはテキストのいずれかをサポートできるreadJSONステップがあるため、Pipeline Utility Stepsプラグインを使用することをお勧めします: https://jenkins.io/doc/pipeline/steps/pipeline-utility-steps /#readjson-read-json-from-files-in-the-workspace
script{
def foo_json = sh(returnStdout:true, script: "aws --output json XXX").trim()
def foo = readJSON text: foo_json
}
これにはホワイトリストや追加のものは必要ありません。
パイプラインプラグインの実装方法は、重要なGroovyコードに非常に深刻な影響を及ぼします。このリンクは、起こりうる問題を回避する方法を説明しています: https://github.com/jenkinsci/pipeline-plugin/blob/master/TUTORIAL.md#serializing-local-variables
特定のケースでは、@NonCPS
アノテーションをslurpJSON
に追加し、JSONオブジェクトではなくmap-of-mapsを返すことを検討します。コードがきれいに見えるだけでなく、特にそのJSONが複雑な場合は、より効率的です。
私のNoobの間違い。古いパイプラインプラグインであるjenkins 1.6から誰かのコードを移動しましたか?最新の2.xジェンキンを実行しているサーバーへ。
この理由で失敗しました: "Java.io.NotSerializableException:groovy.lang.IntRange"上記のエラーのために、この投稿を何度も読み続けています。実現:for(1..numSlavesのnum){IntRange-シリアル化できないオブジェクトタイプ。
単純な形式で書き直します:for(num = 1; num <= numSlaves; num ++)
すべては世界に良いです。
Javaやgroovyはあまり使用しません。
みんなありがとう。
この投稿の他のアイデアは役に立ちましたが、私が探していたすべてではありませんでした-私は自分のニーズに合った部品を抽出し、自分のマジックをいくつか追加しました...
def jsonSlurpLaxWithoutSerializationTroubles(String jsonText)
{
return new JsonSlurperClassic().parseText(
new JsonBuilder(
new JsonSlurper()
.setType(JsonParserType.LAX)
.parseText(jsonText)
)
.toString()
)
}
はい、私がコードのgitコミットで述べたように、"Wildly-ineffecient、but tiny coefficient:JSON Slurp solution"(この目的で大丈夫です)。私が解決するために必要な側面:
Java.io.NotSerializableException
問題から完全に逃れます@NonCPS
を不要にする厄介なネストされたコンストラクターでも)Jenkinsパイプラインのドキュメント外 でもっと簡単な方法を見つけました
作業例
import groovy.json.JsonSlurperClassic
@NonCPS
def jsonParse(def json) {
new groovy.json.JsonSlurperClassic().parseText(json)
}
@NonCPS
def jobs(list) {
list
.grep { it.value == true }
.collect { [ name : it.key.toString(),
branch : it.value.toString() ] }
}
node {
def params = jsonParse(env.choice_app)
def forBuild = jobs(params)
}
JENKINS-26481 -ワークフローの制限により、Groovyクロージャーまたはクロージャーに依存する構文を実際に使用することはできないため、.collectEntriesを使用するGroovy標準を実行することはできません。結果のエントリの値としてステップをリストおよび生成します。また、Forループに標準> Java構文を使用することもできません-つまり、「for(String s:strings)」-代わりに、古い学校のカウンターベースのforループを使用する必要があります。