From 71c33045d97514ecb821839dbddc761edb6ce17f Mon Sep 17 00:00:00 2001 From: Nigel Magnay Date: Sat, 10 Sep 2016 17:16:20 +0100 Subject: [PATCH 01/14] Initial configurings. Signed-off-by: Nigel Magnay --- .gitignore | 3 + pom.xml | 40 ++ snowglobe-core/pom.xml | 131 ++++ .../groovy/com/nirima/snowglobe/SGExec.groovy | 330 ++++++++++ .../com/nirima/snowglobe/consul/Consul.groovy | 116 ++++ .../snowglobe/consul/ConsulActions.groovy | 66 ++ .../com/nirima/snowglobe/core/Context.groovy | 580 ++++++++++++++++++ .../com/nirima/snowglobe/core/DSL.groovy | 392 ++++++++++++ .../nirima/snowglobe/core/Interfaces.groovy | 27 + .../com/nirima/snowglobe/core/System.groovy | 85 +++ .../com/nirima/snowglobe/docker/Docker.groovy | 303 +++++++++ .../snowglobe/docker/DockerActions.groovy | 159 +++++ .../com/nirima/snowglobe/graph/SGNode.groovy | 344 +++++++++++ .../nirima/snowglobe/plan/PlanBuilder.groovy | 251 ++++++++ .../com/nirima/snowglobe/plan/PlanType.java | 9 + .../resources/com/nirima/simple-docker.sg | 45 ++ .../src/test/resources/logback.groovy | 36 ++ snowglobe-exe/pom.xml | 64 ++ .../java/com/nirima/snowglobe/Action.java | 10 + .../com/nirima/snowglobe/SnowGlobeApp.java | 103 ++++ snowglobe-jenkins-plugin/pom.xml | 182 ++++++ .../jenkins/plugins/snowglobe/Consts.java | 18 + .../SnowGlobePluginConfiguration.java | 41 ++ .../snowglobe/action/RegisteredScript.java | 33 + .../action/RegisteredScriptAction.java | 105 ++++ .../plugins/snowglobe/action/RunLink.java | 24 + .../snowglobe/action/SnowGlobeAction.java | 177 ++++++ .../snowglobe/action/SnowGlobeData.java | 90 +++ .../snowglobe/calls/SnowGlobeStep.java | 90 +++ .../plugins/snowglobe/source/GlobeSource.java | 34 + .../source/GlobeSourceDescriptor.java | 15 + .../snowglobe/source/GlobeSourceExisting.java | 38 ++ .../snowglobe/source/GlobeSourceFile.java | 53 ++ .../snowglobe/source/GlobeSourceScript.java | 40 ++ .../SnowGlobePluginConfiguration/config.jelly | 11 + .../action/RegisteredScriptAction/index.jelly | 23 + .../RegisteredScriptAction/summary.jelly | 12 + .../action/SnowGlobeAction/index.jelly | 40 ++ .../action/SnowGlobeAction/summary.jelly | 11 + .../calls/SnowGlobeApplyStep/config.jelly | 16 + .../calls/SnowGlobeDestroyStep/config.jelly | 10 + .../SnowGlobeRegisterScript/config.jelly | 15 + .../calls/SnowGlobeStep/config.jelly | 19 + .../source/GlobeSourceExisting/config.jelly | 10 + .../source/GlobeSourceFile/config.jelly | 14 + .../source/GlobeSourceScript/config.jelly | 14 + .../main/webapp/images/32x32/snow-globe.png | Bin 0 -> 1176 bytes .../src/main/webapp/js/snowglobe-manage.js | 34 + snowglobe-shaded/pom.xml | 283 +++++++++ 49 files changed, 4546 insertions(+) create mode 100644 pom.xml create mode 100644 snowglobe-core/pom.xml create mode 100644 snowglobe-core/src/main/groovy/com/nirima/snowglobe/SGExec.groovy create mode 100644 snowglobe-core/src/main/groovy/com/nirima/snowglobe/consul/Consul.groovy create mode 100644 snowglobe-core/src/main/groovy/com/nirima/snowglobe/consul/ConsulActions.groovy create mode 100644 snowglobe-core/src/main/groovy/com/nirima/snowglobe/core/Context.groovy create mode 100644 snowglobe-core/src/main/groovy/com/nirima/snowglobe/core/DSL.groovy create mode 100644 snowglobe-core/src/main/groovy/com/nirima/snowglobe/core/Interfaces.groovy create mode 100644 snowglobe-core/src/main/groovy/com/nirima/snowglobe/core/System.groovy create mode 100644 snowglobe-core/src/main/groovy/com/nirima/snowglobe/docker/Docker.groovy create mode 100644 snowglobe-core/src/main/groovy/com/nirima/snowglobe/docker/DockerActions.groovy create mode 100644 snowglobe-core/src/main/groovy/com/nirima/snowglobe/graph/SGNode.groovy create mode 100644 snowglobe-core/src/main/groovy/com/nirima/snowglobe/plan/PlanBuilder.groovy create mode 100644 snowglobe-core/src/main/java/com/nirima/snowglobe/plan/PlanType.java create mode 100644 snowglobe-core/src/test/resources/com/nirima/simple-docker.sg create mode 100644 snowglobe-core/src/test/resources/logback.groovy create mode 100644 snowglobe-exe/pom.xml create mode 100644 snowglobe-exe/src/main/java/com/nirima/snowglobe/Action.java create mode 100644 snowglobe-exe/src/main/java/com/nirima/snowglobe/SnowGlobeApp.java create mode 100644 snowglobe-jenkins-plugin/pom.xml create mode 100644 snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/Consts.java create mode 100644 snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/SnowGlobePluginConfiguration.java create mode 100644 snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/action/RegisteredScript.java create mode 100644 snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/action/RegisteredScriptAction.java create mode 100644 snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/action/RunLink.java create mode 100644 snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/action/SnowGlobeAction.java create mode 100644 snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/action/SnowGlobeData.java create mode 100644 snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/calls/SnowGlobeStep.java create mode 100644 snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/source/GlobeSource.java create mode 100644 snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/source/GlobeSourceDescriptor.java create mode 100644 snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/source/GlobeSourceExisting.java create mode 100644 snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/source/GlobeSourceFile.java create mode 100644 snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/source/GlobeSourceScript.java create mode 100644 snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/SnowGlobePluginConfiguration/config.jelly create mode 100644 snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/action/RegisteredScriptAction/index.jelly create mode 100644 snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/action/RegisteredScriptAction/summary.jelly create mode 100644 snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/action/SnowGlobeAction/index.jelly create mode 100644 snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/action/SnowGlobeAction/summary.jelly create mode 100644 snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/calls/SnowGlobeApplyStep/config.jelly create mode 100644 snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/calls/SnowGlobeDestroyStep/config.jelly create mode 100644 snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/calls/SnowGlobeRegisterScript/config.jelly create mode 100644 snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/calls/SnowGlobeStep/config.jelly create mode 100644 snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/source/GlobeSourceExisting/config.jelly create mode 100644 snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/source/GlobeSourceFile/config.jelly create mode 100644 snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/source/GlobeSourceScript/config.jelly create mode 100644 snowglobe-jenkins-plugin/src/main/webapp/images/32x32/snow-globe.png create mode 100644 snowglobe-jenkins-plugin/src/main/webapp/js/snowglobe-manage.js create mode 100644 snowglobe-shaded/pom.xml diff --git a/.gitignore b/.gitignore index 32858aa..a4b6fee 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,6 @@ # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* +target +.idea +*.iml diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..63b1a91 --- /dev/null +++ b/pom.xml @@ -0,0 +1,40 @@ + + + 4.0.0 + com.nirima + snowglobe + pom + 0.1-SNAPSHOT + + + 1.5 + 2.0 + 2.4.7 + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.3 + + 1.8 + 1.8 + UTF-8 + true + + + + + + + + + snowglobe-core + snowglobe-exe + snowglobe-shaded + snowglobe-jenkins-plugin + + + diff --git a/snowglobe-core/pom.xml b/snowglobe-core/pom.xml new file mode 100644 index 0000000..b672f7a --- /dev/null +++ b/snowglobe-core/pom.xml @@ -0,0 +1,131 @@ + + + 4.0.0 + com.nirima.snowglobe + snowglobe-core + + + com.nirima + snowglobe + 0.1-SNAPSHOT + + + + + + + + + org.codehaus.gmavenplus + gmavenplus-plugin + ${gmavenVersion} + + + + addSources + addTestSources + + compile + + testCompile + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + junit + junit + 4.12 + test + + + org.codehaus.groovy + groovy-all + + 2.4.7 + + + com.github.docker-java + docker-java + 3.0.6-jenkins + + + + + + + + com.orbitz.consul + consul-client + 0.12.7 + + + org.javers + javers-core + 2.1.2 + + + org.slf4j + slf4j-api + 1.7.21 + + + ch.qos.logback + logback-core + 1.1.7 + test + + + ch.qos.logback + logback-classic + 1.1.7 + test + + + + + + false + + central + bintray + http://jcenter.bintray.com + + + + diff --git a/snowglobe-core/src/main/groovy/com/nirima/snowglobe/SGExec.groovy b/snowglobe-core/src/main/groovy/com/nirima/snowglobe/SGExec.groovy new file mode 100644 index 0000000..2f7c9e0 --- /dev/null +++ b/snowglobe-core/src/main/groovy/com/nirima/snowglobe/SGExec.groovy @@ -0,0 +1,330 @@ +package com.nirima.snowglobe + +import com.nirima.snowglobe.core.Context +import com.nirima.snowglobe.core.Core +import com.nirima.snowglobe.core.Module +import com.nirima.snowglobe.core.Provider +import com.nirima.snowglobe.core.Resource +import com.nirima.snowglobe.core.SnowGlobe +import com.nirima.snowglobe.core.SnowGlobeContext +import com.nirima.snowglobe.core.SnowGlobeSystem +import com.nirima.snowglobe.core.State +import com.nirima.snowglobe.graph.Graph +import com.nirima.snowglobe.graph.GraphBuilder +import com.nirima.snowglobe.plan.Plan +import com.nirima.snowglobe.plan.PlanBuilder +import com.nirima.snowglobe.plan.PlanType +import com.nirima.snowglobe.state.* +import org.codehaus.groovy.runtime.InvokerHelper + +import java.lang.reflect.Field +import java.lang.reflect.Modifier +import java.security.AccessController +import java.security.PrivilegedAction + +/** + * Created by magnayn on 06/09/2016. + */ +class SGExec { + + InputStream planFile, stateFile; + + SnowGlobeSystem dsl = new SnowGlobeSystem(); + public SnowGlobe snowGlobe; + public SnowGlobe stateGlobe; + private Graph g,stateGraph; + + private SnowGlobeContext sgContext; + + public SGExec( File plan) { + + this.planFile = new FileInputStream(plan) + + init(); + } + + SGExec( + File file, File file1) { + + this.planFile = new FileInputStream(file) + if( file1 != null && file1.exists()) + this.stateFile = new FileInputStream(file1); + init(); + } + + SGExec( + InputStream file, InputStream state) { + + planFile = file; + stateFile = state; + + + init(); + } + + String save() { + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + SGWriter sw = new SGWriter(byteArrayOutputStream); + sw.write(snowGlobe); + byteArrayOutputStream.close(); + return new String(byteArrayOutputStream.toByteArray()); + } + + void init() { + dsl.parseScript( planFile ); + snowGlobe = dsl.runScript(); + + sgContext = new SnowGlobeContext(snowGlobe); + sgContext.initModules(); + + + if( stateFile != null ) { + dsl.parseScript(stateFile); + stateGlobe = dsl.runScript(); + + //SnowGlobeContext sctxt = dsl.getState(stateGlobe); + SnowGlobeContext stContext = new SnowGlobeContext(stateGlobe); + + stContext.initModules(); + + // Don't init it. + + mergeState(snowGlobe, stateGlobe); + + } + + + + sgContext.buildModules(); + + g = new GraphBuilder().build(sgContext); + } + + void mergeState(SnowGlobe globe, SnowGlobe state) { + assert globe != null + assert state != null + + state.modules.each { stateModule -> + + Module globeModule = globe.getModule(stateModule.id); + if( globeModule == null ) { + globe.addModule( stateModule ) + } else { + // it existed + + stateModule.resources.each { stateResource -> + Resource globeResource = globeModule.getResource(stateResource.getClass(), stateResource.id ); + + if( globeResource == null ) { + globeModule.addResource( stateResource ); + stateResource.savedState = stateResource.state; + stateResource.state = null; + } else { + // It existed + globeResource.savedState = stateResource.state; + globeResource.savedState.resource = globeResource; + } + + } + + } + + } + + + } + + public void graph(OutputStream os) { + String out = new GraphBuilder().graphViz(g); + os.write(out.getBytes()); + } + + public void apply() { + PlanBuilder pb = new PlanBuilder(); + + Plan plan = pb.buildPlan(sgContext, PlanType.Apply); + + plan.execute(); + + + } + + public void destroy() { + PlanBuilder pb = new PlanBuilder(); + + Plan plan = pb.buildPlan(sgContext, PlanType.Destroy); + + plan.execute(); + + + } + + +} + +public class SGWriter { + + OutputStream os; + + public SGWriter(OutputStream os) { + this.os = os; + } + + public void write(SnowGlobe sg) { + os << "snowglobe {\n" + + sg.modules.each { + module -> + os << " module(\"${module.id}\") { \n"; + + + module.resources.each { resource -> + + + write(resource); + + } + + os << " }\n"; + } + + os << "}\n" + } + + public void write(Resource resource) { + + State self = resource.savedState; + if( self != null ) { + String name = Core.INSTANCE.getNameForClass(resource.getClass()); + os << " ${name}(\"${resource.id}\") {\n" + os << writeObject(self); + os << " }\n"; + } + } + public String writeObject(Object self) { + + String os = ""; + + Class klass = self.getClass(); + boolean groovyObject = self instanceof GroovyObject; + + while (klass != null && klass != State.class) { + for (final Field field : klass.getDeclaredFields()) { + if ((field.getModifiers() & Modifier.STATIC) == 0) { + if (groovyObject && field.getName().equals("metaClass")) { + continue; + } + AccessController.doPrivileged(new PrivilegedAction() { + public Object run() { + field.setAccessible(true); + return null; + } + }); + + try { + Object value = field.get(self); + if( value instanceof Context ) + value = ((Context)value).getProxy(); + + if( value instanceof List ) { + + if( value.size() == 0 ) + continue; + // Could introstpect the type maybe + Object item = value.get(0); + if( !item.getClass().isPrimitive() && !(item.getClass() == String.class || item instanceof GString) ) { + + value.each { + os += " ${field.getName()} { \n"; + + os += writeObject(it); + + os += " }\n" + } + + + continue; + } + + } + + + os += " ${field.getName()} = "; + + + if( value instanceof Provider ) { + String name = Core.INSTANCE.getNameForClass( value.getClass() ); + String id = value.id; + if( id == null ) + os += " ${name}(null)"; + else + os += " ${name}(\"${value.id}\")"; + } else { + os += stringObject(value); + + + } + os += "\n"; + } catch (Exception e) { + + } + + } + } + + klass = klass.getSuperclass(); + + } + + return os; + + } + + public String stringObject(Object value) { + if( value == null ) + return "null"; + + if( value instanceof String || value instanceof GString ) { + return getValue(value); + } + if( value instanceof Map ) { + String obj = null; + value.each { + if( obj == null ) obj = "[" else obj += ", "; + + obj = obj + "${stringObject(it.key)} : ${stringObject(it.value)}" + } + return obj + "]"; + } + + if( value instanceof List ) { + String obj = null; + value.each { + if( obj == null ) obj = "[" else obj += ", "; + + obj = obj + "${stringObject(it)}" + } + return obj + "]"; + } + + return InvokerHelper.toString(value) + } + + public String getProperty(property) { + "${property.key} = ${getValue(property.value)}" + } + + public String getValue(Object o) { + if( o instanceof String || o instanceof GString) { + // TODO : quoted string + + String qs = o.replace("\\", "\\\\") + .replace("\$", "\\\$") + .replace("\"", "\\\""); + + return "\"${qs}\""; + } + + return "${o}"; + } +} \ No newline at end of file diff --git a/snowglobe-core/src/main/groovy/com/nirima/snowglobe/consul/Consul.groovy b/snowglobe-core/src/main/groovy/com/nirima/snowglobe/consul/Consul.groovy new file mode 100644 index 0000000..aae3f3c --- /dev/null +++ b/snowglobe-core/src/main/groovy/com/nirima/snowglobe/consul/Consul.groovy @@ -0,0 +1,116 @@ +package com.nirima.snowglobe.consul + +import com.google.common.net.HostAndPort +import com.nirima.snowglobe.core.ResourceState +import com.nirima.snowglobe.core.Module +import com.nirima.snowglobe.core.Provider +import com.nirima.snowglobe.core.Resource +import com.nirima.snowglobe.plan.NodePair +import com.nirima.snowglobe.plan.PlanAction +import com.orbitz.consul.Consul + +/** + * Created by magnayn on 04/09/2016. + */ +class ConsulProvider extends Provider { + + public String address; + + ConsulProvider(Module module, String id, Closure closure) { + super(module, id, closure) + } + + public Consul getConsulClient() { + + Thread.sleep(1000); + + Consul consul = Consul.builder() + .withHostAndPort(HostAndPort.fromString(address)) + .build(); + + return consul; + } +} + +class ConsulKeyPrefixState extends ResourceState { + String path_prefix; + Map subkeys; + + ConsulKeyPrefixState(Resource parent, Closure closure) { + super(parent, closure); + } + + Closure getDefaults() { + return { + if(provider == null) { + provider = consul_provider(null); + } + } + } +} + +class ConsulKeyPrefix extends Resource { + + + ConsulKeyPrefix(Module module, String id, Closure closure) { + super(module, id, closure) + } + + + public PlanAction assess() { + return new ConsulKeyPrefixAction(this); + } +} + +class ConsulKeysStateKey implements Serializable { + String path; + String value; +} + + +class ConsulKeysState extends ResourceState { + List key = []; + + ConsulKeysState(Resource parent, Closure closure) { + super(parent, closure); + } + + public void key(Closure c) { + ConsulKeysStateKey p = new ConsulKeysStateKey(); + c.delegate = p; + c.resolveStrategy = Closure.DELEGATE_FIRST + + c() + + key << p; + } + + @Override + void accept(Object context) { + + key=[] + + super.accept(context) + } + + Closure getDefaults() { + return { + if(provider == null) { + provider = consul_provider(null); + } + } + } +} + +class ConsulKeys extends Resource { + + + ConsulKeys(Module module, String id, Closure closure) { + super(module, id, closure) + } + + public PlanAction assess() { + return new ConsulKeysAction(this); + } + +} diff --git a/snowglobe-core/src/main/groovy/com/nirima/snowglobe/consul/ConsulActions.groovy b/snowglobe-core/src/main/groovy/com/nirima/snowglobe/consul/ConsulActions.groovy new file mode 100644 index 0000000..923428e --- /dev/null +++ b/snowglobe-core/src/main/groovy/com/nirima/snowglobe/consul/ConsulActions.groovy @@ -0,0 +1,66 @@ +package com.nirima.snowglobe.consul + +import com.github.dockerjava.api.DockerClient +import com.github.dockerjava.api.command.CreateContainerCmd +import com.nirima.snowglobe.docker.DockerContainerState +import com.nirima.snowglobe.docker.DockerProvider +import com.nirima.snowglobe.plan.NodePair +import com.nirima.snowglobe.plan.PlanAction +import com.nirima.snowglobe.plan.PlanActionBase +import com.orbitz.consul.Consul +import com.orbitz.consul.KeyValueClient + + + + +class ConsulKeyPrefixAction extends PlanActionBase { + + + ConsulKeyPrefixAction(ConsulKeyPrefix nodePair) { + super(nodePair) + } + + + @Override + ConsulKeyPrefixState create(ConsulKeyPrefixState desiredState) { + ConsulProvider dp = desiredState.getProvider(); + Consul client = dp.getConsulClient(); + KeyValueClient kvClient = client.keyValueClient(); + + String prefix = desiredState.path_prefix; + + desiredState.subkeys.each { + it -> + String key = prefix + it.key; + kvClient.putValue(key, it.value); + } + + + return desiredState; + } + +} + +class ConsulKeysAction extends PlanActionBase { + + + ConsulKeysAction(ConsulKeys nodePair) { + super(nodePair) + } + + @Override + ConsulKeysState create(ConsulKeysState desiredState) { + ConsulProvider dp = desiredState.getProvider(); + Consul client = dp.getConsulClient(); + KeyValueClient kvClient = client.keyValueClient(); + + desiredState.key.each { + kvClient.putValue(it.path, it.value); + } + + return desiredState + } + + + +} \ No newline at end of file diff --git a/snowglobe-core/src/main/groovy/com/nirima/snowglobe/core/Context.groovy b/snowglobe-core/src/main/groovy/com/nirima/snowglobe/core/Context.groovy new file mode 100644 index 0000000..ffd0692 --- /dev/null +++ b/snowglobe-core/src/main/groovy/com/nirima/snowglobe/core/Context.groovy @@ -0,0 +1,580 @@ +package com.nirima.snowglobe.core + +import com.google.common.base.MoreObjects +import com.google.common.base.Objects + +/** + * Created by magnayn on 04/09/2016. + */ +abstract class Context { + private T proxy; + Context(T t) { + proxy = t; + } + + public T getProxy() { return proxy; } + + + public String toString() { + return "Context<${getProxy()}>"; + } +} + +public class Dependency { + Context from; + Context to; + + public Dependency(Context from, Context to) { + assert(from != null); + assert(to != null); + assert(from != to); + + this.from = from + this.to = to + } + + boolean equals(o) { + if (this.is(o)) { + return true + } + if (getClass() != o.class) { + return false + } + + Dependency that = (Dependency) o + + if (from != that.from) { + return false + } + if (to != that.to) { + return false + } + + return true + } + + int hashCode() { + int result + result = (from != null ? from.hashCode() : 0) + result = 31 * result + (to != null ? to.hashCode() : 0) + return result + } + + @Override + public String toString() { + return Objects.toStringHelper(this) + .add("from", from) + .add("to", to) + .toString(); + } +} + +public class SnowGlobeContext extends Context { + + List modules = []; + Set dependencies = []; + + Set processedModules = []; + Set inProcessModules = []; + + + SnowGlobeContext(SnowGlobe proxy) { + super(proxy); + } + + def invokeMethod(String name, Object args) { + Object result = getProxy().invokeMethod(name, args); + if (result instanceof Module) { + ModuleContext child = new ModuleContext(result, this); + modules << child; + result.accept(child) + } + return result; + } + + public void build() { + // Initial visit goes down as far as the modules + initModules(); + + buildModules(); + } + + public void buildModules() { + println("Build Modules") + modules.each { + processModule(it); + } + } + + public void initModules() { + modules = []; + dependencies = []; + processedModules = []; + inProcessModules = []; + println("Visit Modules") + getProxy().accept(this); + } + + private void processModule(ModuleContext context) { + println("Build Module ${context}") + + if( processedModules.contains(context)) + return; + + if( inProcessModules.contains(context) ) + throw new IllegalStateException("Circular dependency on ${context}"); + + inProcessModules.add(context); + context.build(); + inProcessModules.remove(context); + + processedModules.add(context); + println("Built Module ${context}") + } + + public Context getElement(ModuleContext from, Class klass, String id) { + ModuleContext result = modules. + find { it.getProxy().getClass() == klass && it.getProxy().id == id } + if (result == null) + throw IllegalStateException("Cannot find resource ${id} type ${klass}"); + + dependencies << new Dependency(from, result); + + // + if( !processedModules.contains(result) ) + processModule(result); + + return result; + } + + Context getContextFor(Object o) { + Context result = modules.collect { + it.getContextFor(o) + }.find { + it != null } + return result; + } +} + + + +public class ProviderContext extends Context { + ModuleContext parent; + + ProviderContext(Provider proxy, ModuleContext parent) { + super(proxy); + this.parent = parent + } + + def getProperty(String name) { + try { + Provider resource = getProxy(); + return resource.getProperty(name); + } catch(Exception ex) { + println "State for resource " + getProxy() + " does not have property " + name; + throw ex; + } + } + + void setProperty(String name, Object value) { + + println " set ${name}=${value} on ${this}" + + try { + getProxy().setProperty(name,value); + } + catch( Exception ex ) { + println("Error setting state for resource ${getProxy()} property ${name} with value ${value}"); + println ex; + throw ex; + } + + + + } + +} + + +public class ModuleReferenceContext + //extends Context +{ + ModuleContext referenceFrom, referenceTo; + + ModuleReferenceContext(ModuleContext from, ModuleContext to) { + //super(from.getProxy()); + + assert(from != null); + assert(to!=null); + + this.referenceFrom = from; + this.referenceTo = to; + } + def invokeMethod(String name, Object args) { + Object[] theArgs = (Object[]) args; + + Class c = Core.INSTANCE.getClassForName(name); + referenceTo.getElement(referenceFrom,c,(String)theArgs[0]) + } +} + +public class ModuleImportContext extends Context { + ModuleContext parent; + + ModuleImportContext( ModuleImports moduleImports,ModuleContext parent) { + super(moduleImports) + this.parent = parent; + } + + // Within a resource this is used to refer to another resource + def invokeMethod(String name, Object args) { + Object[] theArgs = (Object[])args; + + if( name == "module") { + // Refers to another module + return parent.getElement(this, Module.class, (String)theArgs[0]); + } + + Class c = Core.INSTANCE.getClassForName(name); + + if( c == null ) { + return getProxy().invokeMethod(name, args); + } + + String id = null; + if( ((Object[])args).length > 0 ) + id = ((Object[])args)[0]; + + return parent.getElement(this, c, id); + } +} + +public class ModuleContext extends Context{ + SnowGlobeContext parent; + + List imports = []; + List resources = null; + List providers = []; + List dataSources = []; + + Set processedResources = []; + Set inProcessResources = []; + + Set dependencies = []; + + ModuleContext(Module proxy, SnowGlobeContext parent) { + super(proxy); + this.parent = parent; + } + + Context getContextFor(Object o) { + if( o == proxy ) + return this; + + Context result = resources.find { it.getContextFor(o) != null } + + + return result; + } + + Object invokeMethod(String name, Object args) { + Object result = proxy.invokeMethod(name,args); + if( result instanceof Resource ) { + getProxy().addResource(result); + +// ResourceContext child = new ResourceContext(result, this); +// resources << child; + + + // result.accept( child ) DO NOT ACCEPT THIS YET + } + else if( result instanceof DataSource ) { + DataSourceContext child = new DataSourceContext(result, this); + dataSources << child; + } + else if( result instanceof Provider ) { + ProviderContext child = new ProviderContext(result, this); + providers << child; + } else if( result instanceof ModuleImports ) { + ModuleImportContext child = new ModuleImportContext(result, this); + imports << child; + } + return result; + } + + public void build() { + imports.each() { + it.getProxy().accept(it); + } + + providers.each() { + it.getProxy().accept(it) + } + + dataSources.each() { + it.build() + } + + getResources().each() { + processResource(it); + } + } + + public List getResources() { + if( resources == null ) { + resources = []; + + getProxy().resources.each() { + ResourceContext child = new ResourceContext(it, this); + resources << child; + } + } + return resources; + } + + private void processResource(ResourceContext context) { + if( processedResources.contains(context)) + return; + + if( inProcessResources.contains(context) ) + throw new IllegalStateException("Circular dependency on ${context}"); + + inProcessResources.add(context); + + + context.build(); + + + + + inProcessResources.remove(context); + + processedResources.add(context); + } + + public Context findImport(Class klass, String id) { + for(ModuleImportContext mic : imports) { + Object ctx = mic.getProxy().references.find { + it.getProxy().getClass() == klass && it.getProxy().id == id + } + if( ctx !=null ) + return (Context)ctx; + } + return null; + } + + public Object getElement(Context from, Class klass, String id) { + + println("ModuleContext.getElement ${from} wants {$klass} of id ${id}"); + + if(klass == Module.class ) { + // Need to ask the parent for a module. + Object context = parent.getElement(this, klass, id); + + return new ModuleReferenceContext(this, context); + } + + ProviderContext provider = providers.find { it.getProxy().getClass() == klass && it.getProxy().id == id } + if( provider != null ) { + dependencies << new Dependency(from, provider); + return provider; + } + + DataSourceContext dataSource = dataSources.find { it.getProxy().getClass() == klass && it.getProxy().id == id } + if( dataSource != null ) { + dependencies << new Dependency(from, dataSource); + return dataSource; + } + + ResourceContext result = resources.find { it.getProxy().getClass() == klass && it.getProxy().id == id } + if( result == null ) { + // Perhaps it was imported in the imports {} section + + Context ctx = findImport(klass, id); + if( ctx != null ) + return ctx; + } + + if( result == null ) + throw new IllegalStateException("Cannot find resource ${id} type ${klass}"); + + dependencies << new Dependency(from, result); + // + if( !processedResources.contains(result) ) + processResource(result); + + return result; + } + +} + + +public class StateContext { + + public Context parent; + public ModuleContext moduleContext; + + // Actual object + private State proxy; + + StateContext( + State state, Context context, ModuleContext moduleContext) { + assert context != null + this.parent = context; + this.moduleContext = moduleContext; + this.proxy = state; + } + + def invokeMethod(String name, Object args) { + Object[] theArgs = (Object[]) args; + + if (name == "module") { + // Refers to another module + return moduleContext.getElement(parent, Module.class, (String) theArgs[0]); + } + + Class c = Core.INSTANCE.getClassForName(name); + + if (c == null) { + return getProxy().invokeMethod(name, args); + } + + String id = null; + if (((Object[]) args).length > 0) + id = ((Object[]) args)[0]; + + ModuleContext mc = moduleContext; + mc.getElement(parent, c, id); + } + + + State getProxy() { + return proxy + } +// To get a value + def getProperty(String name) { + + println " get ${name}? on ${this}" + + try { + State resource = getProxy(); + return resource.getProperty(name); + } catch (Exception ex) { + println "State for resource " + getProxy() + " does not have property " + name; + throw ex; + } + } + + void setProperty(String name, Object value) { + + println " set ${name}=${value} on ${this}" + + try { + getProxy().setProperty(name, value); + } + catch (Exception ex) { + println( + "Error setting state for resource ${getProxy()} property ${name} with value ${value}"); + println ex; + throw ex; + } + + println " ±set ${name}=${value} on ${this}" + + } + + void build() { + if( getProxy() != null ) + getProxy().accept(this) + } + + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("parent", parent) + .toString(); + } +} + +public class DataSourceContext extends Context { + + public ModuleContext moduleContext; + public StateContext stateContext; + + DataSourceContext(DataSource proxy, ModuleContext moduleContext) { + super(proxy) + this.moduleContext = moduleContext + + stateContext = new StateContext(proxy.state,this, moduleContext); + } + + def build() { + // getProxy().accept(this) + if (stateContext != null) + stateContext.build() + } + + def propertyMissing(String name) { + try { + DataSource resource = getProxy(); + + try { + return resource.getState().getProperty(name); + } catch(MissingPropertyException e) { + return resource.getProperty(name); + } + } catch(Exception ex) { + println "State for resource " + getProxy() + " does not have property " + name; + throw ex; + } + + + } + +} + +public class ResourceContext extends Context { + + public ModuleContext moduleContext; + + public StateContext stateContext; + public StateContext savedStateContext; + + + ResourceContext(Resource proxy, ModuleContext moduleContext) { + super(proxy); + this.moduleContext = moduleContext + + stateContext = new StateContext(proxy.state,this, moduleContext); + if( proxy.savedState != null ) + savedStateContext = new StateContext(proxy.savedState,this, moduleContext); + + } + + Context getContextFor(Object o) { + if( o == proxy ) + return this; + + return null; + } + + def propertyMissing(String name) { + try { + Resource resource = getProxy(); + return resource.getState().getProperty(name); + } catch(Exception ex) { + println "State for resource " + getProxy() + " does not have property " + name; + throw ex; + } + } + + + def build() { + // getProxy().accept(this) + if( stateContext != null ) + stateContext.build() + if( savedStateContext != null ) + savedStateContext.build() + } +} \ No newline at end of file diff --git a/snowglobe-core/src/main/groovy/com/nirima/snowglobe/core/DSL.groovy b/snowglobe-core/src/main/groovy/com/nirima/snowglobe/core/DSL.groovy new file mode 100644 index 0000000..758efe1 --- /dev/null +++ b/snowglobe-core/src/main/groovy/com/nirima/snowglobe/core/DSL.groovy @@ -0,0 +1,392 @@ +package com.nirima.snowglobe.core + +i +import com.nirima.snowglobe.consul.ConsulKeyPrefix +import com.nirima.snowglobe.consul.ConsulKeys +import com.nirima.snowglobe.docker.DockerImage +import com.nirima.snowglobe.consul.ConsulProvider +import com.nirima.snowglobe.docker.DockerContainer +import com.nirima.snowglobe.docker.DockerProvider +import com.nirima.snowglobe.docker.DockerRegistry +import com.nirima.snowglobe.docker.DockerRegistryImage +import com.nirima.snowglobe.plan.PlanAction + +import java.lang.reflect.ParameterizedType + +public class Core { + Map classesMap = [:]; + + public static Core INSTANCE = new Core(); + + // TODO: Build all this from annotations + private Core() { + classesMap["docker_container"] = DockerContainer.class; + classesMap["docker_provider"] = DockerProvider.class; + classesMap["docker_registry"] = DockerRegistry.class; + classesMap["docker_image"] = DockerImage.class; + classesMap["docker_registry_image"] = DockerRegistryImage.class; + + classesMap["consul_provider"] = ConsulProvider.class; + + classesMap["consul_key_prefix"] = ConsulKeyPrefix.class; + + classesMap["consul_keys"] = ConsulKeys.class; + + } + + public Class getClassForName(String name) { + Class aClass = classesMap.get(name); + if( aClass == null ) + println "I don't know what class ${name} is for"; + + return aClass; + } + + public String getNameForClass(Class klass) { + String name = null; + + classesMap.each { + if( it.value == klass ) + name = it.key; + } + + return name; + } +} + +public class SnowGlobe { + + private transient Closure closure; + + public List modules = []; + + SnowGlobe(Closure closure) { + this.closure = closure + } + + public Module module(String id, Closure clos) { + Module newModule = new Module(this, id, clos); + modules << newModule; + return newModule; + } + + public void accept(Object context) { + + closure.delegate = context; + closure.resolveStrategy = Closure.DELEGATE_FIRST + + closure() + } + + Module getModule(String id) { + modules.find { it.id == id } + } + + def addModule(Module sgModule) { + modules << sgModule; + sgModule.parent = this; + } +} + + + + + + + +public class ModuleImports { + Module module; + transient Closure closure; + + List references = []; + + ModuleImports(Module module, Closure closure) { + this.module = module + this.closure = closure + } + + public Object using(Object c) { + assert(c != null) + references << c; + } + + public void accept(Object context) { + + closure.delegate = context; + closure.resolveStrategy = Closure.DELEGATE_FIRST + + closure() + } + + public String toString() { + return "ModuleImports for ${module}"; + } +} + +public class Module { + protected SnowGlobe parent; + public String id; + private transient Closure closure; + + public List resources = []; + + public Module(SnowGlobe globe, String id, Closure clos) { + this.parent = globe; + this.id = id; + this.closure = clos; + } + + public void accept(Object context) { + println "Accept in ${this}"; + closure.delegate = context; + closure.resolveStrategy = Closure.DELEGATE_FIRST + + closure() + } + + public ModuleImports imports(Closure c) { + return new ModuleImports(this, c); + } + + def methodMissing(String name, def args) { + Class klass = Core.INSTANCE.getClassForName(name); + + Object[] items = (Object)args; + + // Items is either closure or ID, Closure + // We want ID, Closure + Object[] items2 = new Object[3]; + if(items.length == 1) + items2[2] = items[0]; + else if(items.length == 2) { + items2[1] = items[0]; + items2[2] = items[1]; + } + + items2[0] = this; + if( klass == null ) + throw new ClassNotFoundException("Missing ${name}"); + return klass.newInstance(items2); + } + + + @Override + public String toString() { + return "module('${id}')"; + } + + def addResource(Resource sgResource) { + sgResource.module = this; + resources << sgResource; + } + + Resource getResource(Class klass, String id) { + resources.find { it.getClass() == klass && it.id == id } + } +} + +public class State { + public Closure closure; + + public State(Closure closure) { + this.closure = closure; + } + + public void accept(Object context) { + println "State Accept in ${this} CTX= ${context} "; + + assert(context != null) + + closure.delegate = context; + closure.resolveStrategy = Closure.DELEGATE_FIRST + closure() + + Closure defaults = getDefaults(); + defaults.delegate = context; + defaults.resolveStrategy = Closure.DELEGATE_FIRST + defaults(); + + } + + public Closure getDefaults() { + return {} + } +} + +public class ResourceState extends State { + + Resource resource; + public Object provider; + + ResourceState(Resource parent, Closure closure) { + super(closure); + } + + @Override + void accept(Object context) { + super.accept(context) + if( provider == null ) + throw new IllegalStateException("Provider missing for ${this}") + } + + public Provider getProvider() { + if( provider == null ) + return null; + if( provider instanceof Context ) { + return provider.getProxy(); + } + return provider; + } +} + +abstract public class Resource +{ + Module module; + public String id; + + + T state; + T savedState; + + Resource(Module module, String id, Closure closure) { + assert(module != null) + assert(id != null) + + this.module = module; + this.id = id + + //TODO: Not here? + state = newState(closure); + + } + + T newState(Closure closure) { + return ((Class) ((ParameterizedType) this.getClass(). + getGenericSuperclass()).getActualTypeArguments()[0]).newInstance(this,closure); + } + // TODO: I'd like the SGResource to not be aware of Context + public Provider getProvider() { + if( state.provider == null ) + return null; + if( state.provider instanceof Context ) { + return state.provider.getProxy(); + } + return state.provider; + } + + public PlanAction assess() { + // Maybe to another class in config + return null; + } + + public void accept(Object context) { + + println "Accept in ${this}"; + + assert(context != null) + + // Maybe now there is nothing to do here? + + + //state.accept(context); + + /* + closure.delegate = context; + closure.resolveStrategy = Closure.DELEGATE_FIRST + closure() + + Closure defaults = getDefaults(); + defaults.delegate = context; + defaults.resolveStrategy = Closure.DELEGATE_FIRST + defaults(); + + if( provider == null ) + throw new IllegalStateException("Provider missing for ${this}") + */ + } + + // abstract Closure getDefaults(); + + @Override + public String toString() { + return "${getClass()}:${id}" + } +} + + + +public class Provider { + private Module module; + String id; + public transient Closure closure; + + public Object provider; + + Provider(Module module, String id, Closure closure) { + this.module = module; + this.id = id + this.closure = closure + } + + public void accept(Object context) { + println "Accept in ${this}"; + closure.delegate = context; + closure.resolveStrategy = Closure.DELEGATE_FIRST + + closure() + } + + + @Override + public String toString() { + return "provider(${getClass()}):${id}" + } +} + +public class DataSource extends Provider +{ + + T state; + + DataSource(Module module, String id, Closure closure) { + super(module, id, closure) + + // TODO: Not here? + state = newState(closure); + } + + T newState(Closure closure) { + return ((Class) ((ParameterizedType) this.getClass(). + getGenericSuperclass()).getActualTypeArguments()[0]).newInstance(this,closure); + } +} + +public class DataSourceState extends State { + + public Object provider; + DataSource parent; + // Maybe this is a kind of resourceState? hmm. + + DataSourceState(DataSource parent, Closure closure) { + super(closure) + this.parent = parent; + } + + + + @Override + void accept(Object context) { + super.accept(context) + if( provider == null ) + throw new IllegalStateException("Provider missing for ${this}") + } + + public Provider getProvider() { + if( provider == null ) + return null; + if( provider instanceof Context ) { + return provider.getProxy(); + } + return provider; + } +} \ No newline at end of file diff --git a/snowglobe-core/src/main/groovy/com/nirima/snowglobe/core/Interfaces.groovy b/snowglobe-core/src/main/groovy/com/nirima/snowglobe/core/Interfaces.groovy new file mode 100644 index 0000000..8b519db --- /dev/null +++ b/snowglobe-core/src/main/groovy/com/nirima/snowglobe/core/Interfaces.groovy @@ -0,0 +1,27 @@ +package com.nirima.snowglobe.core + +// Element with an ID +public interface IElement { + String getId(); +} + +public interface ISnowglobe extends IElement { + +} + +public interface IModule extends IElement { + +} + +public interface IModuleElement extends IElement { + +} + +public interface IResource extends IModuleElement { + T getState(); + T getSavedState(); +} + +public interface IProvider extends IModuleElement { + +} \ No newline at end of file diff --git a/snowglobe-core/src/main/groovy/com/nirima/snowglobe/core/System.groovy b/snowglobe-core/src/main/groovy/com/nirima/snowglobe/core/System.groovy new file mode 100644 index 0000000..917e9c1 --- /dev/null +++ b/snowglobe-core/src/main/groovy/com/nirima/snowglobe/core/System.groovy @@ -0,0 +1,85 @@ +package com.nirima.snowglobe.core +/** + * Created by magnayn on 04/09/2016. + */ +public class SnowGlobeSystem { + + Script dslScript; + + SnowGlobe globe; + + public SnowGlobeSystem() { + + } + + void parseScript(File dsl) { + dslScript = new GroovyShell().parse(dsl.text); + + + dslScript.metaClass = createEMC(dslScript.class, + { + ExpandoMetaClass emc -> + + emc.snowglobe = { + Closure cl -> + globe = new SnowGlobe(cl); + + + } + + emc.snowglobeData = { + Closure cl -> + globe = new SnowGlobe(cl); + } + + }) + + + } + + void parseScript(InputStream dsl) { + dslScript = new GroovyShell().parse(dsl.text); + + + dslScript.metaClass = createEMC(dslScript.class, + { + ExpandoMetaClass emc -> + + emc.snowglobe = { + Closure cl -> + globe = new SnowGlobe(cl); + + + } + + emc.snowglobeData = { + Closure cl -> + globe = new SnowGlobe(cl); + } + + }) + + + } + + SnowGlobe runScript() { + + dslScript.run(); + return globe; + } + + static ExpandoMetaClass createEMC(Class scriptClass, Closure cl) { + ExpandoMetaClass emc = new ExpandoMetaClass(scriptClass, false); + cl(emc) + emc.initialize() + return emc + } + + static SnowGlobeContext getState(SnowGlobe x) { + SnowGlobeContext sgc = new SnowGlobeContext(x); + + sgc.build(); + + return sgc; + } +} diff --git a/snowglobe-core/src/main/groovy/com/nirima/snowglobe/docker/Docker.groovy b/snowglobe-core/src/main/groovy/com/nirima/snowglobe/docker/Docker.groovy new file mode 100644 index 0000000..f547430 --- /dev/null +++ b/snowglobe-core/src/main/groovy/com/nirima/snowglobe/docker/Docker.groovy @@ -0,0 +1,303 @@ +package com.nirima.snowglobe.docker + +import com.github.dockerjava.api.DockerClient +import com.github.dockerjava.api.command.PullImageCmd +import com.github.dockerjava.api.model.AuthConfig +import com.github.dockerjava.api.model.Image +import com.github.dockerjava.core.DefaultDockerClientConfig +import com.github.dockerjava.core.DockerClientBuilder +import com.github.dockerjava.core.command.PullImageResultCallback +import com.google.common.base.Objects +import com.nirima.snowglobe.core.DataSource +import com.nirima.snowglobe.core.DataSourceState +import com.nirima.snowglobe.core.ResourceState +import com.nirima.snowglobe.core.Module +import com.nirima.snowglobe.core.Provider +import com.nirima.snowglobe.core.Resource +import com.nirima.snowglobe.plan.PlanAction + +/** + * Created by magnayn on 04/09/2016. + */ + +class DockerContainerPort { + int internal; + int external; + + @Override + public String toString() { + return Objects.toStringHelper(this) + .add("internal", internal) + .add("external", external) + .toString(); + } + + boolean equals(o) { + if (this.is(o)) { + return true + } + if (getClass() != o.class) { + return false + } + + DockerContainerPort that = (DockerContainerPort) o + + if (external != that.external) { + return false + } + if (internal != that.internal) { + return false + } + + return true + } + + int hashCode() { + int result + result = internal + result = 31 * result + external + return result + } +} + +class DockerContainerVolume { + String from_container; + + + @Override + public String toString() { + return Objects.toStringHelper(this) + .add("from_container", from_container) + .toString(); + } +} + +public class DockerContainerState extends ResourceState { + public String name; + public String image; + public List links; + + public List ports = []; + public List volumes = []; + public List command; + public List env; + + public String restart; + + public boolean publish_all_ports; + public boolean must_run = true; + + public String id; + + DockerContainerState(Resource parent, Closure closure) { + super(parent, closure); + } + + public void ports(Closure c) { + DockerContainerPort p = new DockerContainerPort(); + c.delegate = p; + c.resolveStrategy = Closure.DELEGATE_FIRST + + c() + + ports<< p; + } + + public void volumes(Closure c) { + DockerContainerVolume p = new DockerContainerVolume(); + c.delegate = p; + c.resolveStrategy = Closure.DELEGATE_FIRST + + c() + + volumes << p; + } + + @Override + void accept(Object context) { + + ports = [] + volumes = [] + + super.accept(context) + } + + Closure getDefaults() { + Closure defaults = { + if( provider == null ) + provider = docker_provider(null); + } + return defaults; + } +} + +public class DockerContainer extends Resource { + + DockerContainer(Module module, String id, + Closure closure) { + super(module, id, closure) + } + + + public PlanAction assess() { + return new DockerContainerAction(this); + } + +} + + + +public class DockerImageState extends ResourceState { + String name; + + String latest; + + String imageId; + + boolean keep_locally; + + public Object registry_provider; + + DockerImageState(Resource parent, Closure closure) { + super(parent, closure); + } + + Closure getDefaults() { + Closure defaults = { + if( provider == null ) + provider = docker_provider(null); + } + return defaults; + } +} + + +public class DockerImage extends Resource { + + + DockerImage(Module module, String id, + Closure closure) { + super(module, id, closure) + } + + + Closure getDefaults() { + return { + if(provider == null) { + provider = docker_provider(null); + } + if( name.contains("/") && registry_provider == null ) { + registry_provider = docker_registry(getRegistry()) + } + } + } + + public String getRegistry() { + if( !name.contains("/") ) + return null; + return name.substring(0,name.indexOf("/")); + } + + public PlanAction assess() { + return new DockerImageAction(this); + } + +} + +public class DockerRegistryImage extends DataSource { + + DockerRegistryImage(Module module, String id, Closure closure) { + super(module, id, closure) + } + + public String getSha256_digest() { + + DockerProvider dp = state.getProvider(); + DockerClient dc = dp.getDockerClient(); + PullImageCmd cmd = dc.pullImageCmd(state.name); + + + // TODO: Figure this out + if( state.name.startsWith("registry")) { + AuthConfig ac = new AuthConfig(); + ac.withUsername("admin"); + ac.withPassword("admin123"); + + cmd.withAuthConfig(ac); + } + + PullImageResultCallback pullImageResultCallback = cmd. + exec(PullImageResultCallback.newInstance()); + + pullImageResultCallback.awaitSuccess(); + + List imgs = dc.listImagesCmd().withImageNameFilter(state.name).exec(); + if( imgs.size() != 1 ) + { + throw new IllegalStateException("Image ${state.name} is ambiguous or empty"); + } + + return imgs.get(0).id; + + + } + + +} + +public class DockerRegistryImageState extends DataSourceState { + String name; + + DockerRegistryImageState(DataSource parent, Closure closure) { + super(parent, closure) + } + Closure getDefaults() { + Closure defaults = { + if( provider == null ) + provider = docker_provider(null); + } + return defaults; + } +// public String getSha256_digest() { +// // Do a pull +// } +} + +public class DockerProvider extends Provider { + public String host; + + DockerProvider(Module module, String id, Closure closure) { + super(module, id, closure) + } + + public DockerClient getDockerClient() { + DefaultDockerClientConfig defaultDockerClientConfig = DefaultDockerClientConfig.createDefaultConfigBuilder() + .withDockerHost(host) + //.withDockerTlsVerify(true) + //.withDockerCertPath("/Users/magnayn/.sdc/docker/jenkins") + .build(); + + DockerClient dockerClient = DockerClientBuilder.getInstance(defaultDockerClientConfig).build(); + + return dockerClient; + } +} + + + +public class DockerRegistry extends Provider { + public String username; + public String password; + + DockerRegistry(Module module, String id, Closure closure) { + super(module, id, closure) + } + + public AuthConfig getAuthConfig() { + AuthConfig ac = new AuthConfig(); + ac.withUsername(username); + ac.withPassword(password); + return ac; + } +} + diff --git a/snowglobe-core/src/main/groovy/com/nirima/snowglobe/docker/DockerActions.groovy b/snowglobe-core/src/main/groovy/com/nirima/snowglobe/docker/DockerActions.groovy new file mode 100644 index 0000000..952fb58 --- /dev/null +++ b/snowglobe-core/src/main/groovy/com/nirima/snowglobe/docker/DockerActions.groovy @@ -0,0 +1,159 @@ +package com.nirima.snowglobe.docker + +import com.github.dockerjava.api.DockerClient +import com.github.dockerjava.api.command.CreateContainerCmd +import com.github.dockerjava.api.command.CreateContainerResponse +import com.github.dockerjava.api.command.PullImageCmd +import com.github.dockerjava.api.exception.NotModifiedException +import com.github.dockerjava.api.model.AuthConfig +import com.github.dockerjava.api.model.ExposedPort +import com.github.dockerjava.api.model.Image +import com.github.dockerjava.api.model.Link +import com.github.dockerjava.api.model.Ports +import com.github.dockerjava.api.model.VolumesFrom +import com.github.dockerjava.core.command.PullImageResultCallback +import com.nirima.snowglobe.plan.PlanActionBase + + +/** + * Created by magnayn on 05/09/2016. + */ + + +public class DockerImageAction extends PlanActionBase { + + DockerImageAction(DockerImage pair) { + super(pair); + } + + public DockerImageState create(DockerImageState state) { + return update(state); // Create == Update + } + + // Functions + public DockerImageState read(DockerImageState state) + { + + DockerProvider dp = state.getProvider(); + DockerClient client = dp.getDockerClient(); + + List images = client.listImagesCmd().withImageNameFilter(state.name).exec(); + if( images.size() == 0 ) + state.latest = null; + state.latest = images.get(0).id; + return state; + } + + + public DockerImageState update(DockerImageState state) { +// DockerProvider dp = state.getProvider(); +// DockerClient dc = dp.getDockerClient(); +// PullImageCmd cmd = dc.pullImageCmd(state.name); +// +// if( state.name.startsWith("registry")) { +// AuthConfig ac = new AuthConfig(); +// ac.withUsername("admin"); +// ac.withPassword("admin123"); +// +// cmd.withAuthConfig(ac); +// } +// +// PullImageResultCallback pullImageResultCallback = cmd. +// exec(PullImageResultCallback.newInstance()); +// +// pullImageResultCallback.awaitSuccess(); +// +// List imgs = dc.listImagesCmd().withImageNameFilter(state.name).exec(); +// if( imgs.size() != 1 ) +// { +// throw new IllegalStateException("Image ${state.name} is ambiguous or empty"); +// } +// +// state.imageId = imgs.get(0).id; +// return state; + } + + +} + + + + +class DockerContainerAction extends PlanActionBase { + + DockerContainerAction(DockerContainer resource) { + super(resource) + } + + @Override + DockerContainerState create(DockerContainerState desiredState) { + DockerProvider dp = desiredState.getProvider(); + DockerClient client = dp.getDockerClient(); + + CreateContainerCmd create = client.createContainerCmd(desiredState.image); + + create.withPublishAllPorts(desiredState.publish_all_ports); + + Ports portBindings = new Ports(); + desiredState.ports.each { + portBindings.bind(ExposedPort.tcp(it.internal), Ports.Binding.bindPort(it.external)); + + } + create.withPortBindings(portBindings); + if (desiredState.env != null) + create.withEnv(desiredState.env); + + if (desiredState.command != null) + create.withCmd(desiredState.command); + + + if (desiredState.name != null) { + create.withName(desiredState.name); + } + + if (desiredState.links != null) { + + def links = desiredState.links.collect { + Link.parse(it) + } + + create.withLinks(links); + } + + if (desiredState.volumes != null) { + + def volumes = desiredState.volumes.collect { + VolumesFrom.parse(it.from_container) + } + + create.withVolumesFrom(volumes) + } + + CreateContainerResponse response = create.exec(); + + if (desiredState.must_run) { + client.startContainerCmd(response.id).exec(); + } + + desiredState.id = response.id; + return desiredState; + + } + + @Override + DockerContainerState delete(DockerContainerState dockerContainer) { + DockerProvider dp = dockerContainer.getProvider(); + DockerClient client = dp.getDockerClient(); + + try { + client.stopContainerCmd(dockerContainer.id).exec(); + } catch(NotModifiedException ex ){ + // Don't mind actually if it's not running + } + client.removeContainerCmd(dockerContainer.id).exec(); + + dockerContainer.id = null; + } + + +} \ No newline at end of file diff --git a/snowglobe-core/src/main/groovy/com/nirima/snowglobe/graph/SGNode.groovy b/snowglobe-core/src/main/groovy/com/nirima/snowglobe/graph/SGNode.groovy new file mode 100644 index 0000000..3883b60 --- /dev/null +++ b/snowglobe-core/src/main/groovy/com/nirima/snowglobe/graph/SGNode.groovy @@ -0,0 +1,344 @@ +package com.nirima.snowglobe.graph + +import com.google.common.base.Objects +import com.nirima.snowglobe.core.Context +import com.nirima.snowglobe.core.Dependency +import com.nirima.snowglobe.core.Module +import com.nirima.snowglobe.core.Provider +import com.nirima.snowglobe.core.Resource +import com.nirima.snowglobe.core.SnowGlobe +import com.nirima.snowglobe.core.SnowGlobeContext + +/** + * Created by magnayn on 05/09/2016. + */ +class SGNode { + + SGNode parentNode; + + //Context represents; + + final Object item; + + List dependencies = [] + List children = [] + + protected SGNode() {} + + protected SGNode(SGNode parentNode, Object item) { + this.parentNode = parentNode; + this.item = item; + + if( parentNode != null ) + parentNode.children << this + } + + public accept(Object visitor) { + visitor.visitNode(this); + children.each { it.accept(visitor) } + } + + public String getId() { + String id; + try { + id = item.getClass().simpleName; + id += ":" + item.id; + } catch(Exception ex) { + + } + + if( parentNode != null ) + return "${parentNode.id}.${id}" + else + return id; + } + + public String getLabel() { + try { + + return "${item.getClass().getSimpleName()}:${item.id}"; + } + catch(Exception e) { + return item.toString(); + } + } + + boolean equals(o) { + if (this.is(o)) { + return true + } + if (getClass() != o.class) { + return false + } + + SGNode node = (SGNode) o + + if (getId() != node.getId()) { + return false + } + + return true + } + + int hashCode() { + return (item != null ? item.hashCode() : 0) + } + + @Override + public String toString() { + return Objects.toStringHelper(this) + .add("item", item) + .toString(); + } +} + +class Graph { + SGNode rootNode; + + // Should be transient as can re-calc + private Map graphNodes = [:]; + private Map graphNodesById = [:]; + + + public static Graph Empty() { + new Graph(new SGNode()) + } + + public SGNode newNode(SGNode parentNode, Context represents) { + return newNodeObject(parentNode, represents.getProxy()); + } + + public SGNode newNodeObject(SGNode parentNode, Object object) { + //this.represents = represents + + SGNode node = new SGNode(parentNode, object); + + graphNodes[ object ] = node; + graphNodesById[ idForNode(node) ] = node; + + return node; + } + + public SGNode getNodeForItem(Object item) { + SGNode node = graphNodes[item]; + if( node == null ) + throw new IllegalStateException("No node for ${item}"); + return node; + } + + Graph(SGNode rootNode) { + this.rootNode = rootNode + } + + public void removeNode(SGNode node) { + assert node != null + + String id = idForNode(node); + + if( node.parentNode != null ) { + node.parentNode.children.remove(node); + } + + // Remove the children too??? + if( node.children.size() > 0 ) { + println "CHILDREN ARE HERE. NOT SURE." + } + + node.children.each { + node.parentNode = null; + } + + graphNodes[node.item] = null; + graphNodesById[id] = null; + } + + + + public void replaceNodeWithThisNode(SGNode node) { + assert node != null + + SGNode current = graphNodesById[ idForNode(node) ]; + if( current != null ) + removeNode(current); + + SGNode parent = graphNodesById[ idForNode(node.parentNode) ]; + SGNode inserted = newNode(parent, node.item); + + // place in the node + node.children.each { + inserted + } + + + } + + public accept(Object visitor) { + rootNode.accept(visitor); + } + + List getNodes() { + List items = []; + items.addAll(graphNodes.values()); + return items; + } + + public static String idForNode(SGNode item) { + + String id = ""; + try { + id = item.getClass().simpleName; + id += ":" + item.id; + } catch(Exception ex) { + + } + + if( item.parentNode != null ) + return "${idForNode(item.parentNode)}.${id}" + else + return id; + } +} + +class GViz { + String nodes = ""; + public void visitNode(SGNode n) { + + if( n.item instanceof SnowGlobe ) { + return; + } + + if( n.item instanceof Module ) { + + if( nodes.length() > 0 ) + nodes += " }\n"; + + nodes = nodes + "\nsubgraph cluster_${n.item.id} {\n"; + nodes += " label = \"${n.id}\"\n"; + } + + nodes = nodes + "\"[root] ${n.id}\" [label = \"${n.getLabel()}\"" + if( n.item instanceof Module ) { + nodes += ", shape=box" + } + if( n.item instanceof Provider ) { + nodes += ", style=filled, fillcolor=blue" + } + if( n.item instanceof Resource ) { + Resource res = n.item; + + if( res.savedState == null ) // Create + nodes += ", style=filled, fillcolor=green" + else if( res.state == null ) // Delete + nodes += ", style=filled, fillcolor=red" + } + nodes += "]\n"; + + } + + public String toString() { + return nodes + "}\n"; + } +} + +public class DependencyList_DFS { + List nodes = []; + + public void insert(SGNode node) { + if( nodes.contains(node) ) + return; // already seen + + node.dependencies.each { + insert(it) + } + + // Try the children too + node.children.each { + insert(it) + } + + nodes << node; + } + +} + +public class GraphBuilder { + + public String graphViz(Graph graph) { + GViz viz = new GViz(); + graph.accept(viz); + + + String nodes = ""; + + + + graph.getNodes().each { n -> + n.dependencies.each { n2 -> + nodes = nodes + "\"[root] ${n.getId()}\" -> \"[root] ${n2.getId()}\"\n"; + } + } + + String g = """ +digraph snowglobe { + + ${viz} + + ${nodes} + +} +""" + return g; + } + + public Graph build(SnowGlobeContext snowGlobeContext) { + + // Create all the nodes + SGNode root = new SGNode(null, snowGlobeContext.getProxy()); + + Graph graph = new Graph(root); + + snowGlobeContext.getModules().each { + SGNode moduleNode = graph.newNode(root, it); + + root.dependencies << moduleNode; + + it.providers.each { p -> + graph.newNode(moduleNode, p); + } + + it.dataSources.each { p -> + graph.newNode(moduleNode, p); + } + + it.resources.each { r -> + graph.newNode(moduleNode, r); + } + + } + + + // Create all the edges + + processDependencies(graph, snowGlobeContext.dependencies); + + snowGlobeContext.getModules().each { + + module -> + processDependencies(graph, module.dependencies); + } + + + return graph; + } + + private void processDependencies(Graph g, Collection deps ) { + deps.each { + dep -> + SGNode source = g.getNodeForItem(dep.from.getProxy()); + SGNode target = g.getNodeForItem(dep.to.getProxy()); + + source.dependencies.add(target); + + } + } +} \ No newline at end of file diff --git a/snowglobe-core/src/main/groovy/com/nirima/snowglobe/plan/PlanBuilder.groovy b/snowglobe-core/src/main/groovy/com/nirima/snowglobe/plan/PlanBuilder.groovy new file mode 100644 index 0000000..ba0314a --- /dev/null +++ b/snowglobe-core/src/main/groovy/com/nirima/snowglobe/plan/PlanBuilder.groovy @@ -0,0 +1,251 @@ +package com.nirima.snowglobe.plan + +import com.nirima.snowglobe.core.ResourceContext +import com.nirima.snowglobe.core.ResourceState +import com.nirima.snowglobe.core.Resource +import com.nirima.snowglobe.core.SnowGlobeContext +import com.nirima.snowglobe.graph.DependencyList_DFS +import com.nirima.snowglobe.graph.Graph +import com.nirima.snowglobe.graph.GraphBuilder +import com.nirima.snowglobe.graph.SGNode +import groovy.transform.CompileStatic + +/** + * Created by magnayn on 05/09/2016. + */ + +enum PlanActionActivity { + Create, + Update, + Delete, + Recreate +} + + +interface PlanAction { + + //void execute(Plan plan, String phase); + + T getResource(); + + + public V create(V t); + public V read(V t); + public V update(V old,V newState); + public V delete(V t); + +} + +abstract class PlanActionBase implements PlanAction { + + private final T resource; + + PlanActionBase(T resource) { + this.resource = resource + } + + T getResource() { + resource; + } + + public V create(V t) { return t; } + public V read(V t) { return t;} + public V update(V old, V newState) { return newState; } + public V delete(V t) { return null;} + + +} + + +public class Plan { + List> actions = []; + PlanType type; + + private final SnowGlobeContext sgContext; + + def phases = ["validate", + "read", + "create", + "update","delete"] + + Plan(SnowGlobeContext sgContext, PlanType type) { + this.sgContext = sgContext + this.type = type + } + + public void execute() { + if (type == PlanType.Apply) { + apply(); + } else { + destroy(); + } + } + + private void apply() { + actions.each() { action -> + + Resource resource = action.getResource(); + + if( resource.savedState != null ) { + action.read( resource.savedState ); + } + } + + actions.each() { action -> + + Resource resource = action.getResource(); + + if( resource.state != null ) { + // Reevaluate as deps may now be ready + processResourceState(resource); + + if( resource.savedState == null ) { + + // nothing saved. these are creates + resource.savedState = action.create(resource.state); + } else { + resource.savedState = action.update(resource.savedState, resource.state); + } + + } else { + // No state, these are deletes. + resource.savedState = action.delete(resource.savedState); + } + + + } + + + } + + private void destroy() { + actions.each() { action -> + + Resource resource = action.getResource(); + + if( resource.savedState != null ) { + action.read( resource.savedState ); + } + } + + // Deletes happen in reverse order + actions.reverse().each() { action -> + + Resource resource = action.getResource(); + + if( resource.savedState != null ) { + // Reevaluate as deps may now be ready + processResourceState(resource); + resource.savedState = action.delete(resource.savedState); + + } else { + + } + } + + + } + + private void processResourceState(Resource resource) { + // Between first eval and now, a value may have changed (or become resolvable). + + // Need to get the ResourceContext for this resource + ResourceContext ctx = sgContext.getContextFor(resource) + + ctx.stateContext.build(); + + + } + +} + +public enum NodePairStatus { + Create,Update,Delete +} + +public class NodePair +{ + final SGNode oldNode, newNode; + + NodePair(SGNode oldNode, SGNode newNode) { + assert( oldNode != null || newNode != null) + this.oldNode = oldNode + this.newNode = newNode + } + + NodePairStatus getStatus() { + if( oldNode == null ) { + return NodePairStatus.Create; + } + if( newNode == null ) + return NodePairStatus.Delete; + + return NodePairStatus.Update; + } + + Object getElement() { + if( newNode != null ) + return newNode.item; + return oldNode.item; + } + + boolean equals(o) { + if (this.is(o)) { + return true + } + if (getClass() != o.class) { + return false + } + + NodePair nodePair = (NodePair) o + + if (newNode != nodePair.newNode) { + return false + } + if (oldNode != nodePair.oldNode) { + return false + } + + return true + } + + int hashCode() { + int result + result = (oldNode != null ? oldNode.hashCode() : 0) + result = 31 * result + (newNode != null ? newNode.hashCode() : 0) + return result + } +} + +public class PlanBuilder { + + Plan plan; + DependencyList_DFS list = new DependencyList_DFS(); + + public Plan buildPlan(SnowGlobeContext sgContext, PlanType type) { + assert sgContext != null + plan = new Plan(sgContext,type); + + Graph stateGraph = new GraphBuilder().build(sgContext); + + list.insert(stateGraph.getRootNode()); + + // Create, Update (or, same) + list.nodes.each { node -> + Object o = node.item + if( o instanceof Resource ) { + + PlanAction action = o.assess() + if( action != null ) + plan.actions << action; + } + } + + + + return plan; + } + + +} + diff --git a/snowglobe-core/src/main/java/com/nirima/snowglobe/plan/PlanType.java b/snowglobe-core/src/main/java/com/nirima/snowglobe/plan/PlanType.java new file mode 100644 index 0000000..021b8aa --- /dev/null +++ b/snowglobe-core/src/main/java/com/nirima/snowglobe/plan/PlanType.java @@ -0,0 +1,9 @@ +package com.nirima.snowglobe.plan; + +/** + * Created by magnayn on 09/09/2016. + */ +public enum PlanType { + Apply, + Destroy +} diff --git a/snowglobe-core/src/test/resources/com/nirima/simple-docker.sg b/snowglobe-core/src/test/resources/com/nirima/simple-docker.sg new file mode 100644 index 0000000..aeffb1c --- /dev/null +++ b/snowglobe-core/src/test/resources/com/nirima/simple-docker.sg @@ -0,0 +1,45 @@ +package com.nirima + +snowglobe +{ + module("base") { + docker_provider { + //host = "tcp://localhost:2376" + host = "unix:///var/run/docker.sock" + } + + docker_container("consul") { + image = "${docker_image("consul").name}" + name = "consul" + restart = "always" + + ports { + internal = 8300 + external = 8300 + } + + ports { + internal = 8301 + external = 8301 + } + + ports { + internal = 8302 + external = 8302 + } + + ports { + internal = 8500 + external = 8500 + } + + command = ["agent", "-dev", "-client", "0.0.0.0"] + } + + docker_image("consul") + { + name = "consul:v0.6.4" + } + + } +} \ No newline at end of file diff --git a/snowglobe-core/src/test/resources/logback.groovy b/snowglobe-core/src/test/resources/logback.groovy new file mode 100644 index 0000000..49eabd4 --- /dev/null +++ b/snowglobe-core/src/test/resources/logback.groovy @@ -0,0 +1,36 @@ +package com.nirima +// LOGBACK LOGGING CONFIGURATION + +import ch.qos.logback.classic.AsyncAppender +import ch.qos.logback.classic.encoder.PatternLayoutEncoder +import ch.qos.logback.core.ConsoleAppender +import ch.qos.logback.core.rolling.FixedWindowRollingPolicy +import ch.qos.logback.core.rolling.RollingFileAppender +import ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy +import ch.qos.logback.classic.encoder.PatternLayoutEncoder + +import static ch.qos.logback.classic.Level.* + + + +def consolePattern = "%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n" + +scan("30 seconds") + + + +appender("STDOUT", ConsoleAppender) { + encoder(PatternLayoutEncoder) { + pattern = consolePattern; + } +} + + +//logger("org.hibernate.cache", TRACE, ["STDOUT"]) +//logger("org.hibernate.impl", DEBUG, ["STDOUT"]) + +//logger("jdbc.sqlonly", INFO, ["STDOUT"], false) +//logger("log4jdbc.debug", DEBUG, ["STDOUT"], false) +//logger("jdbc.audit", DEBUG, ["STDOUT"], false) + +root(INFO, ["STDOUT", "logfile"]) diff --git a/snowglobe-exe/pom.xml b/snowglobe-exe/pom.xml new file mode 100644 index 0000000..91e5337 --- /dev/null +++ b/snowglobe-exe/pom.xml @@ -0,0 +1,64 @@ + + + 4.0.0 + com.nirima.snowglobe + snowglobe-exe + + + com.nirima + snowglobe + 0.1-SNAPSHOT + + + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + + com.nirima.snowglobe.SnowGlobeApp + + + + + + + org.dstovall + onejar-maven-plugin + 1.4.4 + + + + one-jar + + + + + + + + + + + + com.nirima.snowglobe + snowglobe-core + ${project.version} + + + args4j + args4j + 2.0.31 + + + + + onejar-maven-plugin.googlecode.com + http://onejar-maven-plugin.googlecode.com/svn/mavenrepo + + + diff --git a/snowglobe-exe/src/main/java/com/nirima/snowglobe/Action.java b/snowglobe-exe/src/main/java/com/nirima/snowglobe/Action.java new file mode 100644 index 0000000..f925610 --- /dev/null +++ b/snowglobe-exe/src/main/java/com/nirima/snowglobe/Action.java @@ -0,0 +1,10 @@ +package com.nirima.snowglobe; + +/** + * Created by magnayn on 10/09/2016. + */ +public enum Action { + apply, + graph, + destroy +} diff --git a/snowglobe-exe/src/main/java/com/nirima/snowglobe/SnowGlobeApp.java b/snowglobe-exe/src/main/java/com/nirima/snowglobe/SnowGlobeApp.java new file mode 100644 index 0000000..326174c --- /dev/null +++ b/snowglobe-exe/src/main/java/com/nirima/snowglobe/SnowGlobeApp.java @@ -0,0 +1,103 @@ +package com.nirima.snowglobe; + +import org.kohsuke.args4j.Argument; +import org.kohsuke.args4j.CmdLineException; +import org.kohsuke.args4j.CmdLineParser; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * Created by magnayn on 06/09/2016. + */ +public class SnowGlobeApp { + + @Argument + Action action; + + + + public static void main(String[] args) throws IOException { + + new SnowGlobeApp().doMain(args); + } + + public void doMain(String[] args) throws IOException { + CmdLineParser parser = new CmdLineParser(this); + + // if you have a wider console, you could increase the value; + // here 80 is also the default + parser.setUsageWidth(80); + + try { + // parse the arguments. + parser.parseArgument(args); + + // you can parse additional arguments if you want. + // parser.parseArgument("more","args"); + + // after parsing arguments, you should check + // if enough arguments are given. + // if( arguments.isEmpty() ) + // throw new CmdLineException(parser, "No argument is given"); + + } catch( CmdLineException e ) { + // if there's a problem in the command line, + // you'll get this exception. this will report + // an error message. + System.err.println(e.getMessage()); + System.err.println("snowglobe [options...] arguments..."); + // print the list of available options + parser.printUsage(System.err); + System.err.println(); + + + return; + } + + + + // access non-option arguments + System.out.println("other arguments are:"); + + // Initialize + File f1 = new File("snowglobe.sg"); + File f2 = new File("snowglobe.sgstate"); + + SGExec exec = new SGExec(f1,f2); + + + switch(action) { + case graph: + exec.graph(System.out); + break; + case apply: + { + try { + exec.apply(); + } finally { + try(FileOutputStream fos = new FileOutputStream(f2)) + { + fos.write(exec.save().getBytes()); + } + } + } + break; + case destroy: + { + try { + exec.destroy(); + } finally { + try(FileOutputStream fos = new FileOutputStream(f2)) + { + fos.write(exec.save().getBytes()); + } + } + } + break; + } + } +} diff --git a/snowglobe-jenkins-plugin/pom.xml b/snowglobe-jenkins-plugin/pom.xml new file mode 100644 index 0000000..ddd8fbb --- /dev/null +++ b/snowglobe-jenkins-plugin/pom.xml @@ -0,0 +1,182 @@ + + + 4.0.0 + + + org.jenkins-ci.plugins + plugin + 2.14 + + + com.nirima.snowglobe + snowglobe-jenkins-plugin + 0.1-SNAPSHOT + hpi + + Snowglobe plugin + Provide environment setup/teardown + + + + 0.1-SNAPSHOT + + UTF-8 + 3.5.1 + 2.5.1 + 3.0.4 + 1.119 + 2.7 + 1.207 + 18.0 + 3.1.0 + ${project.parent.version} + true + false + 1.8 + 1.8 + 8 + + + + + + magnayn + Nigel Magnay + + + + + + com.nirima.snowglobe + snowglobe-shaded + ${snowglobe.version} + + + + org.jenkins-ci.plugins + ssh-slaves + 1.6 + + + org.jenkins-ci.plugins + token-macro + 1.7 + true + + + org.jenkins-ci.plugins + durable-task + 1.3 + + + + org.hamcrest + hamcrest-all + 1.3 + test + + + + org.mockito + mockito-core + 1.10.19 + test + + + org.jenkins-ci.modules + instance-identity + 1.4 + + + org.jenkins-ci.main + jenkins-core + ${jenkins.version} + + + org.jenkins-ci.main + jenkins-war + ${jenkins.version} + war-for-test + + + org.jenkins-ci.main + jenkins-war + ${jenkins.version} + war + test + + + + + + + maven-compiler-plugin + org.apache.maven.plugins + + + ${jdk.source} + ${jdk.target} + ${jdk.debug} + ${jdk.optimize} + + + + maven-javadoc-plugin + 2.10.1 + + false + + + + org.jenkins-ci.tools + maven-hpi-plugin + ${hpi.plugin.version} + + + com.google.guava + guava + ${guava.version} + + + io.dropwizard.metrics + metrics-core + ${metrics.version} + + + org.kohsuke.stapler + stapler + ${stapler.version} + + + + + org.apache.httpcomponents:*,com.github.docker-java*,com.google.guava*,org.codehaus.groovy* + + + + com.google.common + + + com.google.common. + + + + + org.apache.maven.plugins + maven-release-plugin + + + + org.codehaus.mojo + findbugs-maven-plugin + + + + diff --git a/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/Consts.java b/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/Consts.java new file mode 100644 index 0000000..be97ad9 --- /dev/null +++ b/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/Consts.java @@ -0,0 +1,18 @@ +package com.nirima.jenkins.plugins.snowglobe; + +/** + * Created by magnayn on 09/09/2016. + */ +public class Consts { + public static final String PLUGIN_URL = "/plugin/snowglobe-jenkins-plugin/"; + + /** + * The base URL of the plugin images. + */ + public static final String PLUGIN_IMAGES_URL = PLUGIN_URL + "images/"; + + /** + * The base URL of the plugin javascripts. + */ + public static final String PLUGIN_JS_URL = PLUGIN_URL + "js/"; +} diff --git a/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/SnowGlobePluginConfiguration.java b/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/SnowGlobePluginConfiguration.java new file mode 100644 index 0000000..fae3276 --- /dev/null +++ b/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/SnowGlobePluginConfiguration.java @@ -0,0 +1,41 @@ +package com.nirima.jenkins.plugins.snowglobe; + +import hudson.Extension; +import hudson.Functions; +import hudson.Util; +import jenkins.model.GlobalConfiguration; + +/** + * Created by magnayn on 06/09/2016. + */ +@Extension +public class SnowGlobePluginConfiguration extends GlobalConfiguration { + + private String dotExe; + + /** + * Returns this singleton instance. + * + * @return the singleton. + */ + public static SnowGlobePluginConfiguration get() { + return GlobalConfiguration.all().get(SnowGlobePluginConfiguration.class); + } + + public String getDotExeOrDefault() { + if (Util.fixEmptyAndTrim(dotExe) == null) { + return Functions.isWindows() ? "dot.exe" : "dot"; + } else { + return dotExe; + } + } + + public String getDotExe() { + return dotExe; + } + + public void setDotExe(String dotExe) { + this.dotExe = dotExe; + save(); + } +} diff --git a/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/action/RegisteredScript.java b/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/action/RegisteredScript.java new file mode 100644 index 0000000..b1b684f --- /dev/null +++ b/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/action/RegisteredScript.java @@ -0,0 +1,33 @@ +package com.nirima.jenkins.plugins.snowglobe.action; + +import com.nirima.snowglobe.core.SnowGlobe; + +import java.io.IOException; +import java.io.Serializable; + +import hudson.model.Run; + +/** + * Created by magnayn on 09/09/2016. + */ +public class RegisteredScript implements Serializable { + public final String script; + public final String id; + + public RegisteredScript(String script, String id) { + this.script = script; + this.id = id; + } + + public SnowGlobeData build(Run run) throws IOException { + SnowGlobeAction action = run.getAction(SnowGlobeAction.class); + if( action==null ) + action = new SnowGlobeAction(run); + + SnowGlobeData data = action.createSnowglobe(this); + + run.save(); + return data; + } + +} diff --git a/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/action/RegisteredScriptAction.java b/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/action/RegisteredScriptAction.java new file mode 100644 index 0000000..cddfb17 --- /dev/null +++ b/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/action/RegisteredScriptAction.java @@ -0,0 +1,105 @@ +package com.nirima.jenkins.plugins.snowglobe.action; + +import com.nirima.jenkins.plugins.snowglobe.Consts; + +import org.kohsuke.stapler.QueryParameter; +import org.kohsuke.stapler.StaplerRequest; +import org.kohsuke.stapler.StaplerResponse; +import org.kohsuke.stapler.export.ExportedBean; + +import java.io.IOException; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +import javax.servlet.ServletException; + +import hudson.Extension; +import hudson.model.Action; +import hudson.model.Describable; +import hudson.model.Descriptor; +import hudson.model.Run; +import jenkins.model.Jenkins; + +/** + * Created by magnayn on 09/09/2016. + */ +@ExportedBean +public class RegisteredScriptAction implements Action, Serializable, + Describable { + + public final RunLink runLink; + + public List scripts = new ArrayList<>(); + + public RegisteredScriptAction(Run run) + { + runLink = new RunLink(run); + } + + public RegisteredScript addScript(String script, String id) { + RegisteredScript s = new RegisteredScript(script, id); + scripts.add( s ); + return s; + } + + public RegisteredScript getScript(String id) { + for(RegisteredScript script : scripts) { + if( script.id.equals(id)) + return script; + } + return null; + } + + + @Override + public String getIconFileName() { + return "/plugin/snowglobe-jenkins-plugin/images/32x32/snow-globe.png"; + } + + @Override + public String getDisplayName() { + return "Launch Snowglobe"; + } + + @Override + public String getUrlName() { + return "snowglobe"; + } + + public DescriptorImpl getDescriptor() { + return (DescriptorImpl) Jenkins.getInstance().getDescriptorOrDie(getClass()); + } + + public String getJsUrl(String jsName) { + return Consts.PLUGIN_JS_URL + jsName; + } + + public void doControlSubmit(@QueryParameter("launchId") String launchId, StaplerRequest req, StaplerResponse rsp) throws + ServletException, + IOException + { + launchScript(launchId); + rsp.sendRedirect("snowglobeAction"); + } + + private void launchScript(String launchId) throws IOException { + Run run = runLink.getRun(); + getScript(launchId).build( run ).apply(); + run.save(); + } + + /** + * Just for assisting form related stuff. + */ + @Extension + public static class DescriptorImpl extends Descriptor { + public String getDisplayName() { + return "SnowGlobe"; + } + + public void launch(String id) { + System.out.println("launch"); + } + } +} diff --git a/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/action/RunLink.java b/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/action/RunLink.java new file mode 100644 index 0000000..6dbc73d --- /dev/null +++ b/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/action/RunLink.java @@ -0,0 +1,24 @@ +package com.nirima.jenkins.plugins.snowglobe.action; + +import java.io.Serializable; + +import hudson.model.Project; +import hudson.model.Run; +import jenkins.model.Jenkins; + +/** + * Created by magnayn on 09/09/2016. + */ +public class RunLink implements Serializable { + final int runId; + final String projectName; + + public RunLink(Run run) { + this.runId = run.getNumber(); + this.projectName = run.getParent().getName(); + } + + protected Run getRun() { + return ((Project) Jenkins.getInstance().getItem(projectName)).getBuildByNumber(runId); + } +} diff --git a/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/action/SnowGlobeAction.java b/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/action/SnowGlobeAction.java new file mode 100644 index 0000000..06bc95c --- /dev/null +++ b/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/action/SnowGlobeAction.java @@ -0,0 +1,177 @@ +package com.nirima.jenkins.plugins.snowglobe.action; + +import com.nirima.jenkins.plugins.snowglobe.Consts; +import com.nirima.jenkins.plugins.snowglobe.SnowGlobePluginConfiguration; +import com.nirima.snowglobe.SGExec; + +import org.kohsuke.stapler.QueryParameter; +import org.kohsuke.stapler.StaplerRequest; +import org.kohsuke.stapler.StaplerResponse; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Serializable; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletResponse; + +import hudson.Extension; +import hudson.Launcher; +import hudson.model.Action; +import hudson.model.DependencyGraph; +import hudson.model.Describable; +import hudson.model.Descriptor; +import hudson.model.Hudson; +import hudson.model.Run; +import hudson.util.LogTaskListener; +import jenkins.model.Jenkins; + + +/** + * SnowGlobes linked to a job (action badge) + */ +public class SnowGlobeAction implements Action, Serializable, + Describable { + + private static final Logger LOGGER = Logger.getLogger(SnowGlobeAction.class.getName()); + + private Map states = new HashMap<>(); + public final RunLink runLink; + + public SnowGlobeAction(Run run) { + runLink = new RunLink(run); + } + + public SnowGlobeData createSnowglobe(RegisteredScript script) { + // SnowGlobeData data = new SnowGlobeData(script.scri return null;pt,null); + // states.add( data ); + // return data; + return null; + } + + public SnowGlobeData createSnowglobe(String script) { + return null; + } + + public SnowGlobeData getDataById(String id) { + return states.get(id); + } + + public Collection getStates() { + return states.values(); + } + + @Override + public String getIconFileName() { + return "/plugin/snowglobe-jenkins-plugin/images/32x32/snow-globe.png"; + } + + @Override + public String getDisplayName() { + return "Snowglobe"; + } + + @Override + public String getUrlName() { + return "snowglobeAction"; + } + + public DescriptorImpl getDescriptor() { + return (DescriptorImpl) Jenkins.getInstance().getDescriptorOrDie(getClass()); + } + + + public String getJsUrl(String jsName) { + return Consts.PLUGIN_JS_URL + jsName; + } + + public void doControlSubmit(@QueryParameter("action") String action, + @QueryParameter("launchId") String id, StaplerRequest req, StaplerResponse rsp) throws + ServletException, + IOException, + InterruptedException { + + if( action.equals("apply") ) { + apply(id); + } else if( action.equals("destroy") ) { + destroy(id); + } + + rsp.sendRedirect("."); + } + + public void doDynamic(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException, InterruptedException { + String path = req.getRestOfPath(); + + String id = req.getParameter("id"); + + String graphString = getDataById( id ).graph(); + + rsp.setContentType("image/png"); + + runDot(rsp.getOutputStream(), new ByteArrayInputStream(graphString.getBytes( + Charset.forName("UTF-8"))), "png"); + } + + /** + * Execute the dot command with given input and output stream + * @param type the parameter for the -T option of the graphviz tools + */ + protected void runDot(OutputStream output, InputStream input, String type) + throws IOException { + + String dotPath = SnowGlobePluginConfiguration.get().getDotExeOrDefault(); + Launcher launcher = Jenkins.getInstance().createLauncher(new LogTaskListener(LOGGER, Level.CONFIG)); + try { + launcher.launch() + .cmds(dotPath,"-T" + type, "-Gcharset=UTF-8", "-q1") + .stdin(input) + .stdout(output) + .start().join(); + } catch (InterruptedException e) { + LOGGER.log(Level.SEVERE, "Interrupted while waiting for dot-file to be created", e); + } + finally { + if (output != null) { + output.close(); + } + } + } + + private void apply(String id) throws IOException { + Run run = runLink.getRun(); + getDataById( id ).apply(); + run.save(); + } + + private void destroy(String id) throws IOException { + Run run = runLink.getRun(); + getDataById( id ).destroy(); + run.save(); + } + + public void save(SnowGlobeData data) { + states.put(data.id,data); + } + + /** + * Just for assisting form related stuff. + */ + @Extension + public static class DescriptorImpl extends Descriptor { + public String getDisplayName() { + return "SnowGlobe"; + } + } +} diff --git a/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/action/SnowGlobeData.java b/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/action/SnowGlobeData.java new file mode 100644 index 0000000..74d60f9 --- /dev/null +++ b/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/action/SnowGlobeData.java @@ -0,0 +1,90 @@ +package com.nirima.jenkins.plugins.snowglobe.action; + +import com.nirima.snowglobe.SGExec; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.Serializable; +import java.util.UUID; + +/** + * Created by magnayn on 09/09/2016. + */ +public class SnowGlobeData implements Serializable { + + public String id; + + public String script; + public String state; + + public String lastResult; + + public SnowGlobeData(String id, String script, String state) { + this.id = id; + this.script = script; + this.state = state; + } + + public SnowGlobeData apply() + throws IOException { + InputStream a = new ByteArrayInputStream(script.getBytes("UTF-8")); + InputStream s = null; + if( state != null ) + s = new ByteArrayInputStream(state.getBytes("UTF-8")); + + SGExec exec = new SGExec(a, s ); + + try { + + exec.apply(); + lastResult = "[OK]"; + } catch(Exception ex) { + lastResult = ex.toString(); + } + finally { + state = exec.save(); + + } + return this; + } + + public SnowGlobeData destroy() + throws IOException { + InputStream a = new ByteArrayInputStream(script.getBytes()); + InputStream s = null; + if( state != null ) + s = new ByteArrayInputStream(state.getBytes()); + + SGExec exec = new SGExec( a, s ); + + try { + exec.destroy(); + lastResult = "[OK]"; + } catch(Exception ex) { + lastResult = ex.toString(); + } + finally { + state = exec.save(); + + } + return this; + } + + public String graph() throws IOException { + InputStream a = new ByteArrayInputStream(script.getBytes()); + InputStream s = null; + if( state != null ) + s = new ByteArrayInputStream(state.getBytes()); + + SGExec exec = new SGExec( a, s ); + + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + exec.graph(baos); + baos.close(); + return new String(baos.toByteArray(),"UTF-8"); + + } +} diff --git a/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/calls/SnowGlobeStep.java b/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/calls/SnowGlobeStep.java new file mode 100644 index 0000000..8f5e875 --- /dev/null +++ b/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/calls/SnowGlobeStep.java @@ -0,0 +1,90 @@ +package com.nirima.jenkins.plugins.snowglobe.calls; + +import com.nirima.jenkins.plugins.snowglobe.action.SnowGlobeAction; +import com.nirima.jenkins.plugins.snowglobe.action.SnowGlobeData; +import com.nirima.jenkins.plugins.snowglobe.source.GlobeSource; +import com.nirima.jenkins.plugins.snowglobe.source.GlobeSourceDescriptor; + +import org.kohsuke.stapler.DataBoundConstructor; + +import java.io.IOException; +import java.io.Serializable; + +import javax.annotation.Nonnull; + +import hudson.DescriptorExtensionList; +import hudson.Extension; +import hudson.FilePath; +import hudson.Launcher; +import hudson.model.AbstractProject; +import hudson.model.Run; +import hudson.model.TaskListener; +import hudson.tasks.BuildStepDescriptor; +import hudson.tasks.Builder; +import jenkins.tasks.SimpleBuildStep; + +/** + * Created by magnayn on 10/09/2016. + */ +public class SnowGlobeStep extends Builder implements Serializable, SimpleBuildStep { + public final GlobeSource source; + public final String action; // create, apply, destroy + + @DataBoundConstructor + public SnowGlobeStep(GlobeSource source, String action) { + this.source = source; + this.action = action; + } + + @Override + public void perform(@Nonnull Run run, @Nonnull FilePath filePath, + @Nonnull Launcher launcher, @Nonnull TaskListener taskListener) + throws InterruptedException, IOException { + SnowGlobeData data = source.getData(run, filePath, launcher, taskListener); + + try { + if (action.equals("create")) { + System.out.println("Create"); + } + if (action.equals("apply")) { + System.out.println("Apply"); + } + if (action.equals("Destroy")) { + System.out.println("Destroy"); + } + } + finally{ + SnowGlobeAction action = run.getAction(SnowGlobeAction.class); + if( action == null ) { + action = new SnowGlobeAction(run); + run.addAction(action); + } + + action.save(data); + run.save(); + } + } + + @Override + public DescriptorImpl getDescriptor() { + return (DescriptorImpl) super.getDescriptor(); + } + + @Extension + public static class DescriptorImpl extends BuildStepDescriptor { + + @Override + public boolean isApplicable(Class jobType) { + return true; + } + + @Override + public String getDisplayName() { + return "SnowGlobe"; + } + + public static DescriptorExtensionList getOptionList() { + return GlobeSourceDescriptor.all(); + } + } +} diff --git a/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/source/GlobeSource.java b/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/source/GlobeSource.java new file mode 100644 index 0000000..7e9389a --- /dev/null +++ b/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/source/GlobeSource.java @@ -0,0 +1,34 @@ +package com.nirima.jenkins.plugins.snowglobe.source; + +import com.nirima.jenkins.plugins.snowglobe.action.SnowGlobeData; + +import java.io.IOException; +import java.io.Serializable; + +import hudson.FilePath; +import hudson.Launcher; +import hudson.model.Describable; +import hudson.model.Descriptor; +import hudson.model.Run; +import hudson.model.TaskListener; +import jenkins.model.Jenkins; + +/** + * Created by magnayn on 10/09/2016. + */ +public abstract class GlobeSource implements Describable, Serializable { + + public final String name; + + public GlobeSource(String name) { + this.name = name; + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + public Descriptor getDescriptor() { + return Jenkins.getInstance().getDescriptorOrDie(getClass()); + } + + public abstract SnowGlobeData getData(Run run, FilePath filePath, Launcher launcher, + TaskListener taskListener) throws IOException, InterruptedException; +} diff --git a/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/source/GlobeSourceDescriptor.java b/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/source/GlobeSourceDescriptor.java new file mode 100644 index 0000000..fb11b9c --- /dev/null +++ b/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/source/GlobeSourceDescriptor.java @@ -0,0 +1,15 @@ +package com.nirima.jenkins.plugins.snowglobe.source; + +import hudson.DescriptorExtensionList; +import hudson.model.Descriptor; +import jenkins.model.Jenkins; + +/** + * Created by magnayn on 10/09/2016. + */ +public abstract class GlobeSourceDescriptor extends Descriptor +{ + public static DescriptorExtensionList all() { + return Jenkins.getInstance().getDescriptorList(GlobeSource.class); + } +} diff --git a/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/source/GlobeSourceExisting.java b/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/source/GlobeSourceExisting.java new file mode 100644 index 0000000..381c1ea --- /dev/null +++ b/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/source/GlobeSourceExisting.java @@ -0,0 +1,38 @@ +package com.nirima.jenkins.plugins.snowglobe.source; + +import com.nirima.jenkins.plugins.snowglobe.action.SnowGlobeAction; +import com.nirima.jenkins.plugins.snowglobe.action.SnowGlobeData; + +import org.kohsuke.stapler.DataBoundConstructor; + +import hudson.Extension; +import hudson.FilePath; +import hudson.Launcher; +import hudson.model.Run; +import hudson.model.TaskListener; + +/** + * Created by magnayn on 10/09/2016. + */ +public class GlobeSourceExisting extends GlobeSource { + + @DataBoundConstructor + public GlobeSourceExisting(String name) { + super(name); + } + + @Override + public SnowGlobeData getData(Run run, FilePath filePath, Launcher launcher, + TaskListener taskListener) { + return run.getAction(SnowGlobeAction.class).getDataById(name); + } + + + @Extension + public static final class DescriptorImpl extends GlobeSourceDescriptor { + @Override + public String getDisplayName() { + return "Previously created SnowGlobe"; + } + } +} diff --git a/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/source/GlobeSourceFile.java b/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/source/GlobeSourceFile.java new file mode 100644 index 0000000..c5a6ecd --- /dev/null +++ b/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/source/GlobeSourceFile.java @@ -0,0 +1,53 @@ +package com.nirima.jenkins.plugins.snowglobe.source; + +import com.nirima.jenkins.plugins.snowglobe.action.SnowGlobeAction; +import com.nirima.jenkins.plugins.snowglobe.action.SnowGlobeData; + +import org.apache.commons.io.FileUtils; +import org.kohsuke.stapler.DataBoundConstructor; + +import java.io.File; +import java.io.IOException; + +import hudson.Extension; +import hudson.FilePath; +import hudson.Launcher; +import hudson.model.Run; +import hudson.model.TaskListener; +import hudson.remoting.VirtualChannel; +import hudson.util.IOUtils; +import jenkins.MasterToSlaveFileCallable; + +/** + * Created by magnayn on 10/09/2016. + */ +public class GlobeSourceFile extends GlobeSource { + public final String file; + + @DataBoundConstructor + public GlobeSourceFile(String name, String file) { + super(name); + this.file = file; + } + + @Override + public SnowGlobeData getData(Run run, FilePath filePath, Launcher launcher, + TaskListener taskListener) throws IOException, InterruptedException { + return filePath.act(new MasterToSlaveFileCallable() { + @Override + public SnowGlobeData invoke(File file, VirtualChannel virtualChannel) + throws IOException, InterruptedException { + + return new SnowGlobeData(name, FileUtils.readFileToString(file), null); + } + }); + } + + @Extension + public static final class DescriptorImpl extends GlobeSourceDescriptor { + @Override + public String getDisplayName() { + return "Source file in workspace"; + } + } +} diff --git a/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/source/GlobeSourceScript.java b/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/source/GlobeSourceScript.java new file mode 100644 index 0000000..823e364 --- /dev/null +++ b/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/source/GlobeSourceScript.java @@ -0,0 +1,40 @@ +package com.nirima.jenkins.plugins.snowglobe.source; + +import com.nirima.jenkins.plugins.snowglobe.action.SnowGlobeData; + +import org.kohsuke.stapler.DataBoundConstructor; + +import java.io.IOException; + +import hudson.Extension; +import hudson.FilePath; +import hudson.Launcher; +import hudson.model.Run; +import hudson.model.TaskListener; + +/** + * Created by magnayn on 10/09/2016. + */ +public class GlobeSourceScript extends GlobeSource { + public String script; + + @DataBoundConstructor + public GlobeSourceScript(String name, String script) { + super(name); + this.script = script; + } + + @Override + public SnowGlobeData getData(Run run, FilePath filePath, Launcher launcher, + TaskListener taskListener) throws IOException, InterruptedException { + return new SnowGlobeData(name, script, null); + } + + @Extension + public static final class DescriptorImpl extends GlobeSourceDescriptor { + @Override + public String getDisplayName() { + return "Supplied Script"; + } + } +} diff --git a/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/SnowGlobePluginConfiguration/config.jelly b/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/SnowGlobePluginConfiguration/config.jelly new file mode 100644 index 0000000..eca2b4b --- /dev/null +++ b/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/SnowGlobePluginConfiguration/config.jelly @@ -0,0 +1,11 @@ + + + + + + + + + + diff --git a/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/action/RegisteredScriptAction/index.jelly b/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/action/RegisteredScriptAction/index.jelly new file mode 100644 index 0000000..b86d2d1 --- /dev/null +++ b/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/action/RegisteredScriptAction/index.jelly @@ -0,0 +1,23 @@ + + + + + + + + + + + + + +

