From 695761bd4ca8cde49bb166f2d43507b296fc3cce Mon Sep 17 00:00:00 2001 From: Pedro Bueno Yerbes Date: Tue, 1 Apr 2025 17:33:14 +0200 Subject: [PATCH 01/10] Using configurator for global props --- .../plugins/casc/GlobalNodePropertiesTest.yml | 2 + .../GlobalNodePropertiesConfigurator.java | 93 +++++++++++++++++++ 2 files changed, 95 insertions(+) create mode 100644 plugin/src/main/java/io/jenkins/plugins/casc/core/GlobalNodePropertiesConfigurator.java diff --git a/integrations/src/test/resources/io/jenkins/plugins/casc/GlobalNodePropertiesTest.yml b/integrations/src/test/resources/io/jenkins/plugins/casc/GlobalNodePropertiesTest.yml index c9eeb3aaf9..411f6908ea 100644 --- a/integrations/src/test/resources/io/jenkins/plugins/casc/GlobalNodePropertiesTest.yml +++ b/integrations/src/test/resources/io/jenkins/plugins/casc/GlobalNodePropertiesTest.yml @@ -4,3 +4,5 @@ jenkins: env: - key: FOO value: BAR + - key: FOO2 + value: "" diff --git a/plugin/src/main/java/io/jenkins/plugins/casc/core/GlobalNodePropertiesConfigurator.java b/plugin/src/main/java/io/jenkins/plugins/casc/core/GlobalNodePropertiesConfigurator.java new file mode 100644 index 0000000000..b8df68732a --- /dev/null +++ b/plugin/src/main/java/io/jenkins/plugins/casc/core/GlobalNodePropertiesConfigurator.java @@ -0,0 +1,93 @@ +package io.jenkins.plugins.casc.core; + +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; +//import hudson.EnvVars; +import hudson.EnvVars; +import hudson.Extension; +import hudson.slaves.EnvironmentVariablesNodeProperty; +import hudson.slaves.EnvironmentVariablesNodeProperty.DescriptorImpl; +import hudson.slaves.EnvironmentVariablesNodeProperty.Entry; +import hudson.util.DescribableList; +import hudson.util.PersistedList; +import io.jenkins.plugins.casc.Attribute; +import io.jenkins.plugins.casc.BaseConfigurator; +import io.jenkins.plugins.casc.ConfigurationContext; +import io.jenkins.plugins.casc.ConfiguratorException; +import io.jenkins.plugins.casc.impl.attributes.MultivaluedAttribute; +import io.jenkins.plugins.casc.model.CNode; +import io.jenkins.plugins.casc.model.Mapping; +import io.jenkins.plugins.casc.model.Sequence; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import org.apache.commons.lang.StringUtils; + +@Extension +public class GlobalNodePropertiesConfigurator extends BaseConfigurator { + + @NonNull + @Override + public String getName() { + return "globalNodeProperties"; + } + + @Override + protected EnvironmentVariablesNodeProperty instance(Mapping mapping, + ConfigurationContext context) throws ConfiguratorException { + List vars = getVarsAsList(mapping); + return new EnvironmentVariablesNodeProperty(vars); + } + + @Override + public Class getTarget() { + return EnvironmentVariablesNodeProperty.class; + } + + @NonNull + @Override + public Set> describe() { + Set> attrs = super.describe(); + attrs.add(new MultivaluedAttribute("env", Entry.class)); + return attrs; + } + + @NonNull + @Override + public EnvironmentVariablesNodeProperty configure(CNode c, ConfigurationContext context) throws ConfiguratorException { + Mapping mapping = c.asMapping(); + List variables = getVarsAsList(mapping); + return new EnvironmentVariablesNodeProperty(variables); + } + + @CheckForNull + public CNode describe(EnvironmentVariablesNodeProperty instance, ConfigurationContext context) throws Exception { + Mapping mapping = new Mapping(); + for (Attribute attribute : getAttributes()) { + CNode value = attribute.describe(instance, context); + // Clean empty vars + Sequence values = new Sequence(); + value.asSequence().stream().filter(entry -> StringUtils.isNotBlank(entry.asMapping().get("value").toString())) + .forEach(values::add); + if (value != null) { + mapping.put(attribute.getName(), values); + } + } + return mapping; + } + + + private List getVarsAsList(Mapping m) { + List result = new ArrayList<>(); + if (m.get("env") != null){ + result = m.get("env").asSequence().stream() + .map(pair -> new Entry(pair.asMapping().get("key").asScalar().getValue(), + pair.asMapping().get("value").asScalar().getValue())) + .toList(); + } + return result; + } +} From 3f5d776060bbd85f2d9f17cc05cb4af8f3ed038b Mon Sep 17 00:00:00 2001 From: Francisco Javier Fernandez Gonzalez Date: Wed, 2 Apr 2025 12:32:26 +0200 Subject: [PATCH 02/10] Accept EnvVars wihtout value --- .../casc/GlobalNodePropertiesTest.java | 15 ++++++- .../plugins/casc/GlobalNodePropertiesTest.yml | 1 + .../casc/GlobalNodePropertiesTestExpected.yml | 2 + .../GlobalNodePropertiesConfigurator.java | 44 +++++++++---------- 4 files changed, 38 insertions(+), 24 deletions(-) diff --git a/integrations/src/test/java/io/jenkins/plugins/casc/GlobalNodePropertiesTest.java b/integrations/src/test/java/io/jenkins/plugins/casc/GlobalNodePropertiesTest.java index ba91c2edea..5c5ad20a4d 100644 --- a/integrations/src/test/java/io/jenkins/plugins/casc/GlobalNodePropertiesTest.java +++ b/integrations/src/test/java/io/jenkins/plugins/casc/GlobalNodePropertiesTest.java @@ -15,7 +15,9 @@ import io.jenkins.plugins.casc.misc.JenkinsConfiguredWithCodeRule; import io.jenkins.plugins.casc.misc.junit.jupiter.WithJenkinsConfiguredWithCode; import io.jenkins.plugins.casc.model.CNode; +import java.util.Iterator; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; import jenkins.model.Jenkins; import org.junit.jupiter.api.Test; @@ -33,11 +35,20 @@ void configure(JenkinsConfiguredWithCodeRule j) { Set> entries = ((EnvironmentVariablesNodeProperty) nodeProperties.get(0)) .getEnvVars() .entrySet(); - assertEquals(1, entries.size()); + assertEquals(3, entries.size()); - Map.Entry envVar = entries.iterator().next(); + Iterator> iterator = entries.iterator(); + Map.Entry envVar = iterator.next(); assertEquals("FOO", envVar.getKey()); assertEquals("BAR", envVar.getValue()); + + envVar = iterator.next(); + assertEquals("FOO2", envVar.getKey()); + assertEquals("", envVar.getValue()); + + envVar = iterator.next(); + assertEquals("FOO3", envVar.getKey()); + assertEquals("", envVar.getValue()); } @Test diff --git a/integrations/src/test/resources/io/jenkins/plugins/casc/GlobalNodePropertiesTest.yml b/integrations/src/test/resources/io/jenkins/plugins/casc/GlobalNodePropertiesTest.yml index 411f6908ea..e76e751e4f 100644 --- a/integrations/src/test/resources/io/jenkins/plugins/casc/GlobalNodePropertiesTest.yml +++ b/integrations/src/test/resources/io/jenkins/plugins/casc/GlobalNodePropertiesTest.yml @@ -6,3 +6,4 @@ jenkins: value: BAR - key: FOO2 value: "" + - key: FOO3 \ No newline at end of file diff --git a/integrations/src/test/resources/io/jenkins/plugins/casc/GlobalNodePropertiesTestExpected.yml b/integrations/src/test/resources/io/jenkins/plugins/casc/GlobalNodePropertiesTestExpected.yml index e02c4d039a..bf79b322c9 100644 --- a/integrations/src/test/resources/io/jenkins/plugins/casc/GlobalNodePropertiesTestExpected.yml +++ b/integrations/src/test/resources/io/jenkins/plugins/casc/GlobalNodePropertiesTestExpected.yml @@ -2,3 +2,5 @@ env: - key: "FOO" value: "BAR" + - key: "FOO2" + - key: "FOO3" diff --git a/plugin/src/main/java/io/jenkins/plugins/casc/core/GlobalNodePropertiesConfigurator.java b/plugin/src/main/java/io/jenkins/plugins/casc/core/GlobalNodePropertiesConfigurator.java index b8df68732a..35033dbcbc 100644 --- a/plugin/src/main/java/io/jenkins/plugins/casc/core/GlobalNodePropertiesConfigurator.java +++ b/plugin/src/main/java/io/jenkins/plugins/casc/core/GlobalNodePropertiesConfigurator.java @@ -2,14 +2,9 @@ import edu.umd.cs.findbugs.annotations.CheckForNull; import edu.umd.cs.findbugs.annotations.NonNull; -//import hudson.EnvVars; -import hudson.EnvVars; import hudson.Extension; import hudson.slaves.EnvironmentVariablesNodeProperty; -import hudson.slaves.EnvironmentVariablesNodeProperty.DescriptorImpl; import hudson.slaves.EnvironmentVariablesNodeProperty.Entry; -import hudson.util.DescribableList; -import hudson.util.PersistedList; import io.jenkins.plugins.casc.Attribute; import io.jenkins.plugins.casc.BaseConfigurator; import io.jenkins.plugins.casc.ConfigurationContext; @@ -19,12 +14,8 @@ import io.jenkins.plugins.casc.model.Mapping; import io.jenkins.plugins.casc.model.Sequence; import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; import java.util.List; import java.util.Set; -import java.util.stream.Collectors; -import org.apache.commons.lang.StringUtils; @Extension public class GlobalNodePropertiesConfigurator extends BaseConfigurator { @@ -36,8 +27,8 @@ public String getName() { } @Override - protected EnvironmentVariablesNodeProperty instance(Mapping mapping, - ConfigurationContext context) throws ConfiguratorException { + protected EnvironmentVariablesNodeProperty instance(Mapping mapping, ConfigurationContext context) + throws ConfiguratorException { List vars = getVarsAsList(mapping); return new EnvironmentVariablesNodeProperty(vars); } @@ -51,13 +42,15 @@ public Class getTarget() { @Override public Set> describe() { Set> attrs = super.describe(); - attrs.add(new MultivaluedAttribute("env", Entry.class)); + attrs.add(new MultivaluedAttribute( + "env", Entry.class)); return attrs; } @NonNull @Override - public EnvironmentVariablesNodeProperty configure(CNode c, ConfigurationContext context) throws ConfiguratorException { + public EnvironmentVariablesNodeProperty configure(CNode c, ConfigurationContext context) + throws ConfiguratorException { Mapping mapping = c.asMapping(); List variables = getVarsAsList(mapping); return new EnvironmentVariablesNodeProperty(variables); @@ -68,25 +61,32 @@ public CNode describe(EnvironmentVariablesNodeProperty instance, ConfigurationCo Mapping mapping = new Mapping(); for (Attribute attribute : getAttributes()) { CNode value = attribute.describe(instance, context); - // Clean empty vars - Sequence values = new Sequence(); - value.asSequence().stream().filter(entry -> StringUtils.isNotBlank(entry.asMapping().get("value").toString())) - .forEach(values::add); if (value != null) { + Sequence values = new Sequence(); + value.asSequence().stream().forEach(values::add); mapping.put(attribute.getName(), values); } } return mapping; } - private List getVarsAsList(Mapping m) { List result = new ArrayList<>(); - if (m.get("env") != null){ + if (m.get("env") != null) { result = m.get("env").asSequence().stream() - .map(pair -> new Entry(pair.asMapping().get("key").asScalar().getValue(), - pair.asMapping().get("value").asScalar().getValue())) - .toList(); + .map(pair -> { + if (pair.asMapping().get("key") == null) { + return null; + } + final String key = + pair.asMapping().get("key").asScalar().getValue(); + final String value = pair.asMapping().get("value") == null + ? "" + : pair.asMapping().get("value").asScalar().getValue(); + + return new Entry(key, value); + }) + .toList(); } return result; } From be60c5ab2bcab5df2b58344365848bcbabe9a9e5 Mon Sep 17 00:00:00 2001 From: Pedro Bueno Yerbes Date: Wed, 2 Apr 2025 18:21:16 +0200 Subject: [PATCH 03/10] Testing additional global props --- .../casc/GlobalNodePropertiesTest.java | 22 ++++++++++++++++--- .../plugins/casc/GlobalNodePropertiesTest.yml | 11 +++++++++- .../casc/GlobalNodePropertiesTestExpected.yml | 9 ++++++++ 3 files changed, 38 insertions(+), 4 deletions(-) diff --git a/integrations/src/test/java/io/jenkins/plugins/casc/GlobalNodePropertiesTest.java b/integrations/src/test/java/io/jenkins/plugins/casc/GlobalNodePropertiesTest.java index 5c5ad20a4d..c59a115e32 100644 --- a/integrations/src/test/java/io/jenkins/plugins/casc/GlobalNodePropertiesTest.java +++ b/integrations/src/test/java/io/jenkins/plugins/casc/GlobalNodePropertiesTest.java @@ -7,9 +7,11 @@ import static org.hamcrest.core.Is.is; import static org.junit.jupiter.api.Assertions.assertEquals; +import hudson.node_monitors.DiskSpaceMonitorNodeProperty; import hudson.slaves.EnvironmentVariablesNodeProperty; import hudson.slaves.NodeProperty; import hudson.slaves.NodePropertyDescriptor; +import hudson.tools.ToolLocationNodeProperty; import hudson.util.DescribableList; import io.jenkins.plugins.casc.misc.ConfiguredWithCode; import io.jenkins.plugins.casc.misc.JenkinsConfiguredWithCodeRule; @@ -32,12 +34,15 @@ void configure(JenkinsConfiguredWithCodeRule j) { DescribableList, NodePropertyDescriptor> nodeProperties = jenkins.getGlobalNodeProperties(); - Set> entries = ((EnvironmentVariablesNodeProperty) nodeProperties.get(0)) + assertEquals(3, nodeProperties.size()); + + Set> envVars = ((EnvironmentVariablesNodeProperty) + nodeProperties.get(EnvironmentVariablesNodeProperty.class)) .getEnvVars() .entrySet(); - assertEquals(3, entries.size()); + assertEquals(3, envVars.size()); - Iterator> iterator = entries.iterator(); + Iterator> iterator = envVars.iterator(); Map.Entry envVar = iterator.next(); assertEquals("FOO", envVar.getKey()); assertEquals("BAR", envVar.getValue()); @@ -49,6 +54,17 @@ void configure(JenkinsConfiguredWithCodeRule j) { envVar = iterator.next(); assertEquals("FOO3", envVar.getKey()); assertEquals("", envVar.getValue()); + + DiskSpaceMonitorNodeProperty diskSpace = nodeProperties.get(DiskSpaceMonitorNodeProperty.class); + assertEquals("1GiB", diskSpace.getFreeDiskSpaceThreshold()); + assertEquals("2GiB", diskSpace.getFreeDiskSpaceWarningThreshold()); + assertEquals("1GiB", diskSpace.getFreeTempSpaceThreshold()); + assertEquals("2GiB", diskSpace.getFreeTempSpaceWarningThreshold()); + + ToolLocationNodeProperty toolLocations = nodeProperties.get(ToolLocationNodeProperty.class); + assertEquals(1, toolLocations.getLocations().size()); + assertEquals("Default", toolLocations.getLocations().get(0).getName()); + assertEquals("/home/user/bin/git", toolLocations.getLocations().get(0).getHome()); } @Test diff --git a/integrations/src/test/resources/io/jenkins/plugins/casc/GlobalNodePropertiesTest.yml b/integrations/src/test/resources/io/jenkins/plugins/casc/GlobalNodePropertiesTest.yml index e76e751e4f..35ae547e9c 100644 --- a/integrations/src/test/resources/io/jenkins/plugins/casc/GlobalNodePropertiesTest.yml +++ b/integrations/src/test/resources/io/jenkins/plugins/casc/GlobalNodePropertiesTest.yml @@ -1,9 +1,18 @@ jenkins: globalNodeProperties: + - diskSpaceMonitor: + freeDiskSpaceThreshold: "1GiB" + freeDiskSpaceWarningThreshold: "2GiB" + freeTempSpaceThreshold: "1GiB" + freeTempSpaceWarningThreshold: "2GiB" - envVars: env: - key: FOO value: BAR - key: FOO2 value: "" - - key: FOO3 \ No newline at end of file + - key: FOO3 + - toolLocation: + locations: + - home: "/home/user/bin/git" + key: "hudson.plugins.git.GitTool$DescriptorImpl@Default" \ No newline at end of file diff --git a/integrations/src/test/resources/io/jenkins/plugins/casc/GlobalNodePropertiesTestExpected.yml b/integrations/src/test/resources/io/jenkins/plugins/casc/GlobalNodePropertiesTestExpected.yml index bf79b322c9..b34a3b8583 100644 --- a/integrations/src/test/resources/io/jenkins/plugins/casc/GlobalNodePropertiesTestExpected.yml +++ b/integrations/src/test/resources/io/jenkins/plugins/casc/GlobalNodePropertiesTestExpected.yml @@ -1,6 +1,15 @@ +- diskSpaceMonitor: + freeDiskSpaceThreshold: "1GiB" + freeDiskSpaceWarningThreshold: "2GiB" + freeTempSpaceThreshold: "1GiB" + freeTempSpaceWarningThreshold: "2GiB" - envVars: env: - key: "FOO" value: "BAR" - key: "FOO2" - key: "FOO3" +- toolLocation: + locations: + - home: "/home/user/bin/git" + key: "hudson.plugins.git.GitTool$DescriptorImpl@Default" From e7055abd38b073a7b7142d3aef888a56812d3360 Mon Sep 17 00:00:00 2001 From: Pedro Bueno Yerbes Date: Wed, 2 Apr 2025 18:30:26 +0200 Subject: [PATCH 04/10] Removing not used describe --- .../core/GlobalNodePropertiesConfigurator.java | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/plugin/src/main/java/io/jenkins/plugins/casc/core/GlobalNodePropertiesConfigurator.java b/plugin/src/main/java/io/jenkins/plugins/casc/core/GlobalNodePropertiesConfigurator.java index 35033dbcbc..9ac3c25c5b 100644 --- a/plugin/src/main/java/io/jenkins/plugins/casc/core/GlobalNodePropertiesConfigurator.java +++ b/plugin/src/main/java/io/jenkins/plugins/casc/core/GlobalNodePropertiesConfigurator.java @@ -1,6 +1,5 @@ package io.jenkins.plugins.casc.core; -import edu.umd.cs.findbugs.annotations.CheckForNull; import edu.umd.cs.findbugs.annotations.NonNull; import hudson.Extension; import hudson.slaves.EnvironmentVariablesNodeProperty; @@ -12,7 +11,6 @@ import io.jenkins.plugins.casc.impl.attributes.MultivaluedAttribute; import io.jenkins.plugins.casc.model.CNode; import io.jenkins.plugins.casc.model.Mapping; -import io.jenkins.plugins.casc.model.Sequence; import java.util.ArrayList; import java.util.List; import java.util.Set; @@ -56,20 +54,6 @@ public EnvironmentVariablesNodeProperty configure(CNode c, ConfigurationContext return new EnvironmentVariablesNodeProperty(variables); } - @CheckForNull - public CNode describe(EnvironmentVariablesNodeProperty instance, ConfigurationContext context) throws Exception { - Mapping mapping = new Mapping(); - for (Attribute attribute : getAttributes()) { - CNode value = attribute.describe(instance, context); - if (value != null) { - Sequence values = new Sequence(); - value.asSequence().stream().forEach(values::add); - mapping.put(attribute.getName(), values); - } - } - return mapping; - } - private List getVarsAsList(Mapping m) { List result = new ArrayList<>(); if (m.get("env") != null) { From d1c08d9f15a9a6962e64c78bfe85d18a3f3bc77c Mon Sep 17 00:00:00 2001 From: Pedro Bueno Yerbes Date: Thu, 3 Apr 2025 18:09:08 +0200 Subject: [PATCH 05/10] Switching to use DataBoundConstructor --- .../GlobalNodePropertiesConfigurator.java | 32 +++++-------------- 1 file changed, 8 insertions(+), 24 deletions(-) diff --git a/plugin/src/main/java/io/jenkins/plugins/casc/core/GlobalNodePropertiesConfigurator.java b/plugin/src/main/java/io/jenkins/plugins/casc/core/GlobalNodePropertiesConfigurator.java index 9ac3c25c5b..b3ab3ce0e0 100644 --- a/plugin/src/main/java/io/jenkins/plugins/casc/core/GlobalNodePropertiesConfigurator.java +++ b/plugin/src/main/java/io/jenkins/plugins/casc/core/GlobalNodePropertiesConfigurator.java @@ -4,45 +4,29 @@ import hudson.Extension; import hudson.slaves.EnvironmentVariablesNodeProperty; import hudson.slaves.EnvironmentVariablesNodeProperty.Entry; -import io.jenkins.plugins.casc.Attribute; -import io.jenkins.plugins.casc.BaseConfigurator; import io.jenkins.plugins.casc.ConfigurationContext; import io.jenkins.plugins.casc.ConfiguratorException; -import io.jenkins.plugins.casc.impl.attributes.MultivaluedAttribute; +import io.jenkins.plugins.casc.impl.configurators.DataBoundConfigurator; import io.jenkins.plugins.casc.model.CNode; import io.jenkins.plugins.casc.model.Mapping; import java.util.ArrayList; import java.util.List; -import java.util.Set; @Extension -public class GlobalNodePropertiesConfigurator extends BaseConfigurator { +public class GlobalNodePropertiesConfigurator extends DataBoundConfigurator { - @NonNull - @Override - public String getName() { - return "globalNodeProperties"; + public GlobalNodePropertiesConfigurator() { + this(EnvironmentVariablesNodeProperty.class); } - @Override - protected EnvironmentVariablesNodeProperty instance(Mapping mapping, ConfigurationContext context) - throws ConfiguratorException { - List vars = getVarsAsList(mapping); - return new EnvironmentVariablesNodeProperty(vars); - } - - @Override - public Class getTarget() { - return EnvironmentVariablesNodeProperty.class; + public GlobalNodePropertiesConfigurator(Class clazz) { + super(EnvironmentVariablesNodeProperty.class); } @NonNull @Override - public Set> describe() { - Set> attrs = super.describe(); - attrs.add(new MultivaluedAttribute( - "env", Entry.class)); - return attrs; + public String getName() { + return "globalNodeProperties"; } @NonNull From 4884ab30cf084ac5d432adedf767ccd3428c151a Mon Sep 17 00:00:00 2001 From: Pedro Bueno Yerbes Date: Wed, 9 Apr 2025 15:53:55 +0200 Subject: [PATCH 06/10] Adding variable resolution --- .../GlobalNodePropertiesWithEnvaVarsTest.java | 49 +++++++++++++++++++ .../GlobalNodePropertiesWithEnvVarsTest.yml | 12 +++++ .../GlobalNodePropertiesConfigurator.java | 6 +-- 3 files changed, 64 insertions(+), 3 deletions(-) create mode 100644 integrations/src/test/java/io/jenkins/plugins/casc/GlobalNodePropertiesWithEnvaVarsTest.java create mode 100644 integrations/src/test/resources/io/jenkins/plugins/casc/GlobalNodePropertiesWithEnvVarsTest.yml diff --git a/integrations/src/test/java/io/jenkins/plugins/casc/GlobalNodePropertiesWithEnvaVarsTest.java b/integrations/src/test/java/io/jenkins/plugins/casc/GlobalNodePropertiesWithEnvaVarsTest.java new file mode 100644 index 0000000000..6226e206d3 --- /dev/null +++ b/integrations/src/test/java/io/jenkins/plugins/casc/GlobalNodePropertiesWithEnvaVarsTest.java @@ -0,0 +1,49 @@ +package io.jenkins.plugins.casc; + +import hudson.slaves.EnvironmentVariablesNodeProperty; +import hudson.slaves.NodeProperty; +import hudson.slaves.NodePropertyDescriptor; +import hudson.tools.ToolLocationNodeProperty; +import hudson.util.DescribableList; +import io.jenkins.plugins.casc.misc.ConfiguredWithCode; +import io.jenkins.plugins.casc.misc.Env; +import io.jenkins.plugins.casc.misc.EnvVarsRule; +import io.jenkins.plugins.casc.misc.Envs; +import io.jenkins.plugins.casc.misc.JenkinsConfiguredWithCodeRule; +import java.util.Map; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.RuleChain; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.Matchers.hasSize; + +public class GlobalNodePropertiesWithEnvaVarsTest { + + private JenkinsConfiguredWithCodeRule j = new JenkinsConfiguredWithCodeRule(); + + @Rule + public RuleChain chain = RuleChain.outerRule(new EnvVarsRule()).around(j); + + @Test + @ConfiguredWithCode("GlobalNodePropertiesWithEnvVarsTest.yml") + @Envs({ + @Env(name = "VALUE_1", value = "BAR"), + @Env(name = "TEST_GIT_HOME", value = "git-home") + }) + public void configureWithEnvVarsTest() { + DescribableList, NodePropertyDescriptor> nodeProperties = j.jenkins.getGlobalNodeProperties(); + Map envVars = ((EnvironmentVariablesNodeProperty) + nodeProperties.get(EnvironmentVariablesNodeProperty.class)) + .getEnvVars(); + + assertThat(envVars.size(), is(2)); + assertThat(envVars.get("FOO"), is("BAR")); + assertThat(envVars.get("FOO2"), is("")); + + ToolLocationNodeProperty toolLocations = nodeProperties.get(ToolLocationNodeProperty.class); + assertThat(toolLocations.getLocations(), hasSize(1)); + assertThat(toolLocations.getLocations().get(0).getHome(), is("git-home")); + } +} diff --git a/integrations/src/test/resources/io/jenkins/plugins/casc/GlobalNodePropertiesWithEnvVarsTest.yml b/integrations/src/test/resources/io/jenkins/plugins/casc/GlobalNodePropertiesWithEnvVarsTest.yml new file mode 100644 index 0000000000..9e965cca14 --- /dev/null +++ b/integrations/src/test/resources/io/jenkins/plugins/casc/GlobalNodePropertiesWithEnvVarsTest.yml @@ -0,0 +1,12 @@ +jenkins: + globalNodeProperties: + - envVars: + env: + - key: FOO + value: ${VALUE_1} + - key: FOO2 + value: ${VALUE_2} + - toolLocation: + locations: + - home: "${TEST_GIT_HOME}" + key: "hudson.plugins.git.GitTool$DescriptorImpl@Default" \ No newline at end of file diff --git a/plugin/src/main/java/io/jenkins/plugins/casc/core/GlobalNodePropertiesConfigurator.java b/plugin/src/main/java/io/jenkins/plugins/casc/core/GlobalNodePropertiesConfigurator.java index b3ab3ce0e0..71f6663a49 100644 --- a/plugin/src/main/java/io/jenkins/plugins/casc/core/GlobalNodePropertiesConfigurator.java +++ b/plugin/src/main/java/io/jenkins/plugins/casc/core/GlobalNodePropertiesConfigurator.java @@ -34,11 +34,11 @@ public String getName() { public EnvironmentVariablesNodeProperty configure(CNode c, ConfigurationContext context) throws ConfiguratorException { Mapping mapping = c.asMapping(); - List variables = getVarsAsList(mapping); + List variables = getVarsAsList(mapping, context); return new EnvironmentVariablesNodeProperty(variables); } - private List getVarsAsList(Mapping m) { + private List getVarsAsList(Mapping m, ConfigurationContext context) { List result = new ArrayList<>(); if (m.get("env") != null) { result = m.get("env").asSequence().stream() @@ -50,7 +50,7 @@ private List getVarsAsList(Mapping m) { pair.asMapping().get("key").asScalar().getValue(); final String value = pair.asMapping().get("value") == null ? "" - : pair.asMapping().get("value").asScalar().getValue(); + : context.getSecretSourceResolver().resolve(pair.asMapping().get("value").asScalar().getValue()); return new Entry(key, value); }) From 3309c959ef43073307ee00e2b4125bb114c9d222 Mon Sep 17 00:00:00 2001 From: Pedro Bueno Yerbes Date: Wed, 9 Apr 2025 16:14:57 +0200 Subject: [PATCH 07/10] Added export with vars test --- .../GlobalNodePropertiesWithEnvaVarsTest.java | 21 +++++++++++++++++++ ...lNodePropertiesWithEnvVarsTestExpected.yml | 9 ++++++++ 2 files changed, 30 insertions(+) create mode 100644 integrations/src/test/resources/io/jenkins/plugins/casc/GlobalNodePropertiesWithEnvVarsTestExpected.yml diff --git a/integrations/src/test/java/io/jenkins/plugins/casc/GlobalNodePropertiesWithEnvaVarsTest.java b/integrations/src/test/java/io/jenkins/plugins/casc/GlobalNodePropertiesWithEnvaVarsTest.java index 6226e206d3..59c95b4a10 100644 --- a/integrations/src/test/java/io/jenkins/plugins/casc/GlobalNodePropertiesWithEnvaVarsTest.java +++ b/integrations/src/test/java/io/jenkins/plugins/casc/GlobalNodePropertiesWithEnvaVarsTest.java @@ -10,11 +10,16 @@ import io.jenkins.plugins.casc.misc.EnvVarsRule; import io.jenkins.plugins.casc.misc.Envs; import io.jenkins.plugins.casc.misc.JenkinsConfiguredWithCodeRule; +import io.jenkins.plugins.casc.model.CNode; import java.util.Map; +import org.hamcrest.core.Is; import org.junit.Rule; import org.junit.Test; import org.junit.rules.RuleChain; +import static io.jenkins.plugins.casc.misc.Util.getJenkinsRoot; +import static io.jenkins.plugins.casc.misc.Util.toStringFromYamlFile; +import static io.jenkins.plugins.casc.misc.Util.toYamlString; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.Matchers.hasSize; @@ -46,4 +51,20 @@ public void configureWithEnvVarsTest() { assertThat(toolLocations.getLocations(), hasSize(1)); assertThat(toolLocations.getLocations().get(0).getHome(), is("git-home")); } + + @Test + @ConfiguredWithCode("GlobalNodePropertiesWithEnvVarsTest.yml") + @Envs({ + @Env(name = "VALUE_1", value = "BAR"), + @Env(name = "TEST_GIT_HOME", value = "git-home") + }) + public void export() throws Exception { + ConfiguratorRegistry registry = ConfiguratorRegistry.get(); + ConfigurationContext context = new ConfigurationContext(registry); + CNode yourAttribute = getJenkinsRoot(context).get("globalNodeProperties"); + + String exported = toYamlString(yourAttribute); + String expected = toStringFromYamlFile(this, "GlobalNodePropertiesWithEnvVarsTestExpected.yml"); + assertThat(exported, Is.is(expected)); + } } diff --git a/integrations/src/test/resources/io/jenkins/plugins/casc/GlobalNodePropertiesWithEnvVarsTestExpected.yml b/integrations/src/test/resources/io/jenkins/plugins/casc/GlobalNodePropertiesWithEnvVarsTestExpected.yml new file mode 100644 index 0000000000..b03750ccf4 --- /dev/null +++ b/integrations/src/test/resources/io/jenkins/plugins/casc/GlobalNodePropertiesWithEnvVarsTestExpected.yml @@ -0,0 +1,9 @@ +- envVars: + env: + - key: "FOO" + value: "BAR" + - key: "FOO2" +- toolLocation: + locations: + - home: "git-home" + key: "hudson.plugins.git.GitTool$DescriptorImpl@Default" From 8ea91073f393a8a8f6a64f5ac8d97204128ccaf9 Mon Sep 17 00:00:00 2001 From: Pedro Bueno Yerbes Date: Wed, 9 Apr 2025 16:25:01 +0200 Subject: [PATCH 08/10] spotless --- .../GlobalNodePropertiesWithEnvaVarsTest.java | 28 ++++++++----------- .../GlobalNodePropertiesConfigurator.java | 6 +++- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/integrations/src/test/java/io/jenkins/plugins/casc/GlobalNodePropertiesWithEnvaVarsTest.java b/integrations/src/test/java/io/jenkins/plugins/casc/GlobalNodePropertiesWithEnvaVarsTest.java index 59c95b4a10..339a56a7cd 100644 --- a/integrations/src/test/java/io/jenkins/plugins/casc/GlobalNodePropertiesWithEnvaVarsTest.java +++ b/integrations/src/test/java/io/jenkins/plugins/casc/GlobalNodePropertiesWithEnvaVarsTest.java @@ -1,5 +1,12 @@ package io.jenkins.plugins.casc; +import static io.jenkins.plugins.casc.misc.Util.getJenkinsRoot; +import static io.jenkins.plugins.casc.misc.Util.toStringFromYamlFile; +import static io.jenkins.plugins.casc.misc.Util.toYamlString; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasSize; + import hudson.slaves.EnvironmentVariablesNodeProperty; import hudson.slaves.NodeProperty; import hudson.slaves.NodePropertyDescriptor; @@ -17,13 +24,6 @@ import org.junit.Test; import org.junit.rules.RuleChain; -import static io.jenkins.plugins.casc.misc.Util.getJenkinsRoot; -import static io.jenkins.plugins.casc.misc.Util.toStringFromYamlFile; -import static io.jenkins.plugins.casc.misc.Util.toYamlString; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.Matchers.hasSize; - public class GlobalNodePropertiesWithEnvaVarsTest { private JenkinsConfiguredWithCodeRule j = new JenkinsConfiguredWithCodeRule(); @@ -33,15 +33,12 @@ public class GlobalNodePropertiesWithEnvaVarsTest { @Test @ConfiguredWithCode("GlobalNodePropertiesWithEnvVarsTest.yml") - @Envs({ - @Env(name = "VALUE_1", value = "BAR"), - @Env(name = "TEST_GIT_HOME", value = "git-home") - }) + @Envs({@Env(name = "VALUE_1", value = "BAR"), @Env(name = "TEST_GIT_HOME", value = "git-home")}) public void configureWithEnvVarsTest() { DescribableList, NodePropertyDescriptor> nodeProperties = j.jenkins.getGlobalNodeProperties(); Map envVars = ((EnvironmentVariablesNodeProperty) - nodeProperties.get(EnvironmentVariablesNodeProperty.class)) - .getEnvVars(); + nodeProperties.get(EnvironmentVariablesNodeProperty.class)) + .getEnvVars(); assertThat(envVars.size(), is(2)); assertThat(envVars.get("FOO"), is("BAR")); @@ -54,10 +51,7 @@ public void configureWithEnvVarsTest() { @Test @ConfiguredWithCode("GlobalNodePropertiesWithEnvVarsTest.yml") - @Envs({ - @Env(name = "VALUE_1", value = "BAR"), - @Env(name = "TEST_GIT_HOME", value = "git-home") - }) + @Envs({@Env(name = "VALUE_1", value = "BAR"), @Env(name = "TEST_GIT_HOME", value = "git-home")}) public void export() throws Exception { ConfiguratorRegistry registry = ConfiguratorRegistry.get(); ConfigurationContext context = new ConfigurationContext(registry); diff --git a/plugin/src/main/java/io/jenkins/plugins/casc/core/GlobalNodePropertiesConfigurator.java b/plugin/src/main/java/io/jenkins/plugins/casc/core/GlobalNodePropertiesConfigurator.java index 71f6663a49..e5f143e9d1 100644 --- a/plugin/src/main/java/io/jenkins/plugins/casc/core/GlobalNodePropertiesConfigurator.java +++ b/plugin/src/main/java/io/jenkins/plugins/casc/core/GlobalNodePropertiesConfigurator.java @@ -50,7 +50,11 @@ private List getVarsAsList(Mapping m, ConfigurationContext context) { pair.asMapping().get("key").asScalar().getValue(); final String value = pair.asMapping().get("value") == null ? "" - : context.getSecretSourceResolver().resolve(pair.asMapping().get("value").asScalar().getValue()); + : context.getSecretSourceResolver() + .resolve(pair.asMapping() + .get("value") + .asScalar() + .getValue()); return new Entry(key, value); }) From be00ec15cac82b27d116af4f2350878c17f0ae97 Mon Sep 17 00:00:00 2001 From: Pedro Bueno Yerbes Date: Tue, 15 Apr 2025 10:25:10 +0200 Subject: [PATCH 09/10] Exporting empty env vars --- .../casc/GlobalNodePropertiesTest.java | 2 +- ... GlobalNodePropertiesWithEnvVarsTest.java} | 2 +- .../plugins/casc/GlobalNodePropertiesTest.yml | 1 - .../casc/GlobalNodePropertiesTestExpected.yml | 2 +- ...lNodePropertiesWithEnvVarsTestExpected.yml | 1 + .../plugins/casc/ConfigurationAsCode.java | 2 +- .../GlobalNodePropertiesConfigurator.java | 48 ++++++++----------- .../io/jenkins/plugins/casc/model/CNode.java | 8 ++++ .../io/jenkins/plugins/casc/model/Scalar.java | 11 +++++ 9 files changed, 44 insertions(+), 33 deletions(-) rename integrations/src/test/java/io/jenkins/plugins/casc/{GlobalNodePropertiesWithEnvaVarsTest.java => GlobalNodePropertiesWithEnvVarsTest.java} (98%) diff --git a/integrations/src/test/java/io/jenkins/plugins/casc/GlobalNodePropertiesTest.java b/integrations/src/test/java/io/jenkins/plugins/casc/GlobalNodePropertiesTest.java index c59a115e32..3db3a29870 100644 --- a/integrations/src/test/java/io/jenkins/plugins/casc/GlobalNodePropertiesTest.java +++ b/integrations/src/test/java/io/jenkins/plugins/casc/GlobalNodePropertiesTest.java @@ -40,7 +40,7 @@ void configure(JenkinsConfiguredWithCodeRule j) { nodeProperties.get(EnvironmentVariablesNodeProperty.class)) .getEnvVars() .entrySet(); - assertEquals(3, envVars.size()); + assertEquals(2, envVars.size()); Iterator> iterator = envVars.iterator(); Map.Entry envVar = iterator.next(); diff --git a/integrations/src/test/java/io/jenkins/plugins/casc/GlobalNodePropertiesWithEnvaVarsTest.java b/integrations/src/test/java/io/jenkins/plugins/casc/GlobalNodePropertiesWithEnvVarsTest.java similarity index 98% rename from integrations/src/test/java/io/jenkins/plugins/casc/GlobalNodePropertiesWithEnvaVarsTest.java rename to integrations/src/test/java/io/jenkins/plugins/casc/GlobalNodePropertiesWithEnvVarsTest.java index 339a56a7cd..8cd2d150a9 100644 --- a/integrations/src/test/java/io/jenkins/plugins/casc/GlobalNodePropertiesWithEnvaVarsTest.java +++ b/integrations/src/test/java/io/jenkins/plugins/casc/GlobalNodePropertiesWithEnvVarsTest.java @@ -24,7 +24,7 @@ import org.junit.Test; import org.junit.rules.RuleChain; -public class GlobalNodePropertiesWithEnvaVarsTest { +public class GlobalNodePropertiesWithEnvVarsTest { private JenkinsConfiguredWithCodeRule j = new JenkinsConfiguredWithCodeRule(); diff --git a/integrations/src/test/resources/io/jenkins/plugins/casc/GlobalNodePropertiesTest.yml b/integrations/src/test/resources/io/jenkins/plugins/casc/GlobalNodePropertiesTest.yml index 35ae547e9c..4bc62aa9a5 100644 --- a/integrations/src/test/resources/io/jenkins/plugins/casc/GlobalNodePropertiesTest.yml +++ b/integrations/src/test/resources/io/jenkins/plugins/casc/GlobalNodePropertiesTest.yml @@ -11,7 +11,6 @@ jenkins: value: BAR - key: FOO2 value: "" - - key: FOO3 - toolLocation: locations: - home: "/home/user/bin/git" diff --git a/integrations/src/test/resources/io/jenkins/plugins/casc/GlobalNodePropertiesTestExpected.yml b/integrations/src/test/resources/io/jenkins/plugins/casc/GlobalNodePropertiesTestExpected.yml index b34a3b8583..59693d6d09 100644 --- a/integrations/src/test/resources/io/jenkins/plugins/casc/GlobalNodePropertiesTestExpected.yml +++ b/integrations/src/test/resources/io/jenkins/plugins/casc/GlobalNodePropertiesTestExpected.yml @@ -8,7 +8,7 @@ - key: "FOO" value: "BAR" - key: "FOO2" - - key: "FOO3" + value: "" - toolLocation: locations: - home: "/home/user/bin/git" diff --git a/integrations/src/test/resources/io/jenkins/plugins/casc/GlobalNodePropertiesWithEnvVarsTestExpected.yml b/integrations/src/test/resources/io/jenkins/plugins/casc/GlobalNodePropertiesWithEnvVarsTestExpected.yml index b03750ccf4..56d395429d 100644 --- a/integrations/src/test/resources/io/jenkins/plugins/casc/GlobalNodePropertiesWithEnvVarsTestExpected.yml +++ b/integrations/src/test/resources/io/jenkins/plugins/casc/GlobalNodePropertiesWithEnvVarsTestExpected.yml @@ -3,6 +3,7 @@ - key: "FOO" value: "BAR" - key: "FOO2" + value: "" - toolLocation: locations: - home: "git-home" diff --git a/plugin/src/main/java/io/jenkins/plugins/casc/ConfigurationAsCode.java b/plugin/src/main/java/io/jenkins/plugins/casc/ConfigurationAsCode.java index 3e383cabbc..27f2cd0039 100644 --- a/plugin/src/main/java/io/jenkins/plugins/casc/ConfigurationAsCode.java +++ b/plugin/src/main/java/io/jenkins/plugins/casc/ConfigurationAsCode.java @@ -657,7 +657,7 @@ public Node toYaml(CNode config) throws ConfiguratorException { default: final Scalar scalar = config.asScalar(); final String value = scalar.getValue(); - if (value == null || value.length() == 0) { + if (StringUtils.isBlank(value) && !scalar.isPrintableWhenEmpty()) { return null; } diff --git a/plugin/src/main/java/io/jenkins/plugins/casc/core/GlobalNodePropertiesConfigurator.java b/plugin/src/main/java/io/jenkins/plugins/casc/core/GlobalNodePropertiesConfigurator.java index e5f143e9d1..c4fec5104b 100644 --- a/plugin/src/main/java/io/jenkins/plugins/casc/core/GlobalNodePropertiesConfigurator.java +++ b/plugin/src/main/java/io/jenkins/plugins/casc/core/GlobalNodePropertiesConfigurator.java @@ -1,16 +1,15 @@ package io.jenkins.plugins.casc.core; +import edu.umd.cs.findbugs.annotations.CheckForNull; import edu.umd.cs.findbugs.annotations.NonNull; import hudson.Extension; import hudson.slaves.EnvironmentVariablesNodeProperty; -import hudson.slaves.EnvironmentVariablesNodeProperty.Entry; +import io.jenkins.plugins.casc.Attribute; import io.jenkins.plugins.casc.ConfigurationContext; import io.jenkins.plugins.casc.ConfiguratorException; import io.jenkins.plugins.casc.impl.configurators.DataBoundConfigurator; import io.jenkins.plugins.casc.model.CNode; import io.jenkins.plugins.casc.model.Mapping; -import java.util.ArrayList; -import java.util.List; @Extension public class GlobalNodePropertiesConfigurator extends DataBoundConfigurator { @@ -33,33 +32,26 @@ public String getName() { @Override public EnvironmentVariablesNodeProperty configure(CNode c, ConfigurationContext context) throws ConfiguratorException { - Mapping mapping = c.asMapping(); - List variables = getVarsAsList(mapping, context); - return new EnvironmentVariablesNodeProperty(variables); + return super.configure(c, context); } - private List getVarsAsList(Mapping m, ConfigurationContext context) { - List result = new ArrayList<>(); - if (m.get("env") != null) { - result = m.get("env").asSequence().stream() - .map(pair -> { - if (pair.asMapping().get("key") == null) { - return null; - } - final String key = - pair.asMapping().get("key").asScalar().getValue(); - final String value = pair.asMapping().get("value") == null - ? "" - : context.getSecretSourceResolver() - .resolve(pair.asMapping() - .get("value") - .asScalar() - .getValue()); - - return new Entry(key, value); - }) - .toList(); + @Override + @CheckForNull + public CNode describe(EnvironmentVariablesNodeProperty instance, ConfigurationContext context) throws Exception { + Mapping mapping = new Mapping(); + for (Attribute attribute : getAttributes()) { + CNode value = attribute.describe(instance, context); + if (value != null) { + // Making sure empty variables are part of the export + value.asSequence().forEach(entry -> { + if (entry.asMapping().get("key") != null + && entry.asMapping().get("value") != null) { + entry.asMapping().get("value").asScalar().setPrintableWhenEmpty(true); + } + }); + mapping.put(attribute.getName(), value); + } } - return result; + return mapping; } } diff --git a/plugin/src/main/java/io/jenkins/plugins/casc/model/CNode.java b/plugin/src/main/java/io/jenkins/plugins/casc/model/CNode.java index 35f64dc867..9d7f38d064 100644 --- a/plugin/src/main/java/io/jenkins/plugins/casc/model/CNode.java +++ b/plugin/src/main/java/io/jenkins/plugins/casc/model/CNode.java @@ -35,6 +35,14 @@ default boolean isSensitiveData() { return false; } + /** + * Indicates if the field should be included when describing even if empty + * @return false by default + */ + default boolean isPrintableWhenEmpty() { + return false; + } + /** * Indicate the source (file, line number) this specific configuration node comes from. * This is used to offer relevant diagnostic messages diff --git a/plugin/src/main/java/io/jenkins/plugins/casc/model/Scalar.java b/plugin/src/main/java/io/jenkins/plugins/casc/model/Scalar.java index 14787f6321..8ba6641674 100644 --- a/plugin/src/main/java/io/jenkins/plugins/casc/model/Scalar.java +++ b/plugin/src/main/java/io/jenkins/plugins/casc/model/Scalar.java @@ -16,6 +16,7 @@ public final class Scalar implements CNode, CharSequence { private Source source; private boolean sensitive; private boolean encrypted; + private boolean printableWhenEmpty = false; public enum Format { STRING, @@ -163,6 +164,15 @@ public Source getSource() { return source; } + @Override + public boolean isPrintableWhenEmpty() { + return printableWhenEmpty; + } + + public void setPrintableWhenEmpty(boolean print) { + this.printableWhenEmpty = print; + } + @Override public CNode clone() { return new Scalar(this); @@ -175,5 +185,6 @@ private Scalar(Scalar it) { this.source = it.source; this.sensitive = it.sensitive; this.encrypted = it.encrypted; + this.printableWhenEmpty = it.printableWhenEmpty; } } From 27986fc10eb3f6ae05eba9942384e69ae57f093d Mon Sep 17 00:00:00 2001 From: Pedro Bueno Yerbes Date: Tue, 15 Apr 2025 12:49:05 +0200 Subject: [PATCH 10/10] fixed test --- .../io/jenkins/plugins/casc/GlobalNodePropertiesTest.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/integrations/src/test/java/io/jenkins/plugins/casc/GlobalNodePropertiesTest.java b/integrations/src/test/java/io/jenkins/plugins/casc/GlobalNodePropertiesTest.java index 3db3a29870..c7ddab6fa4 100644 --- a/integrations/src/test/java/io/jenkins/plugins/casc/GlobalNodePropertiesTest.java +++ b/integrations/src/test/java/io/jenkins/plugins/casc/GlobalNodePropertiesTest.java @@ -51,10 +51,6 @@ void configure(JenkinsConfiguredWithCodeRule j) { assertEquals("FOO2", envVar.getKey()); assertEquals("", envVar.getValue()); - envVar = iterator.next(); - assertEquals("FOO3", envVar.getKey()); - assertEquals("", envVar.getValue()); - DiskSpaceMonitorNodeProperty diskSpace = nodeProperties.get(DiskSpaceMonitorNodeProperty.class); assertEquals("1GiB", diskSpace.getFreeDiskSpaceThreshold()); assertEquals("2GiB", diskSpace.getFreeDiskSpaceWarningThreshold());