diff --git a/demos/variables-expansion.yml b/demos/variables-expansion.yml
new file mode 100644
index 0000000..e16ac18
--- /dev/null
+++ b/demos/variables-expansion.yml
@@ -0,0 +1,20 @@
+
+# Using jCasC secret expansion feature
+# Secrets are loaded from :
+# - Docker secrets
+# - Kubernetes/Openshift secrets
+# - HashiCorp Vault
+# - Environment variables
+#
+# cf: https://github.com/jenkinsci/configuration-as-code-plugin/blob/master/docs/features/secrets.adoc
+
+groovy:
+
+ # Asuming secret value is stored in variable named API_KEY
+ - url: https://my.web.site.com/path/to/my/resource?api_key=${API_KEY}
+ # Same with basic auth example
+ - url: https://${USER}:${PASS}@my.web.site.com/path/to/my/resource
+
+ # Asuming secret value is stored in variable named SECRET
+ - script: >
+ println "Displaying my secret : ${SECRET}";
diff --git a/pom.xml b/pom.xml
index cbd2303..bc272d0 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
org.jenkins-ci.plugins
plugin
- 3.18
+ 4.12
io.jenkins.plugins
@@ -12,10 +12,9 @@
1.2-SNAPSHOT
hpi
- 2.60.3
+ 2.222
8
- true
- true
+ 1.42
Configuration as Code Plugin - Groovy Scripting Extension
Plugin that extends JCasC with Groovy scripts execution
@@ -26,6 +25,10 @@
Tomasz Szandala
tomasz.szandala@gmail.com
+
+ jetersen
+ Joseph Petersen
+
@@ -33,14 +36,12 @@
http://opensource.org/licenses/MIT
-
-
- scm:git:git://github.com/jenkinsci/${project.artifactId}-plugin.git
- scm:git:git@github.com:jenkinsci/${project.artifactId}-plugin.git
- http://github.com/jenkinsci/${project.artifactId}-plugin
- HEAD
-
+ scm:git:git://github.com/jenkinsci/${project.artifactId}-plugin.git
+ scm:git:git@github.com:jenkinsci/${project.artifactId}-plugin.git
+ http://github.com/jenkinsci/${project.artifactId}-plugin
+ HEAD
+
@@ -58,7 +59,12 @@
io.jenkins
configuration-as-code
- 1.0
+ ${configuration-as-code.version}
+
+
+ io.jenkins.configuration-as-code
+ test-harness
+ ${configuration-as-code.version}
diff --git a/src/main/java/io/jenkins/plugins/cascgroovy/GroovyScriptCaller.java b/src/main/java/io/jenkins/plugins/cascgroovy/GroovyScriptCaller.java
index fbd9312..edd8828 100644
--- a/src/main/java/io/jenkins/plugins/cascgroovy/GroovyScriptCaller.java
+++ b/src/main/java/io/jenkins/plugins/cascgroovy/GroovyScriptCaller.java
@@ -1,40 +1,40 @@
package io.jenkins.plugins.cascgroovy;
-import jenkins.model.Jenkins;
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-import hudson.EnvVars;
+import groovy.lang.Binding;
+import groovy.lang.GroovyShell;
import hudson.Extension;
import io.jenkins.plugins.casc.Attribute;
import io.jenkins.plugins.casc.ConfigurationContext;
-import io.jenkins.plugins.casc.ConfiguratorException;
import io.jenkins.plugins.casc.Configurator;
+import io.jenkins.plugins.casc.ConfiguratorException;
import io.jenkins.plugins.casc.RootElementConfigurator;
import io.jenkins.plugins.casc.impl.attributes.MultivaluedAttribute;
import io.jenkins.plugins.casc.model.CNode;
-import io.jenkins.plugins.casc.model.Sequence;
-import org.kohsuke.accmod.Restricted;
-import org.kohsuke.accmod.restrictions.NoExternalUse;
-import groovy.lang.GroovyShell;
-import groovy.lang.Binding;
-
-import java.io.PrintWriter;
-import javax.annotation.CheckForNull;
-import javax.annotation.Nonnull;
-import java.io.IOException;
+import io.jenkins.plugins.casc.model.Mapping;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
import java.util.Set;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+import jenkins.model.Jenkins;
+import org.kohsuke.accmod.Restricted;
+import org.kohsuke.accmod.restrictions.NoExternalUse;
+
+import static io.vavr.API.Try;
+import static io.vavr.API.unchecked;
/**
* @author Tomasz Szandala
*/
-@Extension(optional = true)
+@Extension(optional = true, ordinal = -60) // Ordinal -60 Ensure it is loaded after JobDSL
@Restricted(NoExternalUse.class)
public class GroovyScriptCaller implements RootElementConfigurator {
@Override
+ @Nonnull
public String getName() {
return "groovy";
}
@@ -45,6 +45,7 @@ public Class getTarget() {
}
@Override
+ @Nonnull
public Set> describe() {
return Collections.singleton(new MultivaluedAttribute("", GroovyScriptSource.class));
}
@@ -55,42 +56,45 @@ public Boolean[] getTargetComponent(ConfigurationContext context) {
}
@Override
+ @Nonnull
public Boolean[] configure(CNode config, ConfigurationContext context) throws ConfiguratorException {
- //JenkinsJobManagement mng = new JenkinsJobManagement(System.out, new EnvVars(), null, null, LookupStrategy.JENKINS_ROOT);
- final Sequence sources = config.asSequence();
- final Configurator con = context.lookup(GroovyScriptSource.class);
- List generated = new ArrayList<>();
- for (CNode source : sources) {
- final String script;
- try {
- script = con.configure(source, context).getScript();
- } catch (IOException e) {
- throw new ConfiguratorException(this, "Failed to retrieve Groovy script", e);
- }
- try {
- //Binding binding = new Binding();
- //binding.setVariable("foo", new Integer(2));
- //GroovyShell shell = new GroovyShell();
- //shell.evaluate(script);
-
- Binding binding = new Binding();
- //binding.setProperty("out",new PrintWriter(stdout,true));
- //binding.setProperty("stdin",stdin);
- //binding.setProperty("stdout",stdout);
- //binding.setProperty("stderr",stderr);
-
- GroovyShell groovy = new GroovyShell(Jenkins.getActiveInstance().getPluginManager().uberClassLoader, binding);
- groovy.run(script, "ConfigurationAsCodeGroovy", new ArrayList());
-
- generated.add(true);
-
- } catch (Exception ex) {
- throw new ConfiguratorException(this, "Failed to execute script with hash " + script.hashCode(), ex);
- }
- }
- return generated.toArray(new Boolean[generated.size()]);
+ final Configurator c = context.lookup(GroovyScriptSource.class);
+ return config.asSequence().stream()
+ .map(source -> getActualValue(source, context))
+ .map(source -> getScriptFromSource(source, context, c))
+ .map(script -> Try(script::getScript).onSuccess(GroovyScriptCaller.this::runGroovyShell).isSuccess())
+ .toArray(Boolean[]::new);
+ }
+
+ private CNode getActualValue(CNode source, ConfigurationContext context) {
+ return unchecked(() -> source.asMapping().entrySet().stream().findFirst()).apply()
+ .map(entry -> resolveSourceOrGetValue(entry, context))
+ .orElse(source);
}
+ private CNode resolveSourceOrGetValue(Map.Entry entry, ConfigurationContext context) {
+ final Mapping m = new Mapping();
+ m.put(
+ entry.getKey(),
+ context.getSecretSourceResolver().resolve(unchecked(() -> entry.getValue().asScalar().getValue()).apply())
+ );
+ return m;
+ }
+
+ private GroovyScriptSource getScriptFromSource(CNode source, ConfigurationContext context,
+ Configurator configurator) {
+ return unchecked(() ->
+ Try(() -> configurator.configure(source, context))
+ .getOrElseThrow(t -> new ConfiguratorException(this,
+ "Failed to retrieve groovy script", t))).apply();
+ }
+
+ private void runGroovyShell(String script) {
+ final GroovyShell s = new GroovyShell(Jenkins.get().getPluginManager().uberClassLoader, new Binding());
+ unchecked(() -> s.run(script, "ConfigurationAsCodeGroovy", new ArrayList()));
+ }
+
+
@Override
public Boolean[] check(CNode config, ConfigurationContext context) throws ConfiguratorException {
// Any way to dry-run a Groovy script ?
@@ -99,7 +103,7 @@ public Boolean[] check(CNode config, ConfigurationContext context) throws Config
@Nonnull
@Override
- public List getConfigurators(ConfigurationContext context) {
+ public List> getConfigurators(ConfigurationContext context) {
return Collections.singletonList(context.lookup(GroovyScriptSource.class));
}
@@ -108,4 +112,5 @@ public List getConfigurators(ConfigurationContext context) {
public CNode describe(Boolean[] instance, ConfigurationContext context) throws Exception {
return null;
}
+
}
diff --git a/src/test/java/io/jenkins/plugins/cascgroovy/GroovyScriptTest.java b/src/test/java/io/jenkins/plugins/cascgroovy/GroovyScriptTest.java
new file mode 100644
index 0000000..20ff911
--- /dev/null
+++ b/src/test/java/io/jenkins/plugins/cascgroovy/GroovyScriptTest.java
@@ -0,0 +1,24 @@
+package io.jenkins.plugins.cascgroovy;
+
+import io.jenkins.plugins.casc.misc.ConfiguredWithCode;
+import io.jenkins.plugins.casc.misc.JenkinsConfiguredWithCodeRule;
+import jenkins.model.Jenkins;
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.Test;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
+
+public class GroovyScriptTest {
+ @Rule
+ public JenkinsConfiguredWithCodeRule j = new JenkinsConfiguredWithCodeRule();
+
+ @Test
+ @ConfiguredWithCode("casc.yaml")
+ @Ignore
+ public void configure() throws Exception {
+ Jenkins jenkins = Jenkins.get();
+ assertThat(jenkins.getSystemMessage(), is("Hello World"));
+ }
+}
diff --git a/src/test/resources/io/jenkins/plugins/cascgroovy/casc.yaml b/src/test/resources/io/jenkins/plugins/cascgroovy/casc.yaml
new file mode 100644
index 0000000..7dd60fc
--- /dev/null
+++ b/src/test/resources/io/jenkins/plugins/cascgroovy/casc.yaml
@@ -0,0 +1,8 @@
+groovy:
+ - script: |
+ import jenkins.model.Jenkins;
+
+ def systemMessage = "Hello World";
+ def jenkins = Jenkins.get();
+ jenkins.setSystemMessage(systemMessage);
+ jenkins.save();