/*
*
* Copyright 2016 Netflix, 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.
*
*/
package com.netflix.genie.web.tasks.leader;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.netflix.genie.common.dto.Job;
import com.netflix.genie.common.dto.JobExecution;
import com.netflix.genie.common.dto.JobStatus;
import com.netflix.genie.common.exceptions.GenieException;
import com.netflix.genie.core.services.JobPersistenceService;
import com.netflix.genie.core.services.JobSearchService;
import com.netflix.genie.test.categories.UnitTest;
import com.netflix.genie.web.properties.ClusterCheckerProperties;
import com.netflix.genie.web.tasks.GenieTaskScheduleType;
import com.netflix.spectator.api.Counter;
import com.netflix.spectator.api.Registry;
import org.hamcrest.Matchers;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.mockito.Mockito;
import org.springframework.boot.actuate.autoconfigure.ManagementServerProperties;
import org.springframework.http.HttpStatus;
import org.springframework.web.client.HttpServerErrorException;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
/**
* Unit tests for the ClusterCheckerTask class.
*
* @author tgianos
* @since 3.0.0
*/
@Category(UnitTest.class)
public class ClusterCheckerTaskUnitTests {
private ClusterCheckerTask task;
private String hostName;
private JobSearchService jobSearchService;
private JobPersistenceService jobPersistenceService;
private RestTemplate restTemplate;
private Counter lostJobCounter;
private Counter unableToUpdateJobCounter;
/**
* Setup for the tests.
*/
@Before
public void setup() {
this.hostName = UUID.randomUUID().toString();
final ClusterCheckerProperties properties = new ClusterCheckerProperties();
properties.setHealthIndicatorsToIgnore("memory,genie ");
this.jobSearchService = Mockito.mock(JobSearchService.class);
this.jobPersistenceService = Mockito.mock(JobPersistenceService.class);
this.restTemplate = Mockito.mock(RestTemplate.class);
final ManagementServerProperties serverProperties = Mockito.mock(ManagementServerProperties.class);
Mockito.when(serverProperties.getContextPath()).thenReturn("/actuator");
final Registry registry = Mockito.mock(Registry.class);
this.lostJobCounter = Mockito.mock(Counter.class);
Mockito.when(registry.counter("genie.tasks.clusterChecker.lostJobs.rate")).thenReturn(this.lostJobCounter);
this.unableToUpdateJobCounter = Mockito.mock(Counter.class);
Mockito
.when(registry.counter("genie.tasks.clusterChecker.unableToUpdateJob.rate"))
.thenReturn(this.unableToUpdateJobCounter);
this.task = new ClusterCheckerTask(
this.hostName,
properties,
this.jobSearchService,
this.jobPersistenceService,
this.restTemplate,
serverProperties,
registry
);
}
/**
* Make sure run method works.
*
* @throws IOException on error
* @throws GenieException on error
*/
@Test
public void canRun() throws IOException, GenieException {
final String host1 = UUID.randomUUID().toString();
final String host2 = UUID.randomUUID().toString();
final String host3 = UUID.randomUUID().toString();
// Mock the 9 invocations for 3 calls to run
Mockito
.when(this.restTemplate.getForObject(Mockito.anyString(), Mockito.anyObject()))
.thenReturn("")
.thenThrow(new RestClientException("blah"))
.thenReturn("")
.thenReturn("")
.thenThrow(new RestClientException("blah"))
.thenReturn("")
.thenReturn("")
.thenThrow(new HttpServerErrorException(HttpStatus.INTERNAL_SERVER_ERROR, "",
("{\"status\":\"OUT_OF_SERVICE\", \"genie\": { \"status\": \"OUT_OF_SERVICE\"}, "
+ "\"db\": { \"status\": \"OUT_OF_SERVICE\"}}").getBytes(StandardCharsets.UTF_8),
StandardCharsets.UTF_8))
.thenReturn("")
.thenReturn("")
.thenThrow(new HttpServerErrorException(HttpStatus.INTERNAL_SERVER_ERROR, "",
("{\"status\":\"OUT_OF_SERVICE\", \"genie\": { \"status\": \"OUT_OF_SERVICE\"}, "
+ "\"db\": { \"status\": \"OUT_OF_SERVICE\"}}").getBytes(StandardCharsets.UTF_8),
StandardCharsets.UTF_8))
.thenReturn("")
.thenReturn("")
.thenThrow(new HttpServerErrorException(HttpStatus.INTERNAL_SERVER_ERROR, "",
("{\"status\":\"OUT_OF_SERVICE\", \"genie\": { \"status\": \"OUT_OF_SERVICE\"}, "
+ "\"db\": { \"status\": \"UP\"}}").getBytes(StandardCharsets.UTF_8),
StandardCharsets.UTF_8))
.thenReturn("")
.thenReturn("")
.thenThrow(new HttpServerErrorException(HttpStatus.INTERNAL_SERVER_ERROR, "",
("{\"status\":\"OUT_OF_SERVICE\", \"genie\": { \"status\": \"OUT_OF_SERVICE\"}, "
+ "\"db\": { \"status\": \"OUT_OF_SERVICE\"}}").getBytes(StandardCharsets.UTF_8),
StandardCharsets.UTF_8))
.thenReturn("");
final List<String> hostsRunningJobs = Lists.newArrayList(this.hostName, host1, host2, host3);
Mockito.when(this.jobSearchService.getAllHostsWithActiveJobs()).thenReturn(hostsRunningJobs);
final Job job1 = Mockito.mock(Job.class);
final String job1Id = UUID.randomUUID().toString();
Mockito.when(job1.getId()).thenReturn(Optional.of(job1Id));
final Job job2 = Mockito.mock(Job.class);
final String job2Id = UUID.randomUUID().toString();
Mockito.when(job2.getId()).thenReturn(Optional.of(job2Id));
final Job job3 = Mockito.mock(Job.class);
final String job3Id = UUID.randomUUID().toString();
Mockito.when(job3.getId()).thenReturn(Optional.of(job3Id));
final Job job4 = Mockito.mock(Job.class);
final String job4Id = UUID.randomUUID().toString();
Mockito.when(job4.getId()).thenReturn(Optional.of(job4Id));
Mockito
.when(this.jobSearchService.getAllActiveJobsOnHost(host2))
.thenReturn(Sets.newHashSet(job1, job2));
Mockito
.when(this.jobSearchService.getAllActiveJobsOnHost(host3))
.thenReturn(Sets.newHashSet(job3, job4));
Mockito
.doThrow(new RuntimeException("blah"))
.doNothing()
.when(this.jobPersistenceService)
.setJobCompletionInformation(
Mockito.eq(job1Id),
Mockito.eq(JobExecution.LOST_EXIT_CODE),
Mockito.eq(JobStatus.FAILED), Mockito.anyString(),
Mockito.eq(null),
Mockito.eq(null)
);
this.task.run();
Assert.assertThat(this.task.getErrorCountsSize(), Matchers.is(1));
this.task.run();
Assert.assertThat(this.task.getErrorCountsSize(), Matchers.is(1));
this.task.run();
Assert.assertThat(this.task.getErrorCountsSize(), Matchers.is(1));
this.task.run();
Assert.assertThat(this.task.getErrorCountsSize(), Matchers.is(0));
this.task.run();
Assert.assertThat(this.task.getErrorCountsSize(), Matchers.is(0));
this.task.run();
Assert.assertThat(this.task.getErrorCountsSize(), Matchers.is(1));
Mockito.verify(this.jobPersistenceService, Mockito.times(2))
.setJobCompletionInformation(
Mockito.eq(job1Id),
Mockito.eq(JobExecution.LOST_EXIT_CODE),
Mockito.eq(JobStatus.FAILED), Mockito.anyString(),
Mockito.eq(null),
Mockito.eq(null)
);
Mockito.verify(this.jobPersistenceService, Mockito.atLeast(1))
.setJobCompletionInformation(
Mockito.eq(job2Id),
Mockito.eq(JobExecution.LOST_EXIT_CODE),
Mockito.eq(JobStatus.FAILED), Mockito.anyString(),
Mockito.eq(null),
Mockito.eq(null)
);
Mockito.verify(this.lostJobCounter, Mockito.atLeast(2)).increment();
Mockito.verify(this.unableToUpdateJobCounter, Mockito.times(1)).increment();
}
/**
* Make sure we get the right schedule type.
*/
@Test
public void canGetScheduleType() {
Assert.assertThat(this.task.getScheduleType(), Matchers.is(GenieTaskScheduleType.FIXED_RATE));
}
/**
* Make sure the trigger is null.
*/
@Test(expected = UnsupportedOperationException.class)
public void canGetTrigger() {
this.task.getTrigger();
}
/**
* Make sure the get period returns the correct value.
*/
@Test
public void canGetFixedRate() {
Assert.assertThat(this.task.getFixedRate(), Matchers.is(300000L));
}
/**
* Make sure the trigger is null.
*/
@Test(expected = UnsupportedOperationException.class)
public void canGetFixedDelay() {
this.task.getFixedDelay();
}
}