private synchronized RequestStatusResponse updateCluster()

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;
    }
  }