programing

Jenkins 파이프라인 직렬화 가능예외: groovy.json.internal.레이지 맵

telecom 2023. 3. 16. 21:08
반응형

Jenkins 파이프라인 직렬화 가능예외: groovy.json.internal.레이지 맵

해결: S의 아래 답변에 감사드립니다.리치몬드.저장된 모든 맵의 설정을 해제해야 했습니다.groovy.json.internal.LazyMap를 로 하는 것을 .envServers ★★★★★★★★★★★★★★★★★」object용용후 。

추가:이 오류를 찾는 사용자는 Jenkins 파이프라인 단계를 사용할 수 있습니다.readJSON대신 - 자세한 내용은 여기를 참조하십시오.


Jenkins Pipeline을 사용하여 json 문자열로 사용자에게 전달되는 입력을 받으려고 합니다.그러면 파이프라인은 slurper를 사용하여 이것을 해석하고, 나는 중요한 정보를 찾아낸다.그런 다음 이 정보를 사용하여 다른 작업 매개 변수와 병렬로 한 작업을 여러 번 실행합니다.

코드를 "## 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

JsonSlurperClassic★★★★★★ 。

Groovy 2.3 이후(주의: Jenkins 2.7.1은 Groovy 2.4.7 사용)JsonSlurperLazyMapHashMap '이렇게 하다', '이렇게 하다'가 JsonSlurper 스레드 안전성 및 시리얼화 불가.이로 인해 파이프라인 DSL 스크립트의 @NonDSL 함수 이외에서는 사용할 수 없게 됩니다.

다시 ,, 음, 음, 음, 음, 음, 음, 습으로 넘어갈 수도 있다.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"]
    ...
}    

ps를 승인해야 . 아직 승인이 필요합니다.JsonSlurperClassic부르기도 전에 말이야

나는 오늘 이것을 우연히 만났고, 어떤 폭력성을 통해 그것을 해결하는 방법과 잠재적인 이유를 알아냈다.

그 이유부터 시작하는 것이 가장 좋습니다.

Jenkins는 서버 재부팅을 통해 모든 작업을 중단, 일시 중지 및 재개할 수 있는 패러다임을 가지고 있습니다.이를 위해서는 파이프라인과 그 데이터가 완전히 직렬화 가능해야 합니다. 즉, 모든 상태를 저장할 수 있어야 합니다.마찬가지로 빌드 내 노드와 서브 작업 간의 글로벌 변수 상태를 시리얼화할 수 있어야 합니다.이러한 상황은 여러분과 제가 생각하고 있는 일입니다.또한 빌드 단계를 추가할 경우에만 발생하는 이유이기도 합니다.

어떤 이유로든 JSONObject는 기본적으로 직렬화할 수 없습니다.저는 자바 개발자가 아니기 때문에 이 주제에 대해 더 이상 말할 수 없습니다.그루비와 젠킨스에게 얼마나 적용되는지는 모르겠지만 이것을 어떻게 적절하게 고칠 수 있는지에 대한 많은 답변이 있다.자세한 것은, 이 투고를 참조해 주세요.

수정 방법:

방법을 알면 JSONObject를 어떻게든 직렬화할 수 있습니다.그렇지 않으면 해당 유형의 글로벌 변수가 없는지 확인하여 해결할 수 있습니다.

해 보세요.objectglobalvar가 않도록 합니다.

편집: @Sunvic이 코멘트에서 지적한 바와 같이 다음 솔루션은 JSON 어레이에서는 현재 상태로 작동하지 않습니다.

나는 이것을 사용하여 처리했다.JsonSlurper 다음 내는 거죠.HashMap태한한결결결결결 HashMapSerializable.

합니다.new HashMap(Map)JsonSlurper.

@NonCPS
def parseJsonText(String jsonText) {
  final slurper = new JsonSlurper()
  return new HashMap<>(slurper.parseText(jsonText))
}

전체적으로 Pipeline Utility Steps 플러그인은 워크스페이스 또는 텍스트 중 하나의 파일을 지원할 수 있는 단계가 있으므로 사용하는 것을 권장합니다.

답변 중 하나를 상향 투표하고 싶습니다.Pipeline Utility Steps 플러그인에는 readJ가 있으므로 사용하는 것을 권장합니다.워크스페이스 내의 파일 또는 텍스트 중 하나를 지원할 수 있는SON 스텝:https://jenkins.io/doc/pipeline/steps/pipeline-utility-steps/ #readjson-read-json-from-files-in-the-the-files-in-the-files-the-the-the-

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와 관련된 문제를 계속 일으킵니다.

다음 함수를 사용하여 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);

또는 readJ를 사용하는 것이 좋습니다.이전 코멘트에서 설명한 SON 단계:

Map serializableMap = readJSON text: jsonText

Jenkins 블로그에 게재된 베스트 프랙티스(Pipeline scalability best practice)에 따르면 이러한 종류의 작업에는 명령줄 도구 또는 스크립트를 사용할 을 강력히 권장합니다.

Gotcha: Groovy의 XmlSlurper와 JsonSlurper를 사용한 파이프라인 XML 또는 JSON 해석은 특히 피하십시오.명령줄 도구 또는 스크립트를 강력히 선호합니다.

i. Groovy 구현은 복잡하고 결과적으로 파이프라인 사용이 더 취약합니다.

ii. XmlSlurper 및 JsonSlurper는 파이프라인에서 높은 메모리 및 CPU 비용을 부담할 수 있습니다.

iii. xmllint 및 xmlstartlet은 xpath를 통한 XML 추출을 제공하는 명령줄 도구입니다.

