in ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java [1811:2177]
private synchronized RequestStatusResponse updateCluster(ClusterRequest request,
Map<String, String> requestProperties,
boolean fireAgentUpdates, boolean refreshCluster
)
throws AmbariException, AuthorizationException {
RequestStageContainer requestStageContainer = null;
if (request.getClusterId() == null
&& (request.getClusterName() == null
|| request.getClusterName().isEmpty())) {
throw new IllegalArgumentException("Invalid arguments, cluster id or cluster name should not be null");
}
LOG.info("Received a updateCluster request"
+ ", clusterId=" + request.getClusterId()
+ ", clusterName=" + request.getClusterName()
+ ", securityType=" + request.getSecurityType()
+ ", request=" + request);
final Cluster cluster;
if (request.getClusterId() == null) {
cluster = clusters.getCluster(request.getClusterName());
} else {
cluster = clusters.getClusterById(request.getClusterId());
}
List<ConfigurationRequest> desiredConfigs = request.getDesiredConfig();
if (desiredConfigs != null) {
for (ConfigurationRequest configurationRequest : desiredConfigs) {
if (StringUtils.isEmpty(configurationRequest.getVersionTag())) {
configurationRequest.setVersionTag(UUID.randomUUID().toString());
}
}
}
// Ensure the user has access to update this cluster
AuthorizationHelper.verifyAuthorization(ResourceType.CLUSTER, cluster.getResourceId(), RoleAuthorization.AUTHORIZATIONS_UPDATE_CLUSTER);
//save data to return configurations created
List<ConfigurationResponse> configurationResponses =
new LinkedList<>();
ServiceConfigVersionResponse serviceConfigVersionResponse = null;
boolean nonServiceConfigsChanged = false;
if (desiredConfigs != null && request.getServiceConfigVersionRequest() != null) {
String msg = "Unable to set desired configs and rollback at same time, request = " + request;
LOG.error(msg);
throw new IllegalArgumentException(msg);
}
// set the new name of the cluster if change is requested
if (request.getClusterName()!=null && !cluster.getClusterName().equals(request.getClusterName())) {
validateClusterName(request.getClusterName());
if (LOG.isDebugEnabled()) {
LOG.debug("Received cluster name change request from {} to {}", cluster.getClusterName(), request.getClusterName());
}
if(!AuthorizationHelper.isAuthorized(ResourceType.AMBARI, null, EnumSet.of(RoleAuthorization.AMBARI_RENAME_CLUSTER))) {
throw new AuthorizationException("The authenticated user does not have authorization to rename the cluster");
}
cluster.setClusterName(request.getClusterName());
}
//check if desired configs are available in request and they were changed
boolean isConfigurationCreationNeeded = false;
if (desiredConfigs != null) {
for (ConfigurationRequest desiredConfig : desiredConfigs) {
Map<String, String> requestConfigProperties = desiredConfig.getProperties();
Map<String,Map<String,String>> requestConfigAttributes = desiredConfig.getPropertiesAttributes();
// processing password properties
if(requestConfigProperties != null && !requestConfigProperties.isEmpty()) {
Map<PropertyInfo.PropertyType, Set<String>> propertiesTypes = cluster.getConfigPropertiesTypes(
desiredConfig.getType()
);
for (Entry<String, String> property : requestConfigProperties.entrySet()) {
String propertyName = property.getKey();
String propertyValue = property.getValue();
if ((propertiesTypes.containsKey(PropertyType.PASSWORD) &&
propertiesTypes.get(PropertyType.PASSWORD).contains(propertyName)) ||
(requestConfigAttributes != null && requestConfigAttributes.containsKey(PASSWORD) &&
requestConfigAttributes.get(PASSWORD).containsKey(propertyName) &&
requestConfigAttributes.get(PASSWORD).get(propertyName).equals("true"))) {
if (SecretReference.isSecret(propertyValue)) {
SecretReference ref = new SecretReference(propertyValue, cluster);
requestConfigProperties.put(propertyName, ref.getValue());
}
}
}
}
Config clusterConfig = cluster.getDesiredConfigByType(desiredConfig.getType());
Map<String, String> clusterConfigProperties = null;
Map<String,Map<String,String>> clusterConfigAttributes = null;
if (clusterConfig != null) {
clusterConfigProperties = clusterConfig.getProperties();
clusterConfigAttributes = clusterConfig.getPropertiesAttributes();
if (!isAttributeMapsEqual(requestConfigAttributes, clusterConfigAttributes)){
isConfigurationCreationNeeded = true;
break;
}
} else {
isConfigurationCreationNeeded = true;
break;
}
if (requestConfigProperties == null || requestConfigProperties.isEmpty()) {
Config existingConfig = cluster.getConfig(desiredConfig.getType(), desiredConfig.getVersionTag());
if (existingConfig != null) {
if (!StringUtils.equals(existingConfig.getTag(), clusterConfig.getTag())) {
isConfigurationCreationNeeded = true;
break;
}
}
}
if (requestConfigProperties != null && clusterConfigProperties != null) {
if (requestConfigProperties.size() != clusterConfigProperties.size()) {
isConfigurationCreationNeeded = true;
break;
} else {
if ( cluster.getServiceByConfigType(clusterConfig.getType()) != null && clusterConfig.getServiceConfigVersions().isEmpty() ) {
//If there's no service config versions containing this config (except cluster configs), recreate it even if exactly equal
LOG.warn("Existing desired config doesn't belong to any service config version, " +
"forcing config recreation, " +
"clusterName={}, type = {}, tag={}", cluster.getClusterName(), clusterConfig.getType(),
clusterConfig.getTag());
isConfigurationCreationNeeded = true;
break;
}
for (Entry<String, String> property : requestConfigProperties.entrySet()) {
if (!StringUtils.equals(property.getValue(), clusterConfigProperties.get(property.getKey()))) {
isConfigurationCreationNeeded = true;
break;
}
}
}
}
}
}
// set or create configuration mapping (and optionally create the map of properties)
if (isConfigurationCreationNeeded) {
if (!desiredConfigs.isEmpty()) {
Set<Config> configs = new HashSet<>();
String note = null;
for (ConfigurationRequest cr : desiredConfigs) {
String configType = cr.getType();
if (null != cr.getProperties()) {
// !!! empty property sets are supported, and need to be able to use
// previously-defined configs (revert)
Map<String, Config> all = cluster.getConfigsByType(configType);
if (null == all || // none set
!all.containsKey(cr.getVersionTag()) || // tag not set
cr.getProperties().size() > 0) { // properties to set
cr.setClusterName(cluster.getClusterName());
configurationResponses.add(createConfiguration(cr, refreshCluster));
LOG.info(MessageFormat.format("Applying configuration with tag ''{0}'' to cluster ''{1}'' for configuration type {2}",
cr.getVersionTag(),
request.getClusterName(),
configType));
}
}
note = cr.getServiceConfigVersionNote();
Config config = cluster.getConfig(configType, cr.getVersionTag());
if (null != config) {
configs.add(config);
}
}
if (!configs.isEmpty()) {
Map<String, Config> existingConfigTypeToConfig = new HashMap();
for (Config config : configs) {
Config existingConfig = cluster.getDesiredConfigByType(config.getType());
existingConfigTypeToConfig.put(config.getType(), existingConfig);
}
String authName = getAuthName();
serviceConfigVersionResponse = cluster.addDesiredConfig(authName, configs, note);
if (serviceConfigVersionResponse != null) {
List<String> hosts = serviceConfigVersionResponse.getHosts();
int numAffectedHosts = null != hosts ? hosts.size() : 0;
configChangeLog.info("(configchange) Changing default config. cluster: '{}', changed by: '{}', service_name: '{}', config_group: '{}', num affected hosts during creation: '{}', note: '{}'",
request.getClusterName(), authName, serviceConfigVersionResponse.getServiceName(),
serviceConfigVersionResponse.getGroupName(), numAffectedHosts, serviceConfigVersionResponse.getNote());
for (Config config : configs) {
config.getVersion();
serviceConfigVersionResponse.getNote();
configChangeLog.info("(configchange) type: '{}', tag: '{}', version: '{}'", config.getType(), config.getTag(), config.getVersion());
Map<String, String> configKeyToAction = getConfigKeyDeltaToAction(existingConfigTypeToConfig.get(config.getType()), config.getProperties());
Map<String, List<String>> actionToListConfigKeys = inverseMapByValue(configKeyToAction);
if (!actionToListConfigKeys.isEmpty()) {
String configOutput = getActionToConfigListAsString(actionToListConfigKeys);
configChangeLog.info("(configchange) Config type '{}' was modified with the following keys, {}", config.getType(), configOutput);
}
}
} else {
nonServiceConfigsChanged = true;
}
}
}
}
StackId currentVersion = cluster.getCurrentStackVersion();
StackId desiredVersion = cluster.getDesiredStackVersion();
// Set the current version value if its not already set
if (currentVersion == null) {
if(!AuthorizationHelper.isAuthorized(ResourceType.CLUSTER, cluster.getResourceId(), EnumSet.of(RoleAuthorization.CLUSTER_UPGRADE_DOWNGRADE_STACK))) {
throw new AuthorizationException("The authenticated user does not have authorization to modify stack version");
}
cluster.setCurrentStackVersion(desiredVersion);
}
boolean requiresHostListUpdate =
request.getHostNames() != null && !request.getHostNames().isEmpty();
if (requiresHostListUpdate) {
clusters.mapAndPublishHostsToCluster(
request.getHostNames(), request.getClusterName());
}
// set the provisioning state of the cluster
if (null != request.getProvisioningState()) {
State oldProvisioningState = cluster.getProvisioningState();
State provisioningState = State.valueOf(request.getProvisioningState());
if (provisioningState != State.INIT
&& provisioningState != State.INSTALLED) {
LOG.warn(
"Invalid cluster provisioning state {} cannot be set on the cluster {}",
provisioningState, request.getClusterName());
throw new IllegalArgumentException(
"Invalid cluster provisioning state "
+ provisioningState + " cannot be set on cluster "
+ request.getClusterName());
}
if (provisioningState != oldProvisioningState) {
boolean isStateTransitionValid = State.isValidDesiredStateTransition(
oldProvisioningState, provisioningState);
if (!isStateTransitionValid) {
LOG.warn(
"Invalid cluster provisioning state {} cannot be set on the cluster {} because the current state is {}",
provisioningState, request.getClusterName(), oldProvisioningState);
throw new AmbariException("Invalid transition for"
+ " cluster provisioning state" + ", clusterName="
+ cluster.getClusterName() + ", clusterId="
+ cluster.getClusterId() + ", currentProvisioningState="
+ oldProvisioningState + ", newProvisioningState="
+ provisioningState);
}
}
cluster.setProvisioningState(provisioningState);
}
if (null != request.getServiceConfigVersionRequest()) {
if(!AuthorizationHelper.isAuthorized(ResourceType.CLUSTER, cluster.getResourceId(), EnumSet.of(RoleAuthorization.SERVICE_MODIFY_CONFIGS))) {
throw new AuthorizationException("The authenticated user does not have authorization to modify service configurations");
}
ServiceConfigVersionRequest serviceConfigVersionRequest = request.getServiceConfigVersionRequest();
if (StringUtils.isEmpty(serviceConfigVersionRequest.getServiceName()) ||
null == serviceConfigVersionRequest.getVersion()) {
String msg = "Service name and version should be specified in service config version";
LOG.error(msg);
throw new IllegalArgumentException(msg);
}
serviceConfigVersionResponse = cluster.setServiceConfigVersion(serviceConfigVersionRequest.getServiceName(),
serviceConfigVersionRequest.getVersion(), getAuthName(),
serviceConfigVersionRequest.getNote());
}
if (serviceConfigVersionResponse != null) {
if (!configurationResponses.isEmpty()) {
serviceConfigVersionResponse.setConfigurations(configurationResponses);
}
ClusterResponse clusterResponse =
new ClusterResponse(cluster.getClusterId(), cluster.getClusterName(), null, null, null, 0, null, null);
Map<String, Collection<ServiceConfigVersionResponse>> map =
new HashMap<>();
map.put(serviceConfigVersionResponse.getServiceName(), Collections.singletonList(serviceConfigVersionResponse));
clusterResponse.setDesiredServiceConfigVersions(map);
//workaround to be able to retrieve update results in resource provider
//as this method only expected to return request response
saveClusterUpdate(request, clusterResponse);
}
// set the new security type of the cluster if change is requested
SecurityType securityType = request.getSecurityType();
if(securityType != null) {
// if any custom operations are valid and requested, the process of executing them should be initiated,
// most of the validation logic will be left to the KerberosHelper to avoid polluting the controller
if (kerberosHelper.shouldExecuteCustomOperations(securityType, requestProperties)) {
if(!AuthorizationHelper.isAuthorized(ResourceType.CLUSTER, cluster.getResourceId(), EnumSet.of(RoleAuthorization.CLUSTER_TOGGLE_KERBEROS))) {
throw new AuthorizationException("The authenticated user does not have authorization to perform Kerberos-specific operations");
}
try {
requestStageContainer = kerberosHelper.executeCustomOperations(cluster, requestProperties, requestStageContainer,
kerberosHelper.getManageIdentitiesDirective(requestProperties));
} catch (KerberosOperationException e) {
throw new IllegalArgumentException(e.getMessage(), e);
}
} else {
// If force_toggle_kerberos is not specified, null will be returned. Therefore, perform an
// equals check to yield true if the result is Boolean.TRUE, otherwise false.
boolean forceToggleKerberos = kerberosHelper.getForceToggleKerberosDirective(requestProperties);
if (forceToggleKerberos || (cluster.getSecurityType() != securityType)) {
LOG.info("Received cluster security type change request from {} to {} (forced: {})",
cluster.getSecurityType().name(), securityType.name(), forceToggleKerberos);
if ((securityType == SecurityType.KERBEROS) || (securityType == SecurityType.NONE)) {
if (!AuthorizationHelper.isAuthorized(ResourceType.CLUSTER, cluster.getResourceId(), EnumSet.of(RoleAuthorization.CLUSTER_TOGGLE_KERBEROS))) {
throw new AuthorizationException("The authenticated user does not have authorization to enable or disable Kerberos");
}
// Since the security state of the cluster has changed, invoke toggleKerberos to handle
// adding or removing Kerberos from the cluster. This may generate multiple stages
// or not depending the current state of the cluster.
try {
requestStageContainer = kerberosHelper.toggleKerberos(cluster, securityType, requestStageContainer,
kerberosHelper.getManageIdentitiesDirective(requestProperties));
} catch (KerberosOperationException e) {
throw new IllegalArgumentException(e.getMessage(), e);
}
} else {
throw new IllegalArgumentException(String.format("Unexpected security type encountered: %s", securityType.name()));
}
cluster.setSecurityType(securityType);
}
}
}
if (fireAgentUpdates && (serviceConfigVersionResponse != null || nonServiceConfigsChanged)) {
configHelper.updateAgentConfigs(Collections.singleton(cluster.getClusterName()));
}
if (requestStageContainer != null) {
requestStageContainer.persist();
return requestStageContainer.getRequestStatusResponse();
} else {
return null;
}
}