Решено : благодаря приведенному ниже ответу от С.Ричмонда. Мне нужно было отключить все сохраненные карты этого groovy.json.internal.LazyMap
типа, что означало обнуление переменных envServers
и object
после использования.
Дополнительно : люди, ищущие эту ошибку, могут быть заинтересованы в использовании readJSON
вместо этого шага конвейера Jenkins - дополнительную информацию можно найти здесь .
Я пытаюсь использовать Jenkins Pipeline для ввода данных от пользователя, которые передаются в задание в виде строки json. Затем Pipeline разбирает это с помощью slurper, и я выбираю важную информацию. Затем он будет использовать эту информацию для выполнения 1 задания несколько раз параллельно с разными параметрами задания.
Пока я не добавлю код ниже, "## Error when below here is added"
скрипт будет работать нормально. Даже код ниже этой точки будет работать сам по себе. Но при объединении я получаю ошибку ниже.
Я должен отметить, что запущенное задание вызывается и выполняется успешно, но возникает ошибка, приведенная ниже, и не выполняется основное задание. По этой причине основное задание не ждет возврата запущенного задания. Я мог бы попробовать / поймать, build 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
источник
Ответы:
Я сам столкнулся с этим сегодня и с помощью некоторой грубой силы выяснил, как решить эту проблему и, возможно, почему.
Наверное, лучше всего начать с того, почему:
У Дженкинса есть парадигма, в которой все задания могут быть прерваны, приостановлены и возобновлены посредством перезагрузки сервера. Для этого конвейер и его данные должны быть полностью сериализуемыми - IE должен иметь возможность сохранять состояние всего. Точно так же он должен иметь возможность сериализовать состояние глобальных переменных между узлами и подзаданиями в сборке, что, как я думаю, происходит для вас и меня, и почему это происходит только в том случае, если вы добавляете этот дополнительный шаг сборки.
По какой-то причине JSONObject по умолчанию не сериализуемы. Я не разработчик Java, поэтому, к сожалению, не могу сказать больше по этой теме. Существует множество ответов о том, как это исправить, хотя я не знаю, насколько они применимы к Groovy и Jenkins. См. Этот пост для получения дополнительной информации.
Как это исправить:
Если вы знаете как, вы можете каким-то образом сделать JSONObject сериализуемым. В противном случае вы можете решить эту проблему, убедившись, что глобальные переменные не относятся к этому типу.
Попробуйте отключить
object
var или обернуть его в метод, чтобы его область видимости не была глобальной.источник
JsonSlurperClassic
Вместо этого используйте .Поскольку Groovy 2.3 ( примечание: Jenkins 2.7.1 использует Groovy 2.4.7 )
JsonSlurper
возвращаетLazyMap
вместоHashMap
. Это делает новую реализациюJsonSlurper
не потокобезопасной и не сериализуемой. Это делает его непригодным для использования вне функций @NonDSL в сценариях конвейерного DSL.Однако вы можете вернуться к тому,
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
прежде чем его можно будет вызвать.источник
JsonSlurperClassic
?hudson.remoting.ProxyException: org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed: Script1.groovy: 24: unable to resolve class groovy.json.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
шаг, который может поддерживать файлы в рабочей области или текст.источник
Could not find matching constructor for: java.util.HashMap(java.util.ArrayList)
. Документация предполагает, что он должен выдавать список или карту - как вы сконфигурируете возврат карты?Я хочу проголосовать за один из ответов: я бы рекомендовал просто использовать плагин Pipeline Utility Steps, поскольку у него есть шаг readJSON, который может поддерживать файлы в рабочей области или текст: 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 }
Это НЕ требует внесения в белый список или дополнительных материалов.
источник
Это подробный ответ, который был запрошен.
У меня отключение сработало:
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.
источник
Способ реализации плагина конвейера имеет довольно серьезные последствия для нетривиального кода Groovy. Эта ссылка объясняет, как избежать возможных проблем: https://github.com/jenkinsci/pipeline-plugin/blob/master/TUTORIAL.md#serializing-local-variables
В вашем конкретном случае я бы подумал о добавлении
@NonCPS
аннотацииslurpJSON
и возврате карты карт вместо объекта JSON. Код не только выглядит чище, но и более эффективен, особенно если этот JSON сложный.источник
В соответствии с лучшими практиками, опубликованными в блоге Jenkins ( передовая практика масштабируемости конвейера ), настоятельно рекомендуется использовать инструменты командной строки или сценарии для такого рода работы:
Таким образом, это объясняет, почему большинство решений, предлагаемых на этой странице, по умолчанию блокируются песочницей плагина сценария безопасности Jenkins.
Философия языка Groovy ближе к Bash, чем к Python или Java. Кроме того, это означает, что выполнять сложную и тяжелую работу в родном Groovy неестественно.
Учитывая это, я лично решил использовать следующее:
sh('jq <filters_and_options> file.json')
См. Jq Manual и Select objects with jq stackoverflow post для получения дополнительной помощи.
Это немного противоречит интуиции, поскольку Groovy предоставляет множество универсальных методов, которых нет в белом списке по умолчанию.
Если вы решили использовать Groovy язык в любом случае большую часть своей работы, с песочницей включена и чистый (не легко , потому что не естественно), я рекомендую вам проверить белые списки для версии вашей безопасности скрипта плагина , чтобы знать , что ваши возможности: Script белые списки плагинов безопасности
источник
Вы можете использовать следующую функцию для преобразования LazyMap в обычную LinkedHashMap (она сохранит порядок исходных данных):
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);
или лучше использовать шаг readJSON, как отмечалось в предыдущих комментариях:
Map serializableMap = readJSON text: jsonText
источник
Другие идеи в этом посте были полезны, но не совсем все, что я искал, поэтому я извлек части, которые соответствовали моим потребностям, и добавил некоторые из моих собственных магических средств ...
def jsonSlurpLaxWithoutSerializationTroubles(String jsonText) { return new JsonSlurperClassic().parseText( new JsonBuilder( new JsonSlurper() .setType(JsonParserType.LAX) .parseText(jsonText) ) .toString() ) }
Да, как я отмечал в моем собственном git -коммите кода: «Дико неэффективный, но крошечный коэффициент: решение для JSON slurp» (с которым я согласен для этой цели). Аспекты, которые мне нужно было решить:
java.io.NotSerializableException
проблемы, даже если текст JSON определяет вложенные контейнеры@NonCPS
)источник
Ошибка нуба с моей стороны. Перенесли чей-то код из старого плагина конвейера, jenkins 1.6? на сервер с последней версией 2.x jenkins.
Ошибка по этой причине: "java.io.NotSerializableException: groovy.lang.IntRange" Я продолжал читать и читать этот пост несколько раз из-за указанной выше ошибки. Реализовано: for (num in 1..numSlaves) {IntRange - несериализуемый тип объекта.
Переписал в простой форме: for (num = 1; num <= numSlaves; num ++)
С миром все хорошо.
Я не очень часто использую java или groovy.
Спасибо ребята.
источник
Я нашел более простой способ отключить документы для конвейера 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) }
источник