protected void assertJobEquals(final Job expected, final Job actual) { final Builder expectedBuilder = expected.toBuilder(); // hack to make sure that any environment variables that were folded into the created job // because of environment variables set at runtime on the test-running-agent are removed // from the actual when we assert the equality below final Builder actualBuilder = actual.toBuilder(); final Map<String, String> metadata = Maps.newHashMap(actual.getMetadata()); for (final Map.Entry<String, String> entry : DEFAULT_METADATA_ENVVARS.entrySet()) { final String envVar = entry.getKey(); final String metadataKey = entry.getValue(); final String envValue = System.getenv(envVar); if (envValue != null && actual.getMetadata().containsKey(metadataKey) && actual.getMetadata().get(metadataKey).equals(envValue)) { metadata.remove(metadataKey); } } actualBuilder.setMetadata(metadata); // Remove created timestamp set by master actualBuilder.setCreated(null); // copy the hash expectedBuilder.setHash(actualBuilder.build().getId().getHash()); assertEquals(expectedBuilder.build(), actualBuilder.build()); }
/** * Validate the Job's JobId by checking name, version, and hash are * not null or empty, don't contain invalid characters. * * @param job The Job to check. * * @return A set of error Strings */ private Set<String> validateJobId(final Job job) { final Set<String> errors = Sets.newHashSet(); final JobId jobId = job.getId(); if (jobId == null) { errors.add("Job id was not specified."); return errors; } final String jobIdVersion = jobId.getVersion(); final String jobIdHash = jobId.getHash(); final JobId recomputedId = job.toBuilder().build().getId(); errors.addAll(validateJobName(jobId, recomputedId)); errors.addAll(validateJobVersion(jobIdVersion, recomputedId)); if (this.shouldValidateJobHash) { errors.addAll(validateJobHash(jobIdHash, recomputedId)); } return errors; }
@Test public void testJobStatusHostFilter() throws Exception { startDefaultMaster(); final HeliosClient client = defaultClient(); startDefaultAgent(testHost()); // Create a job final Job job = Job.newBuilder() .setName(testJobName) .setVersion(testJobVersion) .setImage(BUSYBOX) .setCommand(IDLE_COMMAND) .setPorts(EMPTY_PORTS) .build(); final CreateJobResponse created = client.createJob(job).get(); assertEquals(CreateJobResponse.Status.OK, created.getStatus()); final String result = cli("status", "--host", "framazama"); assertThat(result, containsString("There are no jobs deployed to hosts with the host pattern")); } }
.setCommand(IDLE_COMMAND) .setCreatingUser(TEST_USER) .build(); final JobId jobId = job.getId(); final CreateJobResponse created = client.createJob(job).get();
@Test public void testTermOnExit() throws Exception { startDefaultMaster(); final String host = testHost(); startDefaultAgent(host); final HeliosClient client = defaultClient(); awaitHostStatus(client, host, UP, LONG_WAIT_SECONDS, SECONDS); // Note: signal 15 is SIGTERM final Job jobToInterrupt = Job.newBuilder() .setName(testJobName) .setVersion(testJobVersion) .setImage(BUSYBOX) .setCommand(asList("/bin/sh", "-c", "trap handle 15; handle() { echo term; exit 0; }; " + "while true; do sleep 1; done")) .build(); final JobId jobId = createJob(jobToInterrupt); deployJob(jobId, host); awaitTaskState(jobId, host, RUNNING); client.setGoal(new Deployment(jobId, Goal.STOP, Deployment.EMTPY_DEPLOYER_USER, Deployment.EMPTY_DEPLOYER_MASTER, Deployment.EMPTY_DEPLOYMENT_GROUP_NAME), host); final TaskStatus taskStatus = awaitTaskState(jobId, host, STOPPED); final String log; try (final DockerClient dockerClient = getNewDockerClient(); LogStream logs = dockerClient.logs(taskStatus.getContainerId(), stdout())) { log = logs.readFully(); } // Message expected, because the SIGTERM handler in the script should have run assertEquals("term\n", log); }
@Test public void testNoIntOnExit() throws Exception { startDefaultMaster(); final String host = testHost(); startDefaultAgent(host); final HeliosClient client = defaultClient(); awaitHostStatus(client, host, UP, LONG_WAIT_SECONDS, SECONDS); // Note: signal 2 is SIGINT final Job jobToInterrupt = Job.newBuilder() .setName(testJobName) .setVersion(testJobVersion) .setImage(BUSYBOX) .setCommand(asList("/bin/sh", "-c", "trap handle 2; handle() { echo int; exit 0; }; " + "while true; do sleep 1; done")) .build(); final JobId jobId = createJob(jobToInterrupt); deployJob(jobId, host); awaitTaskState(jobId, host, RUNNING); client.setGoal(new Deployment(jobId, Goal.STOP, Deployment.EMTPY_DEPLOYER_USER, Deployment.EMPTY_DEPLOYER_MASTER, Deployment.EMPTY_DEPLOYMENT_GROUP_NAME), host); final TaskStatus taskStatus = awaitTaskState(jobId, host, STOPPED); final String log; try (final DockerClient dockerClient = getNewDockerClient(); LogStream logs = dockerClient.logs(taskStatus.getContainerId(), stdout())) { log = logs.readFully(); } // No message expected, since SIGINT should not be sent assertEquals("", log); }
@Test public void testRollingUpdateWithToken() throws Exception { final String host = testHost(); startDefaultAgent(host, "--labels", TEST_LABEL); // Wait for agent to come up final HeliosClient client = defaultClient(); awaitHostStatus(client, testHost(), UP, LONG_WAIT_SECONDS, SECONDS); // Manually deploy a job with a token on the host (i.e. a job not part of the deployment group) final Job job = Job.newBuilder() .setName(testJobName) .setVersion(testJobVersion) .setImage(BUSYBOX) .setCommand(IDLE_COMMAND) .setToken(TOKEN) .build(); final JobId jobId = createJob(job); // Create a deployment-group and trigger a migration rolling-update cli("create-deployment-group", "--json", TEST_GROUP, TEST_LABEL); cli("rolling-update", "--async", "--token", TOKEN, testJobNameAndVersion, TEST_GROUP); // rolling-update should succeed & job should be running awaitDeploymentGroupStatus(defaultClient(), TEST_GROUP, DeploymentGroupStatus.State.DONE); awaitTaskState(jobId, host, TaskStatus.State.RUNNING); // Check that we cannot manually undeploy the job with a token final String output = cli("undeploy", jobId.toString(), host); assertThat(output, containsString("FORBIDDEN")); awaitDeploymentGroupStatus(defaultClient(), TEST_GROUP, DeploymentGroupStatus.State.DONE); awaitTaskState(jobId, host, TaskStatus.State.RUNNING); }
.setHostname(testHost()) .setCommand(command) .build());
protected JobId createJob(final String name, final String version, final String image, final String hostname, final List<String> command, final Map<String, String> env, final Map<String, PortMapping> ports, final Map<ServiceEndpoint, ServicePorts> registration, final Integer gracePeriod, final Map<String, String> volumes, final Date expires) throws Exception { return createJob(Job.newBuilder() .setName(name) .setVersion(version) .setImage(image) .setHostname(hostname) .setCommand(command) .setEnv(env) .setPorts(ports) .setRegistration(registration) .setGracePeriod(gracePeriod) .setVolumes(volumes) .setExpires(expires) .build()); }
/** * Verify that the Helios master generates and returns a hash if the submitted job creation * request does not include one. */ @Test public void testHashLessJobCreation() throws Exception { startDefaultMaster(); final Job job = Job.newBuilder() .setName(testJobName) .setVersion(testJobVersion) .setImage(BUSYBOX) .setCommand(IDLE_COMMAND) .setCreatingUser(TEST_USER) .build(); // Remove the hash from the id in the json serialized job final ObjectNode json = (ObjectNode) Json.reader().readTree(Json.asString(job)); json.set("id", TextNode.valueOf(testJobName + ":" + testJobVersion)); final HttpURLConnection req = post("/jobs?user=" + TEST_USER, Json.asBytes(json)); assertEquals(req.getResponseCode(), 200); final CreateJobResponse res = Json.read(toByteArray(req.getInputStream()), CreateJobResponse.class); assertEquals(OK, res.getStatus()); assertTrue(res.getErrors().isEmpty()); assertEquals(job.getId().toString(), res.getId()); }
@Before public void setup() throws Exception { startDefaultMaster(); client = defaultClient(); startDefaultAgent(testHost()); job = Job.newBuilder() .setName(testJobName) .setVersion(testJobVersion) .setImage(BUSYBOX) .addVolume("/random-vol") .addVolume("/named-vol", "my-volume") .addVolume("/hostname", "/etc/hostname") .setCommand(asList("sh", "-c", "echo foo > /random-vol/foo; " + "echo foo > /named-vol/foo;" + "nc -p 4711 -le dd if=/random-vol/foo;" + "nc -p 4712 -le dd if=/named-vol/foo;" + "nc -p 4713 -le dd if=/hostname")) .addPort("random", PortMapping.of(4711)) .addPort("named", PortMapping.of(4712)) .addPort("hostname", PortMapping.of(4713)) .setCreatingUser(TEST_USER) .build(); }
@Test public void testPortMappingWithBadIp() throws Exception { startDefaultMaster(); startDefaultAgent(testHost()); awaitHostStatus(testHost(), UP, LONG_WAIT_SECONDS, SECONDS); expectedException.expect(IllegalArgumentException.class); expectedException.expectMessage("foobar is not a valid IP address."); // Create job with a portmapping that has a bad IP. Check it throws. createJobRawOutput(Job.newBuilder() .setName(testJobName) .setVersion(testJobVersion) .setImage(BUSYBOX) .setPorts(Collections.singletonMap("foo", PortMapping.builder() .ip("foobar") .internalPort(80) .externalPort(80) .build() )).build()); } }
@Test public void testHttp() throws Exception { startDefaultMaster(); final HeliosClient client = defaultClient(); startDefaultAgent(testHost(), "--service-registry=" + registryAddress); awaitHostStatus(client, testHost(), UP, LONG_WAIT_SECONDS, SECONDS); final HealthCheck healthCheck = HttpHealthCheck.of("http", "/"); // start a container that listens on a poke port, and once poked runs a web server final Job job = Job.newBuilder() .setName(testJobName) .setVersion(testJobVersion) .setImage(NGINX) .setCommand(asList("sh", "-c", "nc -l -p 4711 && nginx -g 'daemon off;'")) .addPort("poke", PortMapping.of(4711)) .addPort("http", PortMapping.of(80)) .addRegistration(ServiceEndpoint.of("foo_service", "foo_proto"), ServicePorts.of("http")) .setHealthCheck(healthCheck) .build(); assertContainerRegistersAfterPoke(client, job); }
@Test public void testPortMappingWithIp() throws Exception { startDefaultMaster(); startDefaultAgent(testHost()); awaitHostStatus(testHost(), UP, LONG_WAIT_SECONDS, SECONDS); // Create job with a portmapping that has an IP. Check it doesn't throw. createJobRawOutput(Job.newBuilder() .setName(testJobName) .setVersion(testJobVersion) .setImage(BUSYBOX) .setPorts(Collections.singletonMap("foo", PortMapping.builder() .ip("127.0.0.1") .internalPort(80) .externalPort(80) .build() )).build()); }
/** * Starts a container that listens on a poke port, and once poked listens on the healthcheck * port. */ private Job pokeJob(final HealthCheck healthCheck) { return Job.newBuilder() .setName(testJobName) .setVersion(testJobVersion) .setImage(ALPINE) .setCommand(asList("sh", "-c", "nc -l -p 4711 && nc -lk -p 4712 -e hostname")) .addPort("poke", PortMapping.of(4711)) .addPort("health", PortMapping.of(4712)) .addRegistration(ServiceEndpoint.of("foo_service", "foo_proto"), ServicePorts.of("health")) .setHealthCheck(healthCheck) .build(); }
@Before public void setup() throws Exception { startDefaultMaster(); client = defaultClient(); startDefaultAgent(testHost()); job = Job.newBuilder() .setName(testJobName) .setVersion(testJobVersion) .setImage(BUSYBOX) .addRamdisk("/much-volatile", "rw,noexec,nosuid,size=1") .setCommand(asList("sh", "-c", "nc -p 4711 -lle sh -c \"df | grep much-volatile\"")) .addPort("df", PortMapping.of(4711)) .setCreatingUser(TEST_USER) .build(); }
@Test public void testInvalidHostname() throws Exception { startDefaultMaster(); startDefaultAgent(testHost()); awaitHostStatus(testHost(), UP, LONG_WAIT_SECONDS, SECONDS); // Create job final String output = createJobRawOutput(Job.newBuilder() .setName(testJobName) .setVersion(testJobVersion) .setImage(BUSYBOX) .setHostname("$%^&") .build()); assertThat(output, Matchers.containsString("Invalid hostname ")); }
@Before public void setup() throws Exception { startDefaultMaster(); client = defaultClient(); startDefaultAgent(testHost()); job = Job.newBuilder() .setName(testJobName) .setVersion(testJobVersion) .setImage(BUSYBOX) .setResources(new Resources(MEMORY, MEMORY_SWAP, CPU_SHARES, CPUSET_CPUS)) .setCommand(IDLE_COMMAND) .setCreatingUser(TEST_USER) .build(); }
@Before public void setup() throws Exception { startDefaultMaster(); client = defaultClient(); startDefaultAgent(testHost()); job = Job.newBuilder() .setName(testJobName) .setVersion(testJobVersion) .setImage(BUSYBOX) .setNetworkMode("host") .setCommand(IDLE_COMMAND) .build(); }