/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.ambari.server.api.services.stackadvisor.commands;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.Map;
import javax.ws.rs.WebApplicationException;
import org.apache.ambari.server.api.services.AmbariMetaInfo;
import org.apache.ambari.server.api.services.stackadvisor.StackAdvisorException;
import org.apache.ambari.server.api.services.stackadvisor.StackAdvisorRequest;
import org.apache.ambari.server.api.services.stackadvisor.StackAdvisorRequest.StackAdvisorRequestBuilder;
import org.apache.ambari.server.api.services.stackadvisor.StackAdvisorRequestException;
import org.apache.ambari.server.api.services.stackadvisor.StackAdvisorResponse;
import org.apache.ambari.server.api.services.stackadvisor.StackAdvisorRunner;
import org.apache.ambari.server.api.services.stackadvisor.commands.StackAdvisorCommand.StackAdvisorData;
import org.apache.commons.io.FileUtils;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.annotate.JsonProperty;
import org.codehaus.jackson.node.ArrayNode;
import org.codehaus.jackson.node.ObjectNode;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
/**
* StackAdvisorCommand unit tests.
*/
public class StackAdvisorCommandTest {
private TemporaryFolder temp = new TemporaryFolder();
@Before
public void setUp() throws IOException {
temp.create();
}
@After
public void tearDown() throws IOException {
temp.delete();
}
@Test(expected = StackAdvisorException.class)
public void testInvoke_invalidRequest_throwsException() throws StackAdvisorException {
File recommendationsDir = temp.newFolder("recommendationDir");
String recommendationsArtifactsLifetime = "1w";
String stackAdvisorScript = "echo";
int requestId = 0;
StackAdvisorRunner saRunner = mock(StackAdvisorRunner.class);
AmbariMetaInfo metaInfo = mock(AmbariMetaInfo.class);
doReturn(Collections.emptyList()).when(metaInfo).getStackParentVersions(anyString(), anyString());
StackAdvisorCommand<TestResource> command = spy(new TestStackAdvisorCommand(recommendationsDir, recommendationsArtifactsLifetime,
stackAdvisorScript, requestId, saRunner, metaInfo));
StackAdvisorRequest request = StackAdvisorRequestBuilder.forStack("stackName", "stackVersion")
.build();
doThrow(new StackAdvisorException("message")).when(command).validate(request);
command.invoke(request);
assertTrue(false);
}
@Test(expected = StackAdvisorException.class)
public void testInvoke_saRunnerNotSucceed_throwsException() throws StackAdvisorException {
File recommendationsDir = temp.newFolder("recommendationDir");
String stackAdvisorScript = "echo";
String recommendationsArtifactsLifetime = "1w";
int requestId = 0;
StackAdvisorRunner saRunner = mock(StackAdvisorRunner.class);
AmbariMetaInfo metaInfo = mock(AmbariMetaInfo.class);
doReturn(Collections.emptyList()).when(metaInfo).getStackParentVersions(anyString(), anyString());
StackAdvisorCommand<TestResource> command = spy(new TestStackAdvisorCommand(recommendationsDir, recommendationsArtifactsLifetime,
stackAdvisorScript, requestId, saRunner, metaInfo));
StackAdvisorRequest request = StackAdvisorRequestBuilder.forStack("stackName", "stackVersion")
.build();
String hostsJSON = "{\"hosts\" : \"localhost\"";
String servicesJSON = "{\"services\" : \"HDFS\"";
StackAdvisorData data = new StackAdvisorData(hostsJSON, servicesJSON);
doReturn(hostsJSON).when(command).getHostsInformation(request);
doReturn(servicesJSON).when(command).getServicesInformation(request);
doReturn(data).when(command)
.adjust(any(StackAdvisorData.class), any(StackAdvisorRequest.class));
doThrow(new StackAdvisorRequestException("error")).when(saRunner)
.runScript(any(String.class), any(StackAdvisorCommandType.class), any(File.class));
command.invoke(request);
assertTrue(false);
}
@Test(expected = WebApplicationException.class)
public void testInvoke_adjustThrowsException_throwsException() throws StackAdvisorException {
File recommendationsDir = temp.newFolder("recommendationDir");
String stackAdvisorScript = "echo";
String recommendationsArtifactsLifetime = "1w";
int requestId = 0;
StackAdvisorRunner saRunner = mock(StackAdvisorRunner.class);
AmbariMetaInfo metaInfo = mock(AmbariMetaInfo.class);
doReturn(Collections.emptyList()).when(metaInfo).getStackParentVersions(anyString(), anyString());
StackAdvisorCommand<TestResource> command = spy(new TestStackAdvisorCommand(recommendationsDir, recommendationsArtifactsLifetime,
stackAdvisorScript, requestId, saRunner, metaInfo));
StackAdvisorRequest request = StackAdvisorRequestBuilder.forStack("stackName", "stackVersion")
.build();
doReturn("{\"hosts\" : \"localhost\"").when(command).getHostsInformation(request);
doReturn("{\"services\" : \"HDFS\"").when(command).getServicesInformation(request);
doThrow(new WebApplicationException()).when(command).adjust(any(StackAdvisorData.class),
any(StackAdvisorRequest.class));
doThrow(new StackAdvisorException("error")).when(saRunner)
.runScript(any(String.class), any(StackAdvisorCommandType.class), any(File.class));
command.invoke(request);
assertTrue(false);
}
@Test
public void testInvoke_success() throws StackAdvisorException {
String expected = "success";
final String testResourceString = String.format("{\"type\": \"%s\"}", expected);
final File recommendationsDir = temp.newFolder("recommendationDir");
String stackAdvisorScript = "echo";
String recommendationsArtifactsLifetime = "1w";
final int requestId = 2;
StackAdvisorRunner saRunner = mock(StackAdvisorRunner.class);
AmbariMetaInfo metaInfo = mock(AmbariMetaInfo.class);
doReturn(Collections.emptyList()).when(metaInfo).getStackParentVersions(anyString(), anyString());
final StackAdvisorCommand<TestResource> command = spy(new TestStackAdvisorCommand(
recommendationsDir, recommendationsArtifactsLifetime, stackAdvisorScript, requestId, saRunner, metaInfo));
StackAdvisorRequest request = StackAdvisorRequestBuilder.forStack("stackName", "stackVersion")
.build();
String hostsJSON = "{\"hosts\" : \"localhost\"";
String servicesJSON = "{\"services\" : \"HDFS\"";
StackAdvisorData data = new StackAdvisorData(hostsJSON, servicesJSON);
doReturn(hostsJSON).when(command).getHostsInformation(request);
doReturn(servicesJSON).when(command).getServicesInformation(request);
doReturn(data).when(command)
.adjust(any(StackAdvisorData.class), any(StackAdvisorRequest.class));
doAnswer(new Answer() {
public Object answer(InvocationOnMock invocation) throws Throwable {
String resultFilePath = String.format("%s/%s", requestId, command.getResultFileName());
File resultFile = new File(recommendationsDir, resultFilePath);
resultFile.getParentFile().mkdirs();
FileUtils.writeStringToFile(resultFile, testResourceString);
return null;
}
}).when(saRunner).runScript(any(String.class), any(StackAdvisorCommandType.class), any(File.class));
TestResource result = command.invoke(request);
assertEquals(expected, result.getType());
assertEquals(requestId, result.getId());
}
@Test
public void testPopulateStackHierarchy() throws Exception {
File file = mock(File.class);
String recommendationsArtifactsLifetime = "1w";
StackAdvisorRunner stackAdvisorRunner = mock(StackAdvisorRunner.class);
AmbariMetaInfo ambariMetaInfo = mock(AmbariMetaInfo.class);
StackAdvisorCommand<TestResource> cmd = new TestStackAdvisorCommand(file, recommendationsArtifactsLifetime, "test", 1,
stackAdvisorRunner, ambariMetaInfo);
ObjectNode objectNode = (ObjectNode) cmd.mapper.readTree("{\"Versions\": " +
"{\"stack_name\": \"stack\", \"stack_version\":\"1.0.0\"}}");
doReturn(Arrays.asList("0.9", "0.8")).when(ambariMetaInfo).getStackParentVersions("stack", "1.0.0");
cmd.populateStackHierarchy(objectNode);
JsonNode stackHierarchy = objectNode.get("Versions").get("stack_hierarchy");
assertNotNull(stackHierarchy);
JsonNode stackName = stackHierarchy.get("stack_name");
assertNotNull(stackName);
assertEquals("stack", stackName.asText());
ArrayNode stackVersions = (ArrayNode) stackHierarchy.get("stack_versions");
assertNotNull(stackVersions);
assertEquals(2, stackVersions.size());
Iterator<JsonNode> stackVersionsElements = stackVersions.getElements();
assertEquals("0.9", stackVersionsElements.next().asText());
assertEquals("0.8", stackVersionsElements.next().asText());
}
@Test
public void testPopulateAmbariServerProperties() throws Exception {
File file = mock(File.class);
String recommendationsArtifactsLifetime = "1w";
StackAdvisorRunner stackAdvisorRunner = mock(StackAdvisorRunner.class);
AmbariMetaInfo ambariMetaInfo = mock(AmbariMetaInfo.class);
StackAdvisorCommand<TestResource> cmd = new TestStackAdvisorCommand(file, recommendationsArtifactsLifetime, "test", 1,
stackAdvisorRunner, ambariMetaInfo);
ObjectNode objectNode = (ObjectNode) cmd.mapper.readTree("{\"Versions\": " +
"{\"stack_name\": \"stack\", \"stack_version\":\"1.0.0\"}}");
Map<String, String> props = Collections.singletonMap("a", "b");
doReturn(props).when(ambariMetaInfo).getAmbariServerProperties();
cmd.populateAmbariServerInfo(objectNode);
JsonNode serverProperties = objectNode.get("ambari-server-properties");
assertNotNull(serverProperties);
assertEquals("b", serverProperties.iterator().next().getTextValue());
}
@Test
public void testPopulateStackHierarchy_noParents() throws Exception {
File file = mock(File.class);
String recommendationsArtifactsLifetime = "1w";
StackAdvisorRunner stackAdvisorRunner = mock(StackAdvisorRunner.class);
AmbariMetaInfo ambariMetaInfo = mock(AmbariMetaInfo.class);
StackAdvisorCommand<TestResource> cmd = new TestStackAdvisorCommand(file, recommendationsArtifactsLifetime, "test", 1,
stackAdvisorRunner, ambariMetaInfo);
ObjectNode objectNode = (ObjectNode) cmd.mapper.readTree("{\"Versions\": " +
"{\"stack_name\": \"stack\", \"stack_version\":\"1.0.0\"}}");
doReturn(Collections.emptyList()).when(ambariMetaInfo).getStackParentVersions("stack", "1.0.0");
cmd.populateStackHierarchy(objectNode);
JsonNode stackHierarchy = objectNode.get("Versions").get("stack_hierarchy");
assertNotNull(stackHierarchy);
JsonNode stackName = stackHierarchy.get("stack_name");
assertNotNull(stackName);
assertEquals("stack", stackName.asText());
ArrayNode stackVersions = (ArrayNode) stackHierarchy.get("stack_versions");
assertNotNull(stackVersions);
assertEquals(0, stackVersions.size());
}
class TestStackAdvisorCommand extends StackAdvisorCommand<TestResource> {
public TestStackAdvisorCommand(File recommendationsDir, String recommendationsArtifactsLifetime, String stackAdvisorScript,
int requestId, StackAdvisorRunner saRunner, AmbariMetaInfo metaInfo) {
super(recommendationsDir, recommendationsArtifactsLifetime, stackAdvisorScript, requestId, saRunner, metaInfo);
}
@Override
protected void validate(StackAdvisorRequest request) throws StackAdvisorException {
// do nothing
}
@Override
protected String getResultFileName() {
return "result.json";
}
@Override
protected StackAdvisorCommandType getCommandType() {
return StackAdvisorCommandType.RECOMMEND_COMPONENT_LAYOUT;
}
@Override
protected TestResource updateResponse(StackAdvisorRequest request, TestResource response) {
return response;
}
}
public static class TestResource extends StackAdvisorResponse {
@JsonProperty
private String type;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
}