From 8f6622de210ed5ab871ef29b73f647615f9ccfc0 Mon Sep 17 00:00:00 2001 From: Karl DeBisschop Date: Tue, 22 Nov 2022 11:16:04 -0500 Subject: [PATCH] Add option to remove secrets (#63) * Add option to remove secrets --- README.md | 11 ++- build.gradle | 6 +- .../bioraft/rundeck/rancher/Constants.java | 4 + .../rundeck/rancher/MessageReader.java | 5 +- .../rundeck/rancher/RancherLaunchConfig.java | 31 ++++--- .../rancher/RancherResourceModelSource.java | 3 +- .../rancher/RancherUpgradeService.java | 6 +- .../rancher/RancherWebSocketListener.java | 11 +-- .../rancher/RancherAddServiceTest.java | 53 ++++++------ .../rancher/RancherFileCopierTest.java | 28 +------ .../rancher/RancherLaunchConfigTest.java | 82 +++++++++++++------ .../rancher/RancherManageServiceTest.java | 43 +++++----- .../rancher/RancherWebSocketListenerTest.java | 21 ----- src/test/resources/launchConfig.json | 2 +- 14 files changed, 150 insertions(+), 156 deletions(-) diff --git a/README.md b/README.md index cfee9a9..bc2dcea 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ Configuration: Note: -Note that Rancher uses container ID to to construct the URL used to make API requests. +Note that Rancher uses container ID to construct the URL used to make API requests. When services are upgraded, new containers are created so the API urls used in the node operations cannot work anymore. The container does not exist, so the URL will fail. As long nodes are defined by containers, there is probably no way to avoid this. @@ -54,7 +54,7 @@ in the same job context. ### Rancher Node Executor -Executes jobs on remote Docker containers managed by the Rancher host. +Execute jobs on remote Docker containers managed by the Rancher host. Features: @@ -63,7 +63,7 @@ Features: ### Rancher File Copier -Copies files to a node. Can be configured to use Rancher CLI if it is installed and +Copy files to a node. Can be configured to use Rancher CLI if it is installed and available. Otherwise, assembles files from Base64-encoded parts transmitted via Rancher API. @@ -103,9 +103,12 @@ Adds a service to an existing stack. Required inputs: Optional inputs: - Data volumes - - OS environment + - OS environment variables - Service labels - Secrets + - OS environment variables to remove + - Service labels to remove + - Secrets to remove ### Manage Service diff --git a/build.gradle b/build.gradle index 5a44d09..9553324 100644 --- a/build.gradle +++ b/build.gradle @@ -29,8 +29,8 @@ apply plugin: 'eclipse' apply plugin: 'idea' java { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 } compileJava { @@ -100,7 +100,7 @@ dependencies { testImplementation ( 'com.squareup.okhttp3:mockwebserver:3.11.0', 'junit:junit:4.12', - 'org.mockito:mockito-core:3.6+', + 'org.mockito:mockito-core:3.7+', 'org.slf4j:slf4j-simple:1.7.30+', ) } diff --git a/src/main/java/com/bioraft/rundeck/rancher/Constants.java b/src/main/java/com/bioraft/rundeck/rancher/Constants.java index 0700c52..5106a30 100644 --- a/src/main/java/com/bioraft/rundeck/rancher/Constants.java +++ b/src/main/java/com/bioraft/rundeck/rancher/Constants.java @@ -58,6 +58,9 @@ private Constants() { public static final String NODE_ATT_ACTIONS = "actions"; public static final String NODE_ATT_SELF = "self"; + // Splits services by whitespace, comma, or semicolon. + public static final String PERMISSIVE_WHITESPACE_REGEX = "[,; ]+"; + public static final String NODE_LINK_SERVICES = "services"; public static final String STATE_ACTIVE = "active"; @@ -87,6 +90,7 @@ private Constants() { public static final String OPT_IMAGE_UUID = "imageUuid"; public static final String OPT_LABELS = "labels"; public static final String OPT_SECRETS = "secrets"; + public static final String OPT_REMOVE_SECRETS = "removeSecrets"; public static final String OPT_SERVICE_NAME = "serviceName"; public static final String OPT_STACK_NAME = "stackName"; diff --git a/src/main/java/com/bioraft/rundeck/rancher/MessageReader.java b/src/main/java/com/bioraft/rundeck/rancher/MessageReader.java index 47e2512..2417354 100644 --- a/src/main/java/com/bioraft/rundeck/rancher/MessageReader.java +++ b/src/main/java/com/bioraft/rundeck/rancher/MessageReader.java @@ -33,7 +33,6 @@ /** * Reads a message from Rancher transmitting a portion of a Docker multiplexed * stream. - * * To use this class, instantiate with an InputStream, read nextMessage() until * the stream is exhausted, then prepend nextHeader to the next message from * Rancher. @@ -94,7 +93,7 @@ public LogMessage nextMessage() throws IOException { final int frameSize = header.getInt(FRAME_SIZE_OFFSET); // If the Docker frame extends into the next Rancher message, the log - // message will consist of the rest of the buffer and we need to + // message will consist of the rest of the buffer, and we need to // calculate how much of the Docker frame is in the next Rancher // message. Otherwise, just send the frame (which moves the buffer // pointer forward to prepare for the next call of nextMessage()). @@ -144,7 +143,7 @@ public byte[] nextHeader() { public void close() throws IOException { // RancherWebSocketListener will close the stream and release the connection // after we read all the data. - // We cannot call the stream's close method because it an instance of + // We cannot call the stream's close method because it is an instance of // UncloseableInputStream, where close is a no-op. copy(stream, nullOutputStream()); } diff --git a/src/main/java/com/bioraft/rundeck/rancher/RancherLaunchConfig.java b/src/main/java/com/bioraft/rundeck/rancher/RancherLaunchConfig.java index d555e60..cef86c4 100644 --- a/src/main/java/com/bioraft/rundeck/rancher/RancherLaunchConfig.java +++ b/src/main/java/com/bioraft/rundeck/rancher/RancherLaunchConfig.java @@ -28,8 +28,7 @@ import java.util.Iterator; import java.util.Map; -import static com.bioraft.rundeck.rancher.Constants.OPT_DATA_VOLUMES; -import static com.bioraft.rundeck.rancher.Constants.OPT_SECRETS; +import static com.bioraft.rundeck.rancher.Constants.*; import static com.bioraft.rundeck.rancher.Errors.ErrorCause.*; /** @@ -54,6 +53,8 @@ public class RancherLaunchConfig { private Map secretMap; + private Map removeSecretMap; + private final String nodeName; private final PluginLogger logger; @@ -107,15 +108,22 @@ public void removeLabels(String removeLabels) { this.removeLabels = removeLabels; } - public void setSecrets(String secrets) { + public void setSecrets(String secrets, String remove) { this.secrets = secrets; + secretMap = new HashMap<>(); + removeSecretMap = new HashMap<>(); if (secrets != null && secrets.trim().length() > 0) { // Add in the new or replacement secrets specified in the step. - secretMap = new HashMap<>(); - for (String secretId : secrets.split("[,; ]+")) { + for (String secretId : secrets.split(PERMISSIVE_WHITESPACE_REGEX)) { secretMap.put(secretId, secretId); } } + if (remove != null && remove.trim().length() > 0) { + // Add in the new or replacement secrets specified in the step. + for (String secretId : remove.split(PERMISSIVE_WHITESPACE_REGEX)) { + removeSecretMap.put(secretId, secretId); + } + } } /** @@ -207,9 +215,11 @@ private void addSecrets(ObjectNode launchConfig) { } // Add in the new or replacement secrets specified in the step. - for (String secretId : secrets.split("[,; ]+")) { - secretsArray.add((new Strings()).buildSecret(secretId)); - logger.log(Constants.INFO_LEVEL, "Adding secret map to " + secretId); + for (String secretId : secrets.split(PERMISSIVE_WHITESPACE_REGEX)) { + if (!removeSecretMap.containsKey(secretId)) { + secretsArray.add((new Strings()).buildSecret(secretId)); + logger.log(Constants.INFO_LEVEL, "Adding secret map to " + secretId); + } } } } @@ -217,7 +227,8 @@ private void addSecrets(ObjectNode launchConfig) { private void copyOldSecrets(Iterator elements, ArrayNode secretsArray) { while (elements.hasNext()) { JsonNode secretObject = elements.next(); - if (!secretMap.containsKey(secretObject.path("secretId").asText())) { + String key = secretObject.path("secretId").asText(); + if (!secretMap.containsKey(key) && !removeSecretMap.containsKey(key)) { secretsArray.add(secretObject); } } @@ -226,7 +237,7 @@ private void copyOldSecrets(Iterator elements, ArrayNode secretsArray) /** * Add or replace secrets. * - * @throws NodeStepException when secret JSON is malformed (passed up from {@see this.buildSecret()}. + * @throws NodeStepException when secret JSON is malformed (passed up from {@see this.buildSecret()}). */ private void setMountArray(String newData) throws NodeStepException { if (newData != null && newData.length() > 0) { diff --git a/src/main/java/com/bioraft/rundeck/rancher/RancherResourceModelSource.java b/src/main/java/com/bioraft/rundeck/rancher/RancherResourceModelSource.java index 97338af..5b909ff 100644 --- a/src/main/java/com/bioraft/rundeck/rancher/RancherResourceModelSource.java +++ b/src/main/java/com/bioraft/rundeck/rancher/RancherResourceModelSource.java @@ -68,7 +68,7 @@ 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. + // Map stack IDs to stack names once to reduce API calls. HashMap stackNames = new HashMap<>(); /** @@ -426,7 +426,6 @@ private boolean isExclude(String property) { /** * Count the number of containers are in each service for each stack. - * * By constructing a node filter of "seen:1" we can run on only one container in * a service even when we are not limiting the project node set to the one * container per service. diff --git a/src/main/java/com/bioraft/rundeck/rancher/RancherUpgradeService.java b/src/main/java/com/bioraft/rundeck/rancher/RancherUpgradeService.java index 5a5ce23..d1e9914 100644 --- a/src/main/java/com/bioraft/rundeck/rancher/RancherUpgradeService.java +++ b/src/main/java/com/bioraft/rundeck/rancher/RancherUpgradeService.java @@ -86,6 +86,9 @@ public class RancherUpgradeService implements NodeStepPlugin { @PluginProperty(title = "Secrets", description = "Keys for secrets separated by commas or spaces") private String secrets; + @PluginProperty(title = "Secrets to Remove", description = "Keys for secrets separated by commas or spaces") + private String removeSecrets; + @PluginProperty(title = "Start before stopping", description = "Start new container(s) before stopping old", required = true, defaultValue = "true") private Boolean startFirst; @@ -162,6 +165,7 @@ public void executeNodeStep(PluginStepContext ctx, Map cfg, INod dataVolumes = (String) cfg.getOrDefault("dataVolumes", defaultString(dataVolumes)); labels = (String) cfg.getOrDefault("labels", defaultString(labels)); secrets = (String) cfg.getOrDefault("secrets", defaultString(secrets)); + removeSecrets = (String) cfg.getOrDefault("removeSecrets", defaultString(removeSecrets)); removeEnvironment = (String) cfg.getOrDefault("removeEnvironment", defaultString(removeEnvironment)); removeLabels = (String) cfg.getOrDefault("removeLabels", defaultString(removeLabels)); @@ -169,7 +173,7 @@ public void executeNodeStep(PluginStepContext ctx, Map cfg, INod rancherLaunchConfig.setEnvironment(environment); rancherLaunchConfig.setDataVolumes(dataVolumes); rancherLaunchConfig.setLabels(labels); - rancherLaunchConfig.setSecrets(secrets); + rancherLaunchConfig.setSecrets(secrets, removeSecrets); rancherLaunchConfig.removeEnvironment(removeEnvironment); rancherLaunchConfig.removeLabels(removeLabels); diff --git a/src/main/java/com/bioraft/rundeck/rancher/RancherWebSocketListener.java b/src/main/java/com/bioraft/rundeck/rancher/RancherWebSocketListener.java index 5722c35..0b63530 100644 --- a/src/main/java/com/bioraft/rundeck/rancher/RancherWebSocketListener.java +++ b/src/main/java/com/bioraft/rundeck/rancher/RancherWebSocketListener.java @@ -197,7 +197,6 @@ public void putFile(String url, String accessKey, String secretKey, File file, S /** * Runs a command and passes output back to an external listener. - * * Exit status is read after completion from the job's PID file in /tmp. * * @param url The URL the listener should use to launch the job. @@ -219,9 +218,9 @@ private void runJob(String url, String accessKey, String secretKey, ExecutionLis this.listener = listener; this.nextHeader = new byte[0]; - // Even though we are passing data back to an external listener, we need to - // buffer - // the message stream so we can pick out lines that are part of STDERR. + // Even though we are passing data back to an external listener, we + // need to buffer the message stream so we can pick out lines that + // are part of STDERR. output = new StringBuilder(); client.newWebSocket(this.buildRequest(true), this); @@ -234,7 +233,6 @@ private void runJob(String url, String accessKey, String secretKey, ExecutionLis /** * Runs a command, capturing output in a StringBuffer injected on invocation. - * * This is used to get the contents of the PID file when the job ends and * determine the exit status. * @@ -266,8 +264,7 @@ private void run(String url, String accessKey, String secretKey, StringBuilder o /** * Put a file onto the server. - * - * Neither STDIN or STDOUT are attached. The file is sent as a payload with the + * Neither STDIN nor STDOUT are attached. The file is sent as a payload with the * post command. * * @param url The URL the listener should use to launch the job. diff --git a/src/test/java/com/bioraft/rundeck/rancher/RancherAddServiceTest.java b/src/test/java/com/bioraft/rundeck/rancher/RancherAddServiceTest.java index c47eebd..82bcf37 100644 --- a/src/test/java/com/bioraft/rundeck/rancher/RancherAddServiceTest.java +++ b/src/test/java/com/bioraft/rundeck/rancher/RancherAddServiceTest.java @@ -20,8 +20,7 @@ import com.dtolabs.rundeck.plugins.step.PluginStepContext; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; -import org.junit.Before; -import org.junit.Test; +import org.junit.*; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Captor; @@ -64,10 +63,19 @@ public class RancherAddServiceTest extends PluginStepTest { @Captor ArgumentCaptor> captor; + private AutoCloseable closeable; + @Before - public void implSetUp() { - MockitoAnnotations.openMocks(this); - setUp(); + public void implSetUp() throws Exception { + try (AutoCloseable closeable = MockitoAnnotations.openMocks(this)) { + this.closeable = closeable; + setUp(); + } + } + + @After + public void closeService() throws Exception { + this.closeable.close(); } @Test @@ -83,9 +91,8 @@ public void throwExceptionWhenAccessKeyIsMissing() throws StepException { when(cfg.getOrDefault(eq(OPT_SERVICE_NAME), any())).thenReturn(serviceName); when(cfg.getOrDefault(eq(OPT_IMAGE_UUID), any())).thenReturn(imageUuid); - when(framework.getProperty(eq(FMWK_RANCHER_ENDPOINT))).thenReturn("https://rancher.example.com/v1"); - when(framework.getProperty(eq(FMWK_RANCHER_ACCESSKEY_PATH))).thenReturn(null); -// when(framework.getProperty(eq(FMWK_RANCHER_SECRETKEY_PATH))).thenReturn(null); + when(framework.getProperty(FMWK_RANCHER_ENDPOINT)).thenReturn("https://rancher.example.com/v1"); + when(framework.getProperty(FMWK_RANCHER_ACCESSKEY_PATH)).thenReturn(null); when(pluginStepContext.getFramework()).thenReturn(framework); when(pluginStepContext.getFrameworkProject()).thenReturn(projectName); @@ -112,7 +119,7 @@ public void whenStackIdIsGiven() throws StepException, IOException { when(cfg.getOrDefault(eq(OPT_IMAGE_UUID), any())).thenReturn(imageUuid); JsonNode stack = readFromInputStream(getResourceStream("stack.json")); - when(client.get(eq(stackIdRequest))).thenReturn(stack); + when(client.get(stackIdRequest)).thenReturn(stack); JsonNode service = readFromInputStream(getResourceStream("service.json")); when(client.post(anyString(), anyMap())).thenReturn(service); @@ -120,7 +127,7 @@ public void whenStackIdIsGiven() throws StepException, IOException { upgrade = new RancherAddService(client); upgrade.executeStep(ctx, cfg); - verify(client, times(1)).get(eq(stackIdRequest)); + verify(client, times(1)).get(stackIdRequest); verify(client, times(0)).get(anyString(), anyMap()); verify(client, times(1)).post(eq(upgradePostUrl), captor.capture()); Map postMap = captor.getValue(); @@ -171,10 +178,7 @@ public void whenStackDoesNotExist() throws StepException, IOException { JsonNode notFound = readFromInputStream(getResourceStream("not-found.json")); when(client.get(anyString())).thenReturn(notFound); - -// JsonNode noStacks = readFromInputStream(getResourceStream("no-stacks.json")); -// when(client.get(anyString(), anyMap())).thenReturn(noStacks); - + upgrade = new RancherAddService(client); upgrade.executeStep(ctx, cfg); @@ -218,9 +222,6 @@ public void whenStackIdIsNull() throws StepException, IOException { notFound.put("type", "error"); when(client.get(anyString())).thenThrow(new IOException()); -// JsonNode noStacks = readFromInputStream(getResourceStream("no-stacks.json")); -// when(client.get(anyString(), anyMap())).thenReturn(noStacks); - upgrade = new RancherAddService(client); upgrade.executeStep(ctx, cfg); @@ -232,9 +233,6 @@ public void whenStackIdIsNull() throws StepException, IOException { @Test(expected = StepException.class) public void throwExceptionWhenStackIsNotSet() throws StepException, IOException { when(cfg.getOrDefault(eq(OPT_STACK_NAME), any())).thenReturn(""); -// when(cfg.getOrDefault(eq(OPT_ENV_IDS), any())).thenReturn(envIds); -// when(cfg.getOrDefault(eq(OPT_SERVICE_NAME), any())).thenReturn(serviceName); -// when(cfg.getOrDefault(eq(OPT_IMAGE_UUID), any())).thenReturn(imageUuid); runSuccess(); } @@ -242,8 +240,6 @@ public void throwExceptionWhenStackIsNotSet() throws StepException, IOException public void throwExceptionWhenEnvironmentIdIsNotSet() throws StepException, IOException { when(cfg.getOrDefault(eq(OPT_STACK_NAME), any())).thenReturn(stackName); when(cfg.getOrDefault(eq(OPT_ENV_IDS), any())).thenReturn(""); -// when(cfg.getOrDefault(eq(OPT_SERVICE_NAME), any())).thenReturn(serviceName); -// when(cfg.getOrDefault(eq(OPT_IMAGE_UUID), any())).thenReturn(imageUuid); runSuccess(); } @@ -252,7 +248,6 @@ public void throwExceptionWhenServiceNameIsNotSet() throws StepException, IOExce when(cfg.getOrDefault(eq(OPT_STACK_NAME), any())).thenReturn(stackName); when(cfg.getOrDefault(eq(OPT_ENV_IDS), any())).thenReturn(envIds); when(cfg.getOrDefault(eq(OPT_SERVICE_NAME), any())).thenReturn(""); -// when(cfg.getOrDefault(eq(OPT_IMAGE_UUID), any())).thenReturn(imageUuid); runSuccess(); } @@ -271,8 +266,8 @@ public void whenDataVolumesIsGiven() throws StepException, IOException { when(cfg.getOrDefault(eq(OPT_ENV_IDS), any())).thenReturn(envIds); when(cfg.getOrDefault(eq(OPT_SERVICE_NAME), any())).thenReturn(serviceName); when(cfg.getOrDefault(eq(OPT_IMAGE_UUID), any())).thenReturn(imageUuid); - String volume = "/volume:/mountpoint:ro"; - String volume2 = "/volume2:/mountpoint2"; + String volume = "/volume:/mountPoint:ro"; + String volume2 = "/volume2:/mountPoint2"; when(cfg.getOrDefault(eq(OPT_DATA_VOLUMES), any())).thenReturn("[\"" + volume + "\",\"" + volume2 + "\"]"); runSuccess(); assertEquals(volume, getPostMapLaunchConfig().get(OPT_DATA_VOLUMES).elements().next().asText()); @@ -352,12 +347,12 @@ private void runSuccess() throws IOException, StepException { // When given a stack, first look for a matching stack ID. JsonNode notFound = readFromInputStream(getResourceStream("not-found.json")); - when(client.get(eq(stackIdRequest))).thenReturn(notFound); + when(client.get(stackIdRequest)).thenReturn(notFound); // If there is no matching ID, then look for a matching name. JsonNode stacks = readFromInputStream(getResourceStream("stacks.json")); String stackId = stacks.get("data").elements().next().get("id").asText(); - when(client.get(eq(stackNameRequest))).thenReturn(stacks); + when(client.get(stackNameRequest)).thenReturn(stacks); JsonNode service = readFromInputStream(getResourceStream("service.json")); when(client.post(anyString(), anyMap())).thenReturn(service); @@ -365,8 +360,8 @@ private void runSuccess() throws IOException, StepException { upgrade = new RancherAddService(client); upgrade.executeStep(ctx, cfg); - verify(client, times(1)).get(eq(stackIdRequest)); - verify(client, times(1)).get(eq(stackNameRequest)); + verify(client, times(1)).get(stackIdRequest); + verify(client, times(1)).get(stackNameRequest); verify(client, times(1)).post(eq(upgradePostUrl), captor.capture()); postMap = captor.getValue(); diff --git a/src/test/java/com/bioraft/rundeck/rancher/RancherFileCopierTest.java b/src/test/java/com/bioraft/rundeck/rancher/RancherFileCopierTest.java index 654b1dd..2e74086 100644 --- a/src/test/java/com/bioraft/rundeck/rancher/RancherFileCopierTest.java +++ b/src/test/java/com/bioraft/rundeck/rancher/RancherFileCopierTest.java @@ -82,11 +82,6 @@ public void setUp() { when(projectManager.getFrameworkProject(PROJECT_NAME)).thenReturn(rundeckProject); when(rundeckProject.hasProperty(PROJ_RANCHER_CLI_PATH)).thenReturn(false); when(propertyLookup.hasProperty(FMWK_RANCHER_CLI_PATH)).thenReturn(false); -// when(framework.getProjectProperty(anyString(), anyString())).thenReturn(""); - -// when(framework.createFrameworkNode()).thenReturn(host); -// when(framework.getProperty(anyString())).thenReturn("/tmp/"); -// when(host.getOsFamily()).thenReturn("unix"); } private void setUpContainer() { @@ -170,35 +165,14 @@ public void throwListenerException() throws FileCopierException { String destination = "/tmp/file.txt"; subject.copyFile(executionContext, file, node, destination); } - -// @Test(expected = FileCopierException.class) -// public void throwListenerExceptionIfInterrupted() throws FileCopierException, InterruptedException, IOException { -// this.setUpContainer(); -// File file = new File( -// Objects.requireNonNull(getClass().getClassLoader().getResource("stack.json")).getFile() -// ); -// doThrow(new InterruptedException()).when(listener).putFile(anyString(), anyString(), anyString(), eq(file), anyString()); -// RancherFileCopier subject = new RancherFileCopier(listener); -// String destination = "/tmp/file.txt"; -// subject.copyFile(executionContext, file, node, destination); -// -// } - + @Test public void testCopyFileToContainerNoPath() throws FileCopierException, IOException, InterruptedException { this.setUpContainer(); -// when(rundeckProject.hasProperty("project.project.file-copy-destination-dir")).thenReturn(true); -// when(rundeckProject.getProperty("project.project.file-copy-destination-dir")).thenReturn("/tmp"); -// -// when(propertyLookup.hasProperty("framework.framework.file-copy-destination-dir")).thenReturn(true); -// when(propertyLookup.getProperty("framework.framework.file-copy-destination-dir")).thenReturn("/tmp"); - when(framework.getPropertyLookup()).thenReturn(propertyLookup); when(framework.getFrameworkProjectMgr()).thenReturn(projectManager); -// when(projectManager.getFrameworkProject(anyString())).thenReturn(rundeckProject); - RancherFileCopier subject = new RancherFileCopier(listener); File file = new File( Objects.requireNonNull(getClass().getClassLoader().getResource("stack.json")).getFile() diff --git a/src/test/java/com/bioraft/rundeck/rancher/RancherLaunchConfigTest.java b/src/test/java/com/bioraft/rundeck/rancher/RancherLaunchConfigTest.java index c83b006..1fd15be 100644 --- a/src/test/java/com/bioraft/rundeck/rancher/RancherLaunchConfigTest.java +++ b/src/test/java/com/bioraft/rundeck/rancher/RancherLaunchConfigTest.java @@ -5,6 +5,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; @@ -16,8 +17,7 @@ import java.io.InputStreamReader; import java.util.Iterator; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.*; import static org.mockito.Mockito.*; public class RancherLaunchConfigTest { @@ -30,11 +30,19 @@ public class RancherLaunchConfigTest { @Mock PluginLogger logger; + private AutoCloseable closeable; + @Before public void setUp() throws Exception { - reference = (ObjectNode) readFromInputStream(getResourceStream()); - objectNode = (ObjectNode) readFromInputStream(getResourceStream()); - MockitoAnnotations.openMocks(this); + try (AutoCloseable closeable = MockitoAnnotations.openMocks(this)) { + this.closeable = closeable; + reference = (ObjectNode) readFromInputStream(getResourceStream()); + objectNode = (ObjectNode) readFromInputStream(getResourceStream()); + } + } + @After + public void closeService() throws Exception { + this.closeable.close(); } @Test @@ -109,25 +117,34 @@ public void removeNullField() throws NodeStepException { assertEquals("logConfig", text); } - @Test(expected = NodeStepException.class) + @Test /* * If nothing is changed, the launchConfig should be unchanged. */ public void removeInvalidField() throws NodeStepException { verify(logger,never()).log(anyInt(), anyString()); RancherLaunchConfig rancherLaunchConfig = new RancherLaunchConfig(name, objectNode, logger); - rancherLaunchConfig.removeField("environment", "[\"NoSuchValue\",]"); - assertEquals(reference, rancherLaunchConfig.update()); + try { + rancherLaunchConfig.removeField("environment", "[\"NoSuchValue\",]"); + fail(); + } catch (NodeStepException e) { + assertEquals(reference, rancherLaunchConfig.update()); + } } - @Test(expected = NodeStepException.class) + @Test /* * If nothing is changed, the launchConfig should be unchanged. */ - public void addInvalidField() throws NodeStepException { + public void addInvalidField() { verify(logger,never()).log(anyInt(), anyString()); RancherLaunchConfig rancherLaunchConfig = new RancherLaunchConfig(name, objectNode, logger); - rancherLaunchConfig.setField("NoSuchField", "NoSuchValue"); + try { + rancherLaunchConfig.setField("NoSuchField", "NoSuchValue"); + fail(); + } catch (NodeStepException e) { + assertTrue(true); + } } @Test @@ -200,7 +217,7 @@ public void updateMounts() throws NodeStepException { ObjectNode result; int newCount; - rancherLaunchConfig.setDataVolumes("\"/source1:/mountpoint1\""); + rancherLaunchConfig.setDataVolumes("\"/source1:/mountPoint1\""); result = rancherLaunchConfig.update(); elements = result.path("dataVolumes").elements(); newCount = 0; @@ -211,7 +228,7 @@ public void updateMounts() throws NodeStepException { assertEquals(originalCount, newCount); assertEquals(result, reference); - String changedMount = "/source1:/mountpoint1:ro"; + String changedMount = "/source1:/mountPoint1:ro"; rancherLaunchConfig.setDataVolumes("[\"" + changedMount + "\"]"); result = rancherLaunchConfig.update(); elements = result.path("dataVolumes").elements(); @@ -224,7 +241,7 @@ public void updateMounts() throws NodeStepException { assertEquals(originalCount, newCount); assertNotEquals(result, reference); - String newMount = "/source3:/mountpoint3"; + String newMount = "/source3:/mountPoint3"; rancherLaunchConfig.setDataVolumes("[\"" + newMount + "\"]"); result = rancherLaunchConfig.update(); elements = result.path("dataVolumes").elements(); @@ -242,15 +259,20 @@ public void updateMounts() throws NodeStepException { assertEquals(originalCount + 1, newCount); } - @Test(expected = NodeStepException.class) + @Test /* * If nothing is changed, the launchConfig should be unchanged. */ - public void invalidDataVolumes() throws NodeStepException { + public void invalidDataVolumes() { verify(logger,never()).log(anyInt(), anyString()); RancherLaunchConfig rancherLaunchConfig = new RancherLaunchConfig(name, objectNode, logger); rancherLaunchConfig.setDataVolumes("[\"NoSuchValue\",]"); - assertEquals(reference, rancherLaunchConfig.update()); + try { + assertEquals(reference, rancherLaunchConfig.update()); + fail(); + } catch (NodeStepException e) { + assertTrue(true); + } } @Test @@ -262,7 +284,7 @@ public void hasNullDataVolumes() throws NodeStepException { objectNode.put("dataVolumes", (Short) null); verify(logger,never()).log(anyInt(), anyString()); RancherLaunchConfig rancherLaunchConfig = new RancherLaunchConfig(name, objectNode, logger); - rancherLaunchConfig.setDataVolumes("[\"/source1:/mountpoint1\"]"); + rancherLaunchConfig.setDataVolumes("[\"/source1:/mountPoint1\"]"); assertEquals(reference, rancherLaunchConfig.update()); } @@ -274,7 +296,7 @@ public void hasNoDataVolumes() throws NodeStepException { objectNode.remove("dataVolumes"); verify(logger,never()).log(anyInt(), anyString()); RancherLaunchConfig rancherLaunchConfig = new RancherLaunchConfig(name, objectNode, logger); - rancherLaunchConfig.setDataVolumes("[\"/source1:/mountpoint1\"]"); + rancherLaunchConfig.setDataVolumes("[\"/source1:/mountPoint1\"]"); assertEquals(reference, rancherLaunchConfig.update()); } @@ -285,7 +307,7 @@ public void hasNoDataVolumes() throws NodeStepException { public void addSameSecrets() throws NodeStepException { verify(logger,never()).log(anyInt(), anyString()); RancherLaunchConfig rancherLaunchConfig = new RancherLaunchConfig(name, objectNode, logger); - rancherLaunchConfig.setSecrets("1se2"); + rancherLaunchConfig.setSecrets("1se2", ""); assertEquals(reference, rancherLaunchConfig.update()); } @@ -296,7 +318,7 @@ public void addSameSecrets() throws NodeStepException { public void addBothSameSecrets() throws NodeStepException { verify(logger,never()).log(anyInt(), anyString()); RancherLaunchConfig rancherLaunchConfig = new RancherLaunchConfig(name, objectNode, logger); - rancherLaunchConfig.setSecrets("1se1,1se2"); + rancherLaunchConfig.setSecrets("1se1,1se2", ""); assertEquals(reference, rancherLaunchConfig.update()); } @@ -308,7 +330,7 @@ public void addHasNoSecrets() throws NodeStepException { objectNode.remove("secrets"); verify(logger,never()).log(anyInt(), anyString()); RancherLaunchConfig rancherLaunchConfig = new RancherLaunchConfig(name, objectNode, logger); - rancherLaunchConfig.setSecrets("1se1,1se2"); + rancherLaunchConfig.setSecrets("1se1,1se2", ""); assertEquals(reference, rancherLaunchConfig.update()); } @@ -321,7 +343,7 @@ public void addHasNullSecrets() throws NodeStepException { objectNode.put("secrets", (Short) null); verify(logger,never()).log(anyInt(), anyString()); RancherLaunchConfig rancherLaunchConfig = new RancherLaunchConfig(name, objectNode, logger); - rancherLaunchConfig.setSecrets("1se1,1se2"); + rancherLaunchConfig.setSecrets("1se1,1se2", ""); assertEquals(reference, rancherLaunchConfig.update()); } @@ -333,7 +355,19 @@ public void addHasEmptySecrets() throws NodeStepException { objectNode.putArray("secrets"); verify(logger,never()).log(anyInt(), anyString()); RancherLaunchConfig rancherLaunchConfig = new RancherLaunchConfig(name, objectNode, logger); - rancherLaunchConfig.setSecrets("1se1,1se2"); + rancherLaunchConfig.setSecrets("1se1,1se2", ""); + assertEquals(reference, rancherLaunchConfig.update()); + } + + @Test + /* + * If nothing is changed, the launchConfig should be unchanged. + */ + public void addAndRemoveSecrets() throws NodeStepException { + objectNode.putArray("secrets"); + verify(logger,never()).log(anyInt(), anyString()); + RancherLaunchConfig rancherLaunchConfig = new RancherLaunchConfig(name, objectNode, logger); + rancherLaunchConfig.setSecrets("1se1,1se2,1se3", "1se3,1se4"); assertEquals(reference, rancherLaunchConfig.update()); } diff --git a/src/test/java/com/bioraft/rundeck/rancher/RancherManageServiceTest.java b/src/test/java/com/bioraft/rundeck/rancher/RancherManageServiceTest.java index 9e64e6e..bed950d 100644 --- a/src/test/java/com/bioraft/rundeck/rancher/RancherManageServiceTest.java +++ b/src/test/java/com/bioraft/rundeck/rancher/RancherManageServiceTest.java @@ -27,8 +27,7 @@ import java.util.stream.Stream; import static com.bioraft.rundeck.rancher.Constants.*; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.*; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -162,26 +161,22 @@ public void testPostFailure() throws IOException, NodeStepException { verify(client, times(1)).post(any(), eq("")); } - @Test(expected = NodeStepException.class) - public void testNoKey() throws NodeStepException { + @Test + public void testNoKey() { cfg.put("action", "restart"); map.remove(CONFIG_ACCESSKEY_PATH); - // ObjectMapper mapper = new ObjectMapper(); - // String text = readFromInputStream(getResourceStream("service.json")); - // ObjectNode json1 = (ObjectNode) mapper.readTree(text); - // when(client.get(any())).thenReturn(json1); try { RancherManageService subject = new RancherManageService(client); subject.executeNodeStep(ctx, cfg, node); + fail(); } catch (NodeStepException e) { assertEquals("Could not get secret storage path", e.getMessage()); - throw e; } } - @Test(expected = NodeStepException.class) - public void testUnsupportedAction() throws IOException, NodeStepException { + @Test + public void testUnsupportedAction() throws IOException { cfg.put("action", "unsupported"); ObjectMapper mapper = new ObjectMapper(); @@ -193,14 +188,14 @@ public void testUnsupportedAction() throws IOException, NodeStepException { try { RancherManageService subject = new RancherManageService(client); subject.executeNodeStep(ctx, cfg, node); + fail(); } catch (NodeStepException e) { assertEquals("Invalid action: unsupported", e.getMessage()); - throw e; } } - @Test(expected = NodeStepException.class) - public void testEmptyUrl() throws IOException, NodeStepException { + @Test + public void testEmptyUrl() throws IOException { cfg.put("action", "restart"); ObjectMapper mapper = new ObjectMapper(); @@ -214,14 +209,14 @@ public void testEmptyUrl() throws IOException, NodeStepException { try { RancherManageService subject = new RancherManageService(client); subject.executeNodeStep(ctx, cfg, node); + fail(); } catch (NodeStepException e) { assertEquals("No restart URL found", e.getMessage()); - throw e; } } - @Test(expected = NodeStepException.class) - public void testNoServiceDefinition() throws IOException, NodeStepException { + @Test + public void testNoServiceDefinition() throws IOException { cfg.put("action", "restart"); when(client.get(any())).thenThrow(new IOException()); @@ -229,14 +224,14 @@ public void testNoServiceDefinition() throws IOException, NodeStepException { try { RancherManageService subject = new RancherManageService(client); subject.executeNodeStep(ctx, cfg, node); + fail(); } catch (NodeStepException e) { assertEquals("Could not get service definition", e.getMessage()); - throw e; } } - @Test(expected = NodeStepException.class) - public void testActivateAlreadyActive() throws IOException, NodeStepException { + @Test + public void testActivateAlreadyActive() throws IOException { map.put("type", "container"); cfg.put("action", "activate"); @@ -249,14 +244,14 @@ public void testActivateAlreadyActive() throws IOException, NodeStepException { try { RancherManageService subject = new RancherManageService(client); subject.executeNodeStep(ctx, cfg, node); + fail(); } catch (NodeStepException e) { assertEquals("Service state is already active", e.getMessage()); - throw e; } } - @Test(expected = NodeStepException.class) - public void testDeactivateAlreadyInactive() throws IOException, NodeStepException { + @Test + public void testDeactivateAlreadyInactive() throws IOException { cfg.put("action", "deactivate"); ObjectMapper mapper = new ObjectMapper(); @@ -269,9 +264,9 @@ public void testDeactivateAlreadyInactive() throws IOException, NodeStepExceptio try { RancherManageService subject = new RancherManageService(client); subject.executeNodeStep(ctx, cfg, node); + fail(); } catch (NodeStepException e) { assertEquals("Service state must be running, was inactive", e.getMessage()); - throw e; } } diff --git a/src/test/java/com/bioraft/rundeck/rancher/RancherWebSocketListenerTest.java b/src/test/java/com/bioraft/rundeck/rancher/RancherWebSocketListenerTest.java index 0a97f2d..641739f 100644 --- a/src/test/java/com/bioraft/rundeck/rancher/RancherWebSocketListenerTest.java +++ b/src/test/java/com/bioraft/rundeck/rancher/RancherWebSocketListenerTest.java @@ -77,27 +77,6 @@ public void testRunJob() throws InterruptedException, IOException { // verify(listener, times(0)).log(anyInt(), anyString()); } -// @Test(expected = IOException.class) -// public void throwExceptionWhenTokenFails() throws IOException, InterruptedException { -// String url = mockWebServer.url("/v2-beta/").toString(); -// String accessKey = "access"; -// String secretKey = "secret"; -// String[] command = {"ls"}; -// String temp = ""; -// int timeout = 1; -// MockResponse mockedResponse = new MockResponse(); -// mockedResponse.setResponseCode(200); -// mockedResponse.setBody(""); -// mockWebServer.enqueue(mockedResponse); -// MockResponse upgrade = new MockResponse() -// .setStatus("HTTP/1.1 101 Switching Protocols") -// .setHeader("Connection", "Upgrade") -// .setHeader("Upgrade", "websocket") -// .setHeader("Sec-WebSocket-Accept", "null"); -// mockWebServer.enqueue(upgrade); -// RancherWebSocketListener.runJob(url, accessKey, secretKey, command, listener, temp, timeout); -// } - @Test(expected = IOException.class) public void throwExceptionWhenTokenInvalid() throws IOException, InterruptedException { String url = mockWebServer.url("/v2-beta/").toString(); diff --git a/src/test/resources/launchConfig.json b/src/test/resources/launchConfig.json index 783baa3..cc6ccfa 100644 --- a/src/test/resources/launchConfig.json +++ b/src/test/resources/launchConfig.json @@ -1,7 +1,7 @@ { "type": "launchConfig", "dataVolumes": [ - "/source1:/mountpoint1" + "/source1:/mountPoint1" ], "environment": { "DOMAIN": "my-site.development.example.com",