blob: 9b313e95243c7850c2aa073c39c7b2dbd25723ef [file] [log] [blame]
/*
* 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.topology.addservice;
import static org.easymock.EasyMock.expect;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import org.apache.ambari.server.AmbariException;
import org.apache.ambari.server.actionmanager.ActionManager;
import org.apache.ambari.server.actionmanager.RequestFactory;
import org.apache.ambari.server.controller.AddServiceRequest;
import org.apache.ambari.server.controller.AmbariManagementController;
import org.apache.ambari.server.controller.internal.Stack;
import org.apache.ambari.server.state.Cluster;
import org.apache.ambari.server.state.ConfigHelper;
import org.apache.ambari.server.state.SecurityType;
import org.apache.ambari.server.state.Service;
import org.apache.ambari.server.state.StackId;
import org.apache.ambari.server.topology.Configuration;
import org.apache.ambari.server.topology.SecurityConfiguration;
import org.apache.ambari.server.topology.StackFactory;
import org.easymock.EasyMockSupport;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
public class RequestValidatorTest extends EasyMockSupport {
private final AddServiceRequest request = createNiceMock(AddServiceRequest.class);
private final Cluster cluster = createMock(Cluster.class);
private final AmbariManagementController controller = createNiceMock(AmbariManagementController.class);
private final ConfigHelper configHelper = createMock(ConfigHelper.class);
private final StackFactory stackFactory = createNiceMock(StackFactory.class);
private final RequestValidator validator = new RequestValidator(request, cluster, controller, configHelper, stackFactory);
@Before
public void setUp() {
validator.setState(RequestValidator.State.INITIAL);
expect(cluster.getClusterName()).andReturn("TEST").anyTimes();
expect(cluster.getServices()).andStubReturn(ImmutableMap.of());
expect(request.getServices()).andStubReturn(ImmutableSet.of());
expect(request.getComponents()).andStubReturn(ImmutableSet.of());
}
@After
public void tearDown() {
resetAll();
}
@Test
public void cannotConstructInvalidRequestInfo() {
assertThrows(IllegalStateException.class, () -> validator.createValidServiceInfo(null, null));
Stack stack = simpleMockStack();
Map<String, Map<String, Set<String>>> newServices = someNewServices();
Configuration config = Configuration.newEmpty();
validator.setState(RequestValidator.State.INITIAL.with(stack));
assertThrows(IllegalStateException.class, () -> validator.createValidServiceInfo(null, null));
validator.setState(validator.getState().with(config));
assertThrows(IllegalStateException.class, () -> validator.createValidServiceInfo(null, null));
validator.setState(RequestValidator.State.INITIAL.withNewServices(newServices));
assertThrows(IllegalStateException.class, () -> validator.createValidServiceInfo(null, null));
validator.setState(validator.getState().with(stack));
assertThrows(IllegalStateException.class, () -> validator.createValidServiceInfo(null, null));
validator.setState(RequestValidator.State.INITIAL.with(config));
assertThrows(IllegalStateException.class, () -> validator.createValidServiceInfo(null, null));
validator.setState(validator.getState().withNewServices(newServices));
assertThrows(IllegalStateException.class, () -> validator.createValidServiceInfo(null, null));
}
@Test
public void canConstructValidRequestInfo() {
validator.setState(
RequestValidator.State.INITIAL
.withNewServices(someNewServices())
.with(simpleMockStack())
.with(Configuration.newEmpty())
);
ActionManager actionManager = createNiceMock(ActionManager.class);
RequestFactory requestFactory = createNiceMock(RequestFactory.class);
replayAll();
AddServiceInfo addServiceInfo = validator.createValidServiceInfo(actionManager, requestFactory);
assertNotNull(addServiceInfo);
assertSame(request, addServiceInfo.getRequest());
assertEquals(cluster.getClusterName(), addServiceInfo.clusterName());
assertSame(validator.getState().getConfig(), addServiceInfo.getConfig());
assertSame(validator.getState().getStack(), addServiceInfo.getStack());
assertEquals(validator.getState().getNewServices(), addServiceInfo.newServices());
}
@Test
public void cannotConstructTwice() {
ActionManager actionManager = createNiceMock(ActionManager.class);
RequestFactory requestFactory = createNiceMock(RequestFactory.class);
replayAll();
validator.setState(
RequestValidator.State.INITIAL
.withNewServices(someNewServices())
.with(simpleMockStack())
.with(Configuration.newEmpty())
);
validator.createValidServiceInfo(actionManager, requestFactory);
assertThrows(IllegalStateException.class, () -> validator.createValidServiceInfo(actionManager, requestFactory));
}
@Test
public void reportsUnknownStackFromRequest() throws Exception {
StackId requestStackId = new StackId("HDP", "123");
expect(request.getStackId()).andReturn(Optional.of(requestStackId)).anyTimes();
expect(stackFactory.createStack(requestStackId.getStackName(), requestStackId.getStackVersion(), controller)).andThrow(new AmbariException("Stack not found"));
replayAll();
IllegalArgumentException e = assertThrows(IllegalArgumentException.class, validator::validateStack);
assertTrue(e.getMessage().contains(requestStackId.toString()));
assertNull(validator.getState().getStack());
}
@Test
public void reportsUnknownStackFromCluster() throws Exception {
StackId clusterStackId = new StackId("CLUSTER", "555");
expect(request.getStackId()).andReturn(Optional.empty()).anyTimes();
expect(cluster.getCurrentStackVersion()).andReturn(clusterStackId);
expect(stackFactory.createStack(clusterStackId.getStackName(), clusterStackId.getStackVersion(), controller)).andThrow(new AmbariException("Stack not found"));
replayAll();
IllegalStateException e = assertThrows(IllegalStateException.class, validator::validateStack);
assertTrue(e.getMessage().contains(clusterStackId.toString()));
assertNull(validator.getState().getStack());
}
@Test
public void useClusterStackIfAbsentInRequest() throws Exception {
StackId clusterStackId = new StackId("CLUSTER", "123");
Stack expectedStack = createNiceMock(Stack.class);
expect(request.getStackId()).andReturn(Optional.empty()).anyTimes();
expect(cluster.getCurrentStackVersion()).andReturn(clusterStackId);
expect(stackFactory.createStack(clusterStackId.getStackName(), clusterStackId.getStackVersion(), controller)).andReturn(expectedStack);
replayAll();
validator.validateStack();
assertSame(expectedStack, validator.getState().getStack());
}
@Test
public void acceptsKnownServices() {
expect(request.getServices()).andReturn(ImmutableSet.of(AddServiceRequest.Service.of("KAFKA")));
validator.setState(RequestValidator.State.INITIAL.with(simpleMockStack()));
replayAll();
validator.validateServicesAndComponents();
Map<String, Map<String, Set<String>>> expectedNewServices = ImmutableMap.of(
"KAFKA", ImmutableMap.of()
);
assertEquals(expectedNewServices, validator.getState().getNewServices());
}
@Test
public void acceptsKnownComponents() {
expect(request.getComponents()).andReturn(ImmutableSet.of(AddServiceRequest.Component.of("KAFKA_BROKER", "c7401.ambari.apache.org")));
validator.setState(RequestValidator.State.INITIAL.with(simpleMockStack()));
replayAll();
validator.validateServicesAndComponents();
Map<String, Map<String, Set<String>>> expectedNewServices = ImmutableMap.of(
"KAFKA", ImmutableMap.of("KAFKA_BROKER", ImmutableSet.of("c7401.ambari.apache.org"))
);
assertEquals(expectedNewServices, validator.getState().getNewServices());
}
@Test
public void rejectsUnknownService() {
String serviceName = "UNKNOWN_SERVICE";
expect(request.getServices()).andReturn(ImmutableSet.of(AddServiceRequest.Service.of(serviceName)));
validator.setState(RequestValidator.State.INITIAL.with(simpleMockStack()));
replayAll();
IllegalArgumentException e = assertThrows(IllegalArgumentException.class, validator::validateServicesAndComponents);
assertTrue(e.getMessage().contains(serviceName));
assertNull(validator.getState().getNewServices());
}
@Test
public void rejectsUnknownComponent() {
String componentName = "UNKNOWN_COMPONENT";
expect(request.getComponents()).andReturn(ImmutableSet.of(AddServiceRequest.Component.of(componentName, "c7401.ambari.apache.org")));
validator.setState(RequestValidator.State.INITIAL.with(simpleMockStack()));
replayAll();
IllegalArgumentException e = assertThrows(IllegalArgumentException.class, validator::validateServicesAndComponents);
assertTrue(e.getMessage().contains(componentName));
assertNull(validator.getState().getNewServices());
}
@Test
public void rejectsExistingServiceForService() {
String serviceName = "KAFKA";
expect(cluster.getServices()).andReturn(ImmutableMap.of(serviceName, createNiceMock(Service.class))).anyTimes();
expect(request.getServices()).andReturn(ImmutableSet.of(AddServiceRequest.Service.of(serviceName)));
validator.setState(RequestValidator.State.INITIAL.with(simpleMockStack()));
replayAll();
IllegalArgumentException e = assertThrows(IllegalArgumentException.class, validator::validateServicesAndComponents);
assertTrue(e.getMessage().contains(serviceName));
assertNull(validator.getState().getNewServices());
}
@Test
public void rejectsExistingServiceForComponent() {
String serviceName = "KAFKA";
String componentName = "KAFKA_BROKER";
expect(cluster.getServices()).andReturn(ImmutableMap.of(serviceName, createNiceMock(Service.class))).anyTimes();
expect(request.getComponents()).andReturn(ImmutableSet.of(AddServiceRequest.Component.of(componentName, "c7401.ambari.apache.org")));
validator.setState(RequestValidator.State.INITIAL.with(simpleMockStack()));
replayAll();
IllegalArgumentException e = assertThrows(IllegalArgumentException.class, validator::validateServicesAndComponents);
assertTrue(e.getMessage().contains(serviceName));
assertTrue(e.getMessage().contains(componentName));
assertNull(validator.getState().getNewServices());
}
@Test
public void rejectsEmptyServiceAndComponentList() {
replayAll();
assertThrows(IllegalArgumentException.class, validator::validateServicesAndComponents);
assertNull(validator.getState().getNewServices());
}
@Test
public void acceptsKnownHosts() {
Set<String> requestHosts = ImmutableSet.of("c7401.ambari.apache.org", "c7402.ambari.apache.org");
Set<String> otherHosts = ImmutableSet.of("c7403.ambari.apache.org", "c7404.ambari.apache.org");
Set<String> clusterHosts = Sets.union(requestHosts, otherHosts);
expect(cluster.getHostNames()).andReturn(clusterHosts).anyTimes();
validator.setState(RequestValidator.State.INITIAL.withNewServices(ImmutableMap.of(
"KAFKA", ImmutableMap.of("KAFKA_BROKER", requestHosts)
)));
replayAll();
validator.validateHosts();
}
@Test
public void rejectsUnknownHosts() {
Set<String> clusterHosts = ImmutableSet.of("c7401.ambari.apache.org", "c7402.ambari.apache.org");
Set<String> otherHosts = ImmutableSet.of("c7403.ambari.apache.org", "c7404.ambari.apache.org");
Set<String> requestHosts = ImmutableSet.copyOf(Sets.union(clusterHosts, otherHosts));
expect(cluster.getHostNames()).andReturn(clusterHosts).anyTimes();
validator.setState(RequestValidator.State.INITIAL.withNewServices(ImmutableMap.of(
"KAFKA", ImmutableMap.of("KAFKA_BROKER", requestHosts)
)));
replayAll();
IllegalArgumentException e = assertThrows(IllegalArgumentException.class, validator::validateHosts);
assertTrue(e.getMessage(), e.getMessage().contains("host"));
}
@Test
public void acceptsAbsentSecurityWhenClusterHasKerberos() {
expect(cluster.getSecurityType()).andReturn(SecurityType.KERBEROS).anyTimes();
expect(request.getSecurity()).andReturn(Optional.empty()).anyTimes();
replayAll();
validator.validateSecurity();
}
@Test
public void acceptsAbsentSecurityWhenClusterHasNone() {
expect(cluster.getSecurityType()).andReturn(SecurityType.NONE).anyTimes();
expect(request.getSecurity()).andReturn(Optional.empty()).anyTimes();
replayAll();
validator.validateSecurity();
}
@Test
public void acceptsMatchingKerberosSecurity() {
expect(cluster.getSecurityType()).andReturn(SecurityType.KERBEROS).anyTimes();
expect(request.getSecurity()).andReturn(Optional.of(new SecurityConfiguration(SecurityType.KERBEROS))).anyTimes();
replayAll();
validator.validateSecurity();
}
@Test
public void acceptsMatchingNoneSecurity() {
expect(cluster.getSecurityType()).andReturn(SecurityType.NONE).anyTimes();
expect(request.getSecurity()).andReturn(Optional.of(SecurityConfiguration.NONE)).anyTimes();
replayAll();
validator.validateSecurity();
}
@Test
public void rejectsNoneSecurityWhenClusterHasKerberos() {
expect(cluster.getSecurityType()).andReturn(SecurityType.KERBEROS).anyTimes();
expect(request.getSecurity()).andReturn(Optional.of(SecurityConfiguration.NONE)).anyTimes();
replayAll();
IllegalArgumentException e = assertThrows(IllegalArgumentException.class, validator::validateSecurity);
assertTrue(e.getMessage().contains("KERBEROS"));
}
@Test
public void rejectsKerberosSecurityWhenClusterHasNone() {
expect(cluster.getSecurityType()).andReturn(SecurityType.NONE).anyTimes();
expect(request.getSecurity()).andReturn(Optional.of(new SecurityConfiguration(SecurityType.KERBEROS))).anyTimes();
replayAll();
assertThrows(IllegalArgumentException.class, validator::validateSecurity);
IllegalArgumentException e = assertThrows(IllegalArgumentException.class, validator::validateSecurity);
assertTrue(e.getMessage().contains("KERBEROS"));
}
@Test
public void combinesRequestConfigWithClusterAndStack() throws AmbariException {
Configuration requestConfig = Configuration.newEmpty();
requestConfig.setProperty("kafka-broker", "zookeeper.connect", "zookeeper.connect:request");
requestConfig.setProperty("kafka-env", "custom_property", "custom_property:request");
expect(request.getConfiguration()).andReturn(requestConfig.copy()).anyTimes();
Configuration clusterConfig = Configuration.newEmpty();
clusterConfig.setProperty("zookeeper-env", "zk_user", "zk_user:cluster_level");
expect(configHelper.calculateExistingConfigs(cluster)).andReturn(clusterConfig.asPair()).anyTimes();
Stack stack = simpleMockStack();
Configuration stackConfig = Configuration.newEmpty();
stackConfig.setProperty("zookeeper-env", "zk_user", "zk_user:stack_default");
stackConfig.setProperty("zookeeper-env", "zk_log_dir", "zk_log_dir:stack_default");
stackConfig.setProperty("kafka-broker", "zookeeper.connect", "zookeeper.connect:stack_default");
expect(stack.getValidDefaultConfig()).andReturn(stackConfig).anyTimes();
replayAll();
validator.setState(RequestValidator.State.INITIAL.with(stack));
validator.validateConfiguration();
Configuration config = validator.getState().getConfig();
verifyConfigOverrides(requestConfig, clusterConfig, stackConfig, config);
}
@Test
public void rejectsKerberosEnvChange() {
Configuration requestConfig = Configuration.newEmpty();
requestConfig.setProperty("kerberos-env", "some-property", "some-value");
expect(request.getConfiguration()).andReturn(requestConfig.copy()).anyTimes();
replayAll();
assertThrows(IllegalArgumentException.class, validator::validateConfiguration);
}
@Test
public void rejectsKrb5ConfChange() {
Configuration requestConfig = Configuration.newEmpty();
requestConfig.setProperty("krb5-conf", "some-property", "some-value");
expect(request.getConfiguration()).andReturn(requestConfig.copy()).anyTimes();
replayAll();
assertThrows(IllegalArgumentException.class, validator::validateConfiguration);
}
private static void verifyConfigOverrides(Configuration requestConfig, Configuration clusterConfig, Configuration stackConfig, Configuration actualConfig) {
requestConfig.getProperties().forEach(
(type, properties) -> properties.forEach(
(propertyName, propertyValue) -> assertEquals(type + "/" + propertyName, propertyValue, actualConfig.getPropertyValue(type, propertyName))
)
);
clusterConfig.getProperties().forEach(
(type, properties) -> properties.forEach(
(propertyName, propertyValue) -> {
if (!requestConfig.isPropertySet(type, propertyName)) {
assertEquals(type + "/" + propertyName, propertyValue, actualConfig.getPropertyValue(type, propertyName));
}
}
)
);
stackConfig.getProperties().forEach(
(type, properties) -> properties.forEach(
(propertyName, propertyValue) -> {
if (!requestConfig.isPropertySet(type, propertyName) && !clusterConfig.isPropertySet(type, propertyName)) {
assertEquals(type + "/" + propertyName, propertyValue, actualConfig.getPropertyValue(type, propertyName));
}
}
)
);
}
private Stack simpleMockStack() {
Stack stack = createNiceMock(Stack.class);
Set<String> stackServices = ImmutableSet.of("KAFKA", "ZOOKEEPER");
expect(stack.getServices()).andReturn(stackServices).anyTimes();
expect(stack.getServiceForComponent("KAFKA_BROKER")).andReturn("KAFKA").anyTimes();
expect(stack.getServiceForComponent("ZOOKEEPER_SERVER")).andReturn("ZOOKEEPER").anyTimes();
expect(stack.getServiceForComponent("ZOOKEEPER_CLIENT")).andReturn("ZOOKEEPER").anyTimes();
return stack;
}
private static Map<String, Map<String, Set<String>>> someNewServices() {
return ImmutableMap.of(
"KAFKA", ImmutableMap.of("KAFKA_BROKER", ImmutableSet.of("c7401.ambari.apache.org"))
);
}
private static <T extends Throwable> T assertThrows(Class<T> expectedException, Runnable code) {
try {
code.run();
} catch (Throwable t) {
if (expectedException.isInstance(t)) {
return expectedException.cast(t);
}
throw new AssertionError("Expected exception: " + expectedException + " but " + t.getClass() + " was thrown instead");
}
throw new AssertionError("Expected exception: " + expectedException + ", but was not thrown");
}
}