Skip to content

Commit

Permalink
Some nodes do not have upgrade (#18)
Browse files Browse the repository at this point in the history
Use current launchConfig if upgrade node is empty
  • Loading branch information
kdebisschop authored Jan 23, 2020
1 parent 43eacc2 commit b13ca96
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 73 deletions.
64 changes: 24 additions & 40 deletions src/main/java/com/bioraft/rundeck/rancher/RancherFileCopier.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,13 @@
import java.util.function.Consumer;

import com.dtolabs.rundeck.core.Constants;
import com.dtolabs.rundeck.core.common.Framework;
import com.dtolabs.rundeck.core.common.INodeEntry;
import com.dtolabs.rundeck.core.execution.ExecutionContext;
import com.dtolabs.rundeck.core.execution.impl.common.BaseFileCopier;
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.utils.ResolverUtil;
import com.dtolabs.rundeck.core.execution.workflow.steps.FailureReason;
import com.dtolabs.rundeck.core.plugins.Plugin;
import com.dtolabs.rundeck.core.plugins.configuration.*;
Expand All @@ -51,6 +51,7 @@
import com.dtolabs.rundeck.plugins.util.DescriptionBuilder;

import static com.bioraft.rundeck.rancher.RancherShared.*;
import static com.dtolabs.rundeck.core.Constants.DEBUG_LEVEL;

/**
* RancherStubFileCopier provider for the FileCopier service
Expand Down Expand Up @@ -130,22 +131,29 @@ private String copyFile(final ExecutionContext context, final File scriptfile, f
remotefile = destinationPath;
}
// write to a local temp file or use the input file
final File localTempfile = null != scriptfile ? scriptfile
final File localTempfile = (null != scriptfile) ? scriptfile
: BaseFileCopier.writeTempFile(context, scriptfile, input, script);

// Copy the file over
System.out.println("copying file: '" + localTempfile.getAbsolutePath() + "' to: '" + node.getNodename() + ":"
+ remotefile + "'");

String searchPath = ResolverUtil.resolveProperty(RancherShared.RANCHER_CONFIG_CLI_PATH, "", node,
context.getFramework().getFrameworkProjectMgr().getFrameworkProject(context.getFrameworkProject()),
context.getFramework());
Framework framework = context.getFramework();
String project = context.getFrameworkProject();
String searchPath = framework.getProjectProperty(project, PROJ_RANCHER_CLI_PATH);
if (searchPath == null) {
searchPath = framework.getProperty(FMWK_RANCHER_CLI_PATH);
}

try {
String result;
if (searchPath.equals("")) {
return copyViaApi(nodeAttributes, accessKey, secretKey, localTempfile, remotefile);
result = copyViaApi(context, nodeAttributes, accessKey, secretKey, localTempfile, remotefile);
} else {
return copyViaCli(nodeAttributes, accessKey, secretKey, localTempfile, remotefile, searchPath);
result = copyViaCli(context, nodeAttributes, accessKey, secretKey, localTempfile, remotefile, searchPath);
}
context.getExecutionLogger().log(DEBUG_LEVEL, "Copied '" + localTempfile + "' to '" + result );
return result;
} finally {
if (null == scriptfile) {
if (!ScriptfileUtils.releaseTempFile(localTempfile)) {
Expand All @@ -156,8 +164,9 @@ private String copyFile(final ExecutionContext context, final File scriptfile, f
}
}

private String copyViaCli(Map<String, String> nodeAttributes, String accessKey, String secretKey,
private String copyViaCli(final ExecutionContext context, Map<String, String> nodeAttributes, String accessKey, String secretKey,
File localTempFile, String remotefile, String searchPath) throws FileCopierException {
context.getExecutionLogger().log(DEBUG_LEVEL, "PATH: '" + searchPath + "'");
String path = localTempFile.getAbsolutePath();
String instance = nodeAttributes.get("externalId");
String[] command = {"rancher", "docker", "cp", path, instance + ":" + remotefile};
Expand All @@ -166,19 +175,20 @@ private String copyViaCli(Map<String, String> nodeAttributes, String accessKey,
try {
ProcessBuilder builder = new ProcessBuilder();
Map<String, String> environment = builder.environment();
context.getExecutionLogger().log(DEBUG_LEVEL, "CMD: '" + String.join(" ", command) + "'");
environment.put("PATH", searchPath);
environment.put("RANCHER_ENVIRONMENT", nodeAttributes.get("environment"));
environment.put("RANCHER_DOCKER_HOST", nodeAttributes.get("hostId"));
environment.put("RANCHER_DOCKER_HOST", nodeAttributes.get("hostname"));
environment.put("RANCHER_URL", nodeAttributes.get("execute").replaceFirst("/projects/.*$", ""));
environment.put("RANCHER_ACCESS_KEY", accessKey);
environment.put("RANCHER_SECRET_KEY", secretKey);
if (isWindows) {
throw new FileCopierException("Windows is not currently supported.",
FileCopyFailureReason.UnsupportedOperatingSystem);
throw new FileCopierException("Windows is not currently supported.", FileCopyFailureReason.UnsupportedOperatingSystem);
} else {
builder.command(command);
}
builder.directory(new File(System.getProperty("user.home")));
context.getExecutionLogger().log(DEBUG_LEVEL, "CMD: '" + String.join(" ", command) + "'");
builder.directory(new File(System.getProperty("java.io.tmpdir")));
Process process = builder.start();
StreamGobbler streamGobbler = new StreamGobbler(process.getInputStream(), System.out::println);
Executors.newSingleThreadExecutor().submit(streamGobbler);
Expand All @@ -193,49 +203,23 @@ private String copyViaCli(Map<String, String> nodeAttributes, String accessKey,
return remotefile;
}

private String copyViaApi(Map<String, String> nodeAttributes, String accessKey, String secretKey, File file,
private String copyViaApi(final ExecutionContext context, Map<String, String> nodeAttributes, String accessKey, String secretKey, File file,
String destination) throws FileCopierException {
try {
String url = nodeAttributes.get("execute");
RancherWebSocketListener.putFile(url, accessKey, secretKey, file, destination);
context.getExecutionLogger().log(DEBUG_LEVEL, "PUT: '" + file + "'");
} catch (IOException | InterruptedException e) {
throw new FileCopierException(e.getMessage(), FileCopyFailureReason.ConnectionFailure);
}
return destination;
}

public enum FileCopyFailureReason implements FailureReason {
/**
* Requested file could not be found
*/
FileNotFound,
/**
* Process could not be started
*/
IOException,
/**
* Process was interrupted
*/
InterruptedException,
/**
* Timeout on connection
*/
ConnectionTimeout,
/**
* Connection unsuccessful
*/
ConnectionFailure,
/**
* Authentication unsuccessful
*/
AuthenticationFailure,
/**
* Command or script execution result code was not zero
*/
NonZeroResultCode,
/**
* Operating system not currently supported
*/
UnsupportedOperatingSystem
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import com.dtolabs.rundeck.plugins.util.DescriptionBuilder;

import static com.bioraft.rundeck.rancher.RancherShared.*;
import static com.dtolabs.rundeck.core.Constants.DEBUG_LEVEL;

/**
* RancherNodeExecutorPlugin is a {@link NodeExecutor} plugin implementation for
Expand Down Expand Up @@ -94,7 +95,9 @@ public NodeExecutorResult executeCommand(final ExecutionContext context, final S
context.getFramework().getFrameworkProjectMgr().getFrameworkProject(context.getFrameworkProject()),
context.getFramework());
try {
context.getExecutionLogger().log(DEBUG_LEVEL, "Running " + String.join(" ", command));
RancherWebSocketListener.runJob(url, accessKey, secretKey, command, listener, temp, timeout);
context.getExecutionLogger().log(DEBUG_LEVEL, "Ran " + String.join(" ", command));
} catch (IOException e) {
return NodeExecutorResultImpl.createFailure(StepFailureReason.IOFailure, e.getMessage(), node);
} catch (InterruptedException e) {
Expand All @@ -103,7 +106,9 @@ public NodeExecutorResult executeCommand(final ExecutionContext context, final S

String[] pidFile;
try {
pidFile = this.readLogFile(temp + ".pid", url).split(" +");
String file = temp + ".pid";
context.getExecutionLogger().log(DEBUG_LEVEL, "Reading '" + file + "' on " + url);
pidFile = this.readLogFile(file, url).split(" +");
} catch (IOException e) {
return NodeExecutorResultImpl.createFailure(StepFailureReason.IOFailure, e.getMessage(), node);
} catch (InterruptedException e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
*/
package com.bioraft.rundeck.rancher;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Iterator;
import java.util.Map;
Expand All @@ -39,6 +38,7 @@
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;

import com.google.common.collect.ImmutableMap;
import okhttp3.Credentials;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
Expand All @@ -48,6 +48,7 @@
import okhttp3.Response;

import static com.bioraft.rundeck.rancher.RancherShared.*;
import static com.dtolabs.rundeck.core.Constants.DEBUG_LEVEL;
import static com.dtolabs.rundeck.core.plugins.configuration.StringRenderingConstants.CODE_SYNTAX_MODE;
import static com.dtolabs.rundeck.core.plugins.configuration.StringRenderingConstants.DISPLAY_TYPE_KEY;

Expand Down Expand Up @@ -90,6 +91,8 @@ public class RancherUpgradeService implements NodeStepPlugin {

OkHttpClient client;

private final static int intervalMillis = 2000;

public RancherUpgradeService() {
client = new OkHttpClient();
}
Expand Down Expand Up @@ -126,25 +129,25 @@ public void executeNodeStep(PluginStepContext ctx, Map<String, Object> cfg, INod

String upgradeUrl = service.path("actions").path("upgrade").asText();
if (upgradeUrl.length() == 0) {
throw new NodeStepException("No upgrade URL found", ErrorCause.MissingUpgradeURL,
node.getNodename());
throw new NodeStepException("No upgrade URL found", ErrorCause.MissingUpgradeURL, node.getNodename());
}
JsonNode launchConfig = service.path("upgrade").path("inServiceStrategy").path("launchConfig");
if (launchConfig.isMissingNode() || launchConfig.isNull()) {
launchConfig = service.path("launchConfig");
}
JsonNode upgrade = service.path("upgrade");
if (upgrade.isMissingNode()) {
throw new NodeStepException("No upgrade data found", ErrorCause.NoUpgradeData,
node.getNodename());
if (launchConfig.isMissingNode() || launchConfig.isNull()) {
throw new NodeStepException("No upgrade data found", ErrorCause.NoUpgradeData, node.getNodename());
}
ObjectNode launchConfigObject = (ObjectNode) launchConfig;

if (dockerImage == null || dockerImage.length() == 0) {
if ((dockerImage == null || dockerImage.length() == 0) && cfg.containsKey("dockerImage")) {
dockerImage = (String) cfg.get("dockerImage");
}
if (dockerImage != null && dockerImage.length() > 0) {
logger.log(Constants.INFO_LEVEL, "Setting image to " + dockerImage);
((ObjectNode) upgrade.get("inServiceStrategy").get("launchConfig")).put("imageUuid",
"docker:" + dockerImage);
launchConfigObject.put("imageUuid","docker:" + dockerImage);
}

((ObjectNode) upgrade.get("inServiceStrategy")).put("startFirst", startFirst);
if ((environment == null || environment.isEmpty()) && cfg.containsKey("environment")) {
environment = (String) cfg.get("environment");
}
Expand All @@ -157,27 +160,30 @@ public void executeNodeStep(PluginStepContext ctx, Map<String, Object> cfg, INod
secrets = (String) cfg.get("secrets");
}

if (cfg.containsKey("startFirst")) {
startFirst = cfg.get("startFirst").equals("true");
}
if (startFirst == null) {
startFirst = (Boolean) cfg.get("dockerImage");
startFirst = true;
}

this.setEnvVars(upgrade);
this.setLabels(upgrade);
this.addSecrets(upgrade);
this.setEnvVars(launchConfigObject);
this.setLabels(launchConfigObject);
this.addSecrets(launchConfigObject);

doUpgrade(accessKey, secretKey, upgradeUrl, upgrade.toString());
doUpgrade(accessKey, secretKey, upgradeUrl, launchConfigObject);

logger.log(Constants.INFO_LEVEL, "Upgraded " + nodeName);
}

/**
* Adds/modifies environment variables.
*
* @param upgrade JsonNode representing the target upgraded configuration.
* @param launchConfig JsonNode representing the target upgraded configuration.
*/
private void setEnvVars(JsonNode upgrade) throws NodeStepException {
private void setEnvVars(ObjectNode launchConfig) throws NodeStepException {
if (environment != null && environment.length() > 0) {
ObjectNode envObject = (ObjectNode) upgrade.get("inServiceStrategy").get("launchConfig").get("environment");
ObjectNode envObject = (ObjectNode) launchConfig.get("environment");
ObjectMapper objectMapper = new ObjectMapper();
try {
JsonNode map = objectMapper.readTree(ensureStringIsJsonObject(environment));
Expand All @@ -198,11 +204,11 @@ private void setEnvVars(JsonNode upgrade) throws NodeStepException {
/**
* Adds/modifies labels based on the step's labels setting.
*
* @param upgrade JsonNode representing the target upgraded configuration.
* @param launchConfig JsonNode representing the target upgraded configuration.
*/
private void setLabels(JsonNode upgrade) throws NodeStepException {
private void setLabels(ObjectNode launchConfig) throws NodeStepException {
if (labels != null && labels.length() > 0) {
ObjectNode labelObject = (ObjectNode) upgrade.get("inServiceStrategy").get("launchConfig").get("labels");
ObjectNode labelObject = (ObjectNode) launchConfig.get("labels");
ObjectMapper objectMapper = new ObjectMapper();
try {
JsonNode map = objectMapper.readTree(ensureStringIsJsonObject(labels));
Expand All @@ -223,22 +229,27 @@ private void setLabels(JsonNode upgrade) throws NodeStepException {
/**
* Add or replace secrets.
*
* @param upgrade JsonNode representing the target upgraded configuration.
* @param launchConfig JsonNode representing the target upgraded configuration.
* @throws NodeStepException when secret JSON is malformed (passed up from {@see this.buildSecret()}.
*/
private void addSecrets(JsonNode upgrade) throws NodeStepException {
private void addSecrets(ObjectNode launchConfig) throws NodeStepException {
if (secrets != null && secrets.length() > 0) {
JsonNode launchConfig = upgrade.get("inServiceStrategy").get("launchConfig");
ObjectNode launchObject = (ObjectNode) launchConfig;
ArrayNode secretsArray = launchObject.putArray("secrets");
// Copy existing secrets, skipping any that we want to add or overwrite.
Iterator<JsonNode> elements = null;
boolean hasOldSecrets = false;
if (launchConfig.has("secrets") && !launchConfig.get("secrets").isNull()) {
hasOldSecrets = true;
elements = launchConfig.get("secrets").elements();
}

ArrayNode secretsArray = launchConfig.putArray("secrets");

// Copy existing secrets, skipping any that we want to add or overwrite.
if (launchConfig.has("secrets")) {
Iterator<JsonNode> elements = launchConfig.get("secrets").elements();
if (hasOldSecrets && elements != null) {
while (elements.hasNext()) {
JsonNode secretObject = elements.next();
// @todo this only works for a single secret added.
if (!secretObject.get("secretId").asText().equals(secrets)) {
if (!secretObject.path("secretId").asText().equals(secrets)) {
secretsArray.add(secretObject);
}
}
Expand All @@ -258,11 +269,27 @@ private void addSecrets(JsonNode upgrade) throws NodeStepException {
* @param accessKey Rancher access key
* @param secretKey Rancher secret key
* @param upgradeUrl Rancher API url
* @param upgrade The JSON string representing the desired upgrade state.
* @param launchConfig The JSON string representing the desired upgrade state.
* @throws NodeStepException when upgrade is interrupted.
*/
private void doUpgrade(String accessKey, String secretKey, String upgradeUrl, String upgrade)
private void doUpgrade(String accessKey, String secretKey, String upgradeUrl, JsonNode launchConfig)
throws NodeStepException {
Map<String, Object> inServiceStrategy = ImmutableMap.<String, Object>builder() //
.put("type", "inServiceUpgradeStrategy") //
.put("batchSize", 1) //
.put("intervalMillis", intervalMillis) //
.put("startFirst", startFirst) //
.put("launchConfig", launchConfig) //
.build();
ObjectMapper mapper = new ObjectMapper();
String upgrade;
try {
upgrade = "{\"type\":\"serviceUpgrade\",\"inServiceStrategy\":" + mapper.writeValueAsString(inServiceStrategy) + "}";
logger.log(DEBUG_LEVEL, upgrade);
} catch (IOException e) {
throw new NodeStepException("Failed post to " + upgradeUrl, e, ErrorCause.InvalidConfiguration, nodeName);
}

JsonNode service = apiPost(accessKey, secretKey, upgradeUrl, upgrade);
String state = service.get("state").asText();
String link = service.get("links").get("self").asText();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ public void setUp() {
when(storageTree.getResource(anyString())).thenReturn(treeResource);
when(treeResource.getContents()).thenReturn(contents);
when(client.newCall(any())).thenReturn(call);
cfg.put("startFirst", "true");
}

@Test
Expand Down

0 comments on commit b13ca96

Please sign in to comment.