iv. jq는 JSON에 동일한 기능을 제공합니다.

v. 이러한 추출 도구는 HTTP API에서 정보를 가져오기 위해 컬 또는 wget에 결합될 수 있습니다.

따라서 이 페이지에서 제안하는 대부분의 솔루션이 Jenkins 보안 스크립트 플러그인의 샌드박스에 의해 기본적으로 차단되는 이유를 설명합니다.

Groovy의 언어 철학은 Python이나 Java보다 Bash에 가깝다.또한, 그것은 그루비 원주민에서 복잡하고 힘든 일을 하는 것이 자연스럽지 않다는 것을 의미한다.

그래서 저는 개인적으로 다음과 같은 것을 사용하기로 했습니다.

sh('jq <filters_and_options> file.json')

자세한 내용은 jq 수동 및 jq stackoverflow 게시물이 있는 개체 선택을 참조하십시오.

Groovy는 기본 화이트리스트에 없는 많은 일반 메서드를 제공하므로 이 방법은 약간 직관적입니다.

대부분의 작업에 Groovy 언어를 사용하기로 결정한 경우(자연스럽지 않기 때문에 쉽지 않음) 보안 스크립트 플러그인의 화이트리스트를 확인하여 가능성을 알아보는 것이 좋습니다.스크립트 보안 플러그인 화이트리스트

파이프라인 플러그인이 구현된 방법은 사소한 Groovy 코드에 상당히 심각한 영향을 미칩니다.이 링크에서는 발생할 수 있는 문제를 회피하는 방법에 대해 설명합니다.https://github.com/jenkinsci/pipeline-plugin/blob/master/TUTORIAL.md#serializing-local-variables

당신의 구체적인 경우, 저는 추가하기를 고려하겠습니다.@NonCPS을 붙일 수 있습니다.slurpJSONJSON j j j j j j j j j j j j j j j j j j j j.코드가 깔끔해 보일 뿐만 아니라 특히 JSON이 복잡한 경우에는 더욱 효율적입니다.

이 투고에 있는 다른 아이디어들은 도움이 되었지만, 제가 찾던 모든 것은 아니었습니다.그래서 저는 제 요구에 맞는 부품을 추출하고 제 자신의 마술들을 추가했습니다.

def jsonSlurpLaxWithoutSerializationTroubles(String jsonText)
{
    return new JsonSlurperClassic().parseText(
        new JsonBuilder(
            new JsonSlurper()
                .setType(JsonParserType.LAX)
                .parseText(jsonText)
        )
        .toString()
    )
}

네, 제가 코드의 git 커밋에서 언급했듯이, "야생적으로 부족하지만 작은 계수: JSON slurp solution"입니다(이 목적을 위해 저는 괜찮습니다).해결해야 할 점은 다음과 같습니다.

  1. 에서 완전히 벗어나다java.io.NotSerializableExceptionJSON 텍스트가 중첩된 컨테이너를 정의하는 경우에도 문제
  2. 맵 컨테이너와 어레이 컨테이너 모두에 대응
  3. LAX 해석 지원(내 상황에 가장 중요한 부분)
  4. 구현이 용이함(어색하게 중첩된 컨스트럭터도 필요 없음)@NonCPS)

내 실수야.이전 파이프라인 플러그인 jenkins 1.6에서 최신 2.x jenkins를 실행하는 서버로 일부 코드를 이동했습니다.

다음 이유로 실패했습니다.java.io시리얼화 불가예외: groovy.lang.IntRange"는 위의 오류로 인해 이 글을 여러 번 읽고 또 읽었습니다.실현: (num in 1..numSlaves) {IntRange - 직렬화할 수 없는 개체 유형.

간단한 형식으로 다시 작성: (num = 1, num <= numSlaves, num++)

세상은 다 좋다.

나는 자바나 그루비를 자주 사용하지 않는다.

여러분 감사합니다.

젠킨스 파이프라인을 위해 의사로부터 더 쉬운 방법을 찾아냈어

작업 예

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 closures 또는 closures에 의존하는 구문을 사용할 수 없기 때문에 목록에 .collectries를 사용하여 스텝을 결과 엔트리의 값으로 생성하는 Groovy 표준을 >에서 수행할 수 없습니다.For loops(String s: strings)에는 표준> Java 구문을 사용할 수 없습니다.루프에는 구식의 카운터 베이스를 사용할 필요가 있습니다.

JsonSlupurClassic을 사용할 수 없는 경우 LazyMap을 LinkedHashMap으로 변환하는 간단한 방법을 참조하십시오.

def deserialize(String jsonStr) {
    def obj = new JsonSlurper().parseText(jsonStr)
    if (obj instanceof Map) {
        return cloneMap(obj)
    } else if (obj instanceof List) {
        return cloneList(obj)
    } else {
        return obj
    }
    return
}

def cloneMap(Map map) {
    def res = [:]
    map.each {e ->
        def key = e.getKey()
        def value = e.getValue()
        if (value instanceof Map) {
            res.put(key, cloneMap(value))
        } else if (value instanceof List) {
            res.put(key, cloneList(value))
        } else {
            res.put(key, value)
        }
    }
    return res
}

def cloneList(List list) {
    def res = []
    list.each {i ->
        if (i instanceof List) {
            res.add(cloneList(i))
        } else if (i instanceof Map) {
            res.add(cloneMap(i))
        } else {
            res.add(i)
        }
    }
    return res
}

언급URL : https://stackoverflow.com/questions/37864542/jenkins-pipeline-notserializableexception-groovy-json-internal-lazymap

반응형