diff --git a/deployment-manager/.gitignore b/deployment-manager/.gitignore new file mode 100644 index 0000000..e90878c --- /dev/null +++ b/deployment-manager/.gitignore @@ -0,0 +1,7 @@ +*.classpath +*.project +*.iml +*.settings +.idea/ +work/ +target/ diff --git a/deployment-manager/CONTRIBUTING.md b/deployment-manager/CONTRIBUTING.md new file mode 100644 index 0000000..e69de29 diff --git a/deployment-manager/LICENSE b/deployment-manager/LICENSE new file mode 100644 index 0000000..ad410e1 --- /dev/null +++ b/deployment-manager/LICENSE @@ -0,0 +1,201 @@ +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/deployment-manager/README.md b/deployment-manager/README.md new file mode 100644 index 0000000..5fa4c9c --- /dev/null +++ b/deployment-manager/README.md @@ -0,0 +1,29 @@ +# Openstack Murano Deployment Manager Plugin + +This plugin manages Openstack Murano environments + +# Build + mvn clean verify +Creates the plugin HPI package for use with Jenkins. + +# Run by Maven + mvn hpi:run +Runs Jenkins with the plugin installed + +# License + + (The Apache v2 License) + + Copyright 2016 Google Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/deployment-manager/pom.xml b/deployment-manager/pom.xml new file mode 100644 index 0000000..43c7d37 --- /dev/null +++ b/deployment-manager/pom.xml @@ -0,0 +1,139 @@ + + 4.0.0 + + + org.jenkins-ci.plugins + plugin + 2.15 + + + Murano Deployment Manager Jenkins Plugin + + + This plugin provides integration with Openstack Murano + + + https://wiki.jenkins-ci.org/display/JENKINS/Murano+Deployment+Manager+Plugin + murano-deployment-manager + hpi + 0.1-SNAPSHOT + + + + The Apache V2 License + http://www.apache.org/licenses/LICENSE-2.0 + repo + + + + + + Alexey Khivin + akhivin@gmail.com + + + + + 8 + 2.23 + UTF-8 + 3.0.2 + + + + + repo.jenkins-ci.org + Jenkins Repository + http://repo.jenkins-ci.org/public/ + + + jgit-repository + Eclipse JGit Repository + http://download.eclipse.org/jgit/maven + + + + + + repo.jenkins-ci.org + http://repo.jenkins-ci.org/public/ + + + + + + + org.apache.maven.plugins + maven-deploy-plugin + 2.8.2 + + + org.apache.maven.wagon + wagon-http + 1.0-beta-6 + jar + + + + + + + + scm:git:git://github.com/jenkinsci/${project.artifactId}-plugin.git + scm:git:git@github.com:jenkinsci/${project.artifactId}-plugin.git + https://github.com/jenkinsci/${project.artifactId}-plugin + HEAD + + + + + org.mockito + mockito-all + RELEASE + test + + + + org.pacesys + openstack4j-core + ${openstack4jversion} + + + + org.pacesys.openstack4j.connectors + openstack4j-httpclient + ${openstack4jversion} + + + + com.googlecode.json-simple + json-simple + 1.1.1 + + + + org.apache.httpcomponents + httpclient + 4.5.1 + + + + com.fasterxml.jackson.core + jackson-databind + 2.8.3 + + + + com.fasterxml.jackson.dataformat + jackson-dataformat-yaml + 2.7.4 + + + + org.jenkins-ci.plugins + credentials + LATEST + + + diff --git a/deployment-manager/src/main/java/org/openstack/murano/jenkins_plugins/muranoci/deploy/AbstractMuranoDeploymentDescriptor.java b/deployment-manager/src/main/java/org/openstack/murano/jenkins_plugins/muranoci/deploy/AbstractMuranoDeploymentDescriptor.java new file mode 100644 index 0000000..9147772 --- /dev/null +++ b/deployment-manager/src/main/java/org/openstack/murano/jenkins_plugins/muranoci/deploy/AbstractMuranoDeploymentDescriptor.java @@ -0,0 +1,28 @@ +package org.openstack.murano.jenkins_plugins.muranoci.deploy; + +import hudson.model.Descriptor; +import net.sf.json.JSONObject; +import org.kohsuke.stapler.StaplerRequest; + +import static java.util.Objects.requireNonNull; + +public abstract class AbstractMuranoDeploymentDescriptor + extends Descriptor { + + protected AbstractMuranoDeploymentDescriptor(Class clazz) { + super(requireNonNull(clazz)); + load(); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean configure(StaplerRequest req, JSONObject json) throws FormException { + save(); + return true; + } + + public abstract boolean isApplicable(Descriptor descriptor); + public abstract String getDisplayName(); +} diff --git a/deployment-manager/src/main/java/org/openstack/murano/jenkins_plugins/muranoci/deploy/BuildStepDetailsProvider.java b/deployment-manager/src/main/java/org/openstack/murano/jenkins_plugins/muranoci/deploy/BuildStepDetailsProvider.java new file mode 100644 index 0000000..dce30d0 --- /dev/null +++ b/deployment-manager/src/main/java/org/openstack/murano/jenkins_plugins/muranoci/deploy/BuildStepDetailsProvider.java @@ -0,0 +1,36 @@ +package org.openstack.murano.jenkins_plugins.muranoci.deploy; + +import hudson.Extension; +import hudson.ExtensionPoint; +import hudson.model.Describable; +import hudson.tasks.BuildStep; +import hudson.tasks.Shell; + + +public abstract class BuildStepDetailsProvider implements ExtensionPoint { + + protected static String defaultName(BuildStep bs) { + return bs instanceof Describable ? ((Describable) bs).getDescriptor().getDisplayName() + : null; + } + + /** + * @param bs A given {@link BuildStep}. + * @return the details of the build step. + */ + public abstract String getDetails(T bs); + + /** + * {@link BuildStepDetailsProvider} for {@link Shell}. + */ + @Extension + public static class ShellBuildStepDetailsProvider extends BuildStepDetailsProvider { + /** + * {@inheritDoc} + */ + @Override + public String getDetails(Shell shell) { + return shell.getCommand(); + } + } +} diff --git a/deployment-manager/src/main/java/org/openstack/murano/jenkins_plugins/muranoci/deploy/MuranoDeployment.java b/deployment-manager/src/main/java/org/openstack/murano/jenkins_plugins/muranoci/deploy/MuranoDeployment.java new file mode 100644 index 0000000..2182796 --- /dev/null +++ b/deployment-manager/src/main/java/org/openstack/murano/jenkins_plugins/muranoci/deploy/MuranoDeployment.java @@ -0,0 +1,77 @@ +package org.openstack.murano.jenkins_plugins.muranoci.deploy; + +import hudson.DescriptorExtensionList; +import hudson.ExtensionPoint; +import hudson.model.Describable; +import hudson.model.Descriptor; +import jenkins.model.Jenkins; + +import java.util.LinkedList; +import java.util.List; + +import static java.util.Objects.requireNonNull; + +public abstract class MuranoDeployment + implements Describable, ExtensionPoint { + /** + * Json data that describes Murano Environment applications + */ + private String objectModel; + + + public MuranoDeployment() { + super(); + } + + /** + * Contains data that describes Environment within Openstack Cloud + * and connection credentials. + * + * @param objectModel description of environment to be deployed + */ + public MuranoDeployment(String objectModel) { + + this.objectModel = requireNonNull(objectModel, "Object Model should not be Null"); + } + + /** + * Boilerplate, see: + * https://wiki.jenkins-ci.org/display/JENKINS/Defining+a+new+extension+point + * + * @return all registered {@link MuranoDeployment}s + */ + public static DescriptorExtensionList all() { + return Jenkins.getInstance().getDescriptorList(MuranoDeployment.class); + } + + public static List getCompatibleDeployments(Descriptor descriptor) { + LinkedList cloudDeployments = + new LinkedList<>(); + + for (AbstractMuranoDeploymentDescriptor deployment : all()) { + if (!deployment.isApplicable(descriptor)) { + continue; + } + cloudDeployments.add(deployment); + } + + return cloudDeployments; + } + + public String getObjectModel() { + return objectModel; + } + + public void setObjectModel(String objectModel) { + this.objectModel = objectModel; + } + + /** + * Boilerplate, see: https://wiki.jenkins-ci.org/display/JENKINS/Defining+a+new+extension+point + */ + @Override + public Descriptor getDescriptor() { + return (Descriptor) Jenkins.getInstance().getDescriptor(getClass()); + } + +} diff --git a/deployment-manager/src/main/java/org/openstack/murano/jenkins_plugins/muranoci/deploy/MuranoHelper.java b/deployment-manager/src/main/java/org/openstack/murano/jenkins_plugins/muranoci/deploy/MuranoHelper.java new file mode 100644 index 0000000..ed8ca58 --- /dev/null +++ b/deployment-manager/src/main/java/org/openstack/murano/jenkins_plugins/muranoci/deploy/MuranoHelper.java @@ -0,0 +1,363 @@ +package org.openstack.murano.jenkins_plugins.muranoci.deploy; + + +import org.apache.http.client.methods.CloseableHttpResponse; +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; +import org.json.simple.parser.JSONParser; +import org.json.simple.parser.ParseException; +import org.kohsuke.stapler.DataBoundConstructor; +import org.openstack4j.api.OSClient; +import org.openstack4j.api.exceptions.AuthenticationException; +import org.openstack4j.connectors.httpclient.HttpCommand; +import org.openstack4j.core.transport.HttpMethod; +import org.openstack4j.core.transport.HttpRequest; +import org.openstack4j.openstack.OSFactory; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.time.Instant; +import java.util.concurrent.TimeoutException; +import java.util.logging.Level; +import java.util.logging.LogRecord; +import java.util.logging.Logger; + +public class MuranoHelper { + public static final int MURANO_DEFAULT_PORT = 8082; + + private final static Logger LOG = Logger.getLogger(MuranoHelper.class.getName()); + + private OSClient.OSClientV2 os = null; + + /** + * Suppose this is keystone Url + */ + private String serverUrl; + private String username; + private String password; + private String tenantName; + /** + * Default timeout for waiting deployment success + */ + private int timeout = 3600*1000; + + @DataBoundConstructor + public MuranoHelper(String serverUrl, + String username, + String password, + String tenantName) { + this.serverUrl = serverUrl; + this.username = username; + this.password = password; + this.tenantName = tenantName; + } + + public String getServerUrl() { + return serverUrl; + } + + public String getUsername() { + return username; + } + + public String getPassword() { + return password; + } + + public String getTenantName() { + return tenantName; + } + + + /** + * + * @param name New environment name + * @param objectModel object model describing new environment + * @return new environment id + * @throws AuthenticationException in case credentials + */ + public String deployNewFromObjectModel(String name, String objectModel) + throws AuthenticationException { + + this.authenticate(); + + String token = getOSClient().getAccess().getToken().getId(); + + String envId = checkIfDeploymentExists(token, name); + if (envId == null) { + // Create Env + envId = this.createEnvironment(token, name); + + // Create Session + String sessionId = this.createEnvironmentSession(token, envId); + + // Add App to Environment + addApplicationToEnvironment(token, envId, sessionId, objectModel); + + // Deploy + deployEnvironment(token, envId, sessionId); + } + + return envId; + } + + + /** + * Loop around to see if the deployment is a success. This waits for about 10 secs 300 times hoping that + * it finishes. This all depends on teh number of nodes and the speed of the boxes. But seems sufficient. + * + * @param envId Environemnt Id + * @return whether the deployment is a success + * @throws TimeoutException if deployment process still in progress after deadline + */ + public boolean waitDeploymentResult(String envId) throws TimeoutException { + String token = getOSClient().getAccess().getToken().getId(); + + boolean status = false; + Instant deadline = Instant.now().plusMillis(timeout); + + while(true) { + try { + Thread.sleep(10000); + String payload = getResponseForJson( + getMuranoEnpoint(), + MURANO_DEFAULT_PORT, + "/v1/environments/" + envId + "/deployments", + HttpMethod.GET, + token, + null, + null); + JSONParser parser = new JSONParser(); + try { + JSONObject deployments = (JSONObject) parser.parse(payload); + JSONArray deploymentList = (JSONArray) deployments.get("deployments"); + JSONObject thisDeployment = (JSONObject) deploymentList.get(0); + if ("success".equals(thisDeployment.get("state"))) { + status = true; + break; + } + } catch (ParseException pe) { + } + } catch (Exception ex) { + status = false; + break; + } + if (Instant.now().isAfter(deadline)){ + throw new TimeoutException("Environment was not ready in time."); + } + + } + return status; + } + + /** + * Return the Environment id if it exists + * + * @param token + * @param name + * @return + */ + private String checkIfDeploymentExists(String token, String name) { + // TODO: remove string manipulation + String payload = getResponseForJson( + getMuranoEnpoint(), + MURANO_DEFAULT_PORT, + "/v1/environments", + HttpMethod.GET, + token, + null, + null); + String envId = null; + JSONParser parser = new JSONParser(); + try{ + Object obj = parser.parse(payload); + JSONObject response = (JSONObject)obj; + JSONArray environmentArray = (JSONArray) response.get("environments"); + for (Object env: environmentArray) { + JSONObject thisEnv = (JSONObject) env; + String envName = (String) thisEnv.get("name"); + if (envName.equals(name)) { + envId = (String) thisEnv.get("id"); + break; + } + } + }catch(ParseException pe){ + LogRecord logRecord = new LogRecord(Level.WARNING, "Parse exception: position: " + pe.getPosition()); + logRecord.setThrown(pe); + LOG.log(logRecord); + } + return envId; + } + + /** + * Deploy the environment given the environment id and Session Token + */ + private void deployEnvironment(String token, String envId, String sessionId) { + String response = getResponseForJson( + getMuranoEnpoint(), + MURANO_DEFAULT_PORT, + "/v1/environments/" + envId + "/sessions/" + sessionId + "/deploy", + HttpMethod.POST, + token, + null, + null); + } + + + public String getMuranoEnpoint() { + /* + * TODO: This is temporary decision. Murano URL should be obtained from Keystone + */ + String string[] = this.serverUrl.split(":"); + + return string[0] + ":" + string[1]; + } + + /** + * Add the app(K8S) to the environment + * @param token + * @param envId + * @param sessionId + * @param jsonReq + */ + private void addApplicationToEnvironment(String token, String envId, String sessionId, String jsonReq) { + String response = getResponseForJson(this.getMuranoEnpoint(), + MURANO_DEFAULT_PORT, + "/v1/environments/" + envId + "/services", + HttpMethod.POST, + token, + jsonReq, + sessionId); + } + + private String createEnvironmentSession(String token, String envId) { + String payload = getResponseForJson(this.getMuranoEnpoint(), + MURANO_DEFAULT_PORT, + "/v1/environments/" + envId + "/configure", + HttpMethod.POST, + token, + null, + null); + + String sessionId = ""; + JSONParser parser = new JSONParser(); + try{ + Object obj = parser.parse(payload); + JSONObject response = (JSONObject)obj; + sessionId = (String)response.get("id"); + }catch(ParseException pe){ + System.out.println("position: " + pe.getPosition()); + System.out.println(pe); + } + return sessionId; + } + + private String createEnvironment(String token, String envname) { + String reqPayload = "{\"name\":\"" + envname + "\"}"; + String payload = getResponseForJson( + getMuranoEnpoint(), + MURANO_DEFAULT_PORT, + "/v1/environments", + HttpMethod.POST, token, reqPayload, null); + + String envId = ""; + JSONParser parser = new JSONParser(); + try{ + Object obj = parser.parse(payload); + JSONObject response = (JSONObject)obj; + envId = (String)response.get("id"); + }catch(ParseException pe){ + System.out.println("position: " + pe.getPosition()); + System.out.println(pe); + } + + return envId; + } + + /** + * Main helper method to call the Murano API and return the response. Accepts both GET and POST. + * + * @param url Base URL to connect + * @param port Which port is murano listening on + * @param requestPath Path on Murano URL + * @param method GET or POST + * @param token Auth Token + * @param jsonPayload Payload for the message + * @param muranoSessionId Optional Session Id + * @return Response from the Call + */ + private String getResponseForJson(String url, + int port, + String requestPath, + HttpMethod method, + String token, + String jsonPayload, + String muranoSessionId) { + HttpRequest request = HttpRequest.builder().method(method) + .endpoint(url + ":" + port) + .path(requestPath) + .header("X-Auth-Token", token) + .json(jsonPayload) + .build(); + if (muranoSessionId != null) { + request.getHeaders().put("X-Configuration-Session", muranoSessionId); + } + if (jsonPayload != null) { + request = request.toBuilder().json(jsonPayload).build(); + } + + HttpCommand command = HttpCommand.create(request); + CloseableHttpResponse response = null; + try { + response = command.execute(); + } catch(Exception ex) { + ex.printStackTrace(); + return null; + } + + StringBuffer jsonString = new StringBuffer(); + try { + BufferedReader br = new BufferedReader(new InputStreamReader( + response.getEntity().getContent())); + + //Print the raw output of murano api from the server + String output; + + while ((output = br.readLine()) != null) { + jsonString.append(output + "\n"); + } + } catch(Exception ex) { + return null; + } + + return jsonString.toString(); + } + + + /** + * Authenticate to the Openstack instance given the credentials in constructor + * + * @throws AuthenticationException in case of authentication failure. + */ + public void authenticate() throws AuthenticationException { + this.os = OSFactory.builderV2() + .endpoint(this.serverUrl) + .credentials(this.username, this.password) + .tenantName(this.tenantName) + .authenticate(); + } + + + /** + * Helper object to return the OSClient + * @return OSClient V2 + */ + public OSClient.OSClientV2 getOSClient() { + return this.os; + } + + public void setTimeout(int timeout) { + this.timeout = timeout; + } +} + diff --git a/deployment-manager/src/main/java/org/openstack/murano/jenkins_plugins/muranoci/deploy/MuranoManagerBuildWrapper.java b/deployment-manager/src/main/java/org/openstack/murano/jenkins_plugins/muranoci/deploy/MuranoManagerBuildWrapper.java new file mode 100644 index 0000000..ff625cf --- /dev/null +++ b/deployment-manager/src/main/java/org/openstack/murano/jenkins_plugins/muranoci/deploy/MuranoManagerBuildWrapper.java @@ -0,0 +1,211 @@ +package org.openstack.murano.jenkins_plugins.muranoci.deploy; + +import com.cloudbees.plugins.credentials.CredentialsMatchers; +import com.cloudbees.plugins.credentials.CredentialsProvider; +import com.cloudbees.plugins.credentials.common.StandardCredentials; +import com.cloudbees.plugins.credentials.common.StandardListBoxModel; +import com.cloudbees.plugins.credentials.domains.DomainRequirement; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; +import com.fasterxml.jackson.dataformat.yaml.YAMLParser; +import hudson.EnvVars; +import hudson.Extension; +import hudson.FilePath; +import hudson.Launcher; +import hudson.model.*; +import hudson.security.ACL; +import hudson.tasks.BuildWrapper; +import hudson.tasks.BuildWrapperDescriptor; +import hudson.util.ListBoxModel; +import jenkins.model.Jenkins; +import org.apache.http.impl.client.SystemDefaultCredentialsProvider; +import org.kohsuke.stapler.AncestorInPath; +import org.kohsuke.stapler.DataBoundConstructor; +import org.kohsuke.stapler.QueryParameter; +import org.kohsuke.stapler.export.Exported; +import org.openstack.murano.jenkins_plugins.muranoci.deploy.credentials.OpenstackCredentials; + +import java.io.IOException; +import java.io.Serializable; +import java.math.BigInteger; +import java.net.URL; +import java.security.SecureRandom; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static com.google.common.collect.Lists.newArrayList; +import static java.util.Objects.requireNonNull; + + +public class MuranoManagerBuildWrapper extends BuildWrapper implements Serializable { + private static String MURANO_ENV_NAME = "MuranoCI-"; + + + private final MuranoDeployment deployment; + private String credentialsId; + + @DataBoundConstructor + public MuranoManagerBuildWrapper(MuranoDeployment deployment, + String credentialsId) { + + this.deployment = requireNonNull(deployment); + this.credentialsId = requireNonNull(credentialsId); + } + + public String getCredentialsId() { + return credentialsId; + } + + /** + * {@inheritDoc} + */ + @Override + public BuildWrapper.Environment setUp(AbstractBuild build, Launcher launcher, BuildListener listener) + throws IOException, InterruptedException { + EnvVars env = build.getEnvironment(listener); + OpenstackCredentials credentials = getOpenstackCredentials(getCredentialsId()); + + try { + MuranoHelper helper = new MuranoHelper( + credentials.getIdentityServiceEndpoint(), + credentials.getUsername(), + credentials.getPassword().getPlainText(), + credentials.getTenant() + ); + if (env.containsKey("BUILD_ENVIRONMENT_TIMEOUT")) { + int timeout = Integer.parseInt(env.get("BUILD_ENVIRONMENT_TIMEOUT")); + helper.setTimeout(timeout); + } + + //TODO: Remove + try { + ((RepositoryTemplatedDeployment) deployment).readObjectModel(build.getWorkspace()); + } catch (Exception io) { + } + + String name = generateEnvName(); + + String envId = helper.deployNewFromObjectModel( + name, deployment.getObjectModel()); + + boolean result = helper.waitDeploymentResult(envId); + if (!result) { + build.setResult(Result.FAILURE); + } + } catch (Exception e) { + e.printStackTrace(); + build.setResult(Result.FAILURE); + } + + return new JenkinsEnvironmentImpl(env); + } + + private String generateEnvName() { + return MURANO_ENV_NAME + new BigInteger( + 32, + new SecureRandom()) + .toString(16); + } + + private OpenstackCredentials getOpenstackCredentials(String credentialsId) { + List openstackCredentialsList = + CredentialsProvider.lookupCredentials( + OpenstackCredentials.class, + Jenkins.getInstance(), + ACL.SYSTEM); + OpenstackCredentials openstackCredentials = CredentialsMatchers.firstOrNull( + openstackCredentialsList, + CredentialsMatchers.allOf( + CredentialsMatchers.withId(credentialsId))); + + return openstackCredentials; + } + + @Exported + public MuranoDeployment getDeployment() { + return deployment; + } + + /** + * {@inheritDoc} + */ + @Override + public DescriptorImpl getDescriptor() { + return (DescriptorImpl) super.getDescriptor(); + } + + /** + * The descriptor for our {@code MuranoManagerBuildWrapper} plugin. + */ + @Extension + public static final class DescriptorImpl extends BuildWrapperDescriptor { + /** + * {@inheritDoc} + */ + @Override + public boolean isApplicable(AbstractProject project) { + return true; + } + + /** + * {@inheritDoc} + */ + @Override + public String getDisplayName() { + return Messages.MuranoManagerBuildWrapper_DisplayName(); + } + + + @SuppressWarnings("unused") // used by stapler + public ListBoxModel doFillCredentialsIdItems(@AncestorInPath Jenkins context, + @QueryParameter String remoteBase) { + if (context == null || !context.hasPermission(Item.CONFIGURE)) { + return new StandardListBoxModel(); + } + + List domainRequirements = newArrayList(); + return new StandardListBoxModel() + .withEmptySelection() + .withMatching( + CredentialsMatchers.anyOf( + CredentialsMatchers.instanceOf(OpenstackCredentials.class)), + CredentialsProvider.lookupCredentials( + StandardCredentials.class, + context, + ACL.SYSTEM, + domainRequirements)); + } + + } + + private final class JenkinsEnvironmentImpl extends Environment { + private final EnvVars envVars; + + /** + * Construct the instance with a snapshot of the environment within which it was created in case + * values that were used to configure it at the start of the build change before the end. + * + * @param envVars The set of environment variables used to spin up the ephemeral deployment, so + * we can tear it down with the same. + */ + public JenkinsEnvironmentImpl(EnvVars envVars) { + this.envVars = requireNonNull(envVars); + } + + @Override + public void buildEnvVars(Map env) { + super.buildEnvVars(env); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean tearDown(AbstractBuild build, BuildListener listener) + throws IOException, InterruptedException { + return true; + } + } +} diff --git a/deployment-manager/src/main/java/org/openstack/murano/jenkins_plugins/muranoci/deploy/MuranoManagerDeployer.java b/deployment-manager/src/main/java/org/openstack/murano/jenkins_plugins/muranoci/deploy/MuranoManagerDeployer.java new file mode 100644 index 0000000..2ba9ec4 --- /dev/null +++ b/deployment-manager/src/main/java/org/openstack/murano/jenkins_plugins/muranoci/deploy/MuranoManagerDeployer.java @@ -0,0 +1,92 @@ +package org.openstack.murano.jenkins_plugins.muranoci.deploy; + +import hudson.EnvVars; +import hudson.Extension; +import hudson.Launcher; +import hudson.model.AbstractBuild; +import hudson.model.AbstractProject; +import hudson.model.BuildListener; +import hudson.model.Result; +import hudson.tasks.BuildStepDescriptor; +import hudson.tasks.BuildStepMonitor; +import hudson.tasks.Publisher; +import hudson.tasks.Recorder; +import org.kohsuke.stapler.DataBoundConstructor; + +import java.io.IOException; + +/** + * After a successful build, this plugin deploys to Murano Environment via the + * Deployment Manager API. + */ +public class MuranoManagerDeployer extends Recorder { + + @DataBoundConstructor + public MuranoManagerDeployer() { + } + + /** + * {@inheritDoc} + */ + @Override + public boolean perform(AbstractBuild build, Launcher launcher, BuildListener listener) + throws IOException, InterruptedException { + if (build.getResult() != Result.SUCCESS) { + return true; + } + + try { + build.getEnvironment(listener); + } catch (IOException e) { + e.printStackTrace(listener.error(Messages.MuranoManagerDeployer_EnvironmentException())); + build.setResult(Result.FAILURE); + return false; + } + + return true; + } + + /** + * {@inheritDoc} + */ + @Override + public BuildStepMonitor getRequiredMonitorService() { + return BuildStepMonitor.NONE; + } + + + @Extension + public static class DescriptorImpl extends BuildStepDescriptor { + /** + * {@inheritDoc} + */ + @Override + public boolean isApplicable(Class aClass) { + // Indicates that this builder can be used with all kinds of project types + return true; + } + + + /** + * {@inheritDoc} + */ + @Override + public String getDisplayName() { + return Messages.MuranoManagerDeployer_DisplayName(); + } + } + + /** + * {@link BuildStepDetailsProvider} for the Cloud Manager Deployer. + */ + @Extension + public static class DetailsProvider extends BuildStepDetailsProvider { + /** + * {@inheritDoc} + */ + @Override + public String getDetails(MuranoManagerDeployer deployer) { + return "MuranoManagerDeployer"; + } + } +} diff --git a/deployment-manager/src/main/java/org/openstack/murano/jenkins_plugins/muranoci/deploy/RepositoryTemplatedDeployment.java b/deployment-manager/src/main/java/org/openstack/murano/jenkins_plugins/muranoci/deploy/RepositoryTemplatedDeployment.java new file mode 100644 index 0000000..7111111 --- /dev/null +++ b/deployment-manager/src/main/java/org/openstack/murano/jenkins_plugins/muranoci/deploy/RepositoryTemplatedDeployment.java @@ -0,0 +1,101 @@ +package org.openstack.murano.jenkins_plugins.muranoci.deploy; + +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; +import com.fasterxml.jackson.dataformat.yaml.YAMLParser; +import hudson.Extension; +import hudson.FilePath; +import hudson.model.Descriptor; +import org.kohsuke.stapler.DataBoundConstructor; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class RepositoryTemplatedDeployment extends MuranoDeployment { + /** + * The file in the repository that contains muranoci configuration. + */ + public static final String CI_CONFG_FILENAME = ".murano.yml"; + + /** + * + */ + private final String environment; + + /** + * The specific Implemenation of MuranoDeployment that + * gets object model from the file within the repo. + * + * @param environment The name of the environment within the .murano.yml config + */ + @DataBoundConstructor + public RepositoryTemplatedDeployment( + String environment) { + + this.environment = environment; + } + + public String getEnvironment() { + return environment; + } + + /** + * Denotes that this is a cloud deployment plugin. + */ + @Extension + public static class DescriptorImpl extends AbstractMuranoDeploymentDescriptor { + public DescriptorImpl() { + this(RepositoryTemplatedDeployment.class); + } + + public DescriptorImpl(Class clazz) { + super(clazz); + } + + /** + * {@inheritDoc} + */ + @Override + public String getDisplayName() { + return Messages.RepositoryTemplatedMuranoDeployment_DisplayName(); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isApplicable(Descriptor descriptor) { + return true; + } + } + + + public void readObjectModel(FilePath workspace) throws IOException { + String config = null; + try { + config = new FilePath(workspace, ".murano.yml").readToString(); + } catch (IOException e) { + e.printStackTrace(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + YAMLFactory factory = new YAMLFactory(); + ObjectMapper mapper = new ObjectMapper(factory); + HashMap map = mapper.readValue(config, HashMap.class); + Object model = ((Map)((Map)map).get("environments")).get(this.environment); + + JsonFactory jsonFactory = new JsonFactory(); + ObjectMapper mapperModel = new ObjectMapper(jsonFactory); + String string = mapperModel.writeValueAsString(model); + System.out.println(string); + this.setObjectModel(string); + } +} diff --git a/deployment-manager/src/main/java/org/openstack/murano/jenkins_plugins/muranoci/deploy/TemplatedDeployment.java b/deployment-manager/src/main/java/org/openstack/murano/jenkins_plugins/muranoci/deploy/TemplatedDeployment.java new file mode 100644 index 0000000..cb6c40d --- /dev/null +++ b/deployment-manager/src/main/java/org/openstack/murano/jenkins_plugins/muranoci/deploy/TemplatedDeployment.java @@ -0,0 +1,60 @@ +package org.openstack.murano.jenkins_plugins.muranoci.deploy; + +import hudson.Extension; +import hudson.model.Descriptor; + +import hudson.util.FormValidation; +import org.kohsuke.stapler.DataBoundConstructor; +import org.kohsuke.stapler.QueryParameter; +import org.openstack4j.api.exceptions.AuthenticationException; + +import javax.servlet.ServletException; +import java.io.IOException; + +import static java.util.Objects.requireNonNull; + +public class TemplatedDeployment extends MuranoDeployment { + + /** + * The specific Implemenation of MuranoDeployment that + * gets object model from the contructor parameter + * (the direct textarea on Jenkins form) + * + * @param objectModel Object model for Murano environment to be deployed + */ + @DataBoundConstructor + public TemplatedDeployment( + String objectModel) { + super(requireNonNull(objectModel)); + } + + /** + * Denotes that this is a cloud deployment plugin. + */ + @Extension + public static class DescriptorImpl extends AbstractMuranoDeploymentDescriptor { + public DescriptorImpl() { + this(TemplatedDeployment.class); + } + + public DescriptorImpl(Class clazz) { + super(clazz); + } + + /** + * {@inheritDoc} + */ + @Override + public String getDisplayName() { + return Messages.TemplatedMuranoDeployment_DisplayName(); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isApplicable(Descriptor descriptor) { + return true; + } + } +} diff --git a/deployment-manager/src/main/java/org/openstack/murano/jenkins_plugins/muranoci/deploy/credentials/OpenstackCredentials.java b/deployment-manager/src/main/java/org/openstack/murano/jenkins_plugins/muranoci/deploy/credentials/OpenstackCredentials.java new file mode 100644 index 0000000..e9fa747 --- /dev/null +++ b/deployment-manager/src/main/java/org/openstack/murano/jenkins_plugins/muranoci/deploy/credentials/OpenstackCredentials.java @@ -0,0 +1,14 @@ +package org.openstack.murano.jenkins_plugins.muranoci.deploy.credentials; + + +import com.cloudbees.plugins.credentials.Credentials; +import hudson.util.Secret; + +public interface OpenstackCredentials extends Credentials { + String getName(); + String getDescription(); + String getUsername(); + Secret getPassword(); + String getTenant(); + String getIdentityServiceEndpoint(); +} \ No newline at end of file diff --git a/deployment-manager/src/main/java/org/openstack/murano/jenkins_plugins/muranoci/deploy/credentials/OpenstackCredentialsImpl.java b/deployment-manager/src/main/java/org/openstack/murano/jenkins_plugins/muranoci/deploy/credentials/OpenstackCredentialsImpl.java new file mode 100644 index 0000000..ab6150a --- /dev/null +++ b/deployment-manager/src/main/java/org/openstack/murano/jenkins_plugins/muranoci/deploy/credentials/OpenstackCredentialsImpl.java @@ -0,0 +1,109 @@ +package org.openstack.murano.jenkins_plugins.muranoci.deploy.credentials; + + +import com.cloudbees.plugins.credentials.CredentialsDescriptor; +import com.cloudbees.plugins.credentials.NameWith; +import com.cloudbees.plugins.credentials.impl.BaseStandardCredentials; +import com.cloudbees.plugins.credentials.impl.Messages; +import hudson.Extension; +import hudson.util.FormValidation; +import hudson.util.Secret; +import org.kohsuke.stapler.DataBoundConstructor; +import org.kohsuke.stapler.QueryParameter; +import org.openstack.murano.jenkins_plugins.muranoci.deploy.MuranoHelper; +import org.openstack4j.api.exceptions.AuthenticationException; + +import javax.servlet.ServletException; +import java.io.IOException; + +import static java.util.Objects.requireNonNull; + +@NameWith(value = OpenstackCredentialsNameProvider.class, priority = 50) +public class OpenstackCredentialsImpl extends BaseStandardCredentials implements OpenstackCredentials { + private final String name; + + private final String identityServiceEndpoint; + private final String username; + private final Secret password; + private final String tenant; + + @DataBoundConstructor + public OpenstackCredentialsImpl( + String id, + String name, + String description, + String identityServiceEndpoint, + String tenant, + String username, + String password) { + super(id, description); + + this.name = name; + this.identityServiceEndpoint = identityServiceEndpoint; + this.username = username; + this.password = Secret.fromString(requireNonNull(password)); + this.tenant = tenant; + } + + public String getName() { + return this.name; + } + + @Override + public String getUsername() { + return this.username; + } + + @Override + public Secret getPassword() { + return this.password; + } + + @Override + public String getTenant() { + return this.tenant; + } + + @Override + public String getIdentityServiceEndpoint() { + return this.identityServiceEndpoint; + } + + @Extension + public static class Descriptor + extends CredentialsDescriptor { + + public FormValidation doTestConnection(@QueryParameter("identityServiceEndpoint") final String identityServiceEndpoint, + @QueryParameter("tenant") final String tenant, + @QueryParameter("username") final String username, + @QueryParameter("password") final String password) + throws IOException, ServletException { + + MuranoHelper client = new MuranoHelper( + identityServiceEndpoint, + username, + password, + tenant); + + try { + client.authenticate(); + } catch (AuthenticationException ae) { + return FormValidation.error( + "Unable to connect to server. Please check credentials"); + } catch (Exception e) { + return FormValidation.error("Error: " + e.getMessage()); + } + + return FormValidation.ok("Success"); + } + + /** + * {@inheritDoc} + */ + @Override + public String getDisplayName() { +// return Messages.CertificateCredentialsImpl_DisplayName(); + return "Openstack Cloud"; + } + } +} \ No newline at end of file diff --git a/deployment-manager/src/main/java/org/openstack/murano/jenkins_plugins/muranoci/deploy/credentials/OpenstackCredentialsNameProvider.java b/deployment-manager/src/main/java/org/openstack/murano/jenkins_plugins/muranoci/deploy/credentials/OpenstackCredentialsNameProvider.java new file mode 100644 index 0000000..d15bd78 --- /dev/null +++ b/deployment-manager/src/main/java/org/openstack/murano/jenkins_plugins/muranoci/deploy/credentials/OpenstackCredentialsNameProvider.java @@ -0,0 +1,12 @@ +package org.openstack.murano.jenkins_plugins.muranoci.deploy.credentials; + + +import com.cloudbees.plugins.credentials.CredentialsNameProvider; + +public class OpenstackCredentialsNameProvider extends CredentialsNameProvider { + + @Override + public String getName(OpenstackCredentialsImpl openstackCredentials) { + return openstackCredentials.getName(); + } +} \ No newline at end of file diff --git a/deployment-manager/src/main/resources/org/openstack/murano/jenkins_plugins/muranoci/Messages.properties b/deployment-manager/src/main/resources/org/openstack/murano/jenkins_plugins/muranoci/Messages.properties new file mode 100644 index 0000000..6c73396 --- /dev/null +++ b/deployment-manager/src/main/resources/org/openstack/murano/jenkins_plugins/muranoci/Messages.properties @@ -0,0 +1,26 @@ +MuranoDeployment.InvalidDeploySpec=Invalid deployment specification. +MuranoDeployment.DeployComplete=Deployment complete! +MuranoDeployment.InsertRollback=Failure deploying, attempting rollback. +MuranoDeployment.RollbackSuccess=Rollback successful. +MuranoDeployment.DeleteComplete=Deletion complete! +MuranoDeployment.CreatedDeploy=Created new deployment: {0} +MuranoDeployment.CreatedDeployVerbose=Created new deployment: {0} with spec: {1} +MuranoDeployment.DeletedDeploy=Deleted deployment: {0} +MuranoDeployment.CreateDeployException=Exception creating deployment +MuranoDeployment.GetDeployException=Exception getting deployment +MuranoDeployment.DeleteDeployException=Exception deleting deployment +MuranoDeployment.WaitDeploy=Waiting for deployment... +MuranoDeployment.WaitDelete=Waiting for deletion... +MuranoDeployment.WaitTimeoutDeploy=Timed out waiting for deployment. +MuranoDeployment.WaitTimeoutDelete=Timed out waiting for deletion. +MuranoDeployment.DeployFailed=Deployment failed for: {0} +MuranoDeployment.ExecutorExceptionWhileDeploying=Bad state: RPC execution exception while deploying. +MuranoDeployment.IOExceptionWhileDeploying=IO exception while deploying. +MuranoDeployment.LogInsert=Inserting deployment: {0} +MuranoDeployment.LogDelete=Deleting deployment: {0} +MuranoDeploymentDescriptor.BadComponent=Components must match: {0} +MuranoDeploymentDescriptor.NotEmpty=Components cannot be empty +MuranoDeploymentDescriptor.SampleResolution={0}\nSample variable resolution: {1} +MuranoDeploymentModule.AppName=Jenkins Cloud Deployer +MuranoDeploymentModule.ConnectionError=Exception connecting to Deployment Manager +MuranoEnvironmentDeleter_DisplayName=Deployment Turndown diff --git a/deployment-manager/src/main/resources/org/openstack/murano/jenkins_plugins/muranoci/deploy/Messages.properties b/deployment-manager/src/main/resources/org/openstack/murano/jenkins_plugins/muranoci/deploy/Messages.properties new file mode 100644 index 0000000..776faa6 --- /dev/null +++ b/deployment-manager/src/main/resources/org/openstack/murano/jenkins_plugins/muranoci/deploy/Messages.properties @@ -0,0 +1,6 @@ +MuranoManagerDeployer.DisplayName=Openstack Murano Deployer +MuranoManagerDeployer.EnvironmentException=Exception accessing build environment +MuranoManagerBuildWrapper.DisplayName=Murano Deployer +BuildStepDetailsProvider.MavenName=Maven +TemplatedMuranoDeployment.DisplayName=Deploy using ready-made template +RepositoryTemplatedMuranoDeployment.DisplayName=Deploy using configuration from project repository \ No newline at end of file diff --git a/deployment-manager/src/main/resources/org/openstack/murano/jenkins_plugins/muranoci/deploy/MuranoManagerBuildWrapper/config.jelly b/deployment-manager/src/main/resources/org/openstack/murano/jenkins_plugins/muranoci/deploy/MuranoManagerBuildWrapper/config.jelly new file mode 100644 index 0000000..36f33d5 --- /dev/null +++ b/deployment-manager/src/main/resources/org/openstack/murano/jenkins_plugins/muranoci/deploy/MuranoManagerBuildWrapper/config.jelly @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + diff --git a/deployment-manager/src/main/resources/org/openstack/murano/jenkins_plugins/muranoci/deploy/MuranoManagerDeployer/config.jelly b/deployment-manager/src/main/resources/org/openstack/murano/jenkins_plugins/muranoci/deploy/MuranoManagerDeployer/config.jelly new file mode 100644 index 0000000..4fb4c90 --- /dev/null +++ b/deployment-manager/src/main/resources/org/openstack/murano/jenkins_plugins/muranoci/deploy/MuranoManagerDeployer/config.jelly @@ -0,0 +1,17 @@ + + + + + + + + diff --git a/deployment-manager/src/main/resources/org/openstack/murano/jenkins_plugins/muranoci/deploy/RepositoryTemplatedDeployment/config.jelly b/deployment-manager/src/main/resources/org/openstack/murano/jenkins_plugins/muranoci/deploy/RepositoryTemplatedDeployment/config.jelly new file mode 100644 index 0000000..c7d7252 --- /dev/null +++ b/deployment-manager/src/main/resources/org/openstack/murano/jenkins_plugins/muranoci/deploy/RepositoryTemplatedDeployment/config.jelly @@ -0,0 +1,14 @@ + + + + + + + + diff --git a/deployment-manager/src/main/resources/org/openstack/murano/jenkins_plugins/muranoci/deploy/TemplatedDeployment/config.jelly b/deployment-manager/src/main/resources/org/openstack/murano/jenkins_plugins/muranoci/deploy/TemplatedDeployment/config.jelly new file mode 100644 index 0000000..f6b2489 --- /dev/null +++ b/deployment-manager/src/main/resources/org/openstack/murano/jenkins_plugins/muranoci/deploy/TemplatedDeployment/config.jelly @@ -0,0 +1,15 @@ + + + + + + + + + diff --git a/deployment-manager/src/main/resources/org/openstack/murano/jenkins_plugins/muranoci/deploy/TemplatedDeployment/global.jelly b/deployment-manager/src/main/resources/org/openstack/murano/jenkins_plugins/muranoci/deploy/TemplatedDeployment/global.jelly new file mode 100644 index 0000000..3c242ff --- /dev/null +++ b/deployment-manager/src/main/resources/org/openstack/murano/jenkins_plugins/muranoci/deploy/TemplatedDeployment/global.jelly @@ -0,0 +1,12 @@ + + + + + + diff --git a/deployment-manager/src/main/resources/org/openstack/murano/jenkins_plugins/muranoci/index.jelly b/deployment-manager/src/main/resources/org/openstack/murano/jenkins_plugins/muranoci/index.jelly new file mode 100644 index 0000000..2c684f6 --- /dev/null +++ b/deployment-manager/src/main/resources/org/openstack/murano/jenkins_plugins/muranoci/index.jelly @@ -0,0 +1,4 @@ + +
+ This plugin integrates Openstack Murano to Jenkins. +