Skip to content

Commit ddc0d36

Browse files
Allow empty valued variables without ignoring enviroment vars substitution (#2665)
* Using configurator for global props * Accept EnvVars wihtout value * Testing additional global props * Removing not used describe * Switching to use DataBoundConstructor * Adding variable resolution * Added export with vars test * spotless * Exporting empty env vars * fixed test --------- Co-authored-by: Francisco Javier Fernandez Gonzalez <[email protected]>
1 parent 7c3138e commit ddc0d36

File tree

10 files changed

+211
-4
lines changed

10 files changed

+211
-4
lines changed

integrations/src/test/java/io/jenkins/plugins/casc/GlobalNodePropertiesTest.java

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,19 @@
77
import static org.hamcrest.core.Is.is;
88
import static org.junit.jupiter.api.Assertions.assertEquals;
99

10+
import hudson.node_monitors.DiskSpaceMonitorNodeProperty;
1011
import hudson.slaves.EnvironmentVariablesNodeProperty;
1112
import hudson.slaves.NodeProperty;
1213
import hudson.slaves.NodePropertyDescriptor;
14+
import hudson.tools.ToolLocationNodeProperty;
1315
import hudson.util.DescribableList;
1416
import io.jenkins.plugins.casc.misc.ConfiguredWithCode;
1517
import io.jenkins.plugins.casc.misc.JenkinsConfiguredWithCodeRule;
1618
import io.jenkins.plugins.casc.misc.junit.jupiter.WithJenkinsConfiguredWithCode;
1719
import io.jenkins.plugins.casc.model.CNode;
20+
import java.util.Iterator;
1821
import java.util.Map;
22+
import java.util.Map.Entry;
1923
import java.util.Set;
2024
import jenkins.model.Jenkins;
2125
import org.junit.jupiter.api.Test;
@@ -30,14 +34,33 @@ void configure(JenkinsConfiguredWithCodeRule j) {
3034

3135
DescribableList<NodeProperty<?>, NodePropertyDescriptor> nodeProperties = jenkins.getGlobalNodeProperties();
3236

33-
Set<Map.Entry<String, String>> entries = ((EnvironmentVariablesNodeProperty) nodeProperties.get(0))
37+
assertEquals(3, nodeProperties.size());
38+
39+
Set<Map.Entry<String, String>> envVars = ((EnvironmentVariablesNodeProperty)
40+
nodeProperties.get(EnvironmentVariablesNodeProperty.class))
3441
.getEnvVars()
3542
.entrySet();
36-
assertEquals(1, entries.size());
43+
assertEquals(2, envVars.size());
3744

38-
Map.Entry<String, String> envVar = entries.iterator().next();
45+
Iterator<Entry<String, String>> iterator = envVars.iterator();
46+
Map.Entry<String, String> envVar = iterator.next();
3947
assertEquals("FOO", envVar.getKey());
4048
assertEquals("BAR", envVar.getValue());
49+
50+
envVar = iterator.next();
51+
assertEquals("FOO2", envVar.getKey());
52+
assertEquals("", envVar.getValue());
53+
54+
DiskSpaceMonitorNodeProperty diskSpace = nodeProperties.get(DiskSpaceMonitorNodeProperty.class);
55+
assertEquals("1GiB", diskSpace.getFreeDiskSpaceThreshold());
56+
assertEquals("2GiB", diskSpace.getFreeDiskSpaceWarningThreshold());
57+
assertEquals("1GiB", diskSpace.getFreeTempSpaceThreshold());
58+
assertEquals("2GiB", diskSpace.getFreeTempSpaceWarningThreshold());
59+
60+
ToolLocationNodeProperty toolLocations = nodeProperties.get(ToolLocationNodeProperty.class);
61+
assertEquals(1, toolLocations.getLocations().size());
62+
assertEquals("Default", toolLocations.getLocations().get(0).getName());
63+
assertEquals("/home/user/bin/git", toolLocations.getLocations().get(0).getHome());
4164
}
4265

4366
@Test
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package io.jenkins.plugins.casc;
2+
3+
import static io.jenkins.plugins.casc.misc.Util.getJenkinsRoot;
4+
import static io.jenkins.plugins.casc.misc.Util.toStringFromYamlFile;
5+
import static io.jenkins.plugins.casc.misc.Util.toYamlString;
6+
import static org.hamcrest.CoreMatchers.is;
7+
import static org.hamcrest.MatcherAssert.assertThat;
8+
import static org.hamcrest.Matchers.hasSize;
9+
10+
import hudson.slaves.EnvironmentVariablesNodeProperty;
11+
import hudson.slaves.NodeProperty;
12+
import hudson.slaves.NodePropertyDescriptor;
13+
import hudson.tools.ToolLocationNodeProperty;
14+
import hudson.util.DescribableList;
15+
import io.jenkins.plugins.casc.misc.ConfiguredWithCode;
16+
import io.jenkins.plugins.casc.misc.Env;
17+
import io.jenkins.plugins.casc.misc.EnvVarsRule;
18+
import io.jenkins.plugins.casc.misc.Envs;
19+
import io.jenkins.plugins.casc.misc.JenkinsConfiguredWithCodeRule;
20+
import io.jenkins.plugins.casc.model.CNode;
21+
import java.util.Map;
22+
import org.hamcrest.core.Is;
23+
import org.junit.Rule;
24+
import org.junit.Test;
25+
import org.junit.rules.RuleChain;
26+
27+
public class GlobalNodePropertiesWithEnvVarsTest {
28+
29+
private JenkinsConfiguredWithCodeRule j = new JenkinsConfiguredWithCodeRule();
30+
31+
@Rule
32+
public RuleChain chain = RuleChain.outerRule(new EnvVarsRule()).around(j);
33+
34+
@Test
35+
@ConfiguredWithCode("GlobalNodePropertiesWithEnvVarsTest.yml")
36+
@Envs({@Env(name = "VALUE_1", value = "BAR"), @Env(name = "TEST_GIT_HOME", value = "git-home")})
37+
public void configureWithEnvVarsTest() {
38+
DescribableList<NodeProperty<?>, NodePropertyDescriptor> nodeProperties = j.jenkins.getGlobalNodeProperties();
39+
Map<String, String> envVars = ((EnvironmentVariablesNodeProperty)
40+
nodeProperties.get(EnvironmentVariablesNodeProperty.class))
41+
.getEnvVars();
42+
43+
assertThat(envVars.size(), is(2));
44+
assertThat(envVars.get("FOO"), is("BAR"));
45+
assertThat(envVars.get("FOO2"), is(""));
46+
47+
ToolLocationNodeProperty toolLocations = nodeProperties.get(ToolLocationNodeProperty.class);
48+
assertThat(toolLocations.getLocations(), hasSize(1));
49+
assertThat(toolLocations.getLocations().get(0).getHome(), is("git-home"));
50+
}
51+
52+
@Test
53+
@ConfiguredWithCode("GlobalNodePropertiesWithEnvVarsTest.yml")
54+
@Envs({@Env(name = "VALUE_1", value = "BAR"), @Env(name = "TEST_GIT_HOME", value = "git-home")})
55+
public void export() throws Exception {
56+
ConfiguratorRegistry registry = ConfiguratorRegistry.get();
57+
ConfigurationContext context = new ConfigurationContext(registry);
58+
CNode yourAttribute = getJenkinsRoot(context).get("globalNodeProperties");
59+
60+
String exported = toYamlString(yourAttribute);
61+
String expected = toStringFromYamlFile(this, "GlobalNodePropertiesWithEnvVarsTestExpected.yml");
62+
assertThat(exported, Is.is(expected));
63+
}
64+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,17 @@
11
jenkins:
22
globalNodeProperties:
3+
- diskSpaceMonitor:
4+
freeDiskSpaceThreshold: "1GiB"
5+
freeDiskSpaceWarningThreshold: "2GiB"
6+
freeTempSpaceThreshold: "1GiB"
7+
freeTempSpaceWarningThreshold: "2GiB"
38
- envVars:
49
env:
510
- key: FOO
611
value: BAR
12+
- key: FOO2
13+
value: ""
14+
- toolLocation:
15+
locations:
16+
- home: "/home/user/bin/git"
17+
key: "hudson.plugins.git.GitTool$DescriptorImpl@Default"
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,15 @@
1+
- diskSpaceMonitor:
2+
freeDiskSpaceThreshold: "1GiB"
3+
freeDiskSpaceWarningThreshold: "2GiB"
4+
freeTempSpaceThreshold: "1GiB"
5+
freeTempSpaceWarningThreshold: "2GiB"
16
- envVars:
27
env:
38
- key: "FOO"
49
value: "BAR"
10+
- key: "FOO2"
11+
value: ""
12+
- toolLocation:
13+
locations:
14+
- home: "/home/user/bin/git"
15+
key: "hudson.plugins.git.GitTool$DescriptorImpl@Default"
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
jenkins:
2+
globalNodeProperties:
3+
- envVars:
4+
env:
5+
- key: FOO
6+
value: ${VALUE_1}
7+
- key: FOO2
8+
value: ${VALUE_2}
9+
- toolLocation:
10+
locations:
11+
- home: "${TEST_GIT_HOME}"
12+
key: "hudson.plugins.git.GitTool$DescriptorImpl@Default"
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
- envVars:
2+
env:
3+
- key: "FOO"
4+
value: "BAR"
5+
- key: "FOO2"
6+
value: ""
7+
- toolLocation:
8+
locations:
9+
- home: "git-home"
10+
key: "hudson.plugins.git.GitTool$DescriptorImpl@Default"

plugin/src/main/java/io/jenkins/plugins/casc/ConfigurationAsCode.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -657,7 +657,7 @@ public Node toYaml(CNode config) throws ConfiguratorException {
657657
default:
658658
final Scalar scalar = config.asScalar();
659659
final String value = scalar.getValue();
660-
if (value == null || value.length() == 0) {
660+
if (StringUtils.isBlank(value) && !scalar.isPrintableWhenEmpty()) {
661661
return null;
662662
}
663663

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package io.jenkins.plugins.casc.core;
2+
3+
import edu.umd.cs.findbugs.annotations.CheckForNull;
4+
import edu.umd.cs.findbugs.annotations.NonNull;
5+
import hudson.Extension;
6+
import hudson.slaves.EnvironmentVariablesNodeProperty;
7+
import io.jenkins.plugins.casc.Attribute;
8+
import io.jenkins.plugins.casc.ConfigurationContext;
9+
import io.jenkins.plugins.casc.ConfiguratorException;
10+
import io.jenkins.plugins.casc.impl.configurators.DataBoundConfigurator;
11+
import io.jenkins.plugins.casc.model.CNode;
12+
import io.jenkins.plugins.casc.model.Mapping;
13+
14+
@Extension
15+
public class GlobalNodePropertiesConfigurator extends DataBoundConfigurator<EnvironmentVariablesNodeProperty> {
16+
17+
public GlobalNodePropertiesConfigurator() {
18+
this(EnvironmentVariablesNodeProperty.class);
19+
}
20+
21+
public GlobalNodePropertiesConfigurator(Class<?> clazz) {
22+
super(EnvironmentVariablesNodeProperty.class);
23+
}
24+
25+
@NonNull
26+
@Override
27+
public String getName() {
28+
return "globalNodeProperties";
29+
}
30+
31+
@NonNull
32+
@Override
33+
public EnvironmentVariablesNodeProperty configure(CNode c, ConfigurationContext context)
34+
throws ConfiguratorException {
35+
return super.configure(c, context);
36+
}
37+
38+
@Override
39+
@CheckForNull
40+
public CNode describe(EnvironmentVariablesNodeProperty instance, ConfigurationContext context) throws Exception {
41+
Mapping mapping = new Mapping();
42+
for (Attribute attribute : getAttributes()) {
43+
CNode value = attribute.describe(instance, context);
44+
if (value != null) {
45+
// Making sure empty variables are part of the export
46+
value.asSequence().forEach(entry -> {
47+
if (entry.asMapping().get("key") != null
48+
&& entry.asMapping().get("value") != null) {
49+
entry.asMapping().get("value").asScalar().setPrintableWhenEmpty(true);
50+
}
51+
});
52+
mapping.put(attribute.getName(), value);
53+
}
54+
}
55+
return mapping;
56+
}
57+
}

plugin/src/main/java/io/jenkins/plugins/casc/model/CNode.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,14 @@ default boolean isSensitiveData() {
3535
return false;
3636
}
3737

38+
/**
39+
* Indicates if the field should be included when describing even if empty
40+
* @return false by default
41+
*/
42+
default boolean isPrintableWhenEmpty() {
43+
return false;
44+
}
45+
3846
/**
3947
* Indicate the source (file, line number) this specific configuration node comes from.
4048
* This is used to offer relevant diagnostic messages

plugin/src/main/java/io/jenkins/plugins/casc/model/Scalar.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ public final class Scalar implements CNode, CharSequence {
1616
private Source source;
1717
private boolean sensitive;
1818
private boolean encrypted;
19+
private boolean printableWhenEmpty = false;
1920

2021
public enum Format {
2122
STRING,
@@ -163,6 +164,15 @@ public Source getSource() {
163164
return source;
164165
}
165166

167+
@Override
168+
public boolean isPrintableWhenEmpty() {
169+
return printableWhenEmpty;
170+
}
171+
172+
public void setPrintableWhenEmpty(boolean print) {
173+
this.printableWhenEmpty = print;
174+
}
175+
166176
@Override
167177
public CNode clone() {
168178
return new Scalar(this);
@@ -175,5 +185,6 @@ private Scalar(Scalar it) {
175185
this.source = it.source;
176186
this.sensitive = it.sensitive;
177187
this.encrypted = it.encrypted;
188+
this.printableWhenEmpty = it.printableWhenEmpty;
178189
}
179190
}

0 commit comments

Comments
 (0)