在 Jenkins Pipeline 中,是否可以将 env 变量设置为跨阶段可见的代理,而无需更改全局 env 对象?

问题描述 投票:0回答:1

在 Jenkins 管道中,全局

env
对象可用于设置大多数执行上下文中的环境变量,并且这些变量在所有代理之间都是可见的,当并行节点执行发生时,可能会产生意外的竞争条件。我想做的是配置仅在当前运行代理的执行上下文中设置的变量,而不更改其他代理中
env
对象返回的属性,如下所示:

pipeline {
    agent none

    stages {
        stage('Stage1AgentA') {
            agent {
                label "AgentA"
            }
            steps {
               setAgentEnvironmentVariable('MY_VAR', 'MyValue')
               sh 'echo "MY_VAR: $MY_VAR"'      // Should print "MyValue"
               echo "env.MY_VAR: ${env.MY_VAR}" // Should print "MyValue"
            }
        }
        stage('Stage2AgentA') {
            agent {
                label "AgentA"
            }
            steps {
               sh 'echo "MY_VAR: $MY_VAR"'      // Should print "MyValue"
               echo "env.MY_VAR: ${env.MY_VAR}" // Should print "MyValue"
            }
        }
        stage('StageAgentB') {
            agent {
                label "AgentB"
            }
            steps {
               sh 'echo "MY_VAR: $MY_VAR"'      // Should print ""
               echo "env.MY_VAR: ${env.MY_VAR}" // Should print "null"
            }
        }
    }
}

def setAgentEnvironmentVariable(String name, String value)
{
    // Any restricted API is admissible
    // ..
}

我已经知道针对此用例的推荐解决方案将使用

withEnv
基本步骤,但我想使用受限的 Jenkins API 制定更高级的解决方案。

jenkins environment-variables jenkins-pipeline jenkins-agent
1个回答
0
投票

使用 Jenkins 受限 API,可以创建一个实用方法,仅在代理执行上下文中添加变量。该方法可以作为 groovy 代码存储在 Jenkins 共享库中,如下所示:

import hudson.EnvVars;
import hudson.model.Run;
import hudson.model.EnvironmentContributingAction;
import org.jenkinsci.plugins.workflow.cps.EnvActionImpl;

/** Set the given environment variable in the executing agent,
 * without altering the global pipeline environment (That is "env.VARNAME").
 * The variables set this way are persistent across stages. Throws when
 * executed in the controller
 */
def setAgentEnvironmentVariable(String name, String value)
{
    // Determine if I'm running in the controller
    def nodeName = env.NODE_NAME;
    if (nodeName == null || nodeName == 'master' || nodeName == 'built-in')
        throw new Exception("The method is not supported when running in a controller built-in node");

    def action = currentBuild.rawBuild.getAction(NodeVariablesAction.class);
    if (action == null)
    {
        action = new NodeVariablesAction();
        currentBuild.rawBuild.addAction(action);
    }

    action.AddNodeVariable(nodeName, name, value);
}

class NodeVariablesAction implements EnvironmentContributingAction
{
    private HashMap<String, ArrayList<KeyValuePair>> nodeVariables;
    private boolean isSetting;

    public NodeVariablesAction()
    {
        nodeVariables = new HashMap<String, ArrayList<KeyValuePair>>();
    }

    public void AddNodeVariable(String nodeName, String key, String value)
    {
        def variables = nodeVariables.get(nodeName);
        if (variables == null)
        {
            variables = new ArrayList<KeyValuePair>();
            nodeVariables.put(nodeName, variables);
        }

        variables.add(new KeyValuePair(key, value));
    }

    @NonCPS
    public void buildEnvironment(Run build, EnvVars envVars)
    {
        if (isSetting)
            return;

        // Retrieve the global 'env' object (https://www.jenkins.io/doc/book/pipeline/getting-started/#global-variable-reference)
        // from the build and get the 'NODE_NAME' from there. This is the only working
        // way found, so far. NOTE: Avoid a stack overflow by setting a sentinel that
        // will return from this method in subsequent recursions
        isSetting = true;
        def env = EnvActionImpl.forRun(build);
        def currentNodeName = env.getProperty('NODE_NAME');
        isSetting = false;

        def variables = nodeVariables.get(currentNodeName);
        if (variables == null)
            return;

        for (def variable in variables)
            envVars.put(variable.key, variable.value);
    }

    public String getDisplayName()  { return "VariableInjectionAction"; }
    public String getIconFileName() { return null; }
    public String getUrlName()      { return null; }
}

class KeyValuePair
{
    public KeyValuePair(String key, String value)
    {
        this.key = key;
        this.value = value;
    }

    public final String key;
    public final String value;
}
© www.soinside.com 2019 - 2024. All rights reserved.