Gelöst : Dank der unten stehenden Antwort von S.Richmond. Ich musste alle gespeicherten Karten des groovy.json.internal.LazyMap
Typs deaktivieren, was bedeutete, dass die Variablen ungültig wurden envServers
und object
nach der Verwendung.
Zusätzlich : Personen, die nach diesem Fehler suchen, könnten daran interessiert sein, readJSON
stattdessen den Jenkins-Pipeline-Schritt zu verwenden. Weitere Informationen finden Sie hier .
Ich versuche, Jenkins Pipeline zu verwenden, um Eingaben vom Benutzer zu übernehmen, die als JSON-Zeichenfolge an den Job übergeben werden. Pipeline analysiert dies dann mit dem Slurper und ich suche die wichtigen Informationen aus. Diese Informationen werden dann verwendet, um einen Job mehrmals parallel zu unterschiedlichen Jobparametern auszuführen.
Bis ich den Code unten hinzufüge, "## Error when below here is added"
läuft das Skript einwandfrei. Sogar der Code unter diesem Punkt wird von selbst ausgeführt. Aber wenn kombiniert, erhalte ich den folgenden Fehler.
Ich sollte beachten, dass der ausgelöste Job aufgerufen wird und erfolgreich ausgeführt wird, aber der folgende Fehler auftritt und den Hauptjob fehlschlägt. Aus diesem Grund wartet der Hauptjob nicht auf die Rückgabe des ausgelösten Jobs. Ich könnte versuchen, das zu umgehen, build job:
aber ich möchte, dass der Hauptjob auf das Ende des ausgelösten Jobs wartet.
Kann hier jemand helfen? Wenn Sie weitere Informationen benötigen, lassen Sie es mich wissen.
Prost
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']]
}
Error:
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
quelle
Antworten:
Ich bin heute selbst darauf gestoßen und habe durch Bruteforce herausgefunden, wie ich es lösen kann und warum.
Am besten mit dem Warum beginnen:
Jenkins hat ein Paradigma, bei dem alle Jobs durch Serverneustarts unterbrochen, angehalten und wieder aufgenommen werden können. Um dies zu erreichen, müssen die Pipeline und ihre Daten vollständig serialisierbar sein - dh sie müssen in der Lage sein, den Status von allem zu speichern. Ebenso muss es in der Lage sein, den Status globaler Variablen zwischen Knoten und Unterjobs im Build zu serialisieren. Dies geschieht meiner Meinung nach für Sie und mich und warum es nur auftritt, wenn Sie diesen zusätzlichen Build-Schritt hinzufügen.
Aus irgendeinem Grund sind JSONObjects standardmäßig nicht serialisierbar. Ich bin kein Java-Entwickler, daher kann ich leider nicht viel mehr zu diesem Thema sagen. Es gibt viele Antworten darauf, wie man dies richtig beheben kann, obwohl ich nicht weiß, wie sie auf Groovy und Jenkins anwendbar sind. In diesem Beitrag finden Sie weitere Informationen.
So beheben Sie das Problem:
Wenn Sie wissen wie, können Sie das JSONObject möglicherweise irgendwie serialisierbar machen. Andernfalls können Sie das Problem beheben, indem Sie sicherstellen, dass keine globalen Variablen dieses Typs vorhanden sind.
Versuchen Sie, Ihre Variable zu deaktivieren
object
oder in eine Methode zu verpacken, damit ihr Gültigkeitsbereich nicht global ist.quelle
Verwenden Sie
JsonSlurperClassic
stattdessen.Da Groovy 2.3 ( Anmerkung: Jenkins 2.7.1 verwendet Groovy 2.4.7 )
JsonSlurper
zurückLazyMap
stattHashMap
. Dies macht die neue Implementierung vonJsonSlurper
nicht threadsicher und nicht serialisierbar. Dies macht es außerhalb von @ NonDSL-Funktionen in Pipeline-DSL-Skripten unbrauchbar.Sie können jedoch auf
groovy.json.JsonSlurperClassic
das zurückgreifen, das altes Verhalten unterstützt und sicher in Pipeline-Skripten verwendet werden kann.Beispiel
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"] ... }
ps. Sie müssen noch genehmigen,
JsonSlurperClassic
bevor es aufgerufen werden kann.quelle
JsonSlurperClassic
?hudson.remoting.ProxyException: org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed: Script1.groovy: 24: unable to resolve class groovy.json.JsonSlurperClassic
BEARBEITEN: Wie von @Sunvic in den Kommentaren hervorgehoben, funktioniert die folgende Lösung für JSON-Arrays nicht wie sie ist.
Ich habe mich damit befasst, indem ich aus den faulen Ergebnissen
JsonSlurper
ein neues erstellt habeHashMap
.HashMap
istSerializable
.Ich glaube, dass dies eine Whitelist sowohl der
new HashMap(Map)
als auch der erforderlich machteJsonSlurper
.@NonCPS def parseJsonText(String jsonText) { final slurper = new JsonSlurper() return new HashMap<>(slurper.parseText(jsonText)) }
Insgesamt würde ich empfehlen, nur das Plugin " Pipeline Utility Steps" zu verwenden , da es einen
readJSON
Schritt enthält , der entweder Dateien im Arbeitsbereich oder Text unterstützt.quelle
Could not find matching constructor for: java.util.HashMap(java.util.ArrayList)
. Die Dokumentation schlägt vor, eine Liste oder eine Karte auszuspucken. Wie konfigurieren Sie die Rückgabe einer Karte?Ich möchte eine der Antworten positiv bewerten: Ich würde empfehlen, nur das Pipeline Utility Steps-Plugin zu verwenden, da es einen readJSON-Schritt enthält, der entweder Dateien im Arbeitsbereich oder Text unterstützt: https://jenkins.io/doc/pipeline/steps / Pipeline-Utility-Schritte / # 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 }
Dies erfordert KEINE Whitelist oder zusätzliche Inhalte.
quelle
Eine etwas allgemeinere Form der Antwort von @mkobit, die das Dekodieren von Arrays und Karten ermöglichen würde, wäre:
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 }
HINWEIS: Beachten Sie, dass dadurch nur das LazyMap-Objekt der obersten Ebene in eine HashMap konvertiert wird. Verschachtelte LazyMap-Objekte sind weiterhin vorhanden und verursachen weiterhin Probleme mit Jenkins.
quelle
Dies ist die detaillierte Antwort, nach der gefragt wurde.
Das Unset hat bei mir funktioniert:
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
Ich habe die Werte aus der analysierten Antwort gelesen und wenn ich das Objekt nicht mehr benötige, habe ich es deaktiviert.
quelle
Die Art und Weise, wie das Pipeline-Plugin implementiert wurde, hat schwerwiegende Auswirkungen auf nicht trivialen Groovy-Code. Dieser Link erklärt, wie mögliche Probleme vermieden werden können: https://github.com/jenkinsci/pipeline-plugin/blob/master/TUTORIAL.md#serializing-local-variables
In Ihrem speziellen Fall würde ich in Betracht ziehen, anstelle von JSON-Objekten
@NonCPS
Anmerkungen zuslurpJSON
Map-of-Maps hinzuzufügen und diese zurückzugeben. Der Code sieht nicht nur sauberer aus, sondern ist auch effizienter, insbesondere wenn dieser JSON komplex ist.quelle
Gemäß den im Jenkins-Blog veröffentlichten Best Practices ( Best Practice für die Skalierbarkeit von Pipelines ) wird dringend empfohlen, Befehlszeilentools oder Skripte für diese Art von Arbeit zu verwenden:
Daher wird erklärt, warum die meisten auf dieser Seite vorgeschlagenen Lösungen standardmäßig von der Sandbox des Jenkins-Sicherheitsskript-Plugins blockiert werden.
Die Sprachphilosophie von Groovy ist Bash näher als Python oder Java. Es bedeutet auch, dass es nicht selbstverständlich ist, komplexe und schwere Arbeiten in Native Groovy auszuführen.
Vor diesem Hintergrund habe ich mich persönlich für Folgendes entschieden:
sh('jq <filters_and_options> file.json')
Weitere Hilfe finden Sie im jq-Handbuch und unter Objekte mit jq-Stapelüberlauf auswählen .
Dies ist etwas kontraintuitiv, da Groovy viele generische Methoden bereitstellt, die nicht in der Standard-Whitelist enthalten sind.
Wenn Sie sich für den Großteil Ihrer Arbeit ohnehin für die Verwendung der Sprache Groovy mit aktivierter und sauberer Sandbox entscheiden (was nicht einfach, weil nicht natürlich ist), empfehle ich Ihnen, die Whitelists für die Version Ihres Sicherheitsskript-Plugins zu überprüfen, um zu erfahren, welche Möglichkeiten Sie haben: Skript Whitelists für Sicherheits-Plugins
quelle
Mit der folgenden Funktion können Sie LazyMap in eine reguläre LinkedHashMap konvertieren (die Reihenfolge der Originaldaten bleibt erhalten):
LinkedHashMap nonLazyMap (Map lazyMap) { LinkedHashMap res = new LinkedHashMap() lazyMap.each { key, value -> if (value instanceof Map) { res.put (key, nonLazyMap(value)) } else if (value instanceof List) { res.put (key, value.stream().map { it instanceof Map ? nonLazyMap(it) : it }.collect(Collectors.toList())) } else { res.put (key, value) } } return res } ... LazyMap lazyMap = new JsonSlurper().parseText (jsonText) Map serializableMap = nonLazyMap(lazyMap);
oder verwenden Sie besser einen readJSON-Schritt, wie in früheren Kommentaren erwähnt:
Map serializableMap = readJSON text: jsonText
quelle
Die anderen Ideen in diesem Beitrag waren hilfreich, aber nicht alles, wonach ich gesucht habe - also habe ich die Teile extrahiert, die meinen Anforderungen entsprechen, und einige meiner eigenen Magix hinzugefügt ...
def jsonSlurpLaxWithoutSerializationTroubles(String jsonText) { return new JsonSlurperClassic().parseText( new JsonBuilder( new JsonSlurper() .setType(JsonParserType.LAX) .parseText(jsonText) ) .toString() ) }
Ja, wie ich in meinem eigenen Git-Commit des Codes festgestellt habe: "Wild ineffizienter, aber winziger Koeffizient: JSON-Slurp-Lösung" (mit dem ich für diesen Zweck einverstanden bin). Die Aspekte, die ich lösen musste:
java.io.NotSerializableException
Problem vollständig , auch wenn der JSON-Text verschachtelte Container definiert@NonCPS
)quelle
Noob Fehler meinerseits. Jemandes Code aus einem alten Pipeline-Plugin verschoben, Jenkins 1.6? auf einen Server mit den neuesten 2.x Jenkins.
Aus diesem Grund fehlgeschlagen: "java.io.NotSerializableException: groovy.lang.IntRange" Ich habe diesen Beitrag für den obigen Fehler mehrmals gelesen und gelesen. Realisiert: für (num in 1..numSlaves) {IntRange - nicht serialisierbarer Objekttyp.
In einfacher Form umgeschrieben: für (num = 1; num <= numSlaves; num ++)
Alles ist gut mit der Welt.
Ich benutze Java oder Groovy nicht sehr oft.
Danke Leute.
quelle
Ich habe einen einfacheren Weg in Off-Docs für die Jenkins-Pipeline gefunden
Arbeitsbeispiel
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) }
quelle