diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6fcca0d0..58770f2e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -41,9 +41,12 @@ An essential part of getting your change through is to make sure all existing te #### Prerequisites * Maven to build and run tests. -* A GCP project to test on with the compute engine API enabled with all relevant permissions enabled, especially billing. Running integration tests will also incur billing. +* A GCP project to test on with the compute engine API enabled with all relevant permissions + enabled, especially billing. Running integration tests will also incur billing. * For development, IntelliJ is recommended. -* **For Windows Images/VM's**, have Java and OpenSSH pre-installed. We have suggested startup-scripts for installing both if you do not want to pre-install, but pre-installing is advised. +* **For Windows Images/VM's**, have Java and OpenSSH pre-installed. We have suggested + startup-scripts for installing both if you do not want to pre-install, + but pre-installing is advised. #### Running the tests @@ -58,12 +61,16 @@ mvn test ``` ##### Integration Tests -* The following environment variables are required to run the integration tests. 3. and 4. are only required when running a windows integration test. +* The following environment variables are required to run the integration tests. 5, 6, and 7 are + only required when running a windows integration test. 1. GOOGLE_PROJECT_ID 1. GOOGLE_CREDENTIALS +1. GOOGLE_REGION +1. GOOGLE_ZONE 1. GOOGLE_BOOT_DISK_PROJECT_ID 1. GOOGLE_BOOT_DISK_IMAGE_NAME +1. GOOGLE_JENKINS_PASSWORD * Run the following: ``` @@ -71,23 +78,18 @@ mvn verify ``` ###### Windows Integration Test -* By default, in the pom.xml file, exclude running the Windows Integration test unless a windows-related change was made since it will require a windows image built with Packer. -* Exclusion is in these couple of lines between the excludes tags: -``` - - true - false - ... - - **/ComputeEngineCloudWindowsIT.java - **/ConfigAsCodeWindowsIT.java - ... - - +* By default, the integration tests only use linux based agents for testing. If you make a + windows-related change, or otherwise want to test that a change still works for windows agents, + run the tests with the flag `-Dit.windows=true` like this: +```bash +mvn verify -Dit.windows=true ``` -* If you do make a **windows-related change**, remove the windows exclusions temporarily and run the -integration test with mvn verify, +Make sure you have these extra environment variables configured: * GOOGLE_BOOT_DISK_PROJECT_ID will be the same as your project id. - * GOOGLE_BOOT_DISK_IMAGE_NAME will be the name of the image you created using packer in google cloud console. - * More information on building your baseline windows image can be found [here](WINDOWS.md) and an example file can be found [here](windows-it-install.ps1). \ No newline at end of file + * GOOGLE_BOOT_DISK_IMAGE_NAME will be the name of the image you created using packer in Google + cloud console. + * GOOGLE_JENKINS_PASSWORD will be the password you set when creating the image with packer, used + for password based ssh authentication. + * More information on building your baseline windows image can be found [here](WINDOWS.md) + and an example powershell script for setup can be found [here](windows-it-install.ps1). \ No newline at end of file diff --git a/Jenkinsfile b/Jenkinsfile.linux similarity index 92% rename from Jenkinsfile rename to Jenkinsfile.linux index 078f7ba9..5dc4b996 100644 --- a/Jenkinsfile +++ b/Jenkinsfile.linux @@ -28,7 +28,8 @@ pipeline { GOOGLE_ZONE = "${GCE_IT_ZONE}" GOOGLE_SA_NAME = "${GCE_IT_SA}" BUILD_ARTIFACTS_BUCKET = "${GCE_IT_BUCKET}" - BUILD_ARTIFACTS = "build-${BRANCH_NAME}-${BUILD_ID}.tar.gz" + CLEAN_BRANCH_NAME = "${BRANCH_NAME}".replaceAll("[/&;<>|\\]]", "_") + BUILD_ARTIFACTS = "build-${CLEAN_BRANCH_NAME}-${BUILD_ID}.tar.gz" } stages { diff --git a/Jenkinsfile.windows b/Jenkinsfile.windows new file mode 100644 index 00000000..479812e0 --- /dev/null +++ b/Jenkinsfile.windows @@ -0,0 +1,60 @@ +/* + * Copyright 2019 Google Inc. + * + * 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. + */ + +// Windows Agent IT pipeline, run manually. +pipeline { + agent { + kubernetes { + cloud 'kubernetes' + label 'maven-pod' + yamlFile 'jenkins/maven-pod.yaml' + } + } + + environment { + GOOGLE_PROJECT_ID = "${GCE_IT_PROJECT_ID}" + GOOGLE_REGION = "${GCE_IT_REGION}" + GOOGLE_ZONE = "${GCE_IT_ZONE}" + GOOGLE_SA_NAME = "${GCE_IT_SA}" + GOOGLE_BOOT_DISK_PROJECT_ID = "${GCE_WINDOWS_IT_IMAGE_PROJECT}" + GOOGLE_BOOT_DISK_IMAGE_NAME = "${GCE_WINDOWS_IT_IMAGE_NAME}" + BUILD_ARTIFACTS_BUCKET = "${GCE_IT_BUCKET}" + CLEAN_BRANCH_NAME = "${BRANCH_NAME}".replaceAll("[/&;<>|\\]]", "_") + BUILD_ARTIFACTS = "windows-${CLEAN_BRANCH_NAME}-${BUILD_ID}.tar.gz" + } + + stages { + stage("Build and test") { + steps { + container('maven') { + withCredentials([[$class: 'StringBinding', credentialsId: env.GCE_IT_CRED_ID, variable: 'GOOGLE_CREDENTIALS'], + [$class: 'StringBinding', credentialsId: env.GCE_WINDOWS_IT_JENKINS_PASSWORD, variable: 'GOOGLE_JENKINS_PASSWORD']]) { + catchError { + // build + sh "mvn clean package -ntp" + + // run tests + sh "mvn verify -ntp -Dit.windows=true" + } + + sh "jenkins/saveAndCompress.sh" + step([$class: 'ClassicUploadStep', credentialsId: env.GCE_BUCKET_CRED_ID, bucket: "gs://${BUILD_ARTIFACTS_BUCKET}", pattern: env.BUILD_ARTIFACTS]) + } + } + } + } + } +} diff --git a/pom.xml b/pom.xml index 8d608db3..90a9b625 100644 --- a/pom.xml +++ b/pom.xml @@ -73,8 +73,11 @@ 1.8 1.8 ${skipTests} + false 1.14 + 1.3 10 + balanced @@ -172,6 +175,12 @@ ${configuration-as-code.version} test + + org.jenkins-ci.plugins + powershell + ${powershell.version} + test + @@ -262,14 +271,13 @@ 0.5 100 1.0 + ${it.windows} - **/ComputeEngineCloudWindowsIT.java - **/ConfigAsCodeWindowsIT.java **/ITUtil.java -Djenkins.test.timeout=${jenkinsRuleTimeout} - balanced + ${it.runOrder} @@ -332,5 +340,18 @@ 4 + + windows-agents + + + it.windows + true + + + + 8 + reversealphabetical + + diff --git a/src/main/java/com/google/jenkins/plugins/computeengine/ComputeEngineComputer.java b/src/main/java/com/google/jenkins/plugins/computeengine/ComputeEngineComputer.java index 181b29d1..8ba56e5c 100644 --- a/src/main/java/com/google/jenkins/plugins/computeengine/ComputeEngineComputer.java +++ b/src/main/java/com/google/jenkins/plugins/computeengine/ComputeEngineComputer.java @@ -19,14 +19,12 @@ import com.google.api.services.compute.model.Instance; import com.google.api.services.compute.model.Scheduling; import hudson.model.Executor; -import hudson.model.Result; import hudson.model.TaskListener; import hudson.slaves.AbstractCloudComputer; import java.io.IOException; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.logging.Level; -import jenkins.model.CauseOfInterruption; import lombok.extern.java.Log; import org.kohsuke.stapler.DataBoundSetter; import org.kohsuke.stapler.HttpRedirect; @@ -76,14 +74,7 @@ private Boolean getPreemptedStatus(TaskListener listener, String nodeName) { private void interruptExecutor(Executor executor, String nodeName) { log.log(Level.INFO, "Terminating executor " + executor + " node " + nodeName); - executor.interrupt( - Result.FAILURE, - new CauseOfInterruption() { - @Override - public String getShortDescription() { - return "Instance " + nodeName + " was preempted"; - } - }); + executor.abortResult(); } /** diff --git a/src/main/java/com/google/jenkins/plugins/computeengine/ComputeEngineLinuxLauncher.java b/src/main/java/com/google/jenkins/plugins/computeengine/ComputeEngineLinuxLauncher.java index f3d2b793..eb7b52da 100644 --- a/src/main/java/com/google/jenkins/plugins/computeengine/ComputeEngineLinuxLauncher.java +++ b/src/main/java/com/google/jenkins/plugins/computeengine/ComputeEngineLinuxLauncher.java @@ -51,27 +51,18 @@ protected Optional setupConnection( return Optional.empty(); } - Connection cleanupConn; GoogleKeyPair kp = node.getSSHKeyPair().get(); - boolean isBootstrapped = bootstrap(kp, computer, listener); - if (isBootstrapped) { - // connect fresh as ROOT - logInfo(computer, listener, "connect fresh as root"); - cleanupConn = connectToSsh(computer, listener); - if (!cleanupConn.authenticateWithPublicKey( - node.getSshUser(), kp.getPrivateKey().toCharArray(), "")) { - logWarning(computer, listener, "Authentication failed"); - return Optional.empty(); // failed to connect - } - } else { + Optional bootstrapConn = bootstrap(kp, computer, listener); + if (!bootstrapConn.isPresent()) { logWarning(computer, listener, "bootstrapresult failed"); return Optional.empty(); } - return Optional.of(cleanupConn); + return bootstrapConn; } - private boolean bootstrap(GoogleKeyPair kp, ComputeEngineComputer computer, TaskListener listener) + private Optional bootstrap( + GoogleKeyPair kp, ComputeEngineComputer computer, TaskListener listener) throws IOException, Exception { // TODO: better exceptions logInfo(computer, listener, "bootstrap"); ComputeEngineInstance node = computer.getNode(); @@ -105,14 +96,16 @@ private boolean bootstrap(GoogleKeyPair kp, ComputeEngineComputer computer, Task } if (!isAuthenticated) { logWarning(computer, listener, "Authentication failed"); - return false; + return Optional.empty(); } - } finally { + } catch (Exception e) { + logException(computer, listener, "Failed to authenticate with exception: ", e); if (bootstrapConn != null) { bootstrapConn.close(); } + return Optional.empty(); } - return true; + return Optional.of(bootstrapConn); } @Override diff --git a/src/main/java/com/google/jenkins/plugins/computeengine/ComputeEngineWindowsLauncher.java b/src/main/java/com/google/jenkins/plugins/computeengine/ComputeEngineWindowsLauncher.java index 9f775325..7c50e2ca 100644 --- a/src/main/java/com/google/jenkins/plugins/computeengine/ComputeEngineWindowsLauncher.java +++ b/src/main/java/com/google/jenkins/plugins/computeengine/ComputeEngineWindowsLauncher.java @@ -16,6 +16,7 @@ import com.cloudbees.jenkins.plugins.sshcredentials.SSHAuthenticator; import com.google.api.services.compute.model.Operation; +import com.google.common.base.Preconditions; import com.trilead.ssh2.Connection; import hudson.model.TaskListener; import java.io.IOException; @@ -51,21 +52,12 @@ protected Optional setupConnection( logWarning(computer, listener, "Non-windows node provided"); return Optional.empty(); } - boolean isBootstrapped = bootstrap(computer, listener); - if (!isBootstrapped) { + Optional bootstrapConn = bootstrap(computer, listener); + if (!bootstrapConn.isPresent()) { logWarning(computer, listener, "bootstrapresult failed"); return Optional.empty(); } - - // connect fresh as ROOT - logInfo(computer, listener, "connect fresh as root"); - Connection cleanupConn = connectToSsh(computer, listener); - if (!authenticateSSH(node.getSshUser(), node.getWindowsConfig(), cleanupConn, listener)) { - logWarning(computer, listener, "Authentication failed"); - return Optional.empty(); // failed to connect - } - - return Optional.of(cleanupConn); + return bootstrapConn; } private boolean authenticateSSH( @@ -87,11 +79,8 @@ private boolean authenticateSSH( return isAuthenticated; } - private boolean bootstrap(ComputeEngineComputer computer, TaskListener listener) - throws IOException, Exception { // TODO(evanbrown): better exceptions - if (computer == null) { - throw new IllegalArgumentException("A null ComputeEngineComputer was provided"); - } + private Optional bootstrap(ComputeEngineComputer computer, TaskListener listener) { + Preconditions.checkNotNull(computer, "A null ComputeEngineComputer was provided"); logInfo(computer, listener, "bootstrap"); ComputeEngineInstance node = computer.getNode(); @@ -125,14 +114,16 @@ private boolean bootstrap(ComputeEngineComputer computer, TaskListener listener) } if (!isAuthenticated) { logWarning(computer, listener, "Authentication failed"); - return false; + return Optional.empty(); } - } finally { + } catch (Exception e) { + logException(computer, listener, "Failed to authenticate with exception: ", e); if (bootstrapConn != null) { bootstrapConn.close(); } + return Optional.empty(); } - return true; + return Optional.ofNullable(bootstrapConn); } @Override diff --git a/src/main/java/com/google/jenkins/plugins/computeengine/PreemptedCheckCallable.java b/src/main/java/com/google/jenkins/plugins/computeengine/PreemptedCheckCallable.java index 8cd37945..dc74a9db 100644 --- a/src/main/java/com/google/jenkins/plugins/computeengine/PreemptedCheckCallable.java +++ b/src/main/java/com/google/jenkins/plugins/computeengine/PreemptedCheckCallable.java @@ -33,7 +33,7 @@ */ final class PreemptedCheckCallable extends MasterToSlaveCallable { private static final String METADATA_SERVER_URL = - "http://metadata.google.internal/computeMetadata/v1/instance/preempted?wait_for_change=true"; + "http://metadata.google.internal/computeMetadata/v1/instance/preempted?wait_for_change=%s"; private final TaskListener listener; @@ -58,7 +58,18 @@ final class PreemptedCheckCallable extends MasterToSlaveCallable label = - getLabel(ComputeEngineCloudMultipeMatchingConfigurationsIT.class); + getLabel(ComputeEngineCloudMultipleMatchingConfigurationsIT.class); private static Collection planned; @BeforeClass @@ -80,7 +81,6 @@ public static void init() throws Exception { InstanceConfiguration configuration1 = instanceConfigurationBuilder() - .startupScript(DEB_JAVA_STARTUP_SCRIPT) .numExecutorsStr(NUM_EXECUTORS) .labels(LABEL) .template(NULL_TEMPLATE) @@ -90,7 +90,6 @@ public static void init() throws Exception { InstanceConfiguration configuration2 = instanceConfigurationBuilder() - .startupScript(DEB_JAVA_STARTUP_SCRIPT) .numExecutorsStr(NUM_EXECUTORS) .labels(LABEL) .template(NULL_TEMPLATE) @@ -109,21 +108,24 @@ public static void teardown() throws IOException { } @Test - public void testMultipleLabelsProvisionedWithLabels() throws Exception { + public void testMultipleLabelsProvisionedWithLabels() { assertEquals(2, planned.size()); final Iterator iterator = planned.iterator(); - PlannedNode plannedNode = iterator.next(); - checkOneNode(plannedNode, DESC_1); - plannedNode = iterator.next(); - checkOneNode(plannedNode, DESC_2); + PlannedNode firstNode = iterator.next(); + PlannedNode secondNode = iterator.next(); + if (checkOneNode(firstNode, DESC_1)) { + assertTrue(checkOneNode(secondNode, DESC_2)); + } else if (checkOneNode(secondNode, DESC_1)) { + assertTrue(checkOneNode(firstNode, DESC_2)); + } else { + fail("Nodes did not have expected values"); + } } - private void checkOneNode(PlannedNode plannedNode, String desc) - throws InterruptedException, java.util.concurrent.ExecutionException { + private boolean checkOneNode(PlannedNode plannedNode, String desc) { String name = plannedNode.displayName; - plannedNode.future.get(); Node node = jenkinsRule.jenkins.getNode(name); - assertEquals(desc, node.getNodeDescription()); + return desc.equals(node.getNodeDescription()); } } diff --git a/src/test/java/com/google/jenkins/plugins/computeengine/integration/ComputeEngineCloudNoSnapshotCreatedIT.java b/src/test/java/com/google/jenkins/plugins/computeengine/integration/ComputeEngineCloudNoSnapshotCreatedIT.java index 8fc80963..857d1294 100644 --- a/src/test/java/com/google/jenkins/plugins/computeengine/integration/ComputeEngineCloudNoSnapshotCreatedIT.java +++ b/src/test/java/com/google/jenkins/plugins/computeengine/integration/ComputeEngineCloudNoSnapshotCreatedIT.java @@ -16,13 +16,14 @@ package com.google.jenkins.plugins.computeengine.integration; -import static com.google.jenkins.plugins.computeengine.integration.ITUtil.DEB_JAVA_STARTUP_SCRIPT; import static com.google.jenkins.plugins.computeengine.integration.ITUtil.NULL_TEMPLATE; import static com.google.jenkins.plugins.computeengine.integration.ITUtil.NUM_EXECUTORS; import static com.google.jenkins.plugins.computeengine.integration.ITUtil.PROJECT_ID; import static com.google.jenkins.plugins.computeengine.integration.ITUtil.SNAPSHOT_LABEL; +import static com.google.jenkins.plugins.computeengine.integration.ITUtil.SNAPSHOT_TIMEOUT; import static com.google.jenkins.plugins.computeengine.integration.ITUtil.TEST_TIMEOUT_MULTIPLIER; import static com.google.jenkins.plugins.computeengine.integration.ITUtil.ZONE; +import static com.google.jenkins.plugins.computeengine.integration.ITUtil.execute; import static com.google.jenkins.plugins.computeengine.integration.ITUtil.getLabel; import static com.google.jenkins.plugins.computeengine.integration.ITUtil.initClient; import static com.google.jenkins.plugins.computeengine.integration.ITUtil.initCloud; @@ -40,7 +41,6 @@ import hudson.model.Node; import hudson.model.labels.LabelAtom; import hudson.tasks.Builder; -import hudson.tasks.Shell; import java.io.IOException; import java.util.Map; import java.util.concurrent.TimeUnit; @@ -80,7 +80,6 @@ public static void init() throws Exception { InstanceConfiguration instanceConfiguration = ITUtil.instanceConfigurationBuilder() - .startupScript(DEB_JAVA_STARTUP_SCRIPT) .numExecutorsStr(NUM_EXECUTORS) .labels(SNAPSHOT_LABEL) .oneShot(true) @@ -96,7 +95,7 @@ public static void init() throws Exception { .isCreateSnapshot()); FreeStyleProject project = jenkinsRule.createFreeStyleProject(); - Builder step = new Shell("echo works"); + Builder step = execute(Commands.ECHO, "works"); project.getBuildersList().add(step); project.setAssignedLabel(new LabelAtom(SNAPSHOT_LABEL)); @@ -125,7 +124,7 @@ public void testNoSnapshotCreatedInstanceStopping() { public void testNoSnapshotCreatedSnapshotNull() throws Exception { // Wait for one-shot instance to terminate and create the snapshot Awaitility.await() - .timeout(2, TimeUnit.MINUTES) + .timeout(SNAPSHOT_TIMEOUT, TimeUnit.SECONDS) .pollInterval(10, TimeUnit.SECONDS) .until(() -> jenkinsRule.jenkins.getNode(name) == null); client.getSnapshot(PROJECT_ID, name); diff --git a/src/test/java/com/google/jenkins/plugins/computeengine/integration/ComputeEngineCloudNonStandardJavaIT.java b/src/test/java/com/google/jenkins/plugins/computeengine/integration/ComputeEngineCloudNonStandardJavaIT.java index 90216806..93d8e2eb 100644 --- a/src/test/java/com/google/jenkins/plugins/computeengine/integration/ComputeEngineCloudNonStandardJavaIT.java +++ b/src/test/java/com/google/jenkins/plugins/computeengine/integration/ComputeEngineCloudNonStandardJavaIT.java @@ -28,7 +28,9 @@ import static com.google.jenkins.plugins.computeengine.integration.ITUtil.initCredentials; import static com.google.jenkins.plugins.computeengine.integration.ITUtil.instanceConfigurationBuilder; import static com.google.jenkins.plugins.computeengine.integration.ITUtil.teardownResources; +import static com.google.jenkins.plugins.computeengine.integration.ITUtil.windows; import static org.junit.Assert.assertEquals; +import static org.junit.Assume.assumeFalse; import com.google.api.services.compute.model.Instance; import com.google.cloud.graphite.platforms.plugin.client.ComputeClient; @@ -81,6 +83,7 @@ public class ComputeEngineCloudNonStandardJavaIT { @BeforeClass public static void init() throws Exception { + assumeFalse(windows); log.info("init"); initCredentials(jenkinsRule); ComputeEngineCloud cloud = initCloud(jenkinsRule); diff --git a/src/test/java/com/google/jenkins/plugins/computeengine/integration/ComputeEngineCloudOneShotInstanceIT.java b/src/test/java/com/google/jenkins/plugins/computeengine/integration/ComputeEngineCloudOneShotInstanceIT.java index 7b9e310c..79a8a02c 100644 --- a/src/test/java/com/google/jenkins/plugins/computeengine/integration/ComputeEngineCloudOneShotInstanceIT.java +++ b/src/test/java/com/google/jenkins/plugins/computeengine/integration/ComputeEngineCloudOneShotInstanceIT.java @@ -16,11 +16,11 @@ package com.google.jenkins.plugins.computeengine.integration; -import static com.google.jenkins.plugins.computeengine.integration.ITUtil.DEB_JAVA_STARTUP_SCRIPT; import static com.google.jenkins.plugins.computeengine.integration.ITUtil.LABEL; import static com.google.jenkins.plugins.computeengine.integration.ITUtil.NULL_TEMPLATE; import static com.google.jenkins.plugins.computeengine.integration.ITUtil.NUM_EXECUTORS; import static com.google.jenkins.plugins.computeengine.integration.ITUtil.PROJECT_ID; +import static com.google.jenkins.plugins.computeengine.integration.ITUtil.execute; import static com.google.jenkins.plugins.computeengine.integration.ITUtil.getLabel; import static com.google.jenkins.plugins.computeengine.integration.ITUtil.initClient; import static com.google.jenkins.plugins.computeengine.integration.ITUtil.initCloud; @@ -40,7 +40,6 @@ import hudson.model.Result; import hudson.model.labels.LabelAtom; import hudson.tasks.Builder; -import hudson.tasks.Shell; import java.io.IOException; import java.util.Collections; import java.util.Map; @@ -81,7 +80,6 @@ public static void init() throws Exception { cloud.setConfigurations( ImmutableList.of( instanceConfigurationBuilder() - .startupScript(DEB_JAVA_STARTUP_SCRIPT) .numExecutorsStr(NUM_EXECUTORS) .labels(LABEL) .oneShot(true) @@ -92,13 +90,13 @@ public static void init() throws Exception { jenkinsRule.jenkins.getNodesObject().setNodes(Collections.emptyList()); FreeStyleProject project = jenkinsRule.createFreeStyleProject(); - Builder step = new Shell("echo works"); + Builder step = execute(Commands.ECHO, "works"); project.getBuildersList().add(step); project.setAssignedLabel(new LabelAtom(LABEL)); Future buildFuture = project.scheduleBuild2(0); FreeStyleProject otherProject = jenkinsRule.createFreeStyleProject(); - Builder otherStep = new Shell("echo \"also works\""); + Builder otherStep = execute(Commands.ECHO, "\"also works\""); otherProject.getBuildersList().add(otherStep); otherProject.setAssignedLabel(new LabelAtom(LABEL)); otherBuildFuture = otherProject.scheduleBuild2(0); diff --git a/src/test/java/com/google/jenkins/plugins/computeengine/integration/ComputeEngineCloudRestartPreemptedIT.java b/src/test/java/com/google/jenkins/plugins/computeengine/integration/ComputeEngineCloudRestartPreemptedIT.java index a7012018..c9202535 100644 --- a/src/test/java/com/google/jenkins/plugins/computeengine/integration/ComputeEngineCloudRestartPreemptedIT.java +++ b/src/test/java/com/google/jenkins/plugins/computeengine/integration/ComputeEngineCloudRestartPreemptedIT.java @@ -16,13 +16,13 @@ package com.google.jenkins.plugins.computeengine.integration; -import static com.google.jenkins.plugins.computeengine.integration.ITUtil.DEB_JAVA_STARTUP_SCRIPT; import static com.google.jenkins.plugins.computeengine.integration.ITUtil.LABEL; import static com.google.jenkins.plugins.computeengine.integration.ITUtil.NULL_TEMPLATE; import static com.google.jenkins.plugins.computeengine.integration.ITUtil.NUM_EXECUTORS; import static com.google.jenkins.plugins.computeengine.integration.ITUtil.PROJECT_ID; import static com.google.jenkins.plugins.computeengine.integration.ITUtil.TEST_TIMEOUT_MULTIPLIER; import static com.google.jenkins.plugins.computeengine.integration.ITUtil.ZONE; +import static com.google.jenkins.plugins.computeengine.integration.ITUtil.execute; import static com.google.jenkins.plugins.computeengine.integration.ITUtil.getLabel; import static com.google.jenkins.plugins.computeengine.integration.ITUtil.initClient; import static com.google.jenkins.plugins.computeengine.integration.ITUtil.initCloud; @@ -46,7 +46,6 @@ import hudson.model.queue.QueueTaskFuture; import hudson.slaves.NodeProvisioner.PlannedNode; import hudson.tasks.Builder; -import hudson.tasks.Shell; import java.io.IOException; import java.util.Collection; import java.util.Iterator; @@ -85,7 +84,6 @@ public static void init() throws Exception { InstanceConfiguration configuration = instanceConfigurationBuilder() - .startupScript(DEB_JAVA_STARTUP_SCRIPT) .numExecutorsStr(NUM_EXECUTORS) .labels(LABEL) .template(NULL_TEMPLATE) @@ -115,7 +113,7 @@ public void testIfNodeWasPreempted() throws Exception { assertTrue("Configuration was set as preemptible but saw as not", computer.getPreemptible()); FreeStyleProject project = jenkinsRule.createFreeStyleProject(); - Builder step = new Shell("sleep 60"); + Builder step = execute(Commands.SLEEP, "60"); project.getBuildersList().add(step); project.setAssignedLabel(new LabelAtom(LABEL)); QueueTaskFuture taskFuture = project.scheduleBuild2(0); @@ -131,10 +129,10 @@ public void testIfNodeWasPreempted() throws Exception { assertEquals(FAILURE, freeStyleBuild.getResult()); Awaitility.await() - .timeout(9, TimeUnit.MINUTES) - .until( - () -> - freeStyleBuild.getNextBuild() != null - && freeStyleBuild.getNextBuild().getResult() == SUCCESS); + .timeout(5, TimeUnit.MINUTES) + .until(() -> freeStyleBuild.getNextBuild() != null); + FreeStyleBuild nextBuild = freeStyleBuild.getNextBuild(); + Awaitility.await().timeout(5, TimeUnit.MINUTES).until(() -> nextBuild.getResult() != null); + assertEquals(SUCCESS, nextBuild.getResult()); } } diff --git a/src/test/java/com/google/jenkins/plugins/computeengine/integration/ComputeEngineCloudSnapshotCreatedIT.java b/src/test/java/com/google/jenkins/plugins/computeengine/integration/ComputeEngineCloudSnapshotCreatedIT.java index a8575e07..a24f6b7f 100644 --- a/src/test/java/com/google/jenkins/plugins/computeengine/integration/ComputeEngineCloudSnapshotCreatedIT.java +++ b/src/test/java/com/google/jenkins/plugins/computeengine/integration/ComputeEngineCloudSnapshotCreatedIT.java @@ -16,12 +16,13 @@ package com.google.jenkins.plugins.computeengine.integration; -import static com.google.jenkins.plugins.computeengine.integration.ITUtil.DEB_JAVA_STARTUP_SCRIPT; import static com.google.jenkins.plugins.computeengine.integration.ITUtil.NULL_TEMPLATE; import static com.google.jenkins.plugins.computeengine.integration.ITUtil.NUM_EXECUTORS; import static com.google.jenkins.plugins.computeengine.integration.ITUtil.PROJECT_ID; import static com.google.jenkins.plugins.computeengine.integration.ITUtil.SNAPSHOT_LABEL; +import static com.google.jenkins.plugins.computeengine.integration.ITUtil.SNAPSHOT_TIMEOUT; import static com.google.jenkins.plugins.computeengine.integration.ITUtil.TEST_TIMEOUT_MULTIPLIER; +import static com.google.jenkins.plugins.computeengine.integration.ITUtil.execute; import static com.google.jenkins.plugins.computeengine.integration.ITUtil.getLabel; import static com.google.jenkins.plugins.computeengine.integration.ITUtil.initClient; import static com.google.jenkins.plugins.computeengine.integration.ITUtil.initCloud; @@ -43,7 +44,6 @@ import hudson.model.Result; import hudson.model.labels.LabelAtom; import hudson.tasks.Builder; -import hudson.tasks.Shell; import java.io.IOException; import java.util.Map; import java.util.concurrent.TimeUnit; @@ -64,10 +64,8 @@ public class ComputeEngineCloudSnapshotCreatedIT { private static Logger log = Logger.getLogger(ComputeEngineCloudSnapshotCreatedIT.class.getName()); - private static final int SNAPSHOT_TEST_TIMEOUT = 120; - @ClassRule - public static Timeout timeout = new Timeout(10 * TEST_TIMEOUT_MULTIPLIER, TimeUnit.MINUTES); + public static Timeout timeout = new Timeout(15 * TEST_TIMEOUT_MULTIPLIER, TimeUnit.MINUTES); @ClassRule public static JenkinsRule jenkinsRule = new JenkinsRule(); @@ -84,7 +82,6 @@ public static void init() throws Exception { InstanceConfiguration instanceConfiguration = instanceConfigurationBuilder() - .startupScript(DEB_JAVA_STARTUP_SCRIPT) .numExecutorsStr(NUM_EXECUTORS) .labels(SNAPSHOT_LABEL) .oneShot(true) @@ -100,7 +97,7 @@ public static void init() throws Exception { .isCreateSnapshot()); FreeStyleProject project = jenkinsRule.createFreeStyleProject(); - Builder step = new Shell("exit 1"); + Builder step = execute(Commands.EXIT, "1"); project.getBuildersList().add(step); project.setAssignedLabel(new LabelAtom(SNAPSHOT_LABEL)); @@ -110,7 +107,7 @@ public static void init() throws Exception { // Need time for one-shot instance to terminate and create the snapshot Awaitility.await() - .timeout(SNAPSHOT_TEST_TIMEOUT, TimeUnit.SECONDS) + .timeout(SNAPSHOT_TIMEOUT, TimeUnit.SECONDS) .pollInterval(10, TimeUnit.SECONDS) .until(() -> jenkinsRule.jenkins.getNode(worker.getNodeName()) == null); @@ -139,7 +136,7 @@ public void testSnapshotCreatedStatusReady() { @Test public void testSnapshotCreatedOneShotInstanceDeleted() { Awaitility.await() - .timeout(SNAPSHOT_TEST_TIMEOUT, TimeUnit.SECONDS) + .timeout(SNAPSHOT_TIMEOUT, TimeUnit.SECONDS) .until(() -> client.listInstancesWithLabel(PROJECT_ID, label).isEmpty()); } } diff --git a/src/test/java/com/google/jenkins/plugins/computeengine/integration/ComputeEngineCloudTemplateIT.java b/src/test/java/com/google/jenkins/plugins/computeengine/integration/ComputeEngineCloudTemplateIT.java index a1540275..044ea394 100644 --- a/src/test/java/com/google/jenkins/plugins/computeengine/integration/ComputeEngineCloudTemplateIT.java +++ b/src/test/java/com/google/jenkins/plugins/computeengine/integration/ComputeEngineCloudTemplateIT.java @@ -17,10 +17,11 @@ package com.google.jenkins.plugins.computeengine.integration; import static com.google.cloud.graphite.platforms.plugin.client.util.ClientUtil.nameFromSelfLink; -import static com.google.jenkins.plugins.computeengine.integration.ITUtil.DEB_JAVA_STARTUP_SCRIPT; import static com.google.jenkins.plugins.computeengine.integration.ITUtil.LABEL; import static com.google.jenkins.plugins.computeengine.integration.ITUtil.NUM_EXECUTORS; import static com.google.jenkins.plugins.computeengine.integration.ITUtil.PROJECT_ID; +import static com.google.jenkins.plugins.computeengine.integration.ITUtil.RUN_AS_USER; +import static com.google.jenkins.plugins.computeengine.integration.ITUtil.SSH_PRIVATE_KEY; import static com.google.jenkins.plugins.computeengine.integration.ITUtil.TEST_TIMEOUT_MULTIPLIER; import static com.google.jenkins.plugins.computeengine.integration.ITUtil.ZONE; import static com.google.jenkins.plugins.computeengine.integration.ITUtil.createTemplate; @@ -44,7 +45,6 @@ import com.google.jenkins.plugins.computeengine.ComputeEngineCloud; import com.google.jenkins.plugins.computeengine.InstanceConfiguration; import com.trilead.ssh2.Connection; -import com.trilead.ssh2.ServerHostKeyVerifier; import hudson.model.labels.LabelAtom; import hudson.slaves.NodeProvisioner.PlannedNode; import java.io.IOException; @@ -93,7 +93,6 @@ public static void init() throws Exception { cloud.setConfigurations( ImmutableList.of( instanceConfigurationBuilder() - .startupScript(DEB_JAVA_STARTUP_SCRIPT) .numExecutorsStr(NUM_EXECUTORS) .labels(LABEL) .oneShot(false) @@ -156,18 +155,11 @@ public void testConnectionWithTemplateSshKey() throws IOException, Exception { int port = SSH_PORT; conn = new Connection(host, port); conn.connect( - new ServerHostKeyVerifier() { - public boolean verifyServerHostKey( - String hostname, int port, String serverHostKeyAlgorithm, byte[] serverHostKey) - throws Exception { - return true; - } - }, + (hostname, portnum, serverHostKeyAlgorithm, serverHostKey) -> true, SSH_TIMEOUT, SSH_TIMEOUT); - assertTrue( - conn.authenticateWithPublicKey( - ITUtil.SSH_USER, ITUtil.SSH_PRIVATE_KEY.toCharArray(), "")); + log.info(String.format("Connected to SSH. Authenticating as user %s", RUN_AS_USER)); + assertTrue(conn.authenticateWithPublicKey(RUN_AS_USER, SSH_PRIVATE_KEY.toCharArray(), "")); } finally { if (conn != null) { conn.close(); diff --git a/src/test/java/com/google/jenkins/plugins/computeengine/integration/ComputeEngineCloudTemplateNoGoogleLabelsIT.java b/src/test/java/com/google/jenkins/plugins/computeengine/integration/ComputeEngineCloudTemplateNoGoogleLabelsIT.java index b0bb75ef..97016619 100644 --- a/src/test/java/com/google/jenkins/plugins/computeengine/integration/ComputeEngineCloudTemplateNoGoogleLabelsIT.java +++ b/src/test/java/com/google/jenkins/plugins/computeengine/integration/ComputeEngineCloudTemplateNoGoogleLabelsIT.java @@ -17,7 +17,6 @@ package com.google.jenkins.plugins.computeengine.integration; import static com.google.cloud.graphite.platforms.plugin.client.util.ClientUtil.nameFromSelfLink; -import static com.google.jenkins.plugins.computeengine.integration.ITUtil.DEB_JAVA_STARTUP_SCRIPT; import static com.google.jenkins.plugins.computeengine.integration.ITUtil.LABEL; import static com.google.jenkins.plugins.computeengine.integration.ITUtil.NUM_EXECUTORS; import static com.google.jenkins.plugins.computeengine.integration.ITUtil.PROJECT_ID; @@ -85,7 +84,6 @@ public static void init() throws Exception { cloud.setConfigurations( ImmutableList.of( instanceConfigurationBuilder() - .startupScript(DEB_JAVA_STARTUP_SCRIPT) .numExecutorsStr(NUM_EXECUTORS) .labels(LABEL) .oneShot(false) diff --git a/src/test/java/com/google/jenkins/plugins/computeengine/integration/ComputeEngineCloudWindowsIT.java b/src/test/java/com/google/jenkins/plugins/computeengine/integration/ComputeEngineCloudWindowsIT.java deleted file mode 100644 index 89184e9f..00000000 --- a/src/test/java/com/google/jenkins/plugins/computeengine/integration/ComputeEngineCloudWindowsIT.java +++ /dev/null @@ -1,399 +0,0 @@ -/* - * Copyright 2018 Google LLC - * - * 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 - * - * https://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.google.jenkins.plugins.computeengine.integration; - -import static com.google.jenkins.plugins.computeengine.integration.ITUtil.CLOUD_NAME; -import static com.google.jenkins.plugins.computeengine.integration.ITUtil.LABEL; -import static com.google.jenkins.plugins.computeengine.integration.ITUtil.NULL_TEMPLATE; -import static com.google.jenkins.plugins.computeengine.integration.ITUtil.NUM_EXECUTORS; -import static com.google.jenkins.plugins.computeengine.integration.ITUtil.PROJECT_ID; -import static com.google.jenkins.plugins.computeengine.integration.ITUtil.SNAPSHOT_LABEL; -import static com.google.jenkins.plugins.computeengine.integration.ITUtil.ZONE; -import static com.google.jenkins.plugins.computeengine.integration.ITUtil.initCredentials; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; - -import com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey; -import com.cloudbees.plugins.credentials.Credentials; -import com.cloudbees.plugins.credentials.CredentialsScope; -import com.cloudbees.plugins.credentials.CredentialsStore; -import com.cloudbees.plugins.credentials.SystemCredentialsProvider; -import com.cloudbees.plugins.credentials.common.StandardUsernameCredentials; -import com.cloudbees.plugins.credentials.domains.Domain; -import com.google.api.services.compute.model.Image; -import com.google.api.services.compute.model.Instance; -import com.google.api.services.compute.model.Operation; -import com.google.api.services.compute.model.Snapshot; -import com.google.cloud.graphite.platforms.plugin.client.ClientFactory; -import com.google.cloud.graphite.platforms.plugin.client.ComputeClient; -import com.google.common.collect.ImmutableList; -import com.google.jenkins.plugins.computeengine.ComputeEngineCloud; -import com.google.jenkins.plugins.computeengine.InstanceConfiguration; -import com.google.jenkins.plugins.computeengine.WindowsConfiguration; -import com.google.jenkins.plugins.computeengine.client.ClientUtil; -import com.google.jenkins.plugins.computeengine.ssh.GoogleKeyPair; -import hudson.ProxyConfiguration; -import hudson.model.FreeStyleBuild; -import hudson.model.FreeStyleProject; -import hudson.model.Node; -import hudson.model.Result; -import hudson.model.labels.LabelAtom; -import hudson.slaves.NodeProvisioner; -import hudson.tasks.BatchFile; -import hudson.tasks.Builder; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.logging.LogManager; -import java.util.logging.Logger; -import java.util.logging.SimpleFormatter; -import java.util.logging.StreamHandler; -import jenkins.model.Jenkins; -import org.awaitility.Awaitility; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.ClassRule; -import org.junit.Test; -import org.junit.rules.Timeout; -import org.jvnet.hudson.test.JenkinsRule; - -/** Integration test for launching windows VM. Tests that agents can be created properly. */ -public class ComputeEngineCloudWindowsIT { - private static Logger log = Logger.getLogger(ComputeEngineCloudWindowsIT.class.getName()); - - private static final String BOOT_DISK_SIZE_GB_STR = "50"; - private static final String RUN_AS_USER = "jenkins"; - - private static Map INTEGRATION_LABEL; - - static { - INTEGRATION_LABEL = new HashMap<>(); - INTEGRATION_LABEL.put("integration", "delete"); - } - - private static final String RETENTION_TIME_MINUTES_STR = "600"; - private static final String LAUNCH_TIMEOUT_SECONDS_STR = "3000"; - - private static Logger cloudLogger; - private static Logger clientLogger; - private static StreamHandler sh; - private static ByteArrayOutputStream logOutput; - - private static ComputeClient client; - private static String bootDiskProjectId; - private static String bootDiskImageName; - - private static String publicKey; - private static String windowsPrivateKeyCredentialId; - - @ClassRule public static JenkinsRule r = new JenkinsRule(); - @ClassRule public static Timeout timeout = new Timeout(20, TimeUnit.MINUTES); - - @BeforeClass - public static void init() throws Exception { - log.info("init"); - logOutput = new ByteArrayOutputStream(); - sh = new StreamHandler(logOutput, new SimpleFormatter()); - - bootDiskProjectId = System.getenv("GOOGLE_BOOT_DISK_PROJECT_ID"); - assertNotNull("GOOGLE_BOOT_DISK_PROJECT_ID env var must be set", bootDiskProjectId); - - bootDiskImageName = System.getenv("GOOGLE_BOOT_DISK_IMAGE_NAME"); - assertNotNull("GOOGLE_BOOT_DISK_IMAGE_NAME env var must be set", bootDiskImageName); - - Credentials c = initCredentials(r); - - // Create credentials for SSH - GoogleKeyPair kp = GoogleKeyPair.generate(""); - // Have to reformat since GoogleKeyPair's format is for metadata server and not typical public - // key format - publicKey = kp.getPublicKey().trim().substring(1); - - StandardUsernameCredentials windowsPrivateKeyCredential = - new BasicSSHUserPrivateKey( - CredentialsScope.GLOBAL, - null, - RUN_AS_USER, - new BasicSSHUserPrivateKey.DirectEntryPrivateKeySource(kp.getPrivateKey()), - null, - "integration test private key for windows"); - CredentialsStore store = new SystemCredentialsProvider.ProviderImpl().getStore(r.jenkins); - store.addCredentials(Domain.global(), windowsPrivateKeyCredential); - windowsPrivateKeyCredentialId = windowsPrivateKeyCredential.getId(); - - // Add Cloud plugin - ComputeEngineCloud gcp = new ComputeEngineCloud(CLOUD_NAME, PROJECT_ID, PROJECT_ID, "10"); - - // Capture log output to make sense of most failures - cloudLogger = - LogManager.getLogManager() - .getLogger("com.google.jenkins.plugins.computeengine.ComputeEngineCloud"); - if (cloudLogger != null) cloudLogger.addHandler(sh); - - assertEquals(0, r.jenkins.clouds.size()); - r.jenkins.clouds.add(gcp); - assertEquals(1, r.jenkins.clouds.size()); - - // Get a compute client for out-of-band calls to GCE - ClientFactory clientFactory = ClientUtil.getClientFactory(r.jenkins, PROJECT_ID); - client = clientFactory.computeClient(); - assertNotNull("ComputeClient can not be null", client); - - // Other logging - clientLogger = - LogManager.getLogManager() - .getLogger("com.google.jenkins.plugins.computeengine.ComputeClient"); - if (clientLogger != null) clientLogger.addHandler(sh); - - deleteIntegrationInstances(true); - } - - @AfterClass - public static void teardown() throws Exception { - log.info("teardown"); - deleteIntegrationInstances(false); - sh.close(); - log.info(logOutput.toString()); - } - - @Before - public void before() { - ComputeEngineCloud cloud = (ComputeEngineCloud) r.jenkins.clouds.get(0); - cloud.setConfigurations(new ArrayList<>()); - } - - @After - public void after() throws IOException { - Jenkins jenkins = r.getInstance(); - jenkins.proxy = null; - jenkins.save(); - } - - @Test - public void testGoogleCredentialsCreated() { - List creds = - new SystemCredentialsProvider.ProviderImpl() - .getStore(r.jenkins) - .getCredentials(Domain.global()); - assertEquals(2, creds.size()); - } - - @Test // TODO: Group client tests into their own test class - public void testGetImage() throws Exception { - ComputeEngineCloud cloud = (ComputeEngineCloud) r.jenkins.clouds.get(0); - Image i = cloud.getClient().getImage(this.bootDiskProjectId, this.bootDiskImageName); - assertNotNull(i); - } - - // TODO: JENKINS-56163 need to de-dupe integration tests - @Test(timeout = 300000) - public void testWorkerCreated() throws Exception { - // TODO: each test method should probably have its own handler. - logOutput.reset(); - - InstanceConfiguration ic = validInstanceConfiguration1(LABEL, false, false); - ComputeEngineCloud cloud = (ComputeEngineCloud) r.jenkins.clouds.get(0); - cloud.setConfigurations(ImmutableList.of(ic)); - - // Add a new node - Collection planned = cloud.provision(new LabelAtom(LABEL), 1); - - // There should be a planned node - assertEquals(logs(), 1, planned.size()); - - String name = planned.iterator().next().displayName; - - // Wait for the node creation to finish - planned.iterator().next().future.get(); - - // There should be no warning logs - assertEquals(logs(), false, logs().contains("WARNING")); - - Instance i = cloud.getClient().getInstance(PROJECT_ID, ZONE, name); - - // The created instance should have 3 labels - assertEquals(logs(), 3, i.getLabels().size()); - - // Instance should have a label with key CONFIG_LABEL_KEY and value equal to the config's name - // prefix - assertEquals( - logs(), ic.getNamePrefix(), i.getLabels().get(ComputeEngineCloud.CONFIG_LABEL_KEY)); - // proper id label to properly count instances - assertEquals( - logs(), cloud.getInstanceId(), i.getLabels().get(ComputeEngineCloud.CLOUD_ID_LABEL_KEY)); - } - - // Tests snapshot is created when we have failure builds for given node - // Snapshot creation is longer for windows one-shot vm's. - @Test(timeout = 800000) - public void testSnapshotCreated() throws Exception { - logOutput.reset(); - - ComputeEngineCloud cloud = (ComputeEngineCloud) r.jenkins.clouds.get(0); - cloud.setConfigurations(ImmutableList.of(snapshotInstanceConfiguration())); - - FreeStyleProject project = r.createFreeStyleProject(); - Builder step = new BatchFile("exit 1"); - project.getBuildersList().add(step); - project.setAssignedLabel(new LabelAtom(SNAPSHOT_LABEL)); - - FreeStyleBuild build = r.assertBuildStatus(Result.FAILURE, project.scheduleBuild2(0)); - Node worker = build.getBuiltOn(); - - try { - // Need time for one-shot instance to terminate and create the snapshot - Awaitility.await() - .timeout(15, TimeUnit.MINUTES) - .until( - () -> - r.jenkins.getNode(worker.getNodeName()) - == null); // Assert that there is 0 nodes after job finished - - Snapshot createdSnapshot = client.getSnapshot(PROJECT_ID, worker.getNodeName()); - assertNotNull(logs(), createdSnapshot); - assertEquals(logs(), createdSnapshot.getStatus(), "READY"); - } finally { - try { - // cleanup - client.deleteSnapshotAsync(PROJECT_ID, worker.getNodeName()); - } catch (Exception e) { - } - } - } - - // TODO(google-compute-engine-plugin/issues/49): Remove this test when refactoring windows tests. - @Test(timeout = 500000) - public void testIgnoreProxy() throws Exception { - logOutput.reset(); - - ComputeEngineCloud cloud = (ComputeEngineCloud) r.jenkins.clouds.get(0); - Jenkins jenkins = r.getInstance(); - jenkins.proxy = new ProxyConfiguration("127.0.0.1", 8080); - jenkins.proxy.save(); - jenkins.save(); - InstanceConfiguration instanceConfiguration = validInstanceConfiguration1(LABEL, false, true); - instanceConfiguration.setIgnoreProxy(true); - cloud.setConfigurations(ImmutableList.of(instanceConfiguration)); - - FreeStyleProject project = r.createFreeStyleProject(); - Builder step = new BatchFile("echo works"); - project.getBuildersList().add(step); - project.setAssignedLabel(new LabelAtom(LABEL)); - - r.buildAndAssertSuccess(project); - - cloud.getConfigurations().get(0).setIgnoreProxy(false); - Future buildFuture = project.scheduleBuild2(0); - FreeStyleBuild build; - try { - build = buildFuture.get(120, TimeUnit.SECONDS); - } catch (TimeoutException e) { - e.printStackTrace(); - return; - } - assertNotNull(build); - assertEquals(Result.FAILURE, build.getResult()); - } - - /** - * Creates an instance configuration for a node that will get a snapshot created upon deletion if - * there is a build failure. - * - * @return InstanceConfiguration proper instance configuration to test snapshot creation. - */ - private static InstanceConfiguration snapshotInstanceConfiguration() { - return validInstanceConfiguration1(SNAPSHOT_LABEL, true, true); - } - - /** - * Given a job label and whether or not to create a snapshot upon deletion, gives working instance - * configuration to launch an instance. - * - * @param labels What job label to run the instance on. - * @param createSnapshot Whether or not to create a snapshot for the provisioned instance upon - * deletion. - * @return InstanceConfiguration working instance configuration to provision an instance. - */ - private static InstanceConfiguration validInstanceConfiguration1( - String labels, boolean createSnapshot, boolean oneShot) { - - String startupScript = - "Stop-Service sshd\n" - + "$ConfiguredPublicKey = " - + "\"" - + publicKey - + "\"\n" - + "Write-Output \"Second phase\"\n" - + "# We are in the second phase of startup where we need to set up authorized_keys for the specified user.\n" - + "# Create the .ssh folder and authorized_keys file.\n" - + "Set-Content -Path $env:PROGRAMDATA\\ssh\\administrators_authorized_keys -Value $ConfiguredPublicKey\n" - + "icacls $env:PROGRAMDATA\\ssh\\administrators_authorized_keys /inheritance:r\n" - + "icacls $env:PROGRAMDATA\\ssh\\administrators_authorized_keys /grant SYSTEM:`(F`)\n" - + "icacls $env:PROGRAMDATA\\ssh\\administrators_authorized_keys /grant BUILTIN\\Administrators:`(F`)\n" - + "Restart-Service sshd"; - - return ITUtil.instanceConfigurationBuilder() - .numExecutorsStr(NUM_EXECUTORS) - .startupScript(startupScript) - .labels(labels) - .bootDiskSourceImageName( - "projects/" + bootDiskProjectId + "/global/images/" + bootDiskImageName) - .bootDiskSourceImageProject(bootDiskProjectId) - .bootDiskSizeGbStr(BOOT_DISK_SIZE_GB_STR) - .windowsConfiguration( - WindowsConfiguration.builder() - .passwordCredentialsId("") - .privateKeyCredentialsId(windowsPrivateKeyCredentialId) - .build()) - .createSnapshot(createSnapshot) - .retentionTimeMinutesStr(RETENTION_TIME_MINUTES_STR) - .launchTimeoutSecondsStr(LAUNCH_TIMEOUT_SECONDS_STR) - .oneShot(oneShot) - .template(NULL_TEMPLATE) - .googleLabels(INTEGRATION_LABEL) - .build(); - } - - private static void deleteIntegrationInstances(boolean waitForCompletion) throws IOException { - List instances = client.listInstancesWithLabel(PROJECT_ID, INTEGRATION_LABEL); - for (Instance i : instances) { - safeDelete(i.getName(), waitForCompletion); - } - } - - private static void safeDelete(String instanceId, boolean waitForCompletion) { - try { - Operation op = client.terminateInstanceAsync(PROJECT_ID, ZONE, instanceId); - if (waitForCompletion) - client.waitForOperationCompletion(PROJECT_ID, op.getName(), op.getZone(), 3 * 60 * 1000); - } catch (Exception e) { - log.warning(String.format("Error deleting instance %s: %s", instanceId, e.getMessage())); - } - } - - private static String logs() { - sh.flush(); - return logOutput.toString(); - } -} diff --git a/src/test/java/com/google/jenkins/plugins/computeengine/integration/ComputeEngineCloudWorkerCreatedIT.java b/src/test/java/com/google/jenkins/plugins/computeengine/integration/ComputeEngineCloudWorkerCreatedIT.java index 11344dff..a0ef65c4 100644 --- a/src/test/java/com/google/jenkins/plugins/computeengine/integration/ComputeEngineCloudWorkerCreatedIT.java +++ b/src/test/java/com/google/jenkins/plugins/computeengine/integration/ComputeEngineCloudWorkerCreatedIT.java @@ -16,7 +16,6 @@ package com.google.jenkins.plugins.computeengine.integration; -import static com.google.jenkins.plugins.computeengine.integration.ITUtil.DEB_JAVA_STARTUP_SCRIPT; import static com.google.jenkins.plugins.computeengine.integration.ITUtil.LABEL; import static com.google.jenkins.plugins.computeengine.integration.ITUtil.NULL_TEMPLATE; import static com.google.jenkins.plugins.computeengine.integration.ITUtil.NUM_EXECUTORS; @@ -79,7 +78,6 @@ public static void init() throws Exception { instanceConfiguration = instanceConfigurationBuilder() - .startupScript(DEB_JAVA_STARTUP_SCRIPT) .numExecutorsStr(NUM_EXECUTORS) .labels(LABEL) .oneShot(false) diff --git a/src/test/java/com/google/jenkins/plugins/computeengine/integration/ComputeEngineCloudWorkerFailedIT.java b/src/test/java/com/google/jenkins/plugins/computeengine/integration/ComputeEngineCloudWorkerFailedIT.java index 2abd4010..3b527f3f 100644 --- a/src/test/java/com/google/jenkins/plugins/computeengine/integration/ComputeEngineCloudWorkerFailedIT.java +++ b/src/test/java/com/google/jenkins/plugins/computeengine/integration/ComputeEngineCloudWorkerFailedIT.java @@ -26,6 +26,7 @@ import static com.google.jenkins.plugins.computeengine.integration.ITUtil.initCredentials; import static com.google.jenkins.plugins.computeengine.integration.ITUtil.instanceConfigurationBuilder; import static com.google.jenkins.plugins.computeengine.integration.ITUtil.teardownResources; +import static com.google.jenkins.plugins.computeengine.integration.ITUtil.windows; import com.google.cloud.graphite.platforms.plugin.client.ComputeClient; import com.google.common.collect.ImmutableList; @@ -39,6 +40,7 @@ import java.util.concurrent.TimeUnit; import java.util.logging.Logger; import org.junit.AfterClass; +import org.junit.Assume; import org.junit.BeforeClass; import org.junit.ClassRule; import org.junit.Test; @@ -63,6 +65,7 @@ public class ComputeEngineCloudWorkerFailedIT { @BeforeClass public static void init() throws Exception { + Assume.assumeFalse(windows); log.info("init"); initCredentials(jenkinsRule); ComputeEngineCloud cloud = initCloud(jenkinsRule); diff --git a/src/test/java/com/google/jenkins/plugins/computeengine/integration/ConfigAsCodeWindowsIT.java b/src/test/java/com/google/jenkins/plugins/computeengine/integration/ConfigAsCodeNonStandardJavaIT.java similarity index 56% rename from src/test/java/com/google/jenkins/plugins/computeengine/integration/ConfigAsCodeWindowsIT.java rename to src/test/java/com/google/jenkins/plugins/computeengine/integration/ConfigAsCodeNonStandardJavaIT.java index 336ff0d7..344b3336 100644 --- a/src/test/java/com/google/jenkins/plugins/computeengine/integration/ConfigAsCodeWindowsIT.java +++ b/src/test/java/com/google/jenkins/plugins/computeengine/integration/ConfigAsCodeNonStandardJavaIT.java @@ -1,20 +1,28 @@ package com.google.jenkins.plugins.computeengine.integration; +import static com.google.jenkins.plugins.computeengine.integration.ITUtil.PROJECT_ID; import static com.google.jenkins.plugins.computeengine.integration.ITUtil.TEST_TIMEOUT_MULTIPLIER; +import static com.google.jenkins.plugins.computeengine.integration.ITUtil.ZONE; +import static com.google.jenkins.plugins.computeengine.integration.ITUtil.execute; import static com.google.jenkins.plugins.computeengine.integration.ITUtil.getLabel; import static com.google.jenkins.plugins.computeengine.integration.ITUtil.initClient; import static com.google.jenkins.plugins.computeengine.integration.ITUtil.initCredentials; import static com.google.jenkins.plugins.computeengine.integration.ITUtil.teardownResources; +import static com.google.jenkins.plugins.computeengine.integration.ITUtil.windows; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assume.assumeFalse; +import com.google.api.services.compute.model.Instance; import com.google.cloud.graphite.platforms.plugin.client.ComputeClient; import com.google.jenkins.plugins.computeengine.ComputeEngineCloud; import hudson.model.FreeStyleProject; import hudson.model.labels.LabelAtom; -import hudson.tasks.BatchFile; +import hudson.slaves.NodeProvisioner.PlannedNode; import hudson.tasks.Builder; import io.jenkins.plugins.casc.ConfigurationAsCode; import java.io.IOException; +import java.util.Collection; import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.logging.Logger; @@ -25,25 +33,24 @@ import org.junit.rules.Timeout; import org.jvnet.hudson.test.JenkinsRule; -public class ConfigAsCodeWindowsIT { - - private static Logger log = Logger.getLogger(ConfigAsCodeWindowsIT.class.getName()); +/** + * Integration tests for Jenkins agents configured with Configuration as Code with a non-standard + * java path. + */ +public class ConfigAsCodeNonStandardJavaIT { + private static Logger log = Logger.getLogger(ConfigAsCodeNonStandardJavaIT.class.getName()); @ClassRule public static JenkinsRule jenkinsRule = new JenkinsRule(); @ClassRule - public static Timeout timeout = new Timeout(10 * TEST_TIMEOUT_MULTIPLIER, TimeUnit.MINUTES); + public static Timeout timeout = new Timeout(5 * TEST_TIMEOUT_MULTIPLIER, TimeUnit.MINUTES); private static ComputeClient client; - private static Map label = getLabel(ConfigAsCodeWindowsIT.class); + private static Map label = getLabel(ConfigAsCodeNonStandardJavaIT.class); @BeforeClass public static void init() throws Exception { + assumeFalse(windows); log.info("init"); - ConfigurationAsCode.get() - .configure( - ConfigAsCodeWindowsIT.class - .getResource("configuration-as-code-windows-it.yml") - .toString()); initCredentials(jenkinsRule); client = initClient(jenkinsRule, label, log); } @@ -54,7 +61,13 @@ public static void teardown() throws IOException { } @Test - public void testWindowsWorkerCreated() throws Exception { + public void testNonStandardJavaWorkerCreated() throws Exception { + assumeFalse(windows); + ConfigurationAsCode.get() + .configure( + this.getClass() + .getResource("configuration-as-code-non-standard-java-it.yml") + .toString()); ComputeEngineCloud cloud = (ComputeEngineCloud) jenkinsRule.jenkins.clouds.getByName("gce-integration"); @@ -62,11 +75,22 @@ public void testWindowsWorkerCreated() throws Exception { assertEquals(1, cloud.getConfigurations().size()); cloud.getConfigurations().get(0).setGoogleLabels(label); + // Add a new node + Collection planned = + cloud.provision(new LabelAtom("integration-non-standard-java"), 1); + + // There should be a planned node + assertEquals(1, planned.size()); + String name = planned.iterator().next().displayName; + + // Wait for the node creation to finish + planned.iterator().next().future.get(); + Instance instance = client.getInstance(PROJECT_ID, ZONE, name); + assertNotNull(instance); + FreeStyleProject project = jenkinsRule.createFreeStyleProject(); - Builder step = new BatchFile("echo works"); + Builder step = execute(Commands.ECHO, "works"); project.getBuildersList().add(step); - project.setAssignedLabel(new LabelAtom("integration-windows")); - jenkinsRule.buildAndAssertSuccess(project); } } diff --git a/src/test/java/com/google/jenkins/plugins/computeengine/integration/ConfigAsCodeTestIT.java b/src/test/java/com/google/jenkins/plugins/computeengine/integration/ConfigAsCodeTestIT.java index c6cc1114..7fff58bc 100644 --- a/src/test/java/com/google/jenkins/plugins/computeengine/integration/ConfigAsCodeTestIT.java +++ b/src/test/java/com/google/jenkins/plugins/computeengine/integration/ConfigAsCodeTestIT.java @@ -1,5 +1,6 @@ package com.google.jenkins.plugins.computeengine.integration; +import static com.google.jenkins.plugins.computeengine.integration.ITUtil.CONFIG_AS_CODE_PATH; import static com.google.jenkins.plugins.computeengine.integration.ITUtil.PROJECT_ID; import static com.google.jenkins.plugins.computeengine.integration.ITUtil.TEST_TIMEOUT_MULTIPLIER; import static com.google.jenkins.plugins.computeengine.integration.ITUtil.ZONE; @@ -21,6 +22,7 @@ import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.logging.Logger; +import org.awaitility.Awaitility; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.ClassRule; @@ -41,6 +43,8 @@ public class ConfigAsCodeTestIT { @BeforeClass public static void init() throws Exception { log.info("init"); + ConfigurationAsCode.get() + .configure(ConfigAsCodeTestIT.class.getResource(CONFIG_AS_CODE_PATH).toString()); initCredentials(jenkinsRule); client = initClient(jenkinsRule, label, log); } @@ -52,8 +56,6 @@ public static void teardown() throws IOException { @Test public void testWorkerCreated() throws Exception { - ConfigurationAsCode.get() - .configure(this.getClass().getResource("configuration-as-code-it.yml").toString()); ComputeEngineCloud cloud = (ComputeEngineCloud) jenkinsRule.jenkins.clouds.getByName("gce-integration"); @@ -66,35 +68,7 @@ public void testWorkerCreated() throws Exception { cloud.provision(new LabelAtom("integration"), 1); // There should be a planned node - assertEquals(1, planned.size()); - String name = planned.iterator().next().displayName; - - // Wait for the node creation to finish - planned.iterator().next().future.get(); - Instance instance = client.getInstance(PROJECT_ID, ZONE, name); - assertNotNull(instance); - } - - @Test - public void testNonStandardJavaWorkerCreated() throws Exception { - ConfigurationAsCode.get() - .configure( - this.getClass() - .getResource("configuration-as-code-non-standard-java-it.yml") - .toString()); - ComputeEngineCloud cloud = - (ComputeEngineCloud) jenkinsRule.jenkins.clouds.getByName("gce-integration"); - - // Should be 1 configuration - assertEquals(1, cloud.getConfigurations().size()); - cloud.getConfigurations().get(0).setGoogleLabels(label); - - // Add a new node - Collection planned = - cloud.provision(new LabelAtom("integration-non-standard-java"), 1); - - // There should be a planned node - assertEquals(1, planned.size()); + Awaitility.await().timeout(2, TimeUnit.MINUTES).until(() -> planned.size() == 1); String name = planned.iterator().next().displayName; // Wait for the node creation to finish diff --git a/src/test/java/com/google/jenkins/plugins/computeengine/integration/ITUtil.java b/src/test/java/com/google/jenkins/plugins/computeengine/integration/ITUtil.java index a8ce757d..dbafee0c 100644 --- a/src/test/java/com/google/jenkins/plugins/computeengine/integration/ITUtil.java +++ b/src/test/java/com/google/jenkins/plugins/computeengine/integration/ITUtil.java @@ -20,6 +20,7 @@ import static com.google.common.collect.ImmutableList.copyOf; import static com.google.common.collect.ImmutableList.of; import static com.google.jenkins.plugins.computeengine.InstanceConfiguration.METADATA_LINUX_STARTUP_SCRIPT_KEY; +import static com.google.jenkins.plugins.computeengine.InstanceConfiguration.METADATA_WINDOWS_STARTUP_SCRIPT_KEY; import static com.google.jenkins.plugins.computeengine.InstanceConfiguration.NAT_NAME; import static com.google.jenkins.plugins.computeengine.InstanceConfiguration.NAT_TYPE; import static org.junit.Assert.assertEquals; @@ -27,10 +28,13 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.fail; +import com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey; import com.cloudbees.plugins.credentials.Credentials; +import com.cloudbees.plugins.credentials.CredentialsScope; import com.cloudbees.plugins.credentials.CredentialsStore; import com.cloudbees.plugins.credentials.SecretBytes; import com.cloudbees.plugins.credentials.SystemCredentialsProvider; +import com.cloudbees.plugins.credentials.common.StandardUsernameCredentials; import com.cloudbees.plugins.credentials.domains.Domain; import com.google.api.services.compute.model.AccessConfig; import com.google.api.services.compute.model.AttachedDisk; @@ -51,11 +55,15 @@ import com.google.jenkins.plugins.computeengine.AutofilledNetworkConfiguration; import com.google.jenkins.plugins.computeengine.ComputeEngineCloud; import com.google.jenkins.plugins.computeengine.InstanceConfiguration; +import com.google.jenkins.plugins.computeengine.WindowsConfiguration; import com.google.jenkins.plugins.computeengine.client.ClientUtil; import com.google.jenkins.plugins.computeengine.ssh.GoogleKeyPair; import com.google.jenkins.plugins.credentials.oauth.GoogleRobotPrivateKeyCredentials; import com.google.jenkins.plugins.credentials.oauth.JsonServiceAccountConfig; import hudson.model.Node; +import hudson.plugins.powershell.PowerShell; +import hudson.tasks.Builder; +import hudson.tasks.Shell; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; @@ -63,12 +71,16 @@ import java.util.List; import java.util.Map; import java.util.logging.Logger; +import jenkins.util.SystemProperties; import org.apache.commons.lang.SystemUtils; import org.jvnet.hudson.test.JenkinsRule; /** Common logic and constants used throughout the integration tests. */ class ITUtil { - static final String DEB_JAVA_STARTUP_SCRIPT = + static final boolean windows = + Boolean.parseBoolean( + SystemProperties.getString(ITUtil.class.getName() + ".windows", "false")); + private static final String DEB_JAVA_STARTUP_SCRIPT = "#!/bin/bash\n" + "/etc/init.d/ssh stop\n" + "echo \"deb http://http.debian.net/debian stretch-backports main\" | \\\n" @@ -95,14 +107,19 @@ class ITUtil { private static final String CONFIG_DESC = "integration"; private static final String BOOT_DISK_TYPE = ZONE_BASE + "/diskTypes/pd-ssd"; private static final boolean BOOT_DISK_AUTODELETE = true; - private static final String BOOT_DISK_PROJECT_ID = "debian-cloud"; + private static final String BOOT_DISK_PROJECT_ID = + windows ? System.getenv("GOOGLE_BOOT_DISK_PROJECT_ID") : "debian-cloud"; private static final String BOOT_DISK_IMAGE_NAME = - "projects/debian-cloud/global/images/family/debian-9"; - private static final String BOOT_DISK_SIZE_GB_STR = "10"; + windows + ? String.format( + "projects/%s/global/images/%s", + BOOT_DISK_PROJECT_ID, System.getenv("GOOGLE_BOOT_DISK_IMAGE_NAME")) + : "projects/debian-cloud/global/images/family/debian-9"; + private static final String BOOT_DISK_SIZE_GB_STR = windows ? "50" : "10"; private static final Node.Mode NODE_MODE = Node.Mode.EXCLUSIVE; private static final String ACCELERATOR_NAME = ""; private static final String ACCELERATOR_COUNT = ""; - private static final String RUN_AS_USER = "jenkins"; + static final String RUN_AS_USER = "jenkins"; static final String NULL_TEMPLATE = null; private static final String NETWORK_NAME = format("projects/%s/global/networks/default"); private static final String SUBNETWORK_NAME = "default"; @@ -112,10 +129,30 @@ class ITUtil { String.format("%s@%s.iam.gserviceaccount.com", System.getenv("GOOGLE_SA_NAME"), PROJECT_ID); private static final String RETENTION_TIME_MINUTES_STR = ""; private static final String LAUNCH_TIMEOUT_SECONDS_STR = ""; - static final String SSH_USER = "test-user"; - private static final GoogleKeyPair SSH_KEY = GoogleKeyPair.generate(SSH_USER); + static final int SNAPSHOT_TIMEOUT = windows ? 600 : 300; + private static final GoogleKeyPair SSH_KEY = GoogleKeyPair.generate(RUN_AS_USER); static final String SSH_PRIVATE_KEY = SSH_KEY.getPrivateKey(); + private static final String WINDOWS_STARTUP_SCRIPT = + "Stop-Service sshd\n" + + "$ConfiguredPublicKey = " + + "\"" + + SSH_KEY.getPublicKey().trim().substring(RUN_AS_USER.length() + 1) + + "\"\n" + + "Write-Output \"Second phase\"\n" + + "# We are in the second phase of startup where we need to set up authorized_keys for the specified user.\n" + + "# Create the .ssh folder and authorized_keys file.\n" + + "Set-Content -Path $env:PROGRAMDATA\\ssh\\administrators_authorized_keys -Value $ConfiguredPublicKey\n" + + "icacls $env:PROGRAMDATA\\ssh\\administrators_authorized_keys /inheritance:r\n" + + "icacls $env:PROGRAMDATA\\ssh\\administrators_authorized_keys /grant SYSTEM:`(F`)\n" + + "icacls $env:PROGRAMDATA\\ssh\\administrators_authorized_keys /grant BUILTIN\\Administrators:`(F`)\n" + + "Restart-Service sshd"; + private static final String STARTUP_SCRIPT = + windows ? WINDOWS_STARTUP_SCRIPT : DEB_JAVA_STARTUP_SCRIPT; static final int TEST_TIMEOUT_MULTIPLIER = SystemUtils.IS_OS_WINDOWS ? 3 : 1; + static final String CONFIG_AS_CODE_PATH = + windows ? "configuration-as-code-windows-it.yml" : "configuration-as-code-it.yml"; + + private static String windowsPrivateKeyCredentialsId; static String format(String s) { assertNotNull("GOOGLE_PROJECT_ID env var must be set", PROJECT_ID); @@ -154,9 +191,25 @@ static Credentials initCredentials(JenkinsRule r) throws Exception { CredentialsStore store = new SystemCredentialsProvider.ProviderImpl().getStore(r.jenkins); assertNotNull("Credentials store can not be null", store); store.addCredentials(Domain.global(), credentials); + if (windows) { + windowsPrivateKeyCredentialsId = initWindowsSshCredentials(store); + } return credentials; } + private static String initWindowsSshCredentials(CredentialsStore store) throws IOException { + StandardUsernameCredentials windowsPrivateKeyCredentials = + new BasicSSHUserPrivateKey( + CredentialsScope.GLOBAL, + null, + RUN_AS_USER, + new BasicSSHUserPrivateKey.DirectEntryPrivateKeySource(SSH_KEY.getPrivateKey()), + null, + "integration test private key for windows"); + store.addCredentials(Domain.global(), windowsPrivateKeyCredentials); + return windowsPrivateKeyCredentials.getId(); + } + // Add Cloud plugin static ComputeEngineCloud initCloud(JenkinsRule jenkinsRule) { ComputeEngineCloud gcp = new ComputeEngineCloud(CLOUD_NAME, PROJECT_ID, PROJECT_ID, "10"); @@ -176,6 +229,13 @@ static ComputeClient initClient(JenkinsRule jenkinsRule, Map lab return client; } + static Builder execute(Commands command, String argument) { + if (windows) { + return new PowerShell(String.format(command.getWindows(), argument)); + } + return new Shell(String.format(command.getLinux(), argument)); + } + static void teardownResources(ComputeClient client, Map label, Logger log) throws IOException { log.info("teardown"); @@ -206,8 +266,11 @@ static InstanceTemplate createTemplate(Map googleLabels, String .setItems( of( new Metadata.Items() - .setKey(METADATA_LINUX_STARTUP_SCRIPT_KEY) - .setValue(DEB_JAVA_STARTUP_SCRIPT), + .setKey( + windows + ? METADATA_WINDOWS_STARTUP_SCRIPT_KEY + : METADATA_LINUX_STARTUP_SCRIPT_KEY) + .setValue(STARTUP_SCRIPT), new Metadata.Items() .setKey(InstanceConfiguration.SSH_METADATA_KEY) .setValue(SSH_KEY.getPublicKey())))); @@ -235,7 +298,13 @@ static InstanceConfiguration.Builder instanceConfigurationBuilder() { .bootDiskSourceImageName(BOOT_DISK_IMAGE_NAME) .bootDiskSourceImageProject(BOOT_DISK_PROJECT_ID) .bootDiskSizeGbStr(BOOT_DISK_SIZE_GB_STR) - .windowsConfiguration(null) + .windowsConfiguration( + windows + ? WindowsConfiguration.builder() + .passwordCredentialsId("") + .privateKeyCredentialsId(windowsPrivateKeyCredentialsId) + .build() + : null) .remoteFs(null) .networkConfiguration(new AutofilledNetworkConfiguration(NETWORK_NAME, SUBNETWORK_NAME)) .externalAddress(EXTERNAL_ADDR) @@ -247,7 +316,8 @@ static InstanceConfiguration.Builder instanceConfigurationBuilder() { .launchTimeoutSecondsStr(LAUNCH_TIMEOUT_SECONDS_STR) .mode(NODE_MODE) .acceleratorConfiguration(new AcceleratorConfiguration(ACCELERATOR_NAME, ACCELERATOR_COUNT)) - .runAsUser(RUN_AS_USER); + .runAsUser(RUN_AS_USER) + .startupScript(STARTUP_SCRIPT); } /* diff --git a/src/test/resources/com/google/jenkins/plugins/computeengine/integration/configuration-as-code-windows-it.yml b/src/test/resources/com/google/jenkins/plugins/computeengine/integration/configuration-as-code-windows-it.yml index bfa0bb9f..a4fcb5dd 100644 --- a/src/test/resources/com/google/jenkins/plugins/computeengine/integration/configuration-as-code-windows-it.yml +++ b/src/test/resources/com/google/jenkins/plugins/computeengine/integration/configuration-as-code-windows-it.yml @@ -6,12 +6,12 @@ jenkins: instanceCapStr: 10 credentialsId: ${env.GOOGLE_PROJECT_ID} configurations: - - namePrefix: integration-windows - description: integration-windows + - namePrefix: integration + description: integration launchTimeoutSecondsStr: 3000 retentionTimeMinutesStr: 600 mode: EXCLUSIVE - labelString: integration-windows + labelString: integration numExecutorsStr: 1 runAsUser: jenkins remoteFs: '' @@ -20,13 +20,13 @@ jenkins: privateKeyCredentialsId: '' oneShot: true createSnapshot: false - region: "https://www.googleapis.com/compute/v1/projects/${env.GOOGLE_PROJECT_ID}/regions/us-west1" - zone: "https://www.googleapis.com/compute/v1/projects/${env.GOOGLE_PROJECT_ID}/zones/us-west1-a" + region: "https://www.googleapis.com/compute/v1/projects/${env.GOOGLE_PROJECT_ID}/regions/${env.GOOGLE_REGION}" + zone: "https://www.googleapis.com/compute/v1/projects/${env.GOOGLE_PROJECT_ID}/zones/${env.GOOGLE_ZONE}" template: '' # tried not setting, added when 'saved' in UI - machineType: "https://www.googleapis.com/compute/v1/projects/${env.GOOGLE_PROJECT_ID}/zones/us-west1-a/machineTypes/n1-standard-1" + machineType: "https://www.googleapis.com/compute/v1/projects/${env.GOOGLE_PROJECT_ID}/zones/${env.GOOGLE_ZONE}/machineTypes/n1-standard-1" preemptible: false minCpuPlatform: '' # tried not setting, added when 'saved' in UI - startupScript: "Stop-Service sshd\n$ConfiguredPublicKey = \":${env.GOOGLE_PUBLIC_KEY}\"\nWrite-Output \"Second phase\"\nSet-Content -Path $env:PROGRAMDATA\\ssh\\administrators_authorized_keys -Value $ConfiguredPublicKey\nicacls $env:PROGRAMDATA\\ssh\\administrators_authorized_keys /inheritance:r\nicacls $env:PROGRAMDATA\\ssh\\administrators_authorized_keys /grant SYSTEM:`(F`)\nicacls $env:PROGRAMDATA\\ssh\\administrators_authorized_keys /grant BUILTIN\\Administrators:`(F`)\nRestart-Service sshd" + startupScript: "Stop-Service sshd\n$ConfiguredPublicKey = \"jenkins:${env.GOOGLE_PUBLIC_KEY}\"\nWrite-Output \"Second phase\"\nSet-Content -Path $env:PROGRAMDATA\\ssh\\administrators_authorized_keys -Value $ConfiguredPublicKey\nicacls $env:PROGRAMDATA\\ssh\\administrators_authorized_keys /inheritance:r\nicacls $env:PROGRAMDATA\\ssh\\administrators_authorized_keys /grant SYSTEM:`(F`)\nicacls $env:PROGRAMDATA\\ssh\\administrators_authorized_keys /grant BUILTIN\\Administrators:`(F`)\nRestart-Service sshd" networkConfiguration: autofilled: network: default @@ -36,10 +36,10 @@ jenkins: useInternalAddress: false bootDiskSourceImageProject: ${env.GOOGLE_BOOT_DISK_PROJECT_ID} bootDiskSourceImageName: "projects/${env.GOOGLE_BOOT_DISK_PROJECT_ID}/global/images/${env.GOOGLE_BOOT_DISK_IMAGE_NAME}" - bootDiskType: "https://www.googleapis.com/compute/v1/projects/${env.GOOGLE_PROJECT_ID}/zones/us-west1-a/diskTypes/pd-ssd" + bootDiskType: "https://www.googleapis.com/compute/v1/projects/${env.GOOGLE_PROJECT_ID}/zones/${env.GOOGLE_ZONE}/diskTypes/pd-ssd" bootDiskSizeGbStr: 50 bootDiskAutoDelete: true - serviceAccountEmail: '' + serviceAccountEmail: "${env.GOOGLE_SA_NAME}@${env.GOOGLE_PROJECT_ID}.iam.gserviceaccount.com" credentials: system: domainCredentials: diff --git a/windows-it-install.ps1 b/windows-it-install.ps1 index aa72b0bf..a666bf13 100644 --- a/windows-it-install.ps1 +++ b/windows-it-install.ps1 @@ -35,7 +35,7 @@ choco install -y jre8 # Following Step is needed for the startup script in the integration test to work, even if you already configured your own user. Write-Output "Adding build user..." $username = "jenkins" -$password = ConvertTo-SecureString "P4ssword1" -AsPlainText -Force +$password = ConvertTo-SecureString $env:WINDOWS_PASSWORD -AsPlainText -Force New-LocalUser -Name $username -Password $password Add-LocalGroupMember -Group "Administrators" -Member "$username" @@ -43,3 +43,6 @@ Add-LocalGroupMember -Group "Administrators" -Member "$username" Write-Output "Simulating login to register user..." $cred = New-Object System.Management.Automation.PSCredential -ArgumentList $username,$password Start-Process cmd /c -WindowStyle Hidden -Credential $cred -ErrorAction SilentlyContinue + +# Close the door on the way out +choco install undo-winrmconfig-during-shutdown --confirm