diff --git a/README.md b/README.md index 80df91e..4220e97 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ Features: - Project can include multiple environments. - API keys are not exposed in configuration. + - Nodes can be created from containers, services, or both. - Can limit selected containers to one per service. - Can exclude stopped containers. - Can exclude global containers. @@ -75,7 +76,7 @@ Environment ID most correspond to an existing Rancher environment. Stack name mu ### Add Service -Ads a service to an existing stack. Required inputs: +Adds a service to an existing stack. Required inputs: - Environment ID (string) - Stack Name (string) @@ -89,6 +90,10 @@ Optional inputs: - Service labels - Secrets +### Manage Service + +Activate, deactivate, or restart a service. + ## Road Map - 0.6.6 Make File Copier binary-safe. diff --git a/build.gradle b/build.gradle index 0838c45..8f552ac 100644 --- a/build.gradle +++ b/build.gradle @@ -19,7 +19,8 @@ ext.pluginClassNames='com.bioraft.rundeck.rancher.RancherNodeExecutorPlugin,' + 'com.bioraft.rundeck.rancher.RancherResourceModelSourceFactory,' + 'com.bioraft.rundeck.rancher.RancherUpgradeService,' + 'com.bioraft.rundeck.rancher.RancherNewStack,' + -'com.bioraft.rundeck.rancher.RancherAddService' +'com.bioraft.rundeck.rancher.RancherAddService,' + +'com.bioraft.rundeck.rancher.RancherManageService' ext.pluginName = 'Rancher Node Plugins' ext.pluginDescription = 'Interface with Rancher environments' diff --git a/src/main/java/com/bioraft/rundeck/rancher/RancherFileCopier.java b/src/main/java/com/bioraft/rundeck/rancher/RancherFileCopier.java index 529099b..ca52358 100644 --- a/src/main/java/com/bioraft/rundeck/rancher/RancherFileCopier.java +++ b/src/main/java/com/bioraft/rundeck/rancher/RancherFileCopier.java @@ -43,7 +43,9 @@ import com.dtolabs.rundeck.core.execution.script.ScriptfileUtils; import com.dtolabs.rundeck.core.execution.service.FileCopier; import com.dtolabs.rundeck.core.execution.service.FileCopierException; +import com.dtolabs.rundeck.core.execution.service.NodeExecutorResultImpl; import com.dtolabs.rundeck.core.execution.workflow.steps.FailureReason; +import com.dtolabs.rundeck.core.execution.workflow.steps.StepFailureReason; import com.dtolabs.rundeck.core.plugins.Plugin; import com.dtolabs.rundeck.core.plugins.configuration.*; import com.dtolabs.rundeck.core.storage.ResourceMeta; @@ -111,6 +113,12 @@ private String copyFile(final ExecutionContext context, final File scriptfile, f String remotefile; Map nodeAttributes = node.getAttributes(); + + if (nodeAttributes.get("type").equals("service")) { + String message = "File copier is not currently supported for services"; + throw new FileCopierException(message, FileCopyFailureReason.UnsupportedNodeType); + } + String accessKey; String secretKey; try { @@ -133,7 +141,7 @@ private String copyFile(final ExecutionContext context, final File scriptfile, f } // write to a local temp file or use the input file final File localTempfile = (null != scriptfile) ? scriptfile - : BaseFileCopier.writeTempFile(context, scriptfile, input, script); + : BaseFileCopier.writeTempFile(context, null, input, script); // Copy the file over ExecutionLogger logger = context.getExecutionLogger(); @@ -223,7 +231,8 @@ public enum FileCopyFailureReason implements FailureReason { InterruptedException, ConnectionFailure, AuthenticationFailure, - UnsupportedOperatingSystem + UnsupportedOperatingSystem, + UnsupportedNodeType, } private static class StreamGobbler implements Runnable { diff --git a/src/main/java/com/bioraft/rundeck/rancher/RancherManageService.java b/src/main/java/com/bioraft/rundeck/rancher/RancherManageService.java new file mode 100644 index 0000000..373ee02 --- /dev/null +++ b/src/main/java/com/bioraft/rundeck/rancher/RancherManageService.java @@ -0,0 +1,172 @@ +/* + * Copyright 2019 BioRAFT, Inc. (https://bioraft.com) + * + * 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. + */ +package com.bioraft.rundeck.rancher; + +import com.dtolabs.rundeck.core.Constants; +import com.dtolabs.rundeck.core.common.INodeEntry; +import com.dtolabs.rundeck.core.execution.ExecutionContext; +import com.dtolabs.rundeck.core.execution.workflow.steps.node.NodeStepException; +import com.dtolabs.rundeck.core.plugins.Plugin; +import com.dtolabs.rundeck.plugins.PluginLogger; +import com.dtolabs.rundeck.plugins.ServiceNameConstants; +import com.dtolabs.rundeck.plugins.descriptions.PluginDescription; +import com.dtolabs.rundeck.plugins.descriptions.PluginProperty; +import com.dtolabs.rundeck.plugins.descriptions.SelectValues; +import com.dtolabs.rundeck.plugins.step.NodeStepPlugin; +import com.dtolabs.rundeck.plugins.step.PluginStepContext; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import okhttp3.*; +import okhttp3.Request.Builder; + +import java.io.IOException; +import java.util.Map; + +import static com.bioraft.rundeck.rancher.RancherShared.ErrorCause; +import static com.bioraft.rundeck.rancher.RancherShared.loadStoragePathData; + +/** + * Workflow Node Step Plug-in to upgrade a service associated with a node. + * + * @author Karl DeBisschop + * @since 2019-12-20 + */ +@Plugin(name = RancherShared.RANCHER_SERVICE_CONTROLLER, service = ServiceNameConstants.WorkflowNodeStep) +@PluginDescription(title = "Rancher - Manage Service", description = "Start/Stop/Restart the service associated with the selected node.") +public class RancherManageService implements NodeStepPlugin { + + @PluginProperty(title = "Action", description = "What action is desired", required = true, defaultValue = "true") + @SelectValues(values = {"activate", "deactivate", "restart"}) + private String action; + + private String nodeName; + + OkHttpClient client; + + public RancherManageService() { + client = new OkHttpClient(); + } + + public RancherManageService(OkHttpClient client) { + this.client = client; + } + + @Override + public void executeNodeStep(PluginStepContext ctx, Map cfg, INodeEntry node) + throws NodeStepException { + + this.nodeName = node.getNodename(); + ExecutionContext executionContext = ctx.getExecutionContext(); + PluginLogger logger = ctx.getLogger(); + + Map attributes = node.getAttributes(); + + String accessKey; + String secretKey; + try { + accessKey = loadStoragePathData(executionContext, attributes.get(RancherShared.CONFIG_ACCESSKEY_PATH)); + secretKey = loadStoragePathData(executionContext, attributes.get(RancherShared.CONFIG_SECRETKEY_PATH)); + } catch (IOException e) { + throw new NodeStepException("Could not get secret storage path", e, ErrorCause.IOException, this.nodeName); + } + + JsonNode service; + if (attributes.get("type").equals("container")) { + service = apiGet(accessKey, secretKey, attributes.get("services")).path("data").path(0); + } else { + service = apiGet(accessKey, secretKey, attributes.get("self")); + } + String serviceState = service.path("state").asText(); + String body = ""; + switch (action) { + case "activate": + if (serviceState.equals("active")) { + String message = "Service state is already active"; + throw new NodeStepException(message, ErrorCause.ServiceNotRunning, node.getNodename()); + } + break; + case "deactivate": + case "restart": + if (!serviceState.equals("active")) { + String message = "Service state must be running, was " + serviceState; + throw new NodeStepException(message, ErrorCause.ServiceNotRunning, node.getNodename()); + } + break; + } + String url = service.path("actions").path(action).asText(); + if (url.length() == 0) { + throw new NodeStepException("No upgrade URL found", ErrorCause.MissingUpgradeURL, node.getNodename()); + } + + JsonNode newService = apiPost(accessKey, secretKey, url, body); + + logger.log(Constants.INFO_LEVEL, "Upgraded " + nodeName); + } + + /** + * Gets the web socket end point and connection token for an execute request. + * + * @param accessKey Rancher access key + * @param secretKey Rancher secret key + * @param url Rancher API url + * @return JSON from Rancher API request body. + * @throws NodeStepException when there API request fails + */ + private JsonNode apiGet(String accessKey, String secretKey, String url) throws NodeStepException { + try { + Builder builder = new Builder().url(url); + builder.addHeader("Authorization", Credentials.basic(accessKey, secretKey)); + Response response = client.newCall(builder.build()).execute(); + // Since URL comes from the Rancher server itself, assume there are no redirects. + if (response.code() >= 300) { + throw new IOException("API get failed" + response.message()); + } + ObjectMapper mapper = new ObjectMapper(); + assert response.body() != null; + return mapper.readTree(response.body().string()); + } catch (IOException e) { + throw new NodeStepException(e.getMessage(), e, ErrorCause.NoServiceObject, nodeName); + } + } + + /** + * Gets the web socket end point and connection token for an execute request. + * + * @param accessKey Rancher access key + * @param secretKey Rancher secret key + * @param url Rancher API url + * @param body Document contents to POST to Rancher. + * @return JSON from Rancher API request body. + * @throws NodeStepException when there API request fails + */ + private JsonNode apiPost(String accessKey, String secretKey, String url, String body) throws NodeStepException { + RequestBody postBody = RequestBody.create(MediaType.parse("application/json"), body); + try { + Builder builder = new Builder().url(url).post(postBody); + builder.addHeader("Authorization", Credentials.basic(accessKey, secretKey)); + Response response = client.newCall(builder.build()).execute(); + // Since URL comes from the Rancher server itself, assume there are no redirects. + if (response.code() >= 300) { + throw new IOException("API post failed" + response.message()); + } + ObjectMapper mapper = new ObjectMapper(); + assert response.body() != null; + return mapper.readTree(response.body().string()); + } catch (IOException e) { + throw new NodeStepException(e.getMessage(), e, ErrorCause.UpgradeFailure, nodeName); + } + } +} diff --git a/src/main/java/com/bioraft/rundeck/rancher/RancherNodeExecutorPlugin.java b/src/main/java/com/bioraft/rundeck/rancher/RancherNodeExecutorPlugin.java index 2132c36..4cf4fe9 100644 --- a/src/main/java/com/bioraft/rundeck/rancher/RancherNodeExecutorPlugin.java +++ b/src/main/java/com/bioraft/rundeck/rancher/RancherNodeExecutorPlugin.java @@ -77,6 +77,12 @@ public Description getDescription() { public NodeExecutorResult executeCommand(final ExecutionContext context, final String[] command, final INodeEntry node) { Map nodeAttributes = node.getAttributes(); + + if (nodeAttributes.get("type").equals("service")) { + String message = "Node executor is not currently supported for services"; + return NodeExecutorResultImpl.createFailure(StepFailureReason.PluginFailed, message, node); + } + try { accessKey = this.loadStoragePathData(context, nodeAttributes.get(CONFIG_ACCESSKEY_PATH)); secretKey = this.loadStoragePathData(context, nodeAttributes.get(CONFIG_SECRETKEY_PATH)); diff --git a/src/main/java/com/bioraft/rundeck/rancher/RancherResourceModelSource.java b/src/main/java/com/bioraft/rundeck/rancher/RancherResourceModelSource.java index aa776b2..033c5ab 100644 --- a/src/main/java/com/bioraft/rundeck/rancher/RancherResourceModelSource.java +++ b/src/main/java/com/bioraft/rundeck/rancher/RancherResourceModelSource.java @@ -73,6 +73,9 @@ public class RancherResourceModelSource implements ResourceModelSource { // Track how many times each stack_service has been seen. Map seen; + // Map stack IDs to names once to reduce API calls. + HashMap stackNames = new HashMap<>(); + /** * The required object constructor. * @@ -130,42 +133,70 @@ private void getNodesForEnvironment(String environmentId) { Framework.logger.log(Level.WARN, e.getMessage()); } - try { - data = this.getContainers(environmentId); - } catch (IOException e) { - Framework.logger.log(Level.WARN, e.getMessage()); - return; - } - - for (JsonNode node : data) { - if (isExclude(RancherShared.CONFIG_HANDLE_STOPPED) && !node.get("state").asText().equals("running")) { - continue; + if (configuration.getProperty(RancherShared.CONFIG_NODE_TYPE_INCLUDE_CONTAINER, "true").equals("true")) { + try { + data = this.getContainers(environmentId); + } catch (IOException e) { + Framework.logger.log(Level.WARN, e.getMessage()); + return; } - - int count = 0; - if (node.hasNonNull("labels")) { - count = countProcessableByLabel(node.get("labels")); - if (count == 0) { + for (JsonNode node : data) { + if (isExclude(RancherShared.CONFIG_HANDLE_STOPPED) && !node.get("state").asText().equals("running")) { continue; } - } - RancherNode rancherNode = new RancherNode(); - try { - NodeEntryImpl nodeEntry = rancherNode.getNodeEntry(environmentName, node); + int count = 0; + if (node.hasNonNull("labels")) { + count = countProcessableByLabel(node.get("labels")); + if (count == 0) { + continue; + } + } - if (count != 0) { - nodeEntry.setAttribute("seen", Integer.toString(count)); + RancherContainerNode rancherNode = new RancherContainerNode(); + try { + NodeEntryImpl nodeEntry = rancherNode.getNodeEntry(environmentName, node); + + if (count != 0) { + nodeEntry.setAttribute("seen", Integer.toString(count)); + } + + if (nodeEntry.getNodename() == null) { + String name = node.get("name").asText() + "(" + node.get("id").asText() + ")"; + String self = node.get("links").get("self").asText(); + Framework.logger.log(Level.WARN, name + " " + node.get("accountId").asText() + " " + self); + } else { + iNodeEntries.putNode(nodeEntry); + } + } catch (IllegalArgumentException | NullPointerException e) { + Framework.logger.log(Level.WARN, e.getMessage()); } + } + } - if (nodeEntry.getNodename() == null) { - String name = node.get("name").asText() + "(" + node.get("id").asText() + ")"; - String self = node.get("links").get("self").asText(); - Framework.logger.log(Level.WARN, name + " " + node.get("accountId").asText() + " " + self); - } else { - iNodeEntries.putNode(nodeEntry); + if (configuration.getProperty(RancherShared.CONFIG_NODE_TYPE_INCLUDE_SERVICE, "false").equals("true")) { + try { + stackNames = new HashMap<>(); + for (JsonNode node : this.getStacks(environmentId)) { + stackNames.put(node.get("id").asText(), node.get("name").asText()); + } + for (JsonNode node : this.getServices(environmentId)) { + RancherServiceNode rancherNode = new RancherServiceNode(); + try { + NodeEntryImpl nodeEntry = rancherNode.getNodeEntry(environmentName, node); + if (nodeEntry.getNodename() == null) { + String name = node.get("name").asText() + "(" + node.get("id").asText() + ")"; + String self = node.get("links").get("self").asText(); + String message = name + " " + node.get("accountId").asText() + " " + self; + Framework.logger.log(Level.WARN, message); + } else { + iNodeEntries.putNode(nodeEntry); + } + } catch (IllegalArgumentException | NullPointerException e) { + Framework.logger.log(Level.WARN, e.getMessage()); + } } - } catch (IllegalArgumentException | NullPointerException e) { + } catch (IOException e) { Framework.logger.log(Level.WARN, e.getMessage()); } } @@ -192,7 +223,7 @@ private Integer countProcessableByLabel(JsonNode labels) { } if (labels.hasNonNull("io.rancher.stack_service.name")) { - if (configuration.getProperty(RancherShared.CONFIG_LIMIT_ONE_CONTAINER) != null) { + if (configuration.getProperty(RancherShared.CONFIG_LIMIT_ONE_CONTAINER, "false").equals("true")) { String stackService = labels.get("io.rancher.stack_service.name").textValue(); if (stackService != null && seen.containsKey(stackService)) { return 0; @@ -204,18 +235,22 @@ private Integer countProcessableByLabel(JsonNode labels) { return 1; } + private String getStackName(JsonNode node) { + return stackNames.get(node.get("stackId").asText()); + } + /** * Start processing a new node. */ private class RancherNode { // The node being built. - private NodeEntryImpl nodeEntry; + protected NodeEntryImpl nodeEntry; // Tag set for the node being built. - private HashSet tagset; + protected HashSet tagset; // Labels read from the node. - private JsonNode labels; + protected JsonNode labels; public RancherNode() { nodeEntry = new NodeEntryImpl(); @@ -226,48 +261,12 @@ public RancherNode() { } } - public NodeEntryImpl getNodeEntry(String environmentName, JsonNode node) throws NullPointerException { - String name = environmentName + "_" + node.get("name").asText(); - nodeEntry.setNodename(name); - nodeEntry.setHostname(node.path("hostId").asText()); - nodeEntry.setUsername("root"); - nodeEntry.setAttribute("id", node.path("id").asText()); - nodeEntry.setAttribute("externalId", node.path("externalId").asText()); - nodeEntry.setAttribute("file-copier", RancherShared.RANCHER_SERVICE_PROVIDER); - nodeEntry.setAttribute("node-executor", RancherShared.RANCHER_SERVICE_PROVIDER); - nodeEntry.setAttribute("type", node.path("kind").asText()); - nodeEntry.setAttribute("state", node.path("state").asText()); - nodeEntry.setAttribute("account", node.path("accountId").asText()); - nodeEntry.setAttribute("environment", environmentName); - nodeEntry.setAttribute("image", node.path("imageUuid").asText()); - // Storage path for Rancher API access key. - String accessKeyPath = RancherShared.CONFIG_ACCESSKEY_PATH; - nodeEntry.setAttribute(accessKeyPath, configuration.getProperty(accessKeyPath)); - // Storage path for Rancher API secret key. - String secretKeyPath = RancherShared.CONFIG_SECRETKEY_PATH; - nodeEntry.setAttribute(secretKeyPath, configuration.getProperty(secretKeyPath)); - - JsonNode actions = node.path("actions"); - if (actions.hasNonNull("execute")) { - nodeEntry.setAttribute("execute", actions.get("execute").asText()); - } - nodeEntry.setAttribute("services", node.path("links").path("services").asText()); - nodeEntry.setAttribute("self", node.path("links").path("self").asText()); - - if (node.hasNonNull("labels")) { - labels = node.get("labels"); - this.processLabels(node); - } - - return nodeEntry; - } - /** * Adds attributes and tags from labels array. * * @param node The node we are building. */ - private void processLabels(JsonNode node) { + protected void processLabels(JsonNode node) { if (labels.hasNonNull("io.rancher.stack_service.name")) { String stackService = labels.get("io.rancher.stack_service.name").asText(); String[] parts = stackService.split("/"); @@ -355,6 +354,66 @@ private String last(String string) { } } + private class RancherContainerNode extends RancherNode { + public NodeEntryImpl getNodeEntry(String environmentName, JsonNode node) throws NullPointerException { + String name = environmentName + "_" + node.get("name").asText(); + nodeEntry.setNodename(name); + nodeEntry.setHostname(node.path("hostId").asText()); + nodeEntry.setUsername("root"); + nodeEntry.setAttribute("id", node.path("id").asText()); + nodeEntry.setAttribute("externalId", node.path("externalId").asText()); + nodeEntry.setAttribute("file-copier", RancherShared.RANCHER_SERVICE_PROVIDER); + nodeEntry.setAttribute("node-executor", RancherShared.RANCHER_SERVICE_PROVIDER); + nodeEntry.setAttribute("type", node.path("kind").asText()); + nodeEntry.setAttribute("state", node.path("state").asText()); + nodeEntry.setAttribute("account", node.path("accountId").asText()); + nodeEntry.setAttribute("environment", environmentName); + nodeEntry.setAttribute("image", node.path("imageUuid").asText()); + // Storage path for Rancher API access key. + String accessKeyPath = RancherShared.CONFIG_ACCESSKEY_PATH; + nodeEntry.setAttribute(accessKeyPath, configuration.getProperty(accessKeyPath)); + // Storage path for Rancher API secret key. + String secretKeyPath = RancherShared.CONFIG_SECRETKEY_PATH; + nodeEntry.setAttribute(secretKeyPath, configuration.getProperty(secretKeyPath)); + + JsonNode actions = node.path("actions"); + if (actions.hasNonNull("execute")) { + nodeEntry.setAttribute("execute", actions.get("execute").asText()); + } + nodeEntry.setAttribute("services", node.path("links").path("services").asText()); + nodeEntry.setAttribute("self", node.path("links").path("self").asText()); + + if (node.hasNonNull("labels")) { + labels = node.get("labels"); + this.processLabels(node); + } + + return nodeEntry; + } + } + + private class RancherServiceNode extends RancherNode { + public NodeEntryImpl getNodeEntry(String environmentName, JsonNode node) throws NullPointerException { + String name = environmentName + "_" + getStackName(node) + "-" + node.get("name").asText(); + nodeEntry.setNodename(name); + nodeEntry.setUsername("root"); + nodeEntry.setAttribute("id", node.path("id").asText()); + nodeEntry.setAttribute("type", node.path("kind").asText()); + nodeEntry.setAttribute("state", node.path("state").asText()); + nodeEntry.setAttribute("account", node.path("accountId").asText()); + nodeEntry.setAttribute("environment", environmentName); + nodeEntry.setAttribute("image", node.path("launchConfig").path("imageUuid").asText()); + // Storage path for Rancher API access key. + String accessKeyPath = RancherShared.CONFIG_ACCESSKEY_PATH; + nodeEntry.setAttribute(accessKeyPath, configuration.getProperty(accessKeyPath)); + // Storage path for Rancher API secret key. + String secretKeyPath = RancherShared.CONFIG_SECRETKEY_PATH; + nodeEntry.setAttribute(secretKeyPath, configuration.getProperty(secretKeyPath)); + nodeEntry.setAttribute("self", node.path("links").path("self").asText()); + return nodeEntry; + } + } + /** * Returns true if property is set to exclude relevant nodes. * @@ -393,10 +452,45 @@ private Integer countTimesSeen(String name) { * @throws IOException when API request fails. */ private ArrayList getContainers(String environment) throws IOException { + String path = url + "/projects/" + environment + "/containers"; + return getCollection(path); + } + + /** + * Makes the underlying API call to get the list of nodes for the environment. + * + * @param environment The Rancher accountId for the environment. + * @return An array of JsonNodes representing the containers in the environment. + * @throws IOException when API request fails. + */ + private ArrayList getServices(String environment) throws IOException { + String path = url + "/projects/" + environment + "/services"; + return getCollection(path); + } + + /** + * Makes the underlying API call to get the list of nodes for the environment. + * + * @param environment The Rancher accountId for the environment. + * @return An array of JsonNodes representing the containers in the environment. + * @throws IOException when API request fails. + */ + private ArrayList getStacks(String environment) throws IOException { + String path = url + "/projects/" + environment + "/stacks"; + return getCollection(path); + } + + /** + * Makes the underlying API call to get the list of nodes for the environment. + * + * @param path The Rancher accountId for the environment. + * @return An array of JsonNodes representing the containers in the environment. + * @throws IOException when API request fails. + */ + private ArrayList getCollection(String path) throws IOException { ObjectMapper objectMapper = new ObjectMapper(); String accessKey = configuration.getProperty(RancherShared.CONFIG_ACCESSKEY); String secretKey = configuration.getProperty(RancherShared.CONFIG_SECRETKEY); - String path = url + "/projects/" + environment + "/containers"; ArrayList data = new ArrayList<>(); while (!path.equals("null")) { diff --git a/src/main/java/com/bioraft/rundeck/rancher/RancherResourceModelSourceFactory.java b/src/main/java/com/bioraft/rundeck/rancher/RancherResourceModelSourceFactory.java index 9e5473b..f621f54 100644 --- a/src/main/java/com/bioraft/rundeck/rancher/RancherResourceModelSourceFactory.java +++ b/src/main/java/com/bioraft/rundeck/rancher/RancherResourceModelSourceFactory.java @@ -72,8 +72,14 @@ public class RancherResourceModelSourceFactory implements ResourceModelSourceFac builder.property(PropertyUtil.string(CONFIG_STACK_FILTER, "Stack Filter", "A regular expression for stacks to be included", true, "^.*$")); + builder.property(PropertyUtil.bool(CONFIG_NODE_TYPE_INCLUDE_SERVICE, "Use Services", + "Create nodes from services", true, "true")); + + builder.property(PropertyUtil.bool(CONFIG_NODE_TYPE_INCLUDE_CONTAINER, "Use Containers", + "Create nodes from containers", true, "true")); + builder.property(PropertyUtil.bool(CONFIG_LIMIT_ONE_CONTAINER, "Limit to One Container", - "Only run on one container for each service", true, "false")); + "Only run on one container for each service", true, "true")); builder.property(PropertyUtil.select(CONFIG_HANDLE_STOPPED, "Handle Stopped Containers", "Exclude stopped containers", true, "Exclude", Arrays.asList("Exclude", "Include"))); diff --git a/src/main/java/com/bioraft/rundeck/rancher/RancherShared.java b/src/main/java/com/bioraft/rundeck/rancher/RancherShared.java index 53d5519..96b7c6a 100644 --- a/src/main/java/com/bioraft/rundeck/rancher/RancherShared.java +++ b/src/main/java/com/bioraft/rundeck/rancher/RancherShared.java @@ -40,6 +40,8 @@ public class RancherShared { public static final String RANCHER_SERVICE_PROVIDER = "rancher"; + public static final String RANCHER_SERVICE_CONTROLLER = "rancher-service-controller"; + // Resource Model public static final String RANCHER_CONFIG_ENDPOINT = "rancher-api-endpoint"; @@ -49,6 +51,8 @@ public class RancherShared { public static final String CONFIG_ACCESSKEY = "access-key"; public static final String CONFIG_SECRETKEY = "secret-key"; public static final String CONFIG_STACK_FILTER = "stack-filter"; + public static final String CONFIG_NODE_TYPE_INCLUDE_SERVICE = "node-type-include-service"; + public static final String CONFIG_NODE_TYPE_INCLUDE_CONTAINER = "node-type-include-container"; public static final String CONFIG_LIMIT_ONE_CONTAINER = "limit-to-one"; public static final String CONFIG_HANDLE_STOPPED = "exclude-include-restrict-stopped"; public static final String CONFIG_HANDLE_SYSTEM = "io-rancher-container-system"; diff --git a/src/main/java/com/bioraft/rundeck/rancher/RancherUpgradeService.java b/src/main/java/com/bioraft/rundeck/rancher/RancherUpgradeService.java index c6dff7d..c8fd2be 100644 --- a/src/main/java/com/bioraft/rundeck/rancher/RancherUpgradeService.java +++ b/src/main/java/com/bioraft/rundeck/rancher/RancherUpgradeService.java @@ -137,7 +137,12 @@ public void executeNodeStep(PluginStepContext ctx, Map cfg, INod throw new NodeStepException("Could not get secret storage path", e, ErrorCause.IOException, this.nodeName); } - JsonNode service = apiGet(accessKey, secretKey, attributes.get("services")).path("data").path(0); + JsonNode service; + if (attributes.get("type").equals("container")) { + service = apiGet(accessKey, secretKey, attributes.get("services")).path("data").path(0); + } else { + service = apiGet(accessKey, secretKey, attributes.get("self")); + } String serviceState = service.path("state").asText(); if (!serviceState.equals("active")) { String message = "Service state must be running, was " + serviceState; diff --git a/src/test/java/com/bioraft/rundeck/rancher/RancherUpgradeServiceTest.java b/src/test/java/com/bioraft/rundeck/rancher/RancherUpgradeServiceTest.java index 4bb520f..3348932 100644 --- a/src/test/java/com/bioraft/rundeck/rancher/RancherUpgradeServiceTest.java +++ b/src/test/java/com/bioraft/rundeck/rancher/RancherUpgradeServiceTest.java @@ -86,6 +86,7 @@ public class RancherUpgradeServiceTest { public void setUp() { Map map = Stream .of(new String[][]{{"services", "https://rancher.example.com/v2-beta/"}, + {"type", "container"}, {RancherShared.CONFIG_ACCESSKEY_PATH, "keys/rancher/access.key"}, {RancherShared.CONFIG_SECRETKEY_PATH, "keys/rancher/secret.key"},}) .collect(Collectors.toMap(data -> data[0], data -> data[1]));