Launch a SnowGlobe

+
+ +

..

+ + +
+
+
+
diff --git a/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/action/RegisteredScriptAction/summary.jelly b/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/action/RegisteredScriptAction/summary.jelly new file mode 100644 index 0000000..4b3e02d --- /dev/null +++ b/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/action/RegisteredScriptAction/summary.jelly @@ -0,0 +1,12 @@ + + + + +

Launch New SnowGlobe

+ +

Start and stop configurations

+ +
+
diff --git a/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/action/SnowGlobeAction/index.jelly b/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/action/SnowGlobeAction/index.jelly new file mode 100644 index 0000000..2b009d8 --- /dev/null +++ b/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/action/SnowGlobeAction/index.jelly @@ -0,0 +1,40 @@ + + + + + + + + +

Snowglobes

+ + + +
+ + + + + + + +

SnowGlobe ${res.id}

+

Script

+
+                ${res.script}
+                
+

State

+
${res.state}
+

Graph

+

+

Last Result

+

${res.lastResult}

+
+ + +
+
+ +
+
+
diff --git a/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/action/SnowGlobeAction/summary.jelly b/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/action/SnowGlobeAction/summary.jelly new file mode 100644 index 0000000..0c13b40 --- /dev/null +++ b/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/action/SnowGlobeAction/summary.jelly @@ -0,0 +1,11 @@ + + + + +

