Rozwiązany : Dzięki poniższej odpowiedzi od S.Richmond. Musiałem usunąć wszystkie zapisane mapy groovy.json.internal.LazyMap
typu, który oznaczał anulowanie zmiennych envServers
i object
po ich użyciu.
Dodatkowe : osoby szukające tego błędu mogą readJSON
zamiast tego skorzystać z kroku potoku Jenkins - więcej informacji znajdziesz tutaj .
Próbuję użyć Jenkins Pipeline, aby pobrać dane wejściowe od użytkownika, które są przekazywane do zadania jako ciąg JSON. Następnie potok analizuje to za pomocą slurpera i wybieram ważne informacje. Następnie wykorzysta te informacje do wielokrotnego uruchomienia 1 zadania równolegle z różnymi parametrami zadania.
Dopóki nie dodam kodu poniżej, "## Error when below here is added"
skrypt będzie działał poprawnie. Nawet kod poniżej tego punktu będzie działał samodzielnie. Ale po połączeniu otrzymuję poniższy błąd.
Powinienem zauważyć, że wyzwalane zadanie jest wywoływane i działa pomyślnie, ale pojawia się poniższy błąd i kończy się niepowodzeniem głównego zadania. Z tego powodu główne zadanie nie czeka na powrót wyzwolonego zadania. I mogłoby try / catch wokół build job:
jednak chcę Głównym zadaniem czekać na wyzwalane zadania do końca.
Czy ktoś może tu pomóc? Jeśli potrzebujesz więcej informacji, daj mi znać.
Twoje zdrowie
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']]
}
Błąd:
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
źródło
Odpowiedzi:
Sam napotkałem to dzisiaj i dzięki pewnym brutalnym siłom odkryłem, jak to rozwiązać i potencjalnie dlaczego.
Prawdopodobnie najlepiej zacząć od tego, dlaczego:
Jenkins ma paradygmat, w którym wszystkie zadania można przerwać, wstrzymać i wznowić poprzez ponowne uruchomienie serwera. Aby to osiągnąć, potok i jego dane muszą być w pełni serializowalne - tj. Musi mieć możliwość zapisania stanu wszystkiego. Podobnie musi być w stanie serializować stan zmiennych globalnych między węzłami i zadaniami podrzędnymi w kompilacji, co moim zdaniem dzieje się dla ciebie i dla mnie i dlaczego występuje tylko wtedy, gdy dodasz ten dodatkowy krok kompilacji.
Z jakiegoś powodu JSONObject nie jest domyślnie serializowalny. Nie jestem programistą Java, więc nie mogę niestety powiedzieć nic więcej na ten temat. Istnieje wiele odpowiedzi na temat tego, jak można to naprawić, chociaż nie wiem, jak mają one zastosowanie do Groovy'ego i Jenkinsa. Zobacz ten post, aby uzyskać więcej informacji.
Jak to naprawić:
Jeśli wiesz, jak to zrobić, możesz w jakiś sposób uczynić JSONObject możliwym do serializacji. W przeciwnym razie możesz rozwiązać ten problem, upewniając się, że żadne zmienne globalne nie są tego typu.
Spróbuj cofnąć ustawienie
object
zmiennej lub zawinąć ją w metodę, aby jej zakres nie był globalny.źródło
Użyj
JsonSlurperClassic
zamiast tego.Od Groovy 2.3 ( uwaga: Jenkins 2.7.1 używa Groovy 2.4.7 )
JsonSlurper
zwracaLazyMap
zamiastHashMap
. To sprawia, że nowa implementacjaJsonSlurper
nie jest bezpieczna dla wątków i nie jest możliwa do serializacji. To sprawia, że jest bezużyteczny poza funkcjami @NonDSL w skryptach DSL potoku.Możesz jednak wrócić do tego,
groovy.json.JsonSlurperClassic
który obsługuje stare zachowanie i może być bezpiecznie używany w skryptach potoku.Przykład
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. Nadal będziesz musiał zatwierdzić,
JsonSlurperClassic
zanim będzie można go wywołać.źródło
JsonSlurperClassic
?hudson.remoting.ProxyException: org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed: Script1.groovy: 24: unable to resolve class groovy.json.JsonSlurperClassic
EDYCJA: Jak wskazał @Sunvic w komentarzach, poniższe rozwiązanie nie działa tak, jak jest w przypadku tablic JSON.
Poradziłem sobie z tym, używając,
JsonSlurper
a następnie tworząc noweHashMap
z leniwych wyników.HashMap
jestSerializable
.Uważam, że wymagało to białej listy zarówno
new HashMap(Map)
platformyJsonSlurper
.@NonCPS def parseJsonText(String jsonText) { final slurper = new JsonSlurper() return new HashMap<>(slurper.parseText(jsonText)) }
Ogólnie polecam użycie wtyczki Pipeline Utility Steps , ponieważ ma ona
readJSON
krok, który może obsługiwać pliki w obszarze roboczym lub tekst.źródło
Could not find matching constructor for: java.util.HashMap(java.util.ArrayList)
. Dokumentacja sugeruje, że powinien wypluć listę lub mapę - jak skonfigurować zwracanie mapy?Chcę zagłosować za jedną z odpowiedzi: polecam po prostu użycie wtyczki Pipeline Utility Steps, ponieważ ma krok readJSON, który może obsługiwać pliki w obszarze roboczym lub tekst: 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 }
NIE wymaga to żadnej białej listy ani dodatkowych rzeczy.
źródło
Oto szczegółowa odpowiedź, o którą poproszono.
Niespokojony działał dla mnie:
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
Odczytuję wartości z przeanalizowanej odpowiedzi i kiedy nie potrzebuję już obiektu, wyłączam go.
źródło
Nieco bardziej uogólniona forma odpowiedzi z @mkobit, która pozwoliłaby na dekodowanie tablic, a także map, to:
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 }
UWAGA: Należy pamiętać, że spowoduje to tylko konwersję obiektu LazyMap najwyższego poziomu na HashMap. Wszelkie zagnieżdżone obiekty LazyMap nadal tam będą i nadal będą powodować problemy z Jenkins.
źródło
Sposób implementacji wtyczki potoku ma dość poważne konsekwencje dla nietrywialnego kodu Groovy'ego. Ten link wyjaśnia, jak uniknąć możliwych problemów: https://github.com/jenkinsci/pipeline-plugin/blob/master/TUTORIAL.md#serializing-local-variables
W twoim konkretnym przypadku rozważyłbym dodanie
@NonCPS
adnotacjislurpJSON
i zwrócenie mapy map zamiast obiektu JSON. Nie tylko kod wygląda na bardziej przejrzysty, ale jest też bardziej wydajny, zwłaszcza jeśli ten kod JSON jest złożony.źródło
Zgodnie z najlepszymi praktykami opublikowanymi na blogu Jenkins ( najlepsze praktyki dotyczące skalowalności potoków ), zdecydowanie zaleca się używanie narzędzi wiersza poleceń lub skryptów do tego rodzaju pracy:
W ten sposób wyjaśnia, dlaczego większość rozwiązań proponowanych na tej stronie jest domyślnie blokowana przez piaskownicę wtyczki skryptów bezpieczeństwa Jenkins.
Filozofia języka Groovy jest bliższa Bashowi niż Pythonowi czy Javie. Oznacza to również, że wykonywanie skomplikowanej i ciężkiej pracy w rodzimym Groovym nie jest naturalne.
Biorąc to pod uwagę, osobiście zdecydowałem się skorzystać z:
sh('jq <filters_and_options> file.json')
Aby uzyskać dodatkową pomoc, zobacz Podręcznik jq i Wybierz obiekty z postem jq stackoverflow .
Jest to nieco sprzeczne z intuicją, ponieważ Groovy zapewnia wiele ogólnych metod, których nie ma na domyślnej białej liście.
Jeśli mimo wszystko zdecydujesz się używać języka Groovy do większości swojej pracy, z włączoną piaskownicą i czystą (co nie jest łatwe, ponieważ nie jest naturalne), radzę sprawdzić białe listy wersji wtyczki skryptu bezpieczeństwa, aby dowiedzieć się, jakie masz możliwości: Skrypt białe listy wtyczek bezpieczeństwa
źródło
Możesz użyć następującej funkcji, aby przekonwertować LazyMap na zwykły LinkedHashMap (zachowa kolejność oryginalnych danych):
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);
lub lepiej użyj kroku readJSON, jak zauważono we wcześniejszych komentarzach:
Map serializableMap = readJSON text: jsonText
źródło
Inne pomysły w tym poście były pomocne, ale nie wszystko, czego szukałem - więc wyodrębniłem części, które pasowały do moich potrzeb i dodałem trochę mojego własnego magixa ...
def jsonSlurpLaxWithoutSerializationTroubles(String jsonText) { return new JsonSlurperClassic().parseText( new JsonBuilder( new JsonSlurper() .setType(JsonParserType.LAX) .parseText(jsonText) ) .toString() ) }
Tak, jak zauważyłem w moim własnym git commit w kodzie, „Dziwnie nieefektywny, ale mały współczynnik: rozwiązanie slurp JSON” (z którym jestem w porządku w tym celu). Aspekty, które musiałem rozwiązać:
java.io.NotSerializableException
problemu, nawet jeśli tekst JSON definiuje zagnieżdżone kontenery@NonCPS
)źródło
Noob błąd z mojej strony. Przeniesiono czyjś kod ze starej wtyczki potoku, jenkins 1.6? na serwer z najnowszą wersją 2.x jenkins.
Niepowodzenie z tego powodu: „java.io.NotSerializableException: groovy.lang.IntRange” Wielokrotnie czytałem i czytałem ten post z powodu powyższego błędu. Zrealizowane: for (num in 1..numSlaves) {IntRange - nie serializowalny typ obiektu.
Przepisano w prostej formie: for (num = 1; num <= numSlaves; num ++)
Wszystko jest dobrze ze światem.
Nie używam bardzo często Java ani Groovy.
Dzięki chłopaki.
źródło
Znalazłem łatwiejszy sposób w dokumentach poza rurociągiem Jenkinsa
Przykład pracy
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) }
źródło