/*
 * 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.serveraction.upgrades;

import static org.apache.ambari.server.serveraction.upgrades.PreconfigureKerberosAction.UPGRADE_DIRECTION_KEY;
import static org.easymock.EasyMock.anyLong;
import static org.easymock.EasyMock.anyObject;
import static org.easymock.EasyMock.anyString;
import static org.easymock.EasyMock.capture;
import static org.easymock.EasyMock.eq;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.expectLastCall;
import static org.easymock.EasyMock.getCurrentArguments;
import static org.easymock.EasyMock.newCapture;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import jakarta.persistence.EntityManager;

import org.apache.ambari.server.AmbariException;
import org.apache.ambari.server.actionmanager.ActionDBAccessor;
import org.apache.ambari.server.actionmanager.ActionDBAccessorImpl;
import org.apache.ambari.server.actionmanager.ActionManager;
import org.apache.ambari.server.actionmanager.HostRoleCommandFactory;
import org.apache.ambari.server.actionmanager.HostRoleCommandFactoryImpl;
import org.apache.ambari.server.actionmanager.RequestFactory;
import org.apache.ambari.server.actionmanager.StageFactory;
import org.apache.ambari.server.agent.ExecutionCommand;
import org.apache.ambari.server.api.services.AmbariMetaInfo;
import org.apache.ambari.server.api.services.stackadvisor.StackAdvisorHelper;
import org.apache.ambari.server.api.services.stackadvisor.StackAdvisorRequest;
import org.apache.ambari.server.api.services.stackadvisor.recommendations.RecommendationResponse;
import org.apache.ambari.server.audit.AuditLogger;
import org.apache.ambari.server.configuration.Configuration;
import org.apache.ambari.server.controller.AbstractRootServiceResponseFactory;
import org.apache.ambari.server.controller.AmbariCustomCommandExecutionHelper;
import org.apache.ambari.server.controller.AmbariManagementController;
import org.apache.ambari.server.controller.KerberosHelper;
import org.apache.ambari.server.controller.KerberosHelperImpl;
import org.apache.ambari.server.controller.RootServiceResponseFactory;
import org.apache.ambari.server.events.AmbariEvent;
import org.apache.ambari.server.hooks.AmbariEventFactory;
import org.apache.ambari.server.hooks.HookContext;
import org.apache.ambari.server.hooks.HookContextFactory;
import org.apache.ambari.server.hooks.HookService;
import org.apache.ambari.server.hooks.users.PostUserCreationHookContext;
import org.apache.ambari.server.hooks.users.UserCreatedEvent;
import org.apache.ambari.server.hooks.users.UserHookService;
import org.apache.ambari.server.metadata.CachedRoleCommandOrderProvider;
import org.apache.ambari.server.metadata.RoleCommandOrderProvider;
import org.apache.ambari.server.mpack.MpackManagerFactory;
import org.apache.ambari.server.orm.DBAccessor;
import org.apache.ambari.server.orm.dao.ArtifactDAO;
import org.apache.ambari.server.orm.dao.HostDAO;
import org.apache.ambari.server.orm.dao.HostRoleCommandDAO;
import org.apache.ambari.server.orm.dao.KerberosPrincipalDAO;
import org.apache.ambari.server.orm.entities.HostEntity;
import org.apache.ambari.server.orm.entities.KerberosKeytabEntity;
import org.apache.ambari.server.orm.entities.KerberosKeytabPrincipalEntity;
import org.apache.ambari.server.orm.entities.RepositoryVersionEntity;
import org.apache.ambari.server.orm.entities.UpgradeEntity;
import org.apache.ambari.server.scheduler.ExecutionScheduler;
import org.apache.ambari.server.scheduler.ExecutionSchedulerImpl;
import org.apache.ambari.server.security.encryption.CredentialStoreService;
import org.apache.ambari.server.stack.StackManagerFactory;
import org.apache.ambari.server.stack.upgrade.Direction;
import org.apache.ambari.server.stack.upgrade.orchestrate.UpgradeContext;
import org.apache.ambari.server.stack.upgrade.orchestrate.UpgradeContextFactory;
import org.apache.ambari.server.stageplanner.RoleGraphFactory;
import org.apache.ambari.server.state.Cluster;
import org.apache.ambari.server.state.Clusters;
import org.apache.ambari.server.state.ComponentInfo;
import org.apache.ambari.server.state.Config;
import org.apache.ambari.server.state.ConfigFactory;
import org.apache.ambari.server.state.ConfigHelper;
import org.apache.ambari.server.state.Host;
import org.apache.ambari.server.state.HostComponentAdminState;
import org.apache.ambari.server.state.PropertyInfo;
import org.apache.ambari.server.state.SecurityType;
import org.apache.ambari.server.state.Service;
import org.apache.ambari.server.state.ServiceComponent;
import org.apache.ambari.server.state.ServiceComponentFactory;
import org.apache.ambari.server.state.ServiceComponentHost;
import org.apache.ambari.server.state.ServiceComponentHostFactory;
import org.apache.ambari.server.state.ServiceComponentImpl;
import org.apache.ambari.server.state.ServiceFactory;
import org.apache.ambari.server.state.ServiceImpl;
import org.apache.ambari.server.state.ServiceInfo;
import org.apache.ambari.server.state.StackId;
import org.apache.ambari.server.state.cluster.ClusterFactory;
import org.apache.ambari.server.state.cluster.ClusterImpl;
import org.apache.ambari.server.state.configgroup.ConfigGroup;
import org.apache.ambari.server.state.configgroup.ConfigGroupFactory;
import org.apache.ambari.server.state.configgroup.ConfigGroupImpl;
import org.apache.ambari.server.state.host.HostFactory;
import org.apache.ambari.server.state.host.HostImpl;
import org.apache.ambari.server.state.kerberos.KerberosDescriptor;
import org.apache.ambari.server.state.kerberos.KerberosDescriptorFactory;
import org.apache.ambari.server.state.scheduler.RequestExecutionFactory;
import org.apache.ambari.server.state.stack.OsFamily;
import org.apache.ambari.server.state.svccomphost.ServiceComponentHostImpl;
import org.apache.ambari.server.testutils.PartialNiceMockBinder;
import org.apache.ambari.server.topology.PersistedState;
import org.apache.ambari.server.topology.TopologyManager;
import org.apache.ambari.server.topology.tasks.ConfigureClusterTaskFactory;
import org.apache.ambari.server.utils.StageUtils;
import org.apache.commons.collections4.MapUtils;
import org.easymock.Capture;
import org.easymock.EasyMockSupport;
import org.easymock.IAnswer;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.crypto.password.StandardPasswordEncoder;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.assistedinject.FactoryModuleBuilder;
import com.google.inject.name.Names;

public class PreconfigureKerberosActionTest extends EasyMockSupport {

  private static final String CLUSTER_NAME = "c1";

  @Test
  public void testSkipWhenDowngrade() throws Exception {

    Injector injector = getInjector();

    Map<String, String> commandParams = getDefaultCommandParams();
    commandParams.put(UPGRADE_DIRECTION_KEY, Direction.DOWNGRADE.name());

    ExecutionCommand executionCommand = createMockExecutionCommand(commandParams);

    replayAll();

    injector.getInstance(AmbariMetaInfo.class).init();

    PreconfigureKerberosAction action = injector.getInstance(PreconfigureKerberosAction.class);
    ConcurrentMap<String, Object> context = new ConcurrentHashMap<>();
    action.setExecutionCommand(executionCommand);
    action.execute(context);

    verifyAll();
  }

  @Test
  public void testSkipWhenNotKerberos() throws Exception {
    Injector injector = getInjector();

    ExecutionCommand executionCommand = createMockExecutionCommand(getDefaultCommandParams());

    Cluster cluster = createMockCluster(SecurityType.NONE, Collections.<Host>emptyList(),
        Collections.<String, Service>emptyMap(), Collections.<String, List<ServiceComponentHost>>emptyMap(),
        createNiceMock(StackId.class), Collections.<String, Config>emptyMap());

    Clusters clusters = injector.getInstance(Clusters.class);
    expect(clusters.getCluster(CLUSTER_NAME)).andReturn(cluster).atLeastOnce();

    replayAll();

    injector.getInstance(AmbariMetaInfo.class).init();

    PreconfigureKerberosAction action = injector.getInstance(PreconfigureKerberosAction.class);
    ConcurrentMap<String, Object> context = new ConcurrentHashMap<>();
    action.setExecutionCommand(executionCommand);
    action.execute(context);

    verifyAll();
  }

  private Long hostId = 1L;
  private Host createMockHost(String hostname) {
    Host host = createNiceMock(Host.class);
    expect(host.getHostName()).andReturn(hostname).anyTimes();
    expect(host.getHostId()).andReturn(hostId).anyTimes();
    hostId++;
    return host;
  }

  @Test
  @Ignore("Update accordingly to changes")
  public void testUpgrade() throws Exception {
    Capture<? extends Map<String, String>> captureCoreSiteProperties = newCapture();

    Injector injector = getInjector();

    HostDAO hostDAO = injector.getInstance(HostDAO.class);
    EntityManager entityManager = injector.getInstance(EntityManager.class);
    HostEntity hostEntityMock = createNiceMock(HostEntity.class);
    KerberosKeytabPrincipalEntity principalMock = createNiceMock(KerberosKeytabPrincipalEntity.class);
    expect(principalMock.getHostId()).andReturn(1L).anyTimes();
    expect(hostDAO.findByName(anyString())).andReturn(hostEntityMock).anyTimes();
    expect(hostDAO.findById(anyLong())).andReturn(hostEntityMock).anyTimes();
    expect(entityManager.find(eq(KerberosKeytabEntity.class), anyString())).andReturn(createNiceMock(KerberosKeytabEntity.class)).anyTimes();
//    expect(entityManager.find(eq(KerberosPrincipalHostEntity.class), anyObject())).andReturn(createNiceMock(KerberosPrincipalHostEntity.class)).anyTimes();
    expect(entityManager.find(eq(KerberosKeytabPrincipalEntity.class), anyObject())).andReturn(principalMock).anyTimes();

    ExecutionCommand executionCommand = createMockExecutionCommand(getDefaultCommandParams());

    UpgradeEntity upgradeProgress = createMock(UpgradeEntity.class);

    StackId targetStackId = createMock(StackId.class);
    expect(targetStackId.getStackId()).andReturn("HDP-2.6").anyTimes();
    expect(targetStackId.getStackName()).andReturn("HDP").anyTimes();
    expect(targetStackId.getStackVersion()).andReturn("2.6").anyTimes();

    final String hostName1 = "c6401.ambari.apache.org";
    final String hostName2 = "c6402.ambari.apache.org";
    final String hostName3 = "c6403.ambari.apache.org";

    final Host host1 = createMockHost(hostName1);
    Host host2 = createMockHost(hostName2);
    Host host3 = createMockHost(hostName3);
    Map<String, Host> hosts = new HashMap<>();
    hosts.put(hostName1, host1);
    hosts.put(hostName2, host2);
    hosts.put(hostName3, host3);

    Map<String, ServiceComponentHost> nnSchs = Collections.singletonMap(hostName1, createMockServiceComponentHost("HDFS", "NAMENODE", hostName1, host1));
    Map<String, ServiceComponentHost> rmSchs = Collections.singletonMap(hostName2, createMockServiceComponentHost("YARN", "RESOURCEMANAGER", hostName2, host2));
    Map<String, ServiceComponentHost> nmSchs = Collections.singletonMap(hostName2, createMockServiceComponentHost("YARN", "NODEMANAGER", hostName2, host2));
    Map<String, ServiceComponentHost> dnSchs = new HashMap<>();
    final Map<String, ServiceComponentHost> hcSchs = new HashMap<>();
    Map<String, ServiceComponentHost> zkSSchs = new HashMap<>();
    Map<String, ServiceComponentHost> zkCSchs = new HashMap<>();
    Map<String, List<ServiceComponentHost>> serviceComponentHosts = new HashMap<>();

    for (Map.Entry<String, Host> entry : hosts.entrySet()) {
      String hostname = entry.getKey();
      List<ServiceComponentHost> list = new ArrayList<>();
      ServiceComponentHost sch;

      sch = createMockServiceComponentHost("HDFS", "DATANODE", hostname, entry.getValue());
      dnSchs.put(hostname, sch);
      list.add(sch);

      sch = createMockServiceComponentHost("HDFS", "HDFS_CLIENT", hostname, entry.getValue());
      hcSchs.put(hostname, sch);
      list.add(sch);

      sch = createMockServiceComponentHost("ZOOKEEPER", "ZOOKEEPER_SERVER", hostname, entry.getValue());
      zkSSchs.put(hostname, sch);
      list.add(sch);

      sch = createMockServiceComponentHost("ZOOKEEPER", "ZOOKEEPER_CLIENT", hostname, entry.getValue());
      zkCSchs.put(hostname, sch);
      list.add(sch);

      serviceComponentHosts.put(hostname, list);
    }


    Map<String, ServiceComponent> hdfsComponents = new HashMap<>();
    hdfsComponents.put("NAMENODE", createMockServiceComponent("NAMENODE", false, nnSchs));
    hdfsComponents.put("DATANODE", createMockServiceComponent("DATANODE", false, dnSchs));
    hdfsComponents.put("HDFS_CLIENT", createMockServiceComponent("HDFS_CLIENT", true, hcSchs));

    Map<String, ServiceComponent> yarnComponents = new HashMap<>();
    yarnComponents.put("RESOURCEMANAGER", createMockServiceComponent("RESOURCEMANAGER", false, rmSchs));
    yarnComponents.put("NODEMANAGER", createMockServiceComponent("NODEMANAGER", false, nmSchs));

    Map<String, ServiceComponent> zkCompnents = new HashMap<>();
    yarnComponents.put("ZOOKEEPER_SERVER", createMockServiceComponent("ZOOKEEPER_SERVER", false, zkSSchs));
    yarnComponents.put("ZOOKEEPER_CLIENT", createMockServiceComponent("ZOOKEEPER_CLIENT", true, zkCSchs));

    Service hdfsService = createMockService("HDFS", hdfsComponents, targetStackId);
    Service yarnService = createMockService("YARN", yarnComponents, targetStackId);
    Service zkService = createMockService("ZOOKEEPER", zkCompnents, targetStackId);

    Map<String, Service> installedServices = new HashMap<>();
    installedServices.put("HDFS", hdfsService);
    installedServices.put("YARN", yarnService);
    installedServices.put("ZOOKEEPER", zkService);

    Map<String, Map<String, String>> clusterConfig = getClusterConfig();

    Map<String, Config> clusterConfigs = new HashMap<>();
    for (Map.Entry<String, Map<String, String>> entry : clusterConfig.entrySet()) {
      clusterConfigs.put(entry.getKey(), createMockConfig(entry.getValue()));
    }

    Cluster cluster = createMockCluster(SecurityType.KERBEROS, hosts.values(), installedServices, serviceComponentHosts, targetStackId, clusterConfigs);
    expect(cluster.getUpgradeInProgress()).andReturn(upgradeProgress).once();

    RepositoryVersionEntity targetRepositoryVersion = createMock(RepositoryVersionEntity.class);
    expect(targetRepositoryVersion.getStackId()).andReturn(targetStackId).atLeastOnce();

    UpgradeContext upgradeContext = createMock(UpgradeContext.class);
    expect(upgradeContext.getTargetRepositoryVersion(anyString())).andReturn(targetRepositoryVersion).atLeastOnce();

    UpgradeContextFactory upgradeContextFactory = injector.getInstance(UpgradeContextFactory.class);
    expect(upgradeContextFactory.create(cluster, upgradeProgress)).andReturn(upgradeContext).once();

    createMockClusters(injector, cluster);

    List<PropertyInfo> knoxProperties = Arrays.asList(
        crateMockPropertyInfo("knox-env.xml", "knox_user", "knox"),
        crateMockPropertyInfo("knox-env.xml", "knox_group", "knox"),
        crateMockPropertyInfo("knox-env.xml", "knox_principal_name", "KERBEROS_PRINCIPAL"),
        crateMockPropertyInfo("gateway-site.xml", "gateway.port", "8443"),
        crateMockPropertyInfo("gateway-site.xml", "gateway.path", "gateway")
    );

    AmbariMetaInfo ambariMetaInfo = injector.getInstance(AmbariMetaInfo.class);
    expect(ambariMetaInfo.getKerberosDescriptor("HDP", "2.6", false)).
        andReturn(getKerberosDescriptor(false)).once();
    expect(ambariMetaInfo.getKerberosDescriptor("HDP", "2.6", true)).
        andReturn(getKerberosDescriptor(true)).once();
    expect(ambariMetaInfo.isValidService("HDP", "2.6", "BEACON"))
        .andReturn(false).anyTimes();
    expect(ambariMetaInfo.isValidService("HDP", "2.6", "KNOX"))
        .andReturn(true).anyTimes();
    expect(ambariMetaInfo.getService("HDP", "2.6", "KNOX"))
        .andReturn(createMockServiceInfo("KNOX", knoxProperties, Collections.singletonList(createMockComponentInfo("KNOX_GATEWAY")))).anyTimes();

    AmbariManagementController managementController = injector.getInstance(AmbariManagementController.class);
    expect(managementController.findConfigurationTagsWithOverrides(cluster, null, null))
        .andReturn(clusterConfig).once();
    expect(managementController.getAuthName()).andReturn("admin").anyTimes();

    ConfigHelper configHelper = injector.getInstance(ConfigHelper.class);
    expect(configHelper.getEffectiveConfigProperties(cluster, clusterConfig)).andReturn(clusterConfig).anyTimes();
    configHelper.updateConfigType(eq(cluster), eq(targetStackId), eq(managementController), eq("core-site"), capture(captureCoreSiteProperties), anyObject(Collection.class), eq("admin"), anyString());
    expectLastCall().once();

    TopologyManager topologyManager = injector.getInstance(TopologyManager.class);
    expect(topologyManager.getPendingHostComponents()).andReturn(Collections.<String, Collection<String>>emptyMap()).anyTimes();

    StackAdvisorHelper stackAdvisorHelper = injector.getInstance(StackAdvisorHelper.class);
    expect(stackAdvisorHelper.recommend(anyObject(StackAdvisorRequest.class)))
        .andAnswer(new IAnswer<RecommendationResponse>() {
          @Override
          public RecommendationResponse answer() throws Throwable {
            Object[] args = getCurrentArguments();
            StackAdvisorRequest request = (StackAdvisorRequest) args[0];
            StackAdvisorRequest.StackAdvisorRequestType requestType = request.getRequestType();

            if (requestType == StackAdvisorRequest.StackAdvisorRequestType.HOST_GROUPS) {
              RecommendationResponse.Blueprint blueprint = new RecommendationResponse.Blueprint();
              blueprint.setHostGroups(new HashSet<>(Arrays.asList(
                  createRecommendationHostGroup(hostName1,
                      Arrays.asList("ZOOKEEPER_SERVER", "ZOOKEEPER_CLIENT", "HDFS_CLIENT", "DATANODE", "NAMENODE", "KNOX_GATEWAY")),
                  createRecommendationHostGroup(hostName2,
                      Arrays.asList("ZOOKEEPER_SERVER", "ZOOKEEPER_CLIENT", "HDFS_CLIENT", "DATANODE", "RESOURCEMANAGER", "NODEMANAGER")),
                  createRecommendationHostGroup(hostName3,
                      Arrays.asList("ZOOKEEPER_SERVER", "ZOOKEEPER_CLIENT", "HDFS_CLIENT", "DATANODE"))
              )));
              Set<RecommendationResponse.BindingHostGroup> bindingHostGroups = new HashSet<>(Arrays.asList(
                  createBindingHostGroup(hostName1),
                  createBindingHostGroup(hostName2),
                  createBindingHostGroup(hostName3)
              ));

              RecommendationResponse.BlueprintClusterBinding binding = new RecommendationResponse.BlueprintClusterBinding();
              binding.setHostGroups(bindingHostGroups);

              RecommendationResponse.Recommendation recommendation = new RecommendationResponse.Recommendation();
              recommendation.setBlueprint(blueprint);
              recommendation.setBlueprintClusterBinding(binding);

              RecommendationResponse response = new RecommendationResponse();
              response.setRecommendations(recommendation);
              return response;
            } else {
              return null;
            }
          }
        })
        .anyTimes();

    replayAll();

    ambariMetaInfo.init();
    StageUtils.setTopologyManager(injector.getInstance(TopologyManager.class));
    StageUtils.setConfiguration(injector.getInstance(Configuration.class));

    PreconfigureKerberosAction action = injector.getInstance(PreconfigureKerberosAction.class);
    ConcurrentMap<String, Object> context = new ConcurrentHashMap<>();
    action.setExecutionCommand(executionCommand);
    action.execute(context);

    verifyAll();

    Assert.assertTrue(captureCoreSiteProperties.hasCaptured());

    Map<String, String> capturedProperties = captureCoreSiteProperties.getValue();
    Assert.assertFalse(MapUtils.isEmpty(capturedProperties));


    String expectedAuthToLocalRules = "" +
        "RULE:[1:$1@$0](ambari-qa-c1@EXAMPLE.COM)s/.*/ambari-qa/\n" +
        "RULE:[1:$1@$0](hdfs-c1@EXAMPLE.COM)s/.*/hdfs/\n" +
        "RULE:[1:$1@$0](.*@EXAMPLE.COM)s/@.*//\n" +
        "RULE:[2:$1@$0](beacon@EXAMPLE.COM)s/.*/beacon/\n" +
        "RULE:[2:$1@$0](dn@EXAMPLE.COM)s/.*/hdfs/\n" +
        "RULE:[2:$1@$0](knox@EXAMPLE.COM)s/.*/knox/\n" +
        "RULE:[2:$1@$0](nm@EXAMPLE.COM)s/.*/${yarn-env/yarn_user}/\n" +
        "RULE:[2:$1@$0](nn@EXAMPLE.COM)s/.*/hdfs/\n" +
        "RULE:[2:$1@$0](rm@EXAMPLE.COM)s/.*/${yarn-env/yarn_user}/\n" +
        "DEFAULT";

    Assert.assertEquals(3, capturedProperties.size());
    Assert.assertEquals("users", capturedProperties.get("hadoop.proxyuser.knox.groups"));
    Assert.assertEquals("c6401.ambari.apache.org", capturedProperties.get("hadoop.proxyuser.knox.hosts"));
    Assert.assertEquals(expectedAuthToLocalRules, capturedProperties.get("hadoop.security.auth_to_local"));
  }

  private RecommendationResponse.BindingHostGroup createBindingHostGroup(String hostName) {
    RecommendationResponse.BindingHostGroup bindingHostGroup = new RecommendationResponse.BindingHostGroup();
    bindingHostGroup.setName(hostName);
    bindingHostGroup.setHosts(Collections.singleton(Collections.singletonMap("fqdn", hostName)));
    return bindingHostGroup;
  }

  private RecommendationResponse.HostGroup createRecommendationHostGroup(String hostName, List<String> components) {
    Set<Map<String, String>> componentDetails = new HashSet<>();
    for (String component : components) {
      componentDetails.add(Collections.singletonMap("name", component));
    }

    RecommendationResponse.HostGroup hostGroup = new RecommendationResponse.HostGroup();
    hostGroup.setComponents(componentDetails);
    hostGroup.setName(hostName);
    return hostGroup;
  }

  private ComponentInfo createMockComponentInfo(String componentName) {
    ComponentInfo componentInfo = createMock(ComponentInfo.class);
    expect(componentInfo.getName()).andReturn(componentName).anyTimes();
    return componentInfo;
  }

  private PropertyInfo crateMockPropertyInfo(String fileName, String propertyName, String propertyValue) {
    PropertyInfo propertyInfo = createMock(PropertyInfo.class);
    expect(propertyInfo.getFilename()).andReturn(fileName).anyTimes();
    expect(propertyInfo.getName()).andReturn(propertyName).anyTimes();
    expect(propertyInfo.getValue()).andReturn(propertyValue).anyTimes();
    return propertyInfo;
  }

  private ServiceInfo createMockServiceInfo(String name, List<PropertyInfo> properties, List<ComponentInfo> components) {
    ServiceInfo serviceInfo = createMock(ServiceInfo.class);
    expect(serviceInfo.getName()).andReturn(name).anyTimes();
    expect(serviceInfo.getProperties()).andReturn(properties).anyTimes();
    expect(serviceInfo.getComponents()).andReturn(components).anyTimes();
    return serviceInfo;
  }

  private Map<String, Map<String, String>> getClusterConfig() throws URISyntaxException, FileNotFoundException {
    URL url = ClassLoader.getSystemResource("PreconfigureActionTest_cluster_config.json");
    return new Gson().fromJson(new FileReader(new File(url.toURI())),
        new TypeToken<Map<String, Map<String, String>>>() {
        }.getType());
  }

  private KerberosDescriptor getKerberosDescriptor(boolean includePreconfigureData) throws URISyntaxException, IOException {
    URL url;

    if (includePreconfigureData) {
      url = ClassLoader.getSystemResource("PreconfigureActionTest_kerberos_descriptor_stack_preconfigure.json");
    } else {
      url = ClassLoader.getSystemResource("PreconfigureActionTest_kerberos_descriptor_stack.json");
    }

    return new KerberosDescriptorFactory().createInstance(new File(url.toURI()));
  }

  private ServiceComponent createMockServiceComponent(String name, Boolean isClientComponent, Map<String, ServiceComponentHost> serviceComponentHostMap) throws AmbariException {
    ServiceComponent serviceComponent = createMock(ServiceComponent.class);
    expect(serviceComponent.getName()).andReturn(name).anyTimes();
    expect(serviceComponent.isClientComponent()).andReturn(isClientComponent).anyTimes();

    for (Map.Entry<String, ServiceComponentHost> entry : serviceComponentHostMap.entrySet()) {
      expect(serviceComponent.getServiceComponentHost(entry.getKey())).andReturn(serviceComponentHostMap.get(entry.getKey())).anyTimes();
    }

    expect(serviceComponent.getServiceComponentHosts()).andReturn(serviceComponentHostMap).anyTimes();

    return serviceComponent;
  }

  private ServiceComponentHost createMockServiceComponentHost(String serviceName, String componentName, String hostname, Host host) {
    ServiceComponentHost serviceComponentHost = createMock(ServiceComponentHost.class);
    expect(serviceComponentHost.getServiceName()).andReturn(serviceName).anyTimes();
    expect(serviceComponentHost.getServiceComponentName()).andReturn(componentName).anyTimes();
    expect(serviceComponentHost.getHostName()).andReturn(hostname).anyTimes();
    expect(serviceComponentHost.getHost()).andReturn(host).anyTimes();
    expect(serviceComponentHost.getComponentAdminState()).andReturn(HostComponentAdminState.INSERVICE).anyTimes();

    return serviceComponentHost;
  }

  private Service createMockService(String name, Map<String, ServiceComponent> components, StackId desiredStackId) {
    Service service = createMock(Service.class);
    expect(service.getName()).andReturn(name).anyTimes();
    expect(service.getServiceComponents()).andReturn(components).anyTimes();
    expect(service.getDesiredStackId()).andReturn(desiredStackId).anyTimes();
    return service;
  }

  private Clusters createMockClusters(Injector injector, Cluster cluster) throws AmbariException {
    Clusters clusters = injector.getInstance(Clusters.class);
    expect(clusters.getCluster(CLUSTER_NAME)).andReturn(cluster).atLeastOnce();
    return clusters;
  }

  private Cluster createMockCluster(SecurityType securityType, Collection<Host> hosts,
                                    Map<String, Service> services,
                                    Map<String, List<ServiceComponentHost>> serviceComponentHosts,
                                    StackId currentStackId, final Map<String, Config> clusterConfigs) {
    final Cluster cluster = createMock(Cluster.class);
    expect(cluster.getSecurityType()).andReturn(securityType).anyTimes();
    expect(cluster.getClusterName()).andReturn(CLUSTER_NAME).anyTimes();
    expect(cluster.getClusterId()).andReturn(1L).anyTimes();
    expect(cluster.getHosts()).andReturn(hosts).anyTimes();
    expect(cluster.getServices()).andReturn(services).anyTimes();
    expect(cluster.getCurrentStackVersion()).andReturn(currentStackId).anyTimes();

    for (Map.Entry<String, List<ServiceComponentHost>> entry : serviceComponentHosts.entrySet()) {
      expect(cluster.getServiceComponentHosts(entry.getKey())).andReturn(entry.getValue()).atLeastOnce();
    }

    expect(cluster.getServiceComponentHostMap(null, new HashSet<>(Arrays.asList("HDFS", "ZOOKEEPER", "YARN", "KNOX"))))
        .andReturn(null)
        .anyTimes();
    expect(cluster.getServiceComponentHostMap(null, new HashSet<>(Arrays.asList("HDFS", "ZOOKEEPER", "YARN"))))
        .andReturn(null)
        .anyTimes();

    Map<String, String> configTypeService = new HashMap<>();
    configTypeService.put("hdfs-site", "HDFS");
    configTypeService.put("core-site", "HDFS");
    configTypeService.put("hadoop-env", "HDFS");
    configTypeService.put("cluster-env", null);
    configTypeService.put("kerberos-env", "KERBEROS");
    configTypeService.put("ranger-hdfs-audit", "RANGER");
    configTypeService.put("zookeeper-env", "ZOOKEEPER");
    configTypeService.put("gateway-site", "KNOX");

    for (Map.Entry<String, String> entry : configTypeService.entrySet()) {
      expect(cluster.getServiceByConfigType(entry.getKey())).andReturn(entry.getValue()).anyTimes();
    }

    for (Map.Entry<String, Config> entry : clusterConfigs.entrySet()) {
      expect(cluster.getDesiredConfigByType(entry.getKey())).andReturn(entry.getValue()).anyTimes();
      expect(cluster.getConfigsByType(entry.getKey())).andReturn(Collections.singletonMap(entry.getKey(), entry.getValue())).anyTimes();
      expect(cluster.getConfigPropertiesTypes(entry.getKey())).andReturn(Collections.<PropertyInfo.PropertyType, Set<String>>emptyMap()).anyTimes();
    }

    return cluster;
  }

  private Config createMockConfig(Map<String, String> properties) {
    Config config = createMock(Config.class);
    expect(config.getProperties()).andReturn(properties).anyTimes();
    expect(config.getPropertiesAttributes()).andReturn(Collections.<String, Map<String, String>>emptyMap()).anyTimes();
    return config;
  }

  private Map<String, String> getDefaultCommandParams() {
    Map<String, String> commandParams = new HashMap<>();
    commandParams.put("clusterName", CLUSTER_NAME);
    commandParams.put(UPGRADE_DIRECTION_KEY, Direction.UPGRADE.name());
    return commandParams;
  }

  private ExecutionCommand createMockExecutionCommand(Map<String, String> commandParams) {
    ExecutionCommand executionCommand = createMock(ExecutionCommand.class);
    expect(executionCommand.getCommandParams()).andReturn(commandParams).atLeastOnce();
    return executionCommand;
  }

  private Injector getInjector() {
    return Guice.createInjector(new AbstractModule() {
      @Override
      protected void configure() {
        PartialNiceMockBinder.newBuilder(PreconfigureKerberosActionTest.this)
            .addActionDBAccessorConfigsBindings().addLdapBindings().addPasswordEncryptorBindings().build().configure(binder());

        bind(EntityManager.class).toInstance(createMock(EntityManager.class));
        bind(DBAccessor.class).toInstance(createMock(DBAccessor.class));
        bind(UpgradeContextFactory.class).toInstance(createMock(UpgradeContextFactory.class));
        bind(OsFamily.class).toInstance(createMock(OsFamily.class));
        bind(StackManagerFactory.class).toInstance(createMock(StackManagerFactory.class));
        bind(StageFactory.class).toInstance(createMock(StageFactory.class));
        bind(AmbariMetaInfo.class).toInstance(createMock(AmbariMetaInfo.class));
        bind(AmbariCustomCommandExecutionHelper.class).toInstance(createMock(AmbariCustomCommandExecutionHelper.class));
        bind(ActionManager.class).toInstance(createMock(ActionManager.class));
        bind(HostRoleCommandDAO.class).toInstance(createNiceMock(HostRoleCommandDAO.class));
        bind(AuditLogger.class).toInstance(createNiceMock(AuditLogger.class));
        bind(ArtifactDAO.class).toInstance(createNiceMock(ArtifactDAO.class));
        bind(KerberosPrincipalDAO.class).toInstance(createNiceMock(KerberosPrincipalDAO.class));
        bind(RoleCommandOrderProvider.class).to(CachedRoleCommandOrderProvider.class);
        bind(HostRoleCommandFactory.class).to(HostRoleCommandFactoryImpl.class);
        bind(RoleCommandOrderProvider.class).to(CachedRoleCommandOrderProvider.class);
        bind(HostRoleCommandFactory.class).to(HostRoleCommandFactoryImpl.class);
        bind(RoleGraphFactory.class).toInstance(createMock(RoleGraphFactory.class));