SnowGlobe

+ +

Follow link on Left-hand bar for more details

+
+
diff --git a/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/calls/SnowGlobeApplyStep/config.jelly b/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/calls/SnowGlobeApplyStep/config.jelly new file mode 100644 index 0000000..de1f682 --- /dev/null +++ b/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/calls/SnowGlobeApplyStep/config.jelly @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + diff --git a/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/calls/SnowGlobeDestroyStep/config.jelly b/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/calls/SnowGlobeDestroyStep/config.jelly new file mode 100644 index 0000000..79de408 --- /dev/null +++ b/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/calls/SnowGlobeDestroyStep/config.jelly @@ -0,0 +1,10 @@ + + + + + + + + + diff --git a/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/calls/SnowGlobeRegisterScript/config.jelly b/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/calls/SnowGlobeRegisterScript/config.jelly new file mode 100644 index 0000000..9b9fb8a --- /dev/null +++ b/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/calls/SnowGlobeRegisterScript/config.jelly @@ -0,0 +1,15 @@ + + + + + + + + + + + + + diff --git a/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/calls/SnowGlobeStep/config.jelly b/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/calls/SnowGlobeStep/config.jelly new file mode 100644 index 0000000..bd78ef6 --- /dev/null +++ b/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/calls/SnowGlobeStep/config.jelly @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + diff --git a/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/source/GlobeSourceExisting/config.jelly b/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/source/GlobeSourceExisting/config.jelly new file mode 100644 index 0000000..fb89ea9 --- /dev/null +++ b/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/source/GlobeSourceExisting/config.jelly @@ -0,0 +1,10 @@ + + + + + + + + + \ No newline at end of file diff --git a/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/source/GlobeSourceFile/config.jelly b/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/source/GlobeSourceFile/config.jelly new file mode 100644 index 0000000..5273504 --- /dev/null +++ b/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/source/GlobeSourceFile/config.jelly @@ -0,0 +1,14 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/source/GlobeSourceScript/config.jelly b/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/source/GlobeSourceScript/config.jelly new file mode 100644 index 0000000..5251032 --- /dev/null +++ b/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/source/GlobeSourceScript/config.jelly @@ -0,0 +1,14 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/snowglobe-jenkins-plugin/src/main/webapp/images/32x32/snow-globe.png b/snowglobe-jenkins-plugin/src/main/webapp/images/32x32/snow-globe.png new file mode 100644 index 0000000000000000000000000000000000000000..16362ae53944ba5c2a426223dda6f52da6dd9953 GIT binary patch literal 1176 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM3?#3wJbMaAv7|ftIx;Y9?C1WI$O_~uBzpw; zGB8xBF)%c=FfjZA3N^f7U???UV0e|lz+g3lfkC`r&aOZk1_s8Y0G|-o|NsAAV$i+C zpn8)*?FNH75Fs<*?CT5~K=dDIFM|e94Uln%K^Z6uQ2-HyXos+Y;*S|*&_sYzKyiqI zYYdtYyKXb60MUI01qc_S0Aegq)d0oe&Va~5jDoO%3Lxe{NHjs9 zN{C)y_^bRW$pum+B|(0{41(VEo1R6ydB00!^@9X8Mu%=D*%vc;+du96<8!lzbL+hH zj@Tg6zH0f;SJ<^UUIrA|OE;}h5oVsw`h3dr?<|?h&HF#zIl;fiQZ!g-s$8VeyMwc} zy=rvwx7Gf$<=!Hx|M0NheYRg09$PgWY5ehOZkq2Go&~9n*CK)TGbVYvyOgfTn(-UR z;VkfoECwd4>mbbNq%pe!D9B#o>Fdh=fRl}v&uY=0*AIYtDm`5sLp09kPW)-k6ex2v ze|DRQ+rmvD-`u99Xzmt^$~ZA;!cv_;&wc;@f2=)fdF1cI&vVP)R~O%#%Q)*&Mrl>E z^<$A-X z{^X!yf`mlj!IvA%Yt1WXYzs`tkrAHU(0=F8{8`_$jQ1Uz7P&X!|D5uwuIDzoKQbq# zGkBlYUVZoAgl(%i?Kk|5EM1bdq?8Sl# zo|=^=bqTgEQKoXT&fcA8Ji8{HxbRYP?&pPDW}OXUa9q1deBT0|wqm`QIL`l3f$OfC z@`?aK7}>z$`XZM}FJx zk3xJK_DovvM3iU0iQN(HsnIU$dljN4*8ex%sbr-*fAu2Qt1 + + 4.0.0 + + com.nirima + snowglobe + 0.1-SNAPSHOT + + + + com.nirima.snowglobe + snowglobe-shaded + jar + + snowglobe shaded jar for jenkins plugin + + + UTF-8 + + + + 3.0.6-jenkins + + 2.23.1 + 2.6.4 + 4.5 + 1.12 + 1.10 + 2.5 + 2.6 + 1.7.21 + + 1.54 + 2015-01-27T15-02-14 + 19.0 + + + 1.1.7 + 6.9.10 + 4.1.3.Final + 1.3 + 1.8 + 2.3.3 + 1.10.19 + + + 3.0.2 + 3.5.1 + 2.5.3 + 2.19.1 + 2.19.1 + 1.8 + + + + + + com.nirima.snowglobe + snowglobe-core + ${project.version} + true + + + + com.orbitz.consul + consul-client + 0.12.7 + + + org.javers + javers-core + 2.1.2 + + + + + + com.github.docker-java + docker-java + ${docker-java.version} + true + + + com.fasterxml.jackson.jaxrs + jackson-jaxrs-json-provider + ${jackson-jaxrs.version} + + + + org.glassfish.jersey.connectors + jersey-apache-connector + ${jersey.version} + + + org.apache.httpcomponents + httpcore + 4.4.5 + + + org.apache.httpcomponents + httpclient + ${httpclient.version} + + + org.glassfish.jersey.core + jersey-client + ${jersey.version} + + + + + + + + com.kohlschutter.junixsocket + junixsocket-common + 2.0.4 + + + log4j + log4j + + + + + + com.kohlschutter.junixsocket + junixsocket-native-common + 2.0.4 + + + log4j + log4j + + + + + + org.apache.commons + commons-compress + ${commons-compress.version} + + + commons-codec + commons-codec + ${commons-codec.version} + + + commons-lang + commons-lang + ${commons-lang.version} + + + commons-io + commons-io + ${commons-io.version} + + + org.slf4j + slf4j-api + ${slf4j-api.version} + + + org.slf4j + jcl-over-slf4j + 1.7.21 + + + javax.ws.rs + javax.ws.rs-api + 2.0 + + + javax.annotation + javax.annotation-api + 1.2 + + + com.google.guava + guava + ${guava.version} + + + org.bouncycastle + bcpkix-jdk15on + ${bouncycastle.version} + + + + + + com.google.code.findbugs + annotations + 3.0.1 + provided + + + + io.netty + netty-codec-http + ${netty.version} + + + io.netty + netty-handler + ${netty.version} + + + io.netty + netty-handler-proxy + ${netty.version} + + + io.netty + netty-transport-native-epoll + ${netty.version} + linux-x86_64 + + + + + + + ${project.artifactId} + + + maven-shade-plugin + 2.4.3 + + + shade + package + + shade + + + + + true + + + org.glassfish*:* + org.apache.httpcomponents*:* + com.github.docker-java:* + com.nirima.snowglobe:* + com.google.guava:* + + + + + org.apache.http + shaded.org.apache.http + + + com.google.common + shaded.com.google.common + + + + + + + + + + From 98f18feb9291069700c7be6028bbb59c69676588 Mon Sep 17 00:00:00 2001 From: Nigel Magnay Date: Sun, 11 Sep 2016 10:34:17 +0100 Subject: [PATCH 02/14] Add a Jenkinsfile for builds. Signed-off-by: Nigel Magnay --- Jenkinsfile | 35 +++++++++++++++++++++++++++++ snowglobe-exe/pom.xml | 7 +++--- snowglobe-jenkins-plugin/.gitignore | 1 + snowglobe-jenkins-plugin/pom.xml | 15 +++++++++++++ 4 files changed, 55 insertions(+), 3 deletions(-) create mode 100644 Jenkinsfile create mode 100644 snowglobe-jenkins-plugin/.gitignore diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 0000000..7c3435f --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,35 @@ +node { + stage name:"Checkout" + checkout scm; + + stage name:"Dependencies" + buildTempDeps() + + stage name:"Build" + build() + +} + + +def build() { + + def mvnHome = tool 'latest' + env.MAVEN_OPTS="-Xmx2G"; + + sh "${mvnHome}/bin/mvn clean install" + +} + +def buildTempDeps() { + // Temporary dependencies to things that have not yet been fixed + dir('docker-java') { + + git url: "https://github.com/magnayn/docker-java.git" + + def mvnHome = tool 'latest' + env.MAVEN_OPTS="-Xmx2G"; + + sh "${mvnHome}/bin/mvn -DskipTests clean install" + } + +} \ No newline at end of file diff --git a/snowglobe-exe/pom.xml b/snowglobe-exe/pom.xml index 91e5337..5c8177d 100644 --- a/snowglobe-exe/pom.xml +++ b/snowglobe-exe/pom.xml @@ -17,6 +17,7 @@ org.apache.maven.plugins maven-jar-plugin + 3.0.2 @@ -27,7 +28,7 @@ - org.dstovall + com.jolira onejar-maven-plugin 1.4.4 @@ -55,10 +56,10 @@ 2.0.31
- + diff --git a/snowglobe-jenkins-plugin/.gitignore b/snowglobe-jenkins-plugin/.gitignore new file mode 100644 index 0000000..b8f99f5 --- /dev/null +++ b/snowglobe-jenkins-plugin/.gitignore @@ -0,0 +1 @@ +work diff --git a/snowglobe-jenkins-plugin/pom.xml b/snowglobe-jenkins-plugin/pom.xml index ddd8fbb..85e2d39 100644 --- a/snowglobe-jenkins-plugin/pom.xml +++ b/snowglobe-jenkins-plugin/pom.xml @@ -8,6 +8,7 @@ org.jenkins-ci.plugins plugin 2.14 + com.nirima.snowglobe @@ -179,4 +180,18 @@ + + + + repo.jenkins-ci.org + http://repo.jenkins-ci.org/public/ + + + + + + repo.jenkins-ci.org + http://repo.jenkins-ci.org/public/ + + From 06f87db95252c32d7049bab31d389cc514c481df Mon Sep 17 00:00:00 2001 From: Nigel Magnay Date: Sun, 11 Sep 2016 17:21:17 +0100 Subject: [PATCH 03/14] Dependency ordering seems very critical Signed-off-by: Nigel Magnay --- snowglobe-jenkins-plugin/pom.xml | 137 +++++++++++++++++++++++++------ 1 file changed, 110 insertions(+), 27 deletions(-) diff --git a/snowglobe-jenkins-plugin/pom.xml b/snowglobe-jenkins-plugin/pom.xml index 85e2d39..2a7be04 100644 --- a/snowglobe-jenkins-plugin/pom.xml +++ b/snowglobe-jenkins-plugin/pom.xml @@ -49,41 +49,64 @@ + + + org.jenkins-ci.main + jenkins-core + 2.7 + compile + + + org.jenkins-ci.main + jenkins-war + 2.7 + war-for-test + compile + + + org.jenkins-ci.main + jenkins-war + 2.7 + war + test + + + org.jenkins-ci.main + jenkins-test-harness + 2.14 + compile + com.nirima.snowglobe snowglobe-shaded - ${snowglobe.version} + ${project.version} + compile - org.jenkins-ci.plugins ssh-slaves 1.6 + compile org.jenkins-ci.plugins token-macro 1.7 + compile true org.jenkins-ci.plugins durable-task 1.3 + compile - org.hamcrest hamcrest-all 1.3 test - org.mockito mockito-core @@ -94,23 +117,76 @@ org.jenkins-ci.modules instance-identity 1.4 + compile - org.jenkins-ci.main - jenkins-core - ${jenkins.version} + com.google.code.findbugs + annotations + 3.0.0 + provided + true - org.jenkins-ci.main - jenkins-war - ${jenkins.version} - war-for-test + net.jcip + jcip-annotations + 1.0 + provided + true - org.jenkins-ci.main - jenkins-war - ${jenkins.version} - war + org.codehaus.mojo + animal-sniffer-annotations + 1.14 + provided + true + + + javax.servlet + javax.servlet-api + 3.1.0 + test + + + javax.servlet + servlet-api + 2.4 + provided + + + org.jenkins-ci + test-annotations + 1.2 + test + + + junit + junit + 4.12 + test + + + org.slf4j + slf4j-api + 1.7.7 + compile + true + + + org.slf4j + log4j-over-slf4j + 1.7.7 + test + + + org.slf4j + jcl-over-slf4j + 1.7.7 + test + + + org.slf4j + slf4j-jdk14 + 1.7.7 test @@ -120,7 +196,7 @@ maven-compiler-plugin org.apache.maven.plugins - + 3.5.1 ${jdk.source} ${jdk.target} @@ -145,11 +221,7 @@ guava ${guava.version}
- - io.dropwizard.metrics - metrics-core - ${metrics.version} - + org.kohsuke.stapler stapler @@ -157,8 +229,11 @@ + + + - org.apache.httpcomponents:*,com.github.docker-java*,com.google.guava*,org.codehaus.groovy* + org.apache.httpcomponents:*,com.github.docker-java*,com.google.guava* @@ -181,6 +256,14 @@ + + repo.jenkins-ci.org From 3fd682520ab0a8dc18c4f8ce35539839a4306343 Mon Sep 17 00:00:00 2001 From: Nigel Magnay Date: Sun, 11 Sep 2016 17:22:02 +0100 Subject: [PATCH 04/14] Minor updates Signed-off-by: Nigel Magnay --- .../snowglobe/action/RegisteredScript.java | 2 -- .../plugins/snowglobe/action/RunLink.java | 9 ++++++++- .../snowglobe/action/SnowGlobeAction.java | 13 +++---------- .../plugins/snowglobe/action/SnowGlobeData.java | 9 ++++----- .../snowglobe/source/GlobeSourceFile.java | 2 +- .../calls/SnowGlobeApplyStep/config.jelly | 16 ---------------- .../calls/SnowGlobeDestroyStep/config.jelly | 10 ---------- .../calls/SnowGlobeRegisterScript/config.jelly | 15 --------------- 8 files changed, 16 insertions(+), 60 deletions(-) delete mode 100644 snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/calls/SnowGlobeApplyStep/config.jelly delete mode 100644 snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/calls/SnowGlobeDestroyStep/config.jelly delete mode 100644 snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/calls/SnowGlobeRegisterScript/config.jelly diff --git a/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/action/RegisteredScript.java b/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/action/RegisteredScript.java index b1b684f..f801543 100644 --- a/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/action/RegisteredScript.java +++ b/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/action/RegisteredScript.java @@ -1,7 +1,5 @@ package com.nirima.jenkins.plugins.snowglobe.action; -import com.nirima.snowglobe.core.SnowGlobe; - import java.io.IOException; import java.io.Serializable; diff --git a/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/action/RunLink.java b/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/action/RunLink.java index 6dbc73d..4e4609c 100644 --- a/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/action/RunLink.java +++ b/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/action/RunLink.java @@ -4,6 +4,7 @@ import hudson.model.Project; import hudson.model.Run; +import hudson.model.TopLevelItem; import jenkins.model.Jenkins; /** @@ -19,6 +20,12 @@ public RunLink(Run run) { } protected Run getRun() { - return ((Project) Jenkins.getInstance().getItem(projectName)).getBuildByNumber(runId); + Project + item = ((Project) Jenkins.getInstance().getItem(projectName)); + + if( item == null ) + throw new IllegalStateException("Project " + projectName + " no longer exists"); + + return item.getBuildByNumber(runId); } } diff --git a/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/action/SnowGlobeAction.java b/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/action/SnowGlobeAction.java index 06bc95c..0ce1a74 100644 --- a/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/action/SnowGlobeAction.java +++ b/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/action/SnowGlobeAction.java @@ -2,7 +2,6 @@ import com.nirima.jenkins.plugins.snowglobe.Consts; import com.nirima.jenkins.plugins.snowglobe.SnowGlobePluginConfiguration; -import com.nirima.snowglobe.SGExec; import org.kohsuke.stapler.QueryParameter; import org.kohsuke.stapler.StaplerRequest; @@ -14,25 +13,19 @@ import java.io.OutputStream; import java.io.Serializable; import java.nio.charset.Charset; -import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; -import java.util.List; import java.util.Map; -import java.util.UUID; import java.util.logging.Level; import java.util.logging.Logger; import javax.servlet.ServletException; -import javax.servlet.http.HttpServletResponse; import hudson.Extension; import hudson.Launcher; import hudson.model.Action; -import hudson.model.DependencyGraph; import hudson.model.Describable; import hudson.model.Descriptor; -import hudson.model.Hudson; import hudson.model.Run; import hudson.util.LogTaskListener; import jenkins.model.Jenkins; @@ -42,7 +35,7 @@ * SnowGlobes linked to a job (action badge) */ public class SnowGlobeAction implements Action, Serializable, - Describable { + Describable { private static final Logger LOGGER = Logger.getLogger(SnowGlobeAction.class.getName()); @@ -112,7 +105,7 @@ public void doControlSubmit(@QueryParameter("action") String action, } public void doDynamic(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException, InterruptedException { - String path = req.getRestOfPath(); + //String path = req.getRestOfPath(); String id = req.getParameter("id"); @@ -169,7 +162,7 @@ public void save(SnowGlobeData data) { * Just for assisting form related stuff. */ @Extension - public static class DescriptorImpl extends Descriptor { + public static class DescriptorImpl extends Descriptor { public String getDisplayName() { return "SnowGlobe"; } diff --git a/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/action/SnowGlobeData.java b/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/action/SnowGlobeData.java index 74d60f9..59041ee 100644 --- a/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/action/SnowGlobeData.java +++ b/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/action/SnowGlobeData.java @@ -7,7 +7,6 @@ import java.io.IOException; import java.io.InputStream; import java.io.Serializable; -import java.util.UUID; /** * Created by magnayn on 09/09/2016. @@ -52,10 +51,10 @@ public SnowGlobeData apply() public SnowGlobeData destroy() throws IOException { - InputStream a = new ByteArrayInputStream(script.getBytes()); + InputStream a = new ByteArrayInputStream(script.getBytes("UTF-8")); InputStream s = null; if( state != null ) - s = new ByteArrayInputStream(state.getBytes()); + s = new ByteArrayInputStream(state.getBytes("UTF-8")); SGExec exec = new SGExec( a, s ); @@ -73,10 +72,10 @@ public SnowGlobeData destroy() } public String graph() throws IOException { - InputStream a = new ByteArrayInputStream(script.getBytes()); + InputStream a = new ByteArrayInputStream(script.getBytes("UTF-8")); InputStream s = null; if( state != null ) - s = new ByteArrayInputStream(state.getBytes()); + s = new ByteArrayInputStream(state.getBytes("UTF-8")); SGExec exec = new SGExec( a, s ); diff --git a/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/source/GlobeSourceFile.java b/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/source/GlobeSourceFile.java index c5a6ecd..c4d0a64 100644 --- a/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/source/GlobeSourceFile.java +++ b/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/source/GlobeSourceFile.java @@ -38,7 +38,7 @@ public SnowGlobeData getData(Run run, FilePath filePath, Launcher launcher public SnowGlobeData invoke(File file, VirtualChannel virtualChannel) throws IOException, InterruptedException { - return new SnowGlobeData(name, FileUtils.readFileToString(file), null); + return new SnowGlobeData(name, new String(FileUtils.readFileToByteArray(file),"UTF-8"), null); } }); } diff --git a/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/calls/SnowGlobeApplyStep/config.jelly b/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/calls/SnowGlobeApplyStep/config.jelly deleted file mode 100644 index de1f682..0000000 --- a/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/calls/SnowGlobeApplyStep/config.jelly +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - diff --git a/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/calls/SnowGlobeDestroyStep/config.jelly b/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/calls/SnowGlobeDestroyStep/config.jelly deleted file mode 100644 index 79de408..0000000 --- a/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/calls/SnowGlobeDestroyStep/config.jelly +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - diff --git a/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/calls/SnowGlobeRegisterScript/config.jelly b/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/calls/SnowGlobeRegisterScript/config.jelly deleted file mode 100644 index 9b9fb8a..0000000 --- a/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/calls/SnowGlobeRegisterScript/config.jelly +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - From 3e1b6204edd4642182dc5c8395b31fdac50fb8f9 Mon Sep 17 00:00:00 2001 From: Nigel Magnay Date: Mon, 12 Sep 2016 17:03:36 +0100 Subject: [PATCH 05/14] Concentrate on Pipeline based configuration. Signed-off-by: Nigel Magnay --- snowglobe-jenkins-plugin/pom.xml | 10 + .../SnowGlobePluginConfiguration.java | 20 ++ .../snowglobe/action/RegisteredScript.java | 31 --- .../action/RegisteredScriptAction.java | 105 --------- .../plugins/snowglobe/action/RunLink.java | 31 --- .../snowglobe/action/SnowGlobeAction.java | 62 ++++-- .../{action => calls}/SnowGlobeData.java | 16 +- .../snowglobe/calls/SnowGlobeStep.java | 90 -------- .../snowglobe/registry/DeleteListener.java | 16 ++ .../snowglobe/registry/SnowGlobeRegistry.java | 108 ++++++++++ .../plugins/snowglobe/source/GlobeSource.java | 34 --- .../source/GlobeSourceDescriptor.java | 15 -- .../snowglobe/source/GlobeSourceExisting.java | 38 ---- .../snowglobe/source/GlobeSourceFile.java | 53 ----- .../snowglobe/source/GlobeSourceScript.java | 40 ---- .../workflow/SnowGlobeWorkflowStep.java | 203 ++++++++++++++++++ .../action/RegisteredScriptAction/index.jelly | 23 -- .../RegisteredScriptAction/summary.jelly | 12 -- .../action/SnowGlobeAction/index.jelly | 10 +- .../calls/SnowGlobeData/config.jelly | 7 + .../calls/SnowGlobeStep/config.jelly | 19 -- .../source/GlobeSourceExisting/config.jelly | 10 - .../source/GlobeSourceFile/config.jelly | 14 -- .../source/GlobeSourceScript/config.jelly | 14 -- .../SnowGlobeWorkflowStep/config.jelly | 7 + 25 files changed, 431 insertions(+), 557 deletions(-) delete mode 100644 snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/action/RegisteredScript.java delete mode 100644 snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/action/RegisteredScriptAction.java delete mode 100644 snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/action/RunLink.java rename snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/{action => calls}/SnowGlobeData.java (82%) delete mode 100644 snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/calls/SnowGlobeStep.java create mode 100644 snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/registry/DeleteListener.java create mode 100644 snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/registry/SnowGlobeRegistry.java delete mode 100644 snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/source/GlobeSource.java delete mode 100644 snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/source/GlobeSourceDescriptor.java delete mode 100644 snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/source/GlobeSourceExisting.java delete mode 100644 snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/source/GlobeSourceFile.java delete mode 100644 snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/source/GlobeSourceScript.java create mode 100644 snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/workflow/SnowGlobeWorkflowStep.java delete mode 100644 snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/action/RegisteredScriptAction/index.jelly delete mode 100644 snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/action/RegisteredScriptAction/summary.jelly create mode 100644 snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/calls/SnowGlobeData/config.jelly delete mode 100644 snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/calls/SnowGlobeStep/config.jelly delete mode 100644 snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/source/GlobeSourceExisting/config.jelly delete mode 100644 snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/source/GlobeSourceFile/config.jelly delete mode 100644 snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/source/GlobeSourceScript/config.jelly create mode 100644 snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/workflow/SnowGlobeWorkflowStep/config.jelly diff --git a/snowglobe-jenkins-plugin/pom.xml b/snowglobe-jenkins-plugin/pom.xml index 2a7be04..f4f2900 100644 --- a/snowglobe-jenkins-plugin/pom.xml +++ b/snowglobe-jenkins-plugin/pom.xml @@ -82,6 +82,16 @@ ${project.version} compile
+ + org.jenkins-ci.plugins.workflow + workflow-step-api + 2.3 + + + org.jenkins-ci.plugins + script-security + 1.5 + org.jenkins-ci.plugins ssh-slaves diff --git a/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/SnowGlobePluginConfiguration.java b/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/SnowGlobePluginConfiguration.java index fae3276..bd40701 100644 --- a/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/SnowGlobePluginConfiguration.java +++ b/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/SnowGlobePluginConfiguration.java @@ -1,5 +1,11 @@ package com.nirima.jenkins.plugins.snowglobe; +import com.nirima.jenkins.plugins.snowglobe.registry.SnowGlobeRegistry; + +import net.sf.json.JSONObject; + +import org.kohsuke.stapler.StaplerRequest; + import hudson.Extension; import hudson.Functions; import hudson.Util; @@ -13,6 +19,18 @@ public class SnowGlobePluginConfiguration extends GlobalConfiguration { private String dotExe; + private SnowGlobeRegistry registry; + + public SnowGlobePluginConfiguration() { + load(); + } + + @Override + public boolean configure(StaplerRequest req, JSONObject json) throws FormException { + req.bindJSON(this,json); + return true; + } + /** * Returns this singleton instance. * @@ -38,4 +56,6 @@ public void setDotExe(String dotExe) { this.dotExe = dotExe; save(); } + + } diff --git a/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/action/RegisteredScript.java b/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/action/RegisteredScript.java deleted file mode 100644 index f801543..0000000 --- a/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/action/RegisteredScript.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.nirima.jenkins.plugins.snowglobe.action; - -import java.io.IOException; -import java.io.Serializable; - -import hudson.model.Run; - -/** - * Created by magnayn on 09/09/2016. - */ -public class RegisteredScript implements Serializable { - public final String script; - public final String id; - - public RegisteredScript(String script, String id) { - this.script = script; - this.id = id; - } - - public SnowGlobeData build(Run run) throws IOException { - SnowGlobeAction action = run.getAction(SnowGlobeAction.class); - if( action==null ) - action = new SnowGlobeAction(run); - - SnowGlobeData data = action.createSnowglobe(this); - - run.save(); - return data; - } - -} diff --git a/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/action/RegisteredScriptAction.java b/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/action/RegisteredScriptAction.java deleted file mode 100644 index cddfb17..0000000 --- a/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/action/RegisteredScriptAction.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.nirima.jenkins.plugins.snowglobe.action; - -import com.nirima.jenkins.plugins.snowglobe.Consts; - -import org.kohsuke.stapler.QueryParameter; -import org.kohsuke.stapler.StaplerRequest; -import org.kohsuke.stapler.StaplerResponse; -import org.kohsuke.stapler.export.ExportedBean; - -import java.io.IOException; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.List; - -import javax.servlet.ServletException; - -import hudson.Extension; -import hudson.model.Action; -import hudson.model.Describable; -import hudson.model.Descriptor; -import hudson.model.Run; -import jenkins.model.Jenkins; - -/** - * Created by magnayn on 09/09/2016. - */ -@ExportedBean -public class RegisteredScriptAction implements Action, Serializable, - Describable { - - public final RunLink runLink; - - public List scripts = new ArrayList<>(); - - public RegisteredScriptAction(Run run) - { - runLink = new RunLink(run); - } - - public RegisteredScript addScript(String script, String id) { - RegisteredScript s = new RegisteredScript(script, id); - scripts.add( s ); - return s; - } - - public RegisteredScript getScript(String id) { - for(RegisteredScript script : scripts) { - if( script.id.equals(id)) - return script; - } - return null; - } - - - @Override - public String getIconFileName() { - return "/plugin/snowglobe-jenkins-plugin/images/32x32/snow-globe.png"; - } - - @Override - public String getDisplayName() { - return "Launch Snowglobe"; - } - - @Override - public String getUrlName() { - return "snowglobe"; - } - - public DescriptorImpl getDescriptor() { - return (DescriptorImpl) Jenkins.getInstance().getDescriptorOrDie(getClass()); - } - - public String getJsUrl(String jsName) { - return Consts.PLUGIN_JS_URL + jsName; - } - - public void doControlSubmit(@QueryParameter("launchId") String launchId, StaplerRequest req, StaplerResponse rsp) throws - ServletException, - IOException - { - launchScript(launchId); - rsp.sendRedirect("snowglobeAction"); - } - - private void launchScript(String launchId) throws IOException { - Run run = runLink.getRun(); - getScript(launchId).build( run ).apply(); - run.save(); - } - - /** - * Just for assisting form related stuff. - */ - @Extension - public static class DescriptorImpl extends Descriptor { - public String getDisplayName() { - return "SnowGlobe"; - } - - public void launch(String id) { - System.out.println("launch"); - } - } -} diff --git a/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/action/RunLink.java b/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/action/RunLink.java deleted file mode 100644 index 4e4609c..0000000 --- a/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/action/RunLink.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.nirima.jenkins.plugins.snowglobe.action; - -import java.io.Serializable; - -import hudson.model.Project; -import hudson.model.Run; -import hudson.model.TopLevelItem; -import jenkins.model.Jenkins; - -/** - * Created by magnayn on 09/09/2016. - */ -public class RunLink implements Serializable { - final int runId; - final String projectName; - - public RunLink(Run run) { - this.runId = run.getNumber(); - this.projectName = run.getParent().getName(); - } - - protected Run getRun() { - Project - item = ((Project) Jenkins.getInstance().getItem(projectName)); - - if( item == null ) - throw new IllegalStateException("Project " + projectName + " no longer exists"); - - return item.getBuildByNumber(runId); - } -} diff --git a/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/action/SnowGlobeAction.java b/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/action/SnowGlobeAction.java index 0ce1a74..2f0eecd 100644 --- a/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/action/SnowGlobeAction.java +++ b/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/action/SnowGlobeAction.java @@ -2,6 +2,8 @@ import com.nirima.jenkins.plugins.snowglobe.Consts; import com.nirima.jenkins.plugins.snowglobe.SnowGlobePluginConfiguration; +import com.nirima.jenkins.plugins.snowglobe.calls.SnowGlobeData; +import com.nirima.jenkins.plugins.snowglobe.registry.SnowGlobeRegistry; import org.kohsuke.stapler.QueryParameter; import org.kohsuke.stapler.StaplerRequest; @@ -13,9 +15,13 @@ import java.io.OutputStream; import java.io.Serializable; import java.nio.charset.Charset; +import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; +import java.util.HashSet; +import java.util.List; import java.util.Map; +import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; @@ -39,30 +45,53 @@ public class SnowGlobeAction implements Action, Serializable, private static final Logger LOGGER = Logger.getLogger(SnowGlobeAction.class.getName()); - private Map states = new HashMap<>(); - public final RunLink runLink; + private String id; - public SnowGlobeAction(Run run) { - runLink = new RunLink(run); + public SnowGlobeAction(String runId) { + this.id = runId; } - public SnowGlobeData createSnowglobe(RegisteredScript script) { - // SnowGlobeData data = new SnowGlobeData(script.scri return null;pt,null); - // states.add( data ); - // return data; - return null; + public String getId() { + return id; } + public static class ActionData { + public String id; + public SnowGlobeData data; + + public ActionData(String id, SnowGlobeData data) { + this.id = id; + this.data = data; + } + + public String getId() { + return id; + } + + public SnowGlobeData getData() { + return data; + } + } + + public SnowGlobeData createSnowglobe(String script) { return null; } public SnowGlobeData getDataById(String id) { - return states.get(id); + return SnowGlobeRegistry.get().getById(id); } - public Collection getStates() { - return states.values(); + public Collection getStates() { + Set ids = SnowGlobeRegistry.get().getGlobesForRunId(this.id); + + Set data = new HashSet<>(); + + if( ids != null ) + ids.forEach( it -> data.add( new ActionData(it, SnowGlobeRegistry.get().getById(it) )) ); + + return data; + } @Override @@ -143,20 +172,15 @@ protected void runDot(OutputStream output, InputStream input, String type) } private void apply(String id) throws IOException { - Run run = runLink.getRun(); getDataById( id ).apply(); - run.save(); + SnowGlobeRegistry.get().save(); } private void destroy(String id) throws IOException { - Run run = runLink.getRun(); getDataById( id ).destroy(); - run.save(); + SnowGlobeRegistry.get().save(); } - public void save(SnowGlobeData data) { - states.put(data.id,data); - } /** * Just for assisting form related stuff. diff --git a/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/action/SnowGlobeData.java b/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/calls/SnowGlobeData.java similarity index 82% rename from snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/action/SnowGlobeData.java rename to snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/calls/SnowGlobeData.java index 59041ee..957d8af 100644 --- a/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/action/SnowGlobeData.java +++ b/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/calls/SnowGlobeData.java @@ -1,7 +1,11 @@ -package com.nirima.jenkins.plugins.snowglobe.action; +package com.nirima.jenkins.plugins.snowglobe.calls; +import com.nirima.jenkins.plugins.snowglobe.SnowGlobePluginConfiguration; +import com.nirima.jenkins.plugins.snowglobe.registry.SnowGlobeRegistry; import com.nirima.snowglobe.SGExec; +import org.jenkinsci.plugins.scriptsecurity.sandbox.whitelists.Whitelisted; + import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -13,19 +17,18 @@ */ public class SnowGlobeData implements Serializable { - public String id; - public String script; public String state; public String lastResult; - public SnowGlobeData(String id, String script, String state) { - this.id = id; + public SnowGlobeData(String script, String state) { + this.script = script; this.state = state; } + @Whitelisted public SnowGlobeData apply() throws IOException { InputStream a = new ByteArrayInputStream(script.getBytes("UTF-8")); @@ -49,6 +52,7 @@ public SnowGlobeData apply() return this; } + @Whitelisted public SnowGlobeData destroy() throws IOException { InputStream a = new ByteArrayInputStream(script.getBytes("UTF-8")); @@ -71,6 +75,7 @@ public SnowGlobeData destroy() return this; } + @Whitelisted public String graph() throws IOException { InputStream a = new ByteArrayInputStream(script.getBytes("UTF-8")); InputStream s = null; @@ -86,4 +91,5 @@ public String graph() throws IOException { return new String(baos.toByteArray(),"UTF-8"); } + } diff --git a/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/calls/SnowGlobeStep.java b/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/calls/SnowGlobeStep.java deleted file mode 100644 index 8f5e875..0000000 --- a/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/calls/SnowGlobeStep.java +++ /dev/null @@ -1,90 +0,0 @@ -package com.nirima.jenkins.plugins.snowglobe.calls; - -import com.nirima.jenkins.plugins.snowglobe.action.SnowGlobeAction; -import com.nirima.jenkins.plugins.snowglobe.action.SnowGlobeData; -import com.nirima.jenkins.plugins.snowglobe.source.GlobeSource; -import com.nirima.jenkins.plugins.snowglobe.source.GlobeSourceDescriptor; - -import org.kohsuke.stapler.DataBoundConstructor; - -import java.io.IOException; -import java.io.Serializable; - -import javax.annotation.Nonnull; - -import hudson.DescriptorExtensionList; -import hudson.Extension; -import hudson.FilePath; -import hudson.Launcher; -import hudson.model.AbstractProject; -import hudson.model.Run; -import hudson.model.TaskListener; -import hudson.tasks.BuildStepDescriptor; -import hudson.tasks.Builder; -import jenkins.tasks.SimpleBuildStep; - -/** - * Created by magnayn on 10/09/2016. - */ -public class SnowGlobeStep extends Builder implements Serializable, SimpleBuildStep { - public final GlobeSource source; - public final String action; // create, apply, destroy - - @DataBoundConstructor - public SnowGlobeStep(GlobeSource source, String action) { - this.source = source; - this.action = action; - } - - @Override - public void perform(@Nonnull Run run, @Nonnull FilePath filePath, - @Nonnull Launcher launcher, @Nonnull TaskListener taskListener) - throws InterruptedException, IOException { - SnowGlobeData data = source.getData(run, filePath, launcher, taskListener); - - try { - if (action.equals("create")) { - System.out.println("Create"); - } - if (action.equals("apply")) { - System.out.println("Apply"); - } - if (action.equals("Destroy")) { - System.out.println("Destroy"); - } - } - finally{ - SnowGlobeAction action = run.getAction(SnowGlobeAction.class); - if( action == null ) { - action = new SnowGlobeAction(run); - run.addAction(action); - } - - action.save(data); - run.save(); - } - } - - @Override - public DescriptorImpl getDescriptor() { - return (DescriptorImpl) super.getDescriptor(); - } - - @Extension - public static class DescriptorImpl extends BuildStepDescriptor { - - @Override - public boolean isApplicable(Class jobType) { - return true; - } - - @Override - public String getDisplayName() { - return "SnowGlobe"; - } - - public static DescriptorExtensionList getOptionList() { - return GlobeSourceDescriptor.all(); - } - } -} diff --git a/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/registry/DeleteListener.java b/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/registry/DeleteListener.java new file mode 100644 index 0000000..5ddc7b7 --- /dev/null +++ b/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/registry/DeleteListener.java @@ -0,0 +1,16 @@ +package com.nirima.jenkins.plugins.snowglobe.registry; + +import hudson.Extension; +import hudson.model.Run; +import hudson.model.listeners.RunListener; + +/** + * Created by magnayn on 12/09/2016. + */ +@Extension +public class DeleteListener extends RunListener { + @Override + public void onDeleted(Run r) { + SnowGlobeRegistry.get().removeFromRun(r); + } +} \ No newline at end of file diff --git a/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/registry/SnowGlobeRegistry.java b/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/registry/SnowGlobeRegistry.java new file mode 100644 index 0000000..9587e84 --- /dev/null +++ b/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/registry/SnowGlobeRegistry.java @@ -0,0 +1,108 @@ +package com.nirima.jenkins.plugins.snowglobe.registry; + +import com.nirima.jenkins.plugins.snowglobe.action.SnowGlobeAction; +import com.nirima.jenkins.plugins.snowglobe.calls.SnowGlobeData; + +import java.io.IOException; +import java.io.Serializable; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; + +import hudson.Extension; +import hudson.model.Run; +import jenkins.model.GlobalConfiguration; + +/** + * Created by magnayn on 12/09/2016. + */ +@Extension +public class SnowGlobeRegistry extends GlobalConfiguration implements Serializable { + + Map globes = new LinkedHashMap<>(); + + Map> runToIds = new HashMap<>(); + + + public SnowGlobeRegistry() { + load(); + } + + /** + * Returns this singleton instance. + * + * @return the singleton. + */ + public static SnowGlobeRegistry get() { + return GlobalConfiguration.all().get(SnowGlobeRegistry.class); + } + + public SnowGlobeData register(String id, SnowGlobeData data) { + globes.put(id, data); + save(); + return data; + } + + public void register(String id, SnowGlobeData data, Run run) throws IOException { + String runId = makeRunId(run); + register(id,data); + + SnowGlobeAction action = run.getAction(SnowGlobeAction.class); + if (action == null) { + action = new SnowGlobeAction(runId); + run.addAction(action); + } + run.save(); + + if( !runToIds.containsKey(runId)) { + runToIds.put(runId, new HashSet()); + } + + runToIds.get(runId).add(id); + + save(); + } + + public static String makeRunId(Run run) { + return run.getParent().getUrl() + run.getNumber(); + } + + public void remove(String id) { + globes.remove(id); + save(); + } + + public SnowGlobeData getById(String id) { + SnowGlobeData data = globes.get(id); + return data; + } + + public Collection getActiveGlobes() { + Set data = new HashSet<>(); + data.addAll(globes.values()); + + data.stream().filter( it -> it.state != null ); + + return data; + } + + public void removeFromRun(Run r) { + + String runId = makeRunId(r); + + Set items = runToIds.get( runId ); + items.forEach( it -> remove(it) ); + runToIds.remove( runId ); + + + } + + public Set getGlobesForRunId(String id) { + return runToIds.get(id); + } +} + + diff --git a/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/source/GlobeSource.java b/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/source/GlobeSource.java deleted file mode 100644 index 7e9389a..0000000 --- a/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/source/GlobeSource.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.nirima.jenkins.plugins.snowglobe.source; - -import com.nirima.jenkins.plugins.snowglobe.action.SnowGlobeData; - -import java.io.IOException; -import java.io.Serializable; - -import hudson.FilePath; -import hudson.Launcher; -import hudson.model.Describable; -import hudson.model.Descriptor; -import hudson.model.Run; -import hudson.model.TaskListener; -import jenkins.model.Jenkins; - -/** - * Created by magnayn on 10/09/2016. - */ -public abstract class GlobeSource implements Describable, Serializable { - - public final String name; - - public GlobeSource(String name) { - this.name = name; - } - - @SuppressWarnings({"unchecked", "rawtypes"}) - public Descriptor getDescriptor() { - return Jenkins.getInstance().getDescriptorOrDie(getClass()); - } - - public abstract SnowGlobeData getData(Run run, FilePath filePath, Launcher launcher, - TaskListener taskListener) throws IOException, InterruptedException; -} diff --git a/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/source/GlobeSourceDescriptor.java b/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/source/GlobeSourceDescriptor.java deleted file mode 100644 index fb11b9c..0000000 --- a/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/source/GlobeSourceDescriptor.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.nirima.jenkins.plugins.snowglobe.source; - -import hudson.DescriptorExtensionList; -import hudson.model.Descriptor; -import jenkins.model.Jenkins; - -/** - * Created by magnayn on 10/09/2016. - */ -public abstract class GlobeSourceDescriptor extends Descriptor -{ - public static DescriptorExtensionList all() { - return Jenkins.getInstance().getDescriptorList(GlobeSource.class); - } -} diff --git a/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/source/GlobeSourceExisting.java b/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/source/GlobeSourceExisting.java deleted file mode 100644 index 381c1ea..0000000 --- a/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/source/GlobeSourceExisting.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.nirima.jenkins.plugins.snowglobe.source; - -import com.nirima.jenkins.plugins.snowglobe.action.SnowGlobeAction; -import com.nirima.jenkins.plugins.snowglobe.action.SnowGlobeData; - -import org.kohsuke.stapler.DataBoundConstructor; - -import hudson.Extension; -import hudson.FilePath; -import hudson.Launcher; -import hudson.model.Run; -import hudson.model.TaskListener; - -/** - * Created by magnayn on 10/09/2016. - */ -public class GlobeSourceExisting extends GlobeSource { - - @DataBoundConstructor - public GlobeSourceExisting(String name) { - super(name); - } - - @Override - public SnowGlobeData getData(Run run, FilePath filePath, Launcher launcher, - TaskListener taskListener) { - return run.getAction(SnowGlobeAction.class).getDataById(name); - } - - - @Extension - public static final class DescriptorImpl extends GlobeSourceDescriptor { - @Override - public String getDisplayName() { - return "Previously created SnowGlobe"; - } - } -} diff --git a/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/source/GlobeSourceFile.java b/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/source/GlobeSourceFile.java deleted file mode 100644 index c4d0a64..0000000 --- a/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/source/GlobeSourceFile.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.nirima.jenkins.plugins.snowglobe.source; - -import com.nirima.jenkins.plugins.snowglobe.action.SnowGlobeAction; -import com.nirima.jenkins.plugins.snowglobe.action.SnowGlobeData; - -import org.apache.commons.io.FileUtils; -import org.kohsuke.stapler.DataBoundConstructor; - -import java.io.File; -import java.io.IOException; - -import hudson.Extension; -import hudson.FilePath; -import hudson.Launcher; -import hudson.model.Run; -import hudson.model.TaskListener; -import hudson.remoting.VirtualChannel; -import hudson.util.IOUtils; -import jenkins.MasterToSlaveFileCallable; - -/** - * Created by magnayn on 10/09/2016. - */ -public class GlobeSourceFile extends GlobeSource { - public final String file; - - @DataBoundConstructor - public GlobeSourceFile(String name, String file) { - super(name); - this.file = file; - } - - @Override - public SnowGlobeData getData(Run run, FilePath filePath, Launcher launcher, - TaskListener taskListener) throws IOException, InterruptedException { - return filePath.act(new MasterToSlaveFileCallable() { - @Override - public SnowGlobeData invoke(File file, VirtualChannel virtualChannel) - throws IOException, InterruptedException { - - return new SnowGlobeData(name, new String(FileUtils.readFileToByteArray(file),"UTF-8"), null); - } - }); - } - - @Extension - public static final class DescriptorImpl extends GlobeSourceDescriptor { - @Override - public String getDisplayName() { - return "Source file in workspace"; - } - } -} diff --git a/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/source/GlobeSourceScript.java b/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/source/GlobeSourceScript.java deleted file mode 100644 index 823e364..0000000 --- a/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/source/GlobeSourceScript.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.nirima.jenkins.plugins.snowglobe.source; - -import com.nirima.jenkins.plugins.snowglobe.action.SnowGlobeData; - -import org.kohsuke.stapler.DataBoundConstructor; - -import java.io.IOException; - -import hudson.Extension; -import hudson.FilePath; -import hudson.Launcher; -import hudson.model.Run; -import hudson.model.TaskListener; - -/** - * Created by magnayn on 10/09/2016. - */ -public class GlobeSourceScript extends GlobeSource { - public String script; - - @DataBoundConstructor - public GlobeSourceScript(String name, String script) { - super(name); - this.script = script; - } - - @Override - public SnowGlobeData getData(Run run, FilePath filePath, Launcher launcher, - TaskListener taskListener) throws IOException, InterruptedException { - return new SnowGlobeData(name, script, null); - } - - @Extension - public static final class DescriptorImpl extends GlobeSourceDescriptor { - @Override - public String getDisplayName() { - return "Supplied Script"; - } - } -} diff --git a/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/workflow/SnowGlobeWorkflowStep.java b/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/workflow/SnowGlobeWorkflowStep.java new file mode 100644 index 0000000..74b9128 --- /dev/null +++ b/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/workflow/SnowGlobeWorkflowStep.java @@ -0,0 +1,203 @@ +package com.nirima.jenkins.plugins.snowglobe.workflow; + + +import com.nirima.jenkins.plugins.snowglobe.calls.SnowGlobeData; +import com.nirima.jenkins.plugins.snowglobe.registry.SnowGlobeRegistry; + +import org.apache.commons.io.FileUtils; +import org.jenkinsci.plugins.scriptsecurity.sandbox.whitelists.Whitelisted; +import org.jenkinsci.plugins.workflow.steps.AbstractStepDescriptorImpl; +import org.jenkinsci.plugins.workflow.steps.AbstractStepImpl; +import org.jenkinsci.plugins.workflow.steps.AbstractSynchronousStepExecution; +import org.jenkinsci.plugins.workflow.steps.StepContextParameter; +import org.jenkinsci.plugins.workflow.steps.StepExecution; +import org.kohsuke.stapler.DataBoundConstructor; + +import java.io.File; +import java.io.IOException; +import java.io.Serializable; +import java.util.UUID; + +import javax.inject.Inject; + +import hudson.Extension; +import hudson.FilePath; +import hudson.Launcher; +import hudson.model.Run; +import hudson.model.TaskListener; +import hudson.remoting.VirtualChannel; +import jenkins.MasterToSlaveFileCallable; + + +/** + * Created by magnayn on 12/09/2016. + */ +public class SnowGlobeWorkflowStep extends AbstractStepImpl implements Serializable { + + + @DataBoundConstructor + public SnowGlobeWorkflowStep() { //String dummy) { + + } + + + public static SnowGlobeData getScriptData(FilePath filePath, String path) throws IOException, InterruptedException { + final FilePath sgFile = new FilePath(filePath, path); + + System.out.println("Get Data for script @ " + sgFile.toString()); + + return sgFile.act(new MasterToSlaveFileCallable() { + @Override + public SnowGlobeData invoke(File file, VirtualChannel virtualChannel) + throws IOException, InterruptedException { + System.out.println("Get Data for script @ " + file); + + return new SnowGlobeData(new String(FileUtils.readFileToByteArray(file), "UTF-8"), null); + } + }); + } + + public static class SourceExection implements Serializable { + + private final Execution execution; + + SourceExection(Execution execution) { + this.execution = execution; + } + + @Whitelisted + BuildExecution fromFile(String file) throws IOException, InterruptedException { + return new BuildExecution(execution, SnowGlobeWorkflowStep.getScriptData(execution.filePath, file)); + } + + @Whitelisted + BuildExecution fromString(String string) { + SnowGlobeData data = new SnowGlobeData(string, null); + return new BuildExecution(execution,data); + } + } + + public static class BuildExecution implements Serializable { + SnowGlobeData data; + + private final Execution execution; + + public BuildExecution(Execution run, SnowGlobeData data) { + this.execution = run; + this.data = data; + } + + @Whitelisted + public void apply() throws IOException { + data.apply(); + } + @Whitelisted + public void destroy() throws IOException { + data.destroy(); + } + + @Whitelisted + public SnowGlobeRegister register() { + return new SnowGlobeRegister(this); + } + + } + + public static class SnowGlobeRegister implements Serializable { + + private BuildExecution data; + + private String id; + + private boolean withRun = true; + + @Whitelisted + public SnowGlobeRegister(BuildExecution snowGlobeData) { + this.data = snowGlobeData; + this.id = UUID.randomUUID().toString(); + } + + @Whitelisted + public SnowGlobeRegister withId(String id) { + this.id = id; + return this; + } + @Whitelisted + public SnowGlobeRegister withRun(boolean b) { + withRun = b; + + return this; + } + @Whitelisted + public BuildExecution exec() throws IOException { + if( !withRun ) + SnowGlobeRegistry.get().register(id, data.data); + else + SnowGlobeRegistry.get().register(id, data.data, data.execution.run); + + return data; + } + + } + + @Override + public DescriptorImpl getDescriptor() { + return (DescriptorImpl)super.getDescriptor(); + } + + public static class Execution extends AbstractSynchronousStepExecution { + + static final long serialVersionUID = 1L; + + + @StepContextParameter + private transient TaskListener taskListener; + + @StepContextParameter + private transient FilePath filePath; + + @StepContextParameter + private transient Run run; + + @StepContextParameter + private transient Launcher launcher; + + @Inject + private transient SnowGlobeWorkflowStep step; + + + protected SourceExection run() throws Exception { + return new SourceExection(this); + } + } + + + @Extension + public static class DescriptorImpl extends AbstractStepDescriptorImpl { + + public DescriptorImpl() { + super(Execution.class); + } + + public DescriptorImpl( + Class executionType) { + super(executionType); + } + + @Override + public String getFunctionName() { + return "snowglobe"; + } + + @Override + public String getDisplayName() { + return "Make SnowGlobes"; + } + + + } + + + + +} diff --git a/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/action/RegisteredScriptAction/index.jelly b/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/action/RegisteredScriptAction/index.jelly deleted file mode 100644 index b86d2d1..0000000 --- a/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/action/RegisteredScriptAction/index.jelly +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - -

Launch a SnowGlobe

-
- -

..

- - -
-
-
-
diff --git a/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/action/RegisteredScriptAction/summary.jelly b/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/action/RegisteredScriptAction/summary.jelly deleted file mode 100644 index 4b3e02d..0000000 --- a/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/action/RegisteredScriptAction/summary.jelly +++ /dev/null @@ -1,12 +0,0 @@ - - - - -

Launch New SnowGlobe

- -

Start and stop configurations

- -
-
diff --git a/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/action/SnowGlobeAction/index.jelly b/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/action/SnowGlobeAction/index.jelly index 2b009d8..8ab5392 100644 --- a/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/action/SnowGlobeAction/index.jelly +++ b/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/action/SnowGlobeAction/index.jelly @@ -7,9 +7,11 @@

Snowglobes

- +

Run ID = ${it.id}

+ +
@@ -21,14 +23,14 @@

SnowGlobe ${res.id}

Script

-                ${res.script}
+                ${res.data.script}
                 

State

-
${res.state}
+
${res.data.state}

Graph

Last Result

-

${res.lastResult}

+

${res.data.lastResult}


diff --git a/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/calls/SnowGlobeData/config.jelly b/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/calls/SnowGlobeData/config.jelly new file mode 100644 index 0000000..f5b2ee5 --- /dev/null +++ b/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/calls/SnowGlobeData/config.jelly @@ -0,0 +1,7 @@ + + + + +

See documentation

+ +
diff --git a/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/calls/SnowGlobeStep/config.jelly b/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/calls/SnowGlobeStep/config.jelly deleted file mode 100644 index bd78ef6..0000000 --- a/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/calls/SnowGlobeStep/config.jelly +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/source/GlobeSourceExisting/config.jelly b/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/source/GlobeSourceExisting/config.jelly deleted file mode 100644 index fb89ea9..0000000 --- a/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/source/GlobeSourceExisting/config.jelly +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/source/GlobeSourceFile/config.jelly b/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/source/GlobeSourceFile/config.jelly deleted file mode 100644 index 5273504..0000000 --- a/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/source/GlobeSourceFile/config.jelly +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/source/GlobeSourceScript/config.jelly b/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/source/GlobeSourceScript/config.jelly deleted file mode 100644 index 5251032..0000000 --- a/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/source/GlobeSourceScript/config.jelly +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/workflow/SnowGlobeWorkflowStep/config.jelly b/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/workflow/SnowGlobeWorkflowStep/config.jelly new file mode 100644 index 0000000..f5b2ee5 --- /dev/null +++ b/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/workflow/SnowGlobeWorkflowStep/config.jelly @@ -0,0 +1,7 @@ + + + + +

See documentation

+ +
From b28368fe8d72a2409242330fdc472c0137cb5899 Mon Sep 17 00:00:00 2001 From: Nigel Magnay Date: Tue, 13 Sep 2016 11:18:39 +0100 Subject: [PATCH 06/14] A bit of SLF4j. Use reflections to get modules. Signed-off-by: Nigel Magnay --- pom.xml | 15 +++- snowglobe-core/pom.xml | 10 +++ .../groovy/com/nirima/snowglobe/SGExec.groovy | 10 +-- .../com/nirima/snowglobe/consul/Consul.groovy | 11 +-- .../snowglobe/consul/ConsulActions.groovy | 9 --- .../com/nirima/snowglobe/core/Context.groovy | 74 +++++++++++-------- .../com/nirima/snowglobe/core/DSL.groovy | 37 +++++----- .../nirima/snowglobe/core/Interfaces.groovy | 13 ++++ .../com/nirima/snowglobe/docker/Docker.groovy | 14 ++-- .../snowglobe/docker/DockerActions.groovy | 10 +-- .../com/nirima/snowglobe/graph/SGNode.groovy | 8 +- .../nirima/snowglobe/plan/PlanBuilder.groovy | 3 +- .../nirima/snowglobe/test/TestGroovy.groovy | 17 +++++ .../snowglobe/calls/SnowGlobeData.java | 2 + .../registry/SnowGlobeRegistry/config.jelly | 5 ++ snowglobe-shaded/pom.xml | 24 +++++- 16 files changed, 156 insertions(+), 106 deletions(-) create mode 100644 snowglobe-core/src/test/groovy/com/nirima/snowglobe/test/TestGroovy.groovy create mode 100644 snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/registry/SnowGlobeRegistry/config.jelly diff --git a/pom.xml b/pom.xml index 63b1a91..f58d4e0 100644 --- a/pom.xml +++ b/pom.xml @@ -32,9 +32,18 @@ snowglobe-core - snowglobe-exe - snowglobe-shaded - snowglobe-jenkins-plugin + snowglobe-jenkins + snowglobe-exe + snowglobe-shaded + snowglobe-jenkins-plugin + + diff --git a/snowglobe-core/pom.xml b/snowglobe-core/pom.xml index b672f7a..9e4a125 100644 --- a/snowglobe-core/pom.xml +++ b/snowglobe-core/pom.xml @@ -79,6 +79,16 @@ 2.4.7 + + org.reflections + reflections + 0.9.10 + + + com.google.guava + guava + 19.0 + com.github.docker-java docker-java diff --git a/snowglobe-core/src/main/groovy/com/nirima/snowglobe/SGExec.groovy b/snowglobe-core/src/main/groovy/com/nirima/snowglobe/SGExec.groovy index 2f7c9e0..0e3fdc9 100644 --- a/snowglobe-core/src/main/groovy/com/nirima/snowglobe/SGExec.groovy +++ b/snowglobe-core/src/main/groovy/com/nirima/snowglobe/SGExec.groovy @@ -1,14 +1,6 @@ package com.nirima.snowglobe -import com.nirima.snowglobe.core.Context -import com.nirima.snowglobe.core.Core -import com.nirima.snowglobe.core.Module -import com.nirima.snowglobe.core.Provider -import com.nirima.snowglobe.core.Resource -import com.nirima.snowglobe.core.SnowGlobe -import com.nirima.snowglobe.core.SnowGlobeContext -import com.nirima.snowglobe.core.SnowGlobeSystem -import com.nirima.snowglobe.core.State +import com.nirima.snowglobe.core.* import com.nirima.snowglobe.graph.Graph import com.nirima.snowglobe.graph.GraphBuilder import com.nirima.snowglobe.plan.Plan diff --git a/snowglobe-core/src/main/groovy/com/nirima/snowglobe/consul/Consul.groovy b/snowglobe-core/src/main/groovy/com/nirima/snowglobe/consul/Consul.groovy index aae3f3c..7852360 100644 --- a/snowglobe-core/src/main/groovy/com/nirima/snowglobe/consul/Consul.groovy +++ b/snowglobe-core/src/main/groovy/com/nirima/snowglobe/consul/Consul.groovy @@ -1,17 +1,14 @@ package com.nirima.snowglobe.consul import com.google.common.net.HostAndPort -import com.nirima.snowglobe.core.ResourceState -import com.nirima.snowglobe.core.Module -import com.nirima.snowglobe.core.Provider -import com.nirima.snowglobe.core.Resource -import com.nirima.snowglobe.plan.NodePair +import com.nirima.snowglobe.core.* import com.nirima.snowglobe.plan.PlanAction import com.orbitz.consul.Consul /** * Created by magnayn on 04/09/2016. */ +@SGItem("consul_provider") class ConsulProvider extends Provider { public String address; @@ -48,7 +45,7 @@ class ConsulKeyPrefixState extends ResourceState { } } } - +@SGItem("consul_key_prefix") class ConsulKeyPrefix extends Resource { @@ -101,7 +98,7 @@ class ConsulKeysState extends ResourceState { } } } - +@SGItem("consul_keys") class ConsulKeys extends Resource { diff --git a/snowglobe-core/src/main/groovy/com/nirima/snowglobe/consul/ConsulActions.groovy b/snowglobe-core/src/main/groovy/com/nirima/snowglobe/consul/ConsulActions.groovy index 923428e..9c685d5 100644 --- a/snowglobe-core/src/main/groovy/com/nirima/snowglobe/consul/ConsulActions.groovy +++ b/snowglobe-core/src/main/groovy/com/nirima/snowglobe/consul/ConsulActions.groovy @@ -1,18 +1,9 @@ package com.nirima.snowglobe.consul -import com.github.dockerjava.api.DockerClient -import com.github.dockerjava.api.command.CreateContainerCmd -import com.nirima.snowglobe.docker.DockerContainerState -import com.nirima.snowglobe.docker.DockerProvider -import com.nirima.snowglobe.plan.NodePair -import com.nirima.snowglobe.plan.PlanAction import com.nirima.snowglobe.plan.PlanActionBase import com.orbitz.consul.Consul import com.orbitz.consul.KeyValueClient - - - class ConsulKeyPrefixAction extends PlanActionBase { diff --git a/snowglobe-core/src/main/groovy/com/nirima/snowglobe/core/Context.groovy b/snowglobe-core/src/main/groovy/com/nirima/snowglobe/core/Context.groovy index ffd0692..51e0fc4 100644 --- a/snowglobe-core/src/main/groovy/com/nirima/snowglobe/core/Context.groovy +++ b/snowglobe-core/src/main/groovy/com/nirima/snowglobe/core/Context.groovy @@ -1,8 +1,5 @@ package com.nirima.snowglobe.core - -import com.google.common.base.MoreObjects -import com.google.common.base.Objects - +import groovy.util.logging.Slf4j /** * Created by magnayn on 04/09/2016. */ @@ -60,15 +57,18 @@ public class Dependency { return result } + @Override public String toString() { - return Objects.toStringHelper(this) - .add("from", from) - .add("to", to) - .toString(); + return """\ +Dependency{ + from=$from, + to=$to +}""" } } +@Slf4j public class SnowGlobeContext extends Context { List modules = []; @@ -100,7 +100,7 @@ public class SnowGlobeContext extends Context { } public void buildModules() { - println("Build Modules") + log.trace 'Build Modules'; modules.each { processModule(it); } @@ -111,12 +111,12 @@ public class SnowGlobeContext extends Context { dependencies = []; processedModules = []; inProcessModules = []; - println("Visit Modules") + log.trace "Visit Modules" getProxy().accept(this); } private void processModule(ModuleContext context) { - println("Build Module ${context}") + log.debug "Build Module ${context}" if( processedModules.contains(context)) return; @@ -129,12 +129,12 @@ public class SnowGlobeContext extends Context { inProcessModules.remove(context); processedModules.add(context); - println("Built Module ${context}") + log.debug "Built Module ${context}" } public Context getElement(ModuleContext from, Class klass, String id) { ModuleContext result = modules. - find { it.getProxy().getClass() == klass && it.getProxy().id == id } + find { klass.isAssignableFrom( it.getProxy().getClass() ) && it.getProxy().id == id } if (result == null) throw IllegalStateException("Cannot find resource ${id} type ${klass}"); @@ -157,7 +157,7 @@ public class SnowGlobeContext extends Context { } - +@Slf4j public class ProviderContext extends Context { ModuleContext parent; @@ -171,20 +171,20 @@ public class ProviderContext extends Context { Provider resource = getProxy(); return resource.getProperty(name); } catch(Exception ex) { - println "State for resource " + getProxy() + " does not have property " + name; + log.error "State for resource " + getProxy() + " does not have property " + name; throw ex; } } void setProperty(String name, Object value) { - println " set ${name}=${value} on ${this}" + log.debug " set ${name}=${value} on ${this}" try { getProxy().setProperty(name,value); } catch( Exception ex ) { - println("Error setting state for resource ${getProxy()} property ${name} with value ${value}"); + log.error ("Error setting state for resource ${getProxy()} property ${name} with value ${value}"); println ex; throw ex; } @@ -214,7 +214,13 @@ public class ModuleReferenceContext Object[] theArgs = (Object[]) args; Class c = Core.INSTANCE.getClassForName(name); - referenceTo.getElement(referenceFrom,c,(String)theArgs[0]) + + // We can use provider("name") or provider() or provider(null) + String id = null; + if( theArgs.length > 0 ) + id = theArgs[0]; + + referenceTo.getElement(referenceFrom,c,(String)id) } } @@ -249,6 +255,7 @@ public class ModuleImportContext extends Context { } } +@Slf4j public class ModuleContext extends Context{ SnowGlobeContext parent; @@ -355,7 +362,7 @@ public class ModuleContext extends Context{ public Context findImport(Class klass, String id) { for(ModuleImportContext mic : imports) { Object ctx = mic.getProxy().references.find { - it.getProxy().getClass() == klass && it.getProxy().id == id + klass.isAssignableFrom( it.getProxy().getClass() ) && it.getProxy().id == id } if( ctx !=null ) return (Context)ctx; @@ -365,7 +372,7 @@ public class ModuleContext extends Context{ public Object getElement(Context from, Class klass, String id) { - println("ModuleContext.getElement ${from} wants {$klass} of id ${id}"); + log.debug("ModuleContext.getElement ${from} wants {$klass} of id ${id}"); if(klass == Module.class ) { // Need to ask the parent for a module. @@ -374,19 +381,19 @@ public class ModuleContext extends Context{ return new ModuleReferenceContext(this, context); } - ProviderContext provider = providers.find { it.getProxy().getClass() == klass && it.getProxy().id == id } + ProviderContext provider = providers.find { klass.isAssignableFrom( it.getProxy().getClass() ) && it.getProxy().id == id } if( provider != null ) { dependencies << new Dependency(from, provider); return provider; } - DataSourceContext dataSource = dataSources.find { it.getProxy().getClass() == klass && it.getProxy().id == id } + DataSourceContext dataSource = dataSources.find { klass.isAssignableFrom( it.getProxy().getClass() ) && it.getProxy().id == id } if( dataSource != null ) { dependencies << new Dependency(from, dataSource); return dataSource; } - ResourceContext result = resources.find { it.getProxy().getClass() == klass && it.getProxy().id == id } + ResourceContext result = resources.find { klass.isAssignableFrom( it.getProxy().getClass() ) && it.getProxy().id == id } if( result == null ) { // Perhaps it was imported in the imports {} section @@ -408,7 +415,7 @@ public class ModuleContext extends Context{ } - +@Slf4j public class StateContext { public Context parent; @@ -454,13 +461,13 @@ public class StateContext { // To get a value def getProperty(String name) { - println " get ${name}? on ${this}" + log.trace " get ${name}? on ${this}" try { State resource = getProxy(); return resource.getProperty(name); } catch (Exception ex) { - println "State for resource " + getProxy() + " does not have property " + name; + log.error "State for resource " + getProxy() + " does not have property " + name; throw ex; } } @@ -473,7 +480,7 @@ public class StateContext { getProxy().setProperty(name, value); } catch (Exception ex) { - println( + log.error( "Error setting state for resource ${getProxy()} property ${name} with value ${value}"); println ex; throw ex; @@ -491,12 +498,14 @@ public class StateContext { @Override public String toString() { - return MoreObjects.toStringHelper(this) - .add("parent", parent) - .toString(); + return """\ +StateContext{ + parent=$parent +}""" } } +@Slf4j public class DataSourceContext extends Context { public ModuleContext moduleContext; @@ -525,7 +534,7 @@ public class DataSourceContext extends Context { return resource.getProperty(name); } } catch(Exception ex) { - println "State for resource " + getProxy() + " does not have property " + name; + log.error "State for resource " + getProxy() + " does not have property " + name; throw ex; } @@ -534,6 +543,7 @@ public class DataSourceContext extends Context { } +@Slf4j public class ResourceContext extends Context { public ModuleContext moduleContext; @@ -564,7 +574,7 @@ public class ResourceContext extends Context { Resource resource = getProxy(); return resource.getState().getProperty(name); } catch(Exception ex) { - println "State for resource " + getProxy() + " does not have property " + name; + log.error "State for resource " + getProxy() + " does not have property " + name; throw ex; } } diff --git a/snowglobe-core/src/main/groovy/com/nirima/snowglobe/core/DSL.groovy b/snowglobe-core/src/main/groovy/com/nirima/snowglobe/core/DSL.groovy index 758efe1..6c56d10 100644 --- a/snowglobe-core/src/main/groovy/com/nirima/snowglobe/core/DSL.groovy +++ b/snowglobe-core/src/main/groovy/com/nirima/snowglobe/core/DSL.groovy @@ -1,15 +1,7 @@ package com.nirima.snowglobe.core -i -import com.nirima.snowglobe.consul.ConsulKeyPrefix -import com.nirima.snowglobe.consul.ConsulKeys -import com.nirima.snowglobe.docker.DockerImage -import com.nirima.snowglobe.consul.ConsulProvider -import com.nirima.snowglobe.docker.DockerContainer -import com.nirima.snowglobe.docker.DockerProvider -import com.nirima.snowglobe.docker.DockerRegistry -import com.nirima.snowglobe.docker.DockerRegistryImage import com.nirima.snowglobe.plan.PlanAction +import org.reflections.Reflections import java.lang.reflect.ParameterizedType @@ -18,20 +10,27 @@ public class Core { public static Core INSTANCE = new Core(); - // TODO: Build all this from annotations + private Core() { - classesMap["docker_container"] = DockerContainer.class; - classesMap["docker_provider"] = DockerProvider.class; - classesMap["docker_registry"] = DockerRegistry.class; - classesMap["docker_image"] = DockerImage.class; - classesMap["docker_registry_image"] = DockerRegistryImage.class; - classesMap["consul_provider"] = ConsulProvider.class; + Reflections reflections = new Reflections("com.nirima.snowglobe"); + + Set> annotated = reflections.getTypesAnnotatedWith(SGItem.class); + + annotated.each { + + it -> try { + classesMap.put(it.getAnnotation(SGItem.class).value(), it) + } catch(Exception ex) { + ex.printStackTrace(); + } + } - classesMap["consul_key_prefix"] = ConsulKeyPrefix.class; - classesMap["consul_keys"] = ConsulKeys.class; + } + public void register(Class c, String name) { + classesMap[name] = c; } public Class getClassForName(String name) { @@ -181,7 +180,7 @@ public class Module { } Resource getResource(Class klass, String id) { - resources.find { it.getClass() == klass && it.id == id } + resources.find { klass.isAssignableFrom( it.getClass() ) && it.id == id } } } diff --git a/snowglobe-core/src/main/groovy/com/nirima/snowglobe/core/Interfaces.groovy b/snowglobe-core/src/main/groovy/com/nirima/snowglobe/core/Interfaces.groovy index 8b519db..6a4fc01 100644 --- a/snowglobe-core/src/main/groovy/com/nirima/snowglobe/core/Interfaces.groovy +++ b/snowglobe-core/src/main/groovy/com/nirima/snowglobe/core/Interfaces.groovy @@ -1,5 +1,18 @@ package com.nirima.snowglobe.core +import java.lang.annotation.ElementType +import java.lang.annotation.Retention +import java.lang.annotation.Target + +import static java.lang.annotation.RetentionPolicy.RUNTIME + +@Target(value = [ElementType.TYPE] ) +@Retention(RUNTIME) +public @interface SGItem +{ + String value(); +} + // Element with an ID public interface IElement { String getId(); diff --git a/snowglobe-core/src/main/groovy/com/nirima/snowglobe/docker/Docker.groovy b/snowglobe-core/src/main/groovy/com/nirima/snowglobe/docker/Docker.groovy index f547430..ac78354 100644 --- a/snowglobe-core/src/main/groovy/com/nirima/snowglobe/docker/Docker.groovy +++ b/snowglobe-core/src/main/groovy/com/nirima/snowglobe/docker/Docker.groovy @@ -8,12 +8,7 @@ import com.github.dockerjava.core.DefaultDockerClientConfig import com.github.dockerjava.core.DockerClientBuilder import com.github.dockerjava.core.command.PullImageResultCallback import com.google.common.base.Objects -import com.nirima.snowglobe.core.DataSource -import com.nirima.snowglobe.core.DataSourceState -import com.nirima.snowglobe.core.ResourceState -import com.nirima.snowglobe.core.Module -import com.nirima.snowglobe.core.Provider -import com.nirima.snowglobe.core.Resource +import com.nirima.snowglobe.core.* import com.nirima.snowglobe.plan.PlanAction /** @@ -131,6 +126,7 @@ public class DockerContainerState extends ResourceState { } } +@SGItem("docker_container") public class DockerContainer extends Resource { DockerContainer(Module module, String id, @@ -171,7 +167,7 @@ public class DockerImageState extends ResourceState { } } - +@SGItem("docker_image") public class DockerImage extends Resource { @@ -204,6 +200,7 @@ public class DockerImage extends Resource { } +@SGItem("docker_registry_image") public class DockerRegistryImage extends DataSource { DockerRegistryImage(Module module, String id, Closure closure) { @@ -263,6 +260,7 @@ public class DockerRegistryImageState extends DataSourceState { // } } +@SGItem("docker_provider") public class DockerProvider extends Provider { public String host; @@ -284,7 +282,7 @@ public class DockerProvider extends Provider { } - +@SGItem("docker_registry") public class DockerRegistry extends Provider { public String username; public String password; diff --git a/snowglobe-core/src/main/groovy/com/nirima/snowglobe/docker/DockerActions.groovy b/snowglobe-core/src/main/groovy/com/nirima/snowglobe/docker/DockerActions.groovy index 952fb58..86e7a06 100644 --- a/snowglobe-core/src/main/groovy/com/nirima/snowglobe/docker/DockerActions.groovy +++ b/snowglobe-core/src/main/groovy/com/nirima/snowglobe/docker/DockerActions.groovy @@ -3,18 +3,10 @@ package com.nirima.snowglobe.docker import com.github.dockerjava.api.DockerClient import com.github.dockerjava.api.command.CreateContainerCmd import com.github.dockerjava.api.command.CreateContainerResponse -import com.github.dockerjava.api.command.PullImageCmd import com.github.dockerjava.api.exception.NotModifiedException -import com.github.dockerjava.api.model.AuthConfig -import com.github.dockerjava.api.model.ExposedPort -import com.github.dockerjava.api.model.Image -import com.github.dockerjava.api.model.Link -import com.github.dockerjava.api.model.Ports -import com.github.dockerjava.api.model.VolumesFrom -import com.github.dockerjava.core.command.PullImageResultCallback +import com.github.dockerjava.api.model.* import com.nirima.snowglobe.plan.PlanActionBase - /** * Created by magnayn on 05/09/2016. */ diff --git a/snowglobe-core/src/main/groovy/com/nirima/snowglobe/graph/SGNode.groovy b/snowglobe-core/src/main/groovy/com/nirima/snowglobe/graph/SGNode.groovy index 3883b60..1ff9e1d 100644 --- a/snowglobe-core/src/main/groovy/com/nirima/snowglobe/graph/SGNode.groovy +++ b/snowglobe-core/src/main/groovy/com/nirima/snowglobe/graph/SGNode.groovy @@ -1,13 +1,7 @@ package com.nirima.snowglobe.graph import com.google.common.base.Objects -import com.nirima.snowglobe.core.Context -import com.nirima.snowglobe.core.Dependency -import com.nirima.snowglobe.core.Module -import com.nirima.snowglobe.core.Provider -import com.nirima.snowglobe.core.Resource -import com.nirima.snowglobe.core.SnowGlobe -import com.nirima.snowglobe.core.SnowGlobeContext +import com.nirima.snowglobe.core.* /** * Created by magnayn on 05/09/2016. diff --git a/snowglobe-core/src/main/groovy/com/nirima/snowglobe/plan/PlanBuilder.groovy b/snowglobe-core/src/main/groovy/com/nirima/snowglobe/plan/PlanBuilder.groovy index ba0314a..99d5f65 100644 --- a/snowglobe-core/src/main/groovy/com/nirima/snowglobe/plan/PlanBuilder.groovy +++ b/snowglobe-core/src/main/groovy/com/nirima/snowglobe/plan/PlanBuilder.groovy @@ -1,14 +1,13 @@ package com.nirima.snowglobe.plan +import com.nirima.snowglobe.core.Resource import com.nirima.snowglobe.core.ResourceContext import com.nirima.snowglobe.core.ResourceState -import com.nirima.snowglobe.core.Resource import com.nirima.snowglobe.core.SnowGlobeContext import com.nirima.snowglobe.graph.DependencyList_DFS import com.nirima.snowglobe.graph.Graph import com.nirima.snowglobe.graph.GraphBuilder import com.nirima.snowglobe.graph.SGNode -import groovy.transform.CompileStatic /** * Created by magnayn on 05/09/2016. diff --git a/snowglobe-core/src/test/groovy/com/nirima/snowglobe/test/TestGroovy.groovy b/snowglobe-core/src/test/groovy/com/nirima/snowglobe/test/TestGroovy.groovy new file mode 100644 index 0000000..b1c13b3 --- /dev/null +++ b/snowglobe-core/src/test/groovy/com/nirima/snowglobe/test/TestGroovy.groovy @@ -0,0 +1,17 @@ +package com.nirima.snowglobe.test + +import com.nirima.snowglobe.core.Module +import com.nirima.snowglobe.core.SGItem +import com.nirima.snowglobe.docker.DockerProvider + +/** + * Created by magnayn on 13/09/2016. + */ +@SGItem("test_provider") +public class TestDockerProvider extends DockerProvider { + + TestDockerProvider(Module module, String id, + Closure closure) { + super(module, id, closure) + } +} \ No newline at end of file diff --git a/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/calls/SnowGlobeData.java b/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/calls/SnowGlobeData.java index 957d8af..5cb0750 100644 --- a/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/calls/SnowGlobeData.java +++ b/snowglobe-jenkins-plugin/src/main/java/com/nirima/jenkins/plugins/snowglobe/calls/SnowGlobeData.java @@ -3,6 +3,8 @@ import com.nirima.jenkins.plugins.snowglobe.SnowGlobePluginConfiguration; import com.nirima.jenkins.plugins.snowglobe.registry.SnowGlobeRegistry; import com.nirima.snowglobe.SGExec; +import com.nirima.snowglobe.core.Core; +import com.nirima.snowglobe.jenkins.JenkinsDockerProvider; import org.jenkinsci.plugins.scriptsecurity.sandbox.whitelists.Whitelisted; diff --git a/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/registry/SnowGlobeRegistry/config.jelly b/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/registry/SnowGlobeRegistry/config.jelly new file mode 100644 index 0000000..897da22 --- /dev/null +++ b/snowglobe-jenkins-plugin/src/main/resources/com/nirima/jenkins/plugins/snowglobe/registry/SnowGlobeRegistry/config.jelly @@ -0,0 +1,5 @@ + + + + + diff --git a/snowglobe-shaded/pom.xml b/snowglobe-shaded/pom.xml index 88ad0b4..2605e95 100644 --- a/snowglobe-shaded/pom.xml +++ b/snowglobe-shaded/pom.xml @@ -50,6 +50,9 @@ 1.10.19 + + 3.18.2-GA + 3.0.2 3.5.1 2.5.3 @@ -66,6 +69,12 @@ ${project.version} true + + com.nirima.snowglobe + snowglobe-jenkins + ${project.version} + true + com.orbitz.consul @@ -78,7 +87,19 @@ 2.1.2 - + + + org.reflections + reflections + 0.9.10 + true + + + org.javassist + javassist + ${javassist.version} + false + com.github.docker-java @@ -261,6 +282,7 @@ com.github.docker-java:* com.nirima.snowglobe:* com.google.guava:* + org.reflections:* From da92190f2374da6aba30d9c723d36ddc049fda76 Mon Sep 17 00:00:00 2001 From: Nigel Magnay Date: Tue, 13 Sep 2016 11:19:01 +0100 Subject: [PATCH 07/14] Jenkins provider Signed-off-by: Nigel Magnay --- snowglobe-jenkins/pom.xml | 119 ++++++++++++++++++ .../nirima/snowglobe/jenkins/Jenkins.groovy | 26 ++++ 2 files changed, 145 insertions(+) create mode 100644 snowglobe-jenkins/pom.xml create mode 100644 snowglobe-jenkins/src/main/groovy/com/nirima/snowglobe/jenkins/Jenkins.groovy diff --git a/snowglobe-jenkins/pom.xml b/snowglobe-jenkins/pom.xml new file mode 100644 index 0000000..1b20329 --- /dev/null +++ b/snowglobe-jenkins/pom.xml @@ -0,0 +1,119 @@ + + + 4.0.0 + com.nirima.snowglobe + snowglobe-jenkins + + + com.nirima + snowglobe + 0.1-SNAPSHOT + + + + + + + + + org.codehaus.gmavenplus + gmavenplus-plugin + ${gmavenVersion} + + + + addSources + addTestSources + + compile + + testCompile + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + org.jenkins-ci.main + jenkins-core + 2.7 + compile + + + junit + junit + 4.12 + test + + + com.nirima.snowglobe + snowglobe-core + ${project.version} + + + com.nirima + docker-plugin + 0.16.1 + provided + + + + + org.codehaus.groovy + groovy-all + + 2.4.7 + + + + + + + repo.jenkins-ci.org + http://repo.jenkins-ci.org/public/ + + + + false + + central + Central Repository + https://repo.maven.apache.org/maven2 + + + + diff --git a/snowglobe-jenkins/src/main/groovy/com/nirima/snowglobe/jenkins/Jenkins.groovy b/snowglobe-jenkins/src/main/groovy/com/nirima/snowglobe/jenkins/Jenkins.groovy new file mode 100644 index 0000000..c1d3f26 --- /dev/null +++ b/snowglobe-jenkins/src/main/groovy/com/nirima/snowglobe/jenkins/Jenkins.groovy @@ -0,0 +1,26 @@ +package com.nirima.snowglobe.jenkins + +import com.github.dockerjava.api.DockerClient +import com.github.dockerjava.core.DefaultDockerClientConfig +import com.github.dockerjava.core.DockerClientBuilder +import com.nirima.jenkins.plugins.docker.DockerCloud +import com.nirima.snowglobe.core.Module +import com.nirima.snowglobe.core.Provider +import com.nirima.snowglobe.core.SGItem +import com.nirima.snowglobe.docker.DockerProvider + +@SGItem("jenkins_docker_provider") +public class JenkinsDockerProvider extends DockerProvider { + public String cloudName; + + JenkinsDockerProvider(Module module, String id, Closure closure) { + super(module, id, closure) + } + + public DockerClient getDockerClient() { + + def dc = jenkins.model.Jenkins.getInstance().getCloud(cloudName); + + return dc.getClient(); + } +} \ No newline at end of file From a04f73c1ddf2dc2d9110135df283b5ee1c1d27b0 Mon Sep 17 00:00:00 2001 From: Nigel Magnay Date: Tue, 13 Sep 2016 15:06:32 +0100 Subject: [PATCH 08/14] More SLF4j. Signed-off-by: Nigel Magnay --- pom.xml | 6 +++++ .../com/nirima/snowglobe/core/Context.groovy | 10 +++----- .../com/nirima/snowglobe/core/DSL.groovy | 13 ++++++---- .../com/nirima/snowglobe/graph/SGNode.groovy | 4 ++- snowglobe-jenkins-plugin/pom.xml | 25 ++++++++++++------- snowglobe-jenkins/pom.xml | 6 +++-- snowglobe-shaded/pom.xml | 6 +++++ 7 files changed, 47 insertions(+), 23 deletions(-) diff --git a/pom.xml b/pom.xml index f58d4e0..4ecb494 100644 --- a/pom.xml +++ b/pom.xml @@ -12,6 +12,12 @@ 2.4.7 + + scm:git:git://github.com/nirima/SnowGlobe.git + scm:git:git@github.com:nirima/SnowGlobe.git + http://github.com/nirima/SnowGlobe + + diff --git a/snowglobe-core/src/main/groovy/com/nirima/snowglobe/core/Context.groovy b/snowglobe-core/src/main/groovy/com/nirima/snowglobe/core/Context.groovy index 51e0fc4..b0e272d 100644 --- a/snowglobe-core/src/main/groovy/com/nirima/snowglobe/core/Context.groovy +++ b/snowglobe-core/src/main/groovy/com/nirima/snowglobe/core/Context.groovy @@ -184,8 +184,7 @@ public class ProviderContext extends Context { getProxy().setProperty(name,value); } catch( Exception ex ) { - log.error ("Error setting state for resource ${getProxy()} property ${name} with value ${value}"); - println ex; + log.error ("Error setting state for resource ${getProxy()} property ${name} with value ${value}", ex); throw ex; } @@ -474,19 +473,18 @@ public class StateContext { void setProperty(String name, Object value) { - println " set ${name}=${value} on ${this}" + log.debug " set ${name}=${value} on ${this}" try { getProxy().setProperty(name, value); } catch (Exception ex) { log.error( - "Error setting state for resource ${getProxy()} property ${name} with value ${value}"); - println ex; + "Error setting state for resource ${getProxy()} property ${name} with value ${value}", ex); throw ex; } - println " ±set ${name}=${value} on ${this}" + log.debug " done set ${name}=${value} on ${this}" } diff --git a/snowglobe-core/src/main/groovy/com/nirima/snowglobe/core/DSL.groovy b/snowglobe-core/src/main/groovy/com/nirima/snowglobe/core/DSL.groovy index 6c56d10..145f14c 100644 --- a/snowglobe-core/src/main/groovy/com/nirima/snowglobe/core/DSL.groovy +++ b/snowglobe-core/src/main/groovy/com/nirima/snowglobe/core/DSL.groovy @@ -1,10 +1,12 @@ package com.nirima.snowglobe.core import com.nirima.snowglobe.plan.PlanAction +import groovy.util.logging.Slf4j import org.reflections.Reflections import java.lang.reflect.ParameterizedType +@Slf4j public class Core { Map classesMap = [:]; @@ -36,7 +38,7 @@ public class Core { public Class getClassForName(String name) { Class aClass = classesMap.get(name); if( aClass == null ) - println "I don't know what class ${name} is for"; + log.error "I don't know what class ${name} is for"; return aClass; } @@ -122,6 +124,7 @@ public class ModuleImports { } } +@Slf4j public class Module { protected SnowGlobe parent; public String id; @@ -136,7 +139,7 @@ public class Module { } public void accept(Object context) { - println "Accept in ${this}"; + log.debug "Accept in ${this}"; closure.delegate = context; closure.resolveStrategy = Closure.DELEGATE_FIRST @@ -192,7 +195,7 @@ public class State { } public void accept(Object context) { - println "State Accept in ${this} CTX= ${context} "; + log.debug "State Accept in ${this} CTX= ${context} "; assert(context != null) @@ -280,7 +283,7 @@ abstract public class Resource public void accept(Object context) { - println "Accept in ${this}"; + log.debug "Accept in ${this}"; assert(context != null) @@ -328,7 +331,7 @@ public class Provider { } public void accept(Object context) { - println "Accept in ${this}"; + log.debug "Accept in ${this}"; closure.delegate = context; closure.resolveStrategy = Closure.DELEGATE_FIRST diff --git a/snowglobe-core/src/main/groovy/com/nirima/snowglobe/graph/SGNode.groovy b/snowglobe-core/src/main/groovy/com/nirima/snowglobe/graph/SGNode.groovy index 1ff9e1d..5262f39 100644 --- a/snowglobe-core/src/main/groovy/com/nirima/snowglobe/graph/SGNode.groovy +++ b/snowglobe-core/src/main/groovy/com/nirima/snowglobe/graph/SGNode.groovy @@ -2,6 +2,7 @@ package com.nirima.snowglobe.graph import com.google.common.base.Objects import com.nirima.snowglobe.core.* +import groovy.util.logging.Slf4j /** * Created by magnayn on 05/09/2016. @@ -86,6 +87,7 @@ class SGNode { } } +@Slf4j class Graph { SGNode rootNode; @@ -135,7 +137,7 @@ class Graph { // Remove the children too??? if( node.children.size() > 0 ) { - println "CHILDREN ARE HERE. NOT SURE." + log.warn "CHILDREN ARE HERE. NOT SURE." } node.children.each { diff --git a/snowglobe-jenkins-plugin/pom.xml b/snowglobe-jenkins-plugin/pom.xml index f4f2900..b7b3729 100644 --- a/snowglobe-jenkins-plugin/pom.xml +++ b/snowglobe-jenkins-plugin/pom.xml @@ -17,8 +17,15 @@ hpi Snowglobe plugin - Provide environment setup/teardown - + SnowGlobe : Infrastructure As Code + https://wiki.jenkins-ci.org/display/JENKINS/SnowGlobe+Plugin + + + scm:git:git://github.com/nirima/SnowGlobe.git + scm:git:git@github.com:nirima/SnowGlobe.git + http://github.com/nirima/SnowGlobe + + 0.1-SNAPSHOT @@ -27,6 +34,7 @@ 3.5.1 2.5.1 3.0.4 + 0.16.2 1.119 2.7 1.207 @@ -82,6 +90,11 @@ ${project.version} compile + + com.nirima + docker-plugin + ${docker-plugin.version} + org.jenkins-ci.plugins.workflow workflow-step-api @@ -266,13 +279,7 @@ - + diff --git a/snowglobe-jenkins/pom.xml b/snowglobe-jenkins/pom.xml index 1b20329..4c58a16 100644 --- a/snowglobe-jenkins/pom.xml +++ b/snowglobe-jenkins/pom.xml @@ -10,7 +10,9 @@ 0.1-SNAPSHOT - + + 0.16.2 + @@ -87,7 +89,7 @@ com.nirima docker-plugin - 0.16.1 + ${docker-plugin.version} provided diff --git a/snowglobe-shaded/pom.xml b/snowglobe-shaded/pom.xml index 2605e95..0de80ff 100644 --- a/snowglobe-shaded/pom.xml +++ b/snowglobe-shaded/pom.xml @@ -68,6 +68,12 @@ snowglobe-core ${project.version} true + + + docker-java + com.github.docker-java + + com.nirima.snowglobe From d2c37efba2237a58da61fb6dd9005b9c079a01d1 Mon Sep 17 00:00:00 2001 From: Nigel Magnay Date: Tue, 13 Sep 2016 15:22:47 +0100 Subject: [PATCH 09/14] Push some documentation Signed-off-by: Nigel Magnay --- README.md | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b548dc6..645d6be 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,25 @@ # SnowGlobe -Container Management Tool +Infrastructure as Code + +## What is SnowGlobe + +SnowGlobe is a tool in the same area as Terraform, Cloudformation, docker-compose. + +SnowGlobe is currently very experimental. It is useful for us in our deployment scenarios! + +These tools aim to allow you to 'describe' how infrastructure is to be deployed, and allow incremental changes to be made. Instead of writing scripts that perform steps, your configuration defines what you want the outcome to look like, and the tooling figures out the necessary steps needed to make it work. + + +It is very similar to (and deeply inspired by) Terraform - but with some differences + +* SnowGlobe scripts are code. The script is a DSL script built on top of groovy - so all valid groovy (and thus all valid java) can be used. + - This should make more 'advanced' deployment scenarios such as blue/green deployments easier to achieve without building external tools that must modify the deployment descriptor. + +* SnowGlobe is built on Java/Groovy rather than golang + +* Terraform tends to concentrate more on AWS - we are interested (primarily) in Docker, though other providers could easily be added + +* It is easier to build 'layers' in SnowGlobe (e.g: a 'base' layer that defines a consul-on-docker, and an 'app' layer that then uses it. + +* Terraform is much more mature and has more effort applied to it. + From b637d5ede87b78fbbe0fe559bdd2e80d927db4a6 Mon Sep 17 00:00:00 2001 From: Nigel Magnay Date: Wed, 14 Sep 2016 19:19:13 +0100 Subject: [PATCH 10/14] More SLF4J. Defer core startup. Allow function calls to pass up the chain. Signed-off-by: Nigel Magnay --- .../com/nirima/snowglobe/core/Context.groovy | 5 +++ .../com/nirima/snowglobe/core/DSL.groovy | 43 ++++++++++++++++--- 2 files changed, 41 insertions(+), 7 deletions(-) diff --git a/snowglobe-core/src/main/groovy/com/nirima/snowglobe/core/Context.groovy b/snowglobe-core/src/main/groovy/com/nirima/snowglobe/core/Context.groovy index b0e272d..8331cc2 100644 --- a/snowglobe-core/src/main/groovy/com/nirima/snowglobe/core/Context.groovy +++ b/snowglobe-core/src/main/groovy/com/nirima/snowglobe/core/Context.groovy @@ -3,6 +3,7 @@ import groovy.util.logging.Slf4j /** * Created by magnayn on 04/09/2016. */ +@Slf4j abstract class Context { private T proxy; Context(T t) { @@ -478,6 +479,10 @@ public class StateContext { try { getProxy().setProperty(name, value); } + catch(MissingPropertyException e1) { + log.warn( + "Error setting state for resource ${getProxy()} property ${name} with value ${value}"); + } catch (Exception ex) { log.error( "Error setting state for resource ${getProxy()} property ${name} with value ${value}", ex); diff --git a/snowglobe-core/src/main/groovy/com/nirima/snowglobe/core/DSL.groovy b/snowglobe-core/src/main/groovy/com/nirima/snowglobe/core/DSL.groovy index 145f14c..5ba8d1b 100644 --- a/snowglobe-core/src/main/groovy/com/nirima/snowglobe/core/DSL.groovy +++ b/snowglobe-core/src/main/groovy/com/nirima/snowglobe/core/DSL.groovy @@ -8,13 +8,19 @@ import java.lang.reflect.ParameterizedType @Slf4j public class Core { - Map classesMap = [:]; + Map classesMap; public static Core INSTANCE = new Core(); private Core() { + } + public void init() { + + log.info "Initializing class map" + classesMap = [:]; + Reflections reflections = new Reflections("com.nirima.snowglobe"); Set> annotated = reflections.getTypesAnnotatedWith(SGItem.class); @@ -22,7 +28,10 @@ public class Core { annotated.each { it -> try { - classesMap.put(it.getAnnotation(SGItem.class).value(), it) + String name = it.getAnnotation(SGItem.class).value(); + log.info "Seen ${name}" + + classesMap.put(name, it) } catch(Exception ex) { ex.printStackTrace(); } @@ -32,18 +41,28 @@ public class Core { } public void register(Class c, String name) { + if( classesMap == null ) + init(); classesMap[name] = c; } public Class getClassForName(String name) { + if( classesMap == null ) + init(); + Class aClass = classesMap.get(name); - if( aClass == null ) - log.error "I don't know what class ${name} is for"; + if( aClass == null ) { + log.debug "I don't know what class ${name} is for"; + + } return aClass; } public String getNameForClass(Class klass) { + if( classesMap == null ) + init(); + String name = null; classesMap.each { @@ -53,6 +72,12 @@ public class Core { return name; } + + public void dump() { + classesMap.each { + println "Defined ${it.key} as ${it.value}" + } + } } public class SnowGlobe { @@ -166,8 +191,10 @@ public class Module { } items2[0] = this; - if( klass == null ) - throw new ClassNotFoundException("Missing ${name}"); + if( klass == null ) { + // throw new ClassNotFoundException("Missing ${name}"); + return parent.invokeMethod(name, args); + } return klass.newInstance(items2); } @@ -187,6 +214,7 @@ public class Module { } } +@Slf4j public class State { public Closure closure; @@ -241,6 +269,7 @@ public class ResourceState extends State { } } +@Slf4j abstract public class Resource { Module module; @@ -316,7 +345,7 @@ abstract public class Resource } - +@Slf4j public class Provider { private Module module; String id; From 8581cd34efc0c3083a463f2f51440a0cca631e3b Mon Sep 17 00:00:00 2001 From: Nigel Magnay Date: Wed, 14 Sep 2016 19:20:02 +0100 Subject: [PATCH 11/14] Use maven assembly plugin rather than one-jar. Signed-off-by: Nigel Magnay --- snowglobe-exe/pom.xml | 35 +++++++++++++++---- .../com/nirima/snowglobe/SnowGlobeApp.java | 5 ++- .../src/main/resources/logback.groovy | 29 +++++++++++++++ 3 files changed, 61 insertions(+), 8 deletions(-) create mode 100644 snowglobe-exe/src/main/resources/logback.groovy diff --git a/snowglobe-exe/pom.xml b/snowglobe-exe/pom.xml index 5c8177d..8e9b1a0 100644 --- a/snowglobe-exe/pom.xml +++ b/snowglobe-exe/pom.xml @@ -1,5 +1,5 @@ - + 4.0.0 com.nirima.snowglobe snowglobe-exe @@ -13,7 +13,7 @@ - + - com.jolira - onejar-maven-plugin - 1.4.4 + maven-assembly-plugin + + + + com.nirima.snowglobe.SnowGlobeApp + + + + jar-with-dependencies + + + make-assembly + package - one-jar + single @@ -50,11 +60,22 @@ snowglobe-core ${project.version} + args4j args4j 2.0.31 + + ch.qos.logback + logback-core + 1.1.7 + + + ch.qos.logback + logback-classic + 1.1.7 + - - - - + + + commons-io + commons-io + 2.5 + + com.orbitz.consul consul-client diff --git a/snowglobe-core/src/main/groovy/com/nirima/snowglobe/SGExec.groovy b/snowglobe-core/src/main/groovy/com/nirima/snowglobe/SGExec.groovy index 0e3fdc9..784340e 100644 --- a/snowglobe-core/src/main/groovy/com/nirima/snowglobe/SGExec.groovy +++ b/snowglobe-core/src/main/groovy/com/nirima/snowglobe/SGExec.groovy @@ -71,22 +71,22 @@ class SGExec { if( stateFile != null ) { - dsl.parseScript(stateFile); - stateGlobe = dsl.runScript(); + SnowGlobeSystem dslState = new SnowGlobeSystem(); - //SnowGlobeContext sctxt = dsl.getState(stateGlobe); - SnowGlobeContext stContext = new SnowGlobeContext(stateGlobe); + dslState.parseScript(stateFile); + stateGlobe = dslState.runScript(); - stContext.initModules(); + if( stateGlobe != null ) { + SnowGlobeContext stContext = new SnowGlobeContext(stateGlobe); - // Don't init it. + stContext.initModules(); - mergeState(snowGlobe, stateGlobe); + // Don't init it. + mergeState(snowGlobe, stateGlobe); + } } - - sgContext.buildModules(); g = new GraphBuilder().build(sgContext); diff --git a/snowglobe-core/src/main/groovy/com/nirima/snowglobe/docker/DockerActions.groovy b/snowglobe-core/src/main/groovy/com/nirima/snowglobe/docker/DockerActions.groovy index e59ce41..39816dd 100644 --- a/snowglobe-core/src/main/groovy/com/nirima/snowglobe/docker/DockerActions.groovy +++ b/snowglobe-core/src/main/groovy/com/nirima/snowglobe/docker/DockerActions.groovy @@ -5,6 +5,7 @@ import com.github.dockerjava.api.command.CreateContainerCmd import com.github.dockerjava.api.command.CreateContainerResponse import com.github.dockerjava.api.exception.NotModifiedException import com.github.dockerjava.api.model.* +import com.google.common.base.Strings import com.nirima.snowglobe.plan.PlanActionBase import groovy.util.logging.Slf4j import org.slf4j.Logger @@ -100,6 +101,10 @@ class DockerContainerAction extends PlanActionBase Date: Mon, 3 Oct 2016 22:51:10 +0100 Subject: [PATCH 14/14] Add info on available actions Signed-off-by: aszczepaniak --- .../src/main/java/com/nirima/snowglobe/Action.java | 4 +++- .../src/main/java/com/nirima/snowglobe/SnowGlobeApp.java | 8 +++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/snowglobe-exe/src/main/java/com/nirima/snowglobe/Action.java b/snowglobe-exe/src/main/java/com/nirima/snowglobe/Action.java index f925610..f120ab5 100644 --- a/snowglobe-exe/src/main/java/com/nirima/snowglobe/Action.java +++ b/snowglobe-exe/src/main/java/com/nirima/snowglobe/Action.java @@ -6,5 +6,7 @@ public enum Action { apply, graph, - destroy + destroy; + + public static final Action[] ACTIONS = new Action[] {apply, graph, destroy}; } diff --git a/snowglobe-exe/src/main/java/com/nirima/snowglobe/SnowGlobeApp.java b/snowglobe-exe/src/main/java/com/nirima/snowglobe/SnowGlobeApp.java index 5ad0d3d..f03d8ce 100644 --- a/snowglobe-exe/src/main/java/com/nirima/snowglobe/SnowGlobeApp.java +++ b/snowglobe-exe/src/main/java/com/nirima/snowglobe/SnowGlobeApp.java @@ -61,7 +61,13 @@ public void doMain(String[] args) throws IOException { return; } - + if (action == null) { + System.out.println("\nSnowglobe actions are:"); + for (Action a: Action.ACTIONS) { + System.out.println(String.format(" - %s", a.toString())); + } + return; + } // access non-option arguments System.err.println("other arguments are:");