content: /*
   * 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.state;
  
  import static org.easymock.EasyMock.createNiceMock;
  import static org.easymock.EasyMock.expect;
  import static org.easymock.EasyMock.replay;
  import static org.easymock.EasyMock.verify;
  import static org.junit.Assert.assertEquals;
  import static org.junit.Assert.assertFalse;
  import static org.junit.Assert.assertTrue;
  import static org.junit.Assert.fail;
  
  import java.sql.SQLException;
  import java.util.Collection;
  import java.util.Collections;
  import java.util.HashMap;
  import java.util.Map;
  
  import org.apache.ambari.server.AmbariException;
  import org.apache.ambari.server.H2DatabaseCleaner;
  import org.apache.ambari.server.controller.ServiceResponse;
  import org.apache.ambari.server.controller.internal.DeleteHostComponentStatusMetaData;
  import org.apache.ambari.server.orm.GuiceJpaInitializer;
  import org.apache.ambari.server.orm.InMemoryDefaultTestModule;
  import org.apache.ambari.server.orm.OrmTestHelper;
  import org.apache.ambari.server.orm.dao.ClusterServiceDAO;
  import org.apache.ambari.server.orm.entities.ClusterConfigEntity;
  import org.apache.ambari.server.orm.entities.ClusterServiceEntity;
  import org.apache.ambari.server.orm.entities.RepositoryVersionEntity;
  import org.apache.ambari.server.state.configgroup.ConfigGroup;
  import org.apache.commons.collections4.MapUtils;
  import org.junit.After;
  import org.junit.Before;
  import org.junit.Test;
  
  import com.google.inject.Guice;
  import com.google.inject.Injector;
  
  import junit.framework.Assert;
  
  public class ServiceTest {
  
    private Clusters clusters;
    private Cluster cluster;
    private String clusterName;
    private Injector injector;
    private ServiceFactory serviceFactory;
    private ServiceComponentFactory serviceComponentFactory;
    private ServiceComponentHostFactory serviceComponentHostFactory;
    private OrmTestHelper ormTestHelper;
    private ConfigFactory configFactory;
  
    private final String STACK_VERSION = "0.1";
    private final String REPO_VERSION = "0.1-1234";
    private final StackId STACK_ID = new StackId("HDP", STACK_VERSION);
    private RepositoryVersionEntity repositoryVersion;
  
    @Before
    public void setup() throws Exception {
      injector = Guice.createInjector(new InMemoryDefaultTestModule());
      injector.getInstance(GuiceJpaInitializer.class);
      clusters = injector.getInstance(Clusters.class);
      serviceFactory = injector.getInstance(ServiceFactory.class);
      serviceComponentFactory = injector.getInstance(ServiceComponentFactory.class);
      serviceComponentHostFactory = injector.getInstance(ServiceComponentHostFactory.class);
      configFactory = injector.getInstance(ConfigFactory.class);
  
      ormTestHelper = injector.getInstance(OrmTestHelper.class);
      repositoryVersion = ormTestHelper.getOrCreateRepositoryVersion(STACK_ID, REPO_VERSION);
  
      clusterName = "foo";
      clusters.addCluster(clusterName, STACK_ID);
      cluster = clusters.getCluster(clusterName);
      Assert.assertNotNull(cluster);
    }
  
    @After
    public void teardown() throws AmbariException, SQLException {
      H2DatabaseCleaner.clearDatabaseAndStopPersistenceService(injector);
    }
  
    @Test
    public void testCanBeRemoved() throws Exception{
      Service service = cluster.addService("HDFS", repositoryVersion);
  
      for (State state : State.values()) {
        service.setDesiredState(state);
        // service does not have any components, so it can be removed,
        // even if the service is in non-removable state.
        org.junit.Assert.assertTrue(service.canBeRemoved());
      }
  
      ServiceComponent component = service.addServiceComponent("NAMENODE");
  
      // component can be removed
      component.setDesiredState(State.INSTALLED);
  
      for (State state : State.values()) {
        service.setDesiredState(state);
        // should always be true if the sub component can be removed
        org.junit.Assert.assertTrue(service.canBeRemoved());
      }
  
      // can remove a STARTED component as whether a service can be removed
      // is ultimately decided based on if the host components can be removed
      component.setDesiredState(State.INSTALLED);
      addHostToCluster("h1", service.getCluster().getClusterName());
      ServiceComponentHost sch = serviceComponentHostFactory.createNew(component, "h1");
      component.addServiceComponentHost(sch);
      sch.setDesiredState(State.STARTED);
      sch.setState(State.STARTED);
  
      for (State state : State.values()) {
        service.setDesiredState(state);
        // should always be false if the sub component can not be removed
        org.junit.Assert.assertFalse(service.canBeRemoved());
      }
  
      sch.setDesiredState(State.INSTALLED);
      sch.setState(State.INSTALLED);
    }
  
    @Test
    public void testGetAndSetServiceInfo() throws AmbariException {
      String serviceName = "HDFS";
      Service s = serviceFactory.createNew(cluster, serviceName, repositoryVersion);
      cluster.addService(s);
  
      Service service = cluster.getService(serviceName);
      Assert.assertNotNull(service);
  
      StackId desiredStackId = new StackId("HDP-1.2.0");
      String desiredVersion = "1.2.0-1234";
  
      RepositoryVersionEntity desiredRepositoryVersion = ormTestHelper.getOrCreateRepositoryVersion(
          desiredStackId, desiredVersion);
  
      service.setDesiredRepositoryVersion(desiredRepositoryVersion);
      Assert.assertEquals(desiredStackId, service.getDesiredStackId());
  
      service.setDesiredState(State.INSTALLING);
      Assert.assertEquals(State.INSTALLING, service.getDesiredState());
  
      // FIXME todo use DAO to verify persisted object maps to inmemory state
  
    }
  
  
    @Test
    public void testAddGetDeleteServiceComponents() throws AmbariException {
      String serviceName = "HDFS";
      Service s = serviceFactory.createNew(cluster, serviceName, repositoryVersion);
      cluster.addService(s);
  
      Service service = cluster.getService(serviceName);
  
      Assert.assertNotNull(service);
      Assert.assertEquals(serviceName, service.getName());
      Assert.assertEquals(cluster.getClusterId(),
              service.getCluster().getClusterId());
      Assert.assertEquals(cluster.getClusterName(),
              service.getCluster().getClusterName());
      Assert.assertEquals(State.INIT, service.getDesiredState());
      Assert.assertFalse(
              service.getDesiredStackId().getStackId().isEmpty());
  
      Assert.assertTrue(s.getServiceComponents().isEmpty());
  
      ServiceComponent sc1 =
          serviceComponentFactory.createNew(s, "NAMENODE");
      ServiceComponent sc2 =
          serviceComponentFactory.createNew(s, "DATANODE1");
      ServiceComponent sc3 =
          serviceComponentFactory.createNew(s, "DATANODE2");
  
      Map<String, ServiceComponent> comps = new
        HashMap<>();
      comps.put(sc1.getName(), sc1);
      comps.put(sc2.getName(), sc2);
  
      s.addServiceComponents(comps);
  
      Assert.assertEquals(2, s.getServiceComponents().size());
      Assert.assertNotNull(s.getServiceComponent(sc1.getName()));
      Assert.assertNotNull(s.getServiceComponent(sc2.getName()));
  
      try {
        s.getServiceComponent(sc3.getName());
        fail("Expected error when looking for invalid component");
      } catch (Exception e) {
        // Expected
      }
  
      s.addServiceComponent(sc3);
  
      ServiceComponent sc4 = s.addServiceComponent("HDFS_CLIENT");
      Assert.assertNotNull(s.getServiceComponent(sc4.getName()));
      Assert.assertEquals(State.INIT,
          s.getServiceComponent("HDFS_CLIENT").getDesiredState());
      Assert.assertTrue(sc4.isClientComponent());
      Assert.assertEquals(4, s.getServiceComponents().size());
  
      Assert.assertNotNull(s.getServiceComponent(sc3.getName()));
      Assert.assertEquals(sc3.getName(),
          s.getServiceComponent(sc3.getName()).getName());
      Assert.assertEquals(s.getName(),
          s.getServiceComponent(sc3.getName()).getServiceName());
      Assert.assertEquals(cluster.getClusterName(),
          s.getServiceComponent(sc3.getName()).getClusterName());
  
      sc4.setDesiredState(State.INSTALLING);
      Assert.assertEquals(State.INSTALLING,
          s.getServiceComponent("HDFS_CLIENT").getDesiredState());
  
      // delete service component
      s.deleteServiceComponent("NAMENODE", new DeleteHostComponentStatusMetaData());
  
      assertEquals(3, s.getServiceComponents().size());
    }
  
    @Test
    public void testGetAndSetConfigs() {
      // FIXME add unit tests for configs once impl done
      /*
        public Map<String, Config> getDesiredConfigs();
        public void updateDesiredConfigs(Map<String, Config> configs);
       */
    }
  
  
    @Test
    public void testConvertToResponse() throws AmbariException {
      String serviceName = "HDFS";
      Service s = serviceFactory.createNew(cluster, serviceName, repositoryVersion);
      cluster.addService(s);
      Service service = cluster.getService(serviceName);
      Assert.assertNotNull(service);
  
      ServiceResponse r = s.convertToResponse();
      Assert.assertEquals(s.getName(), r.getServiceName());
      Assert.assertEquals(s.getCluster().getClusterName(), r.getClusterName());
      Assert.assertEquals(s.getDesiredStackId().getStackId(), r.getDesiredStackId());
      Assert.assertEquals(s.getDesiredState().toString(), r.getDesiredState());
  
      StackId desiredStackId = new StackId("HDP-1.2.0");
      String desiredVersion = "1.2.0-1234";
  
      RepositoryVersionEntity desiredRepositoryVersion = ormTestHelper.getOrCreateRepositoryVersion(
          desiredStackId, desiredVersion);
  
      service.setDesiredRepositoryVersion(desiredRepositoryVersion);
      service.setDesiredState(State.INSTALLING);
      r = s.convertToResponse();
      Assert.assertEquals(s.getName(), r.getServiceName());
      Assert.assertEquals(s.getCluster().getClusterName(), r.getClusterName());
      Assert.assertEquals(s.getDesiredStackId().getStackId(), r.getDesiredStackId());
      Assert.assertEquals(s.getDesiredState().toString(), r.getDesiredState());
      // FIXME add checks for configs
  
      StringBuilder sb = new StringBuilder();
      s.debugDump(sb);
      // TODO better checks?
      Assert.assertFalse(sb.toString().isEmpty());
  
    }
  
    @Test
    public void testServiceMaintenance() throws Exception {
      String serviceName = "HDFS";
      Service s = serviceFactory.createNew(cluster, serviceName, repositoryVersion);
      cluster.addService(s);
  
      Service service = cluster.getService(serviceName);
      Assert.assertNotNull(service);
  
      ClusterServiceDAO dao = injector.getInstance(ClusterServiceDAO.class);
      ClusterServiceEntity entity = dao.findByClusterAndServiceNames(clusterName, serviceName);
      Assert.assertNotNull(entity);
      Assert.assertEquals(MaintenanceState.OFF, entity.getServiceDesiredStateEntity().getMaintenanceState());
      Assert.assertEquals(MaintenanceState.OFF, service.getMaintenanceState());
  
      service.setMaintenanceState(MaintenanceState.ON);
      Assert.assertEquals(MaintenanceState.ON, service.getMaintenanceState());
  
      entity = dao.findByClusterAndServiceNames(clusterName, serviceName);
      Assert.assertNotNull(entity);
      Assert.assertEquals(MaintenanceState.ON, entity.getServiceDesiredStateEntity().getMaintenanceState());
    }
  
    /**
     * Tests the kerberosEnabledTest value set in the HDFS metainfo file (stacks/HDP/0.1/services/HDFS/metainfo.xml):
     * <pre>
     * {
     *   "or": [
     *     {
     *       "equals": [
     *         "core-site/hadoop.security.authentication",
     *         "kerberos"
     *       ]
     *     },
     *     {
     *       "equals": [
     *         "hdfs-site/hadoop.security.authentication",
     *         "kerberos"
     *       ]
     *     }
     *   ]
     * }
     * </pre>
     */
    @Test
    public void testServiceKerberosEnabledTest() throws Exception {
      String serviceName = "HDFS";
      Service s = serviceFactory.createNew(cluster, serviceName, repositoryVersion);
      cluster.addService(s);
  
      Service service = cluster.getService(serviceName);
      Assert.assertNotNull(service);
  
  
      Map<String, Map<String, String>> map = new HashMap<>();
  
      Assert.assertFalse(service.isKerberosEnabled(null));
  
      Assert.assertFalse(service.isKerberosEnabled(map));
  
      map.put("core-site", Collections.singletonMap("hadoop.security.authentication", "none"));
      map.put("hdfs-site", Collections.singletonMap("hadoop.security.authentication", "none"));
      Assert.assertFalse(service.isKerberosEnabled(map));
  
      map.put("core-site", Collections.singletonMap("hadoop.security.authentication", "kerberos"));
      map.put("hdfs-site", Collections.singletonMap("hadoop.security.authentication", "none"));
      Assert.assertTrue(service.isKerberosEnabled(map));
  
      map.put("core-site", Collections.singletonMap("hadoop.security.authentication", "none"));
      map.put("hdfs-site", Collections.singletonMap("hadoop.security.authentication", "kerberos"));
      Assert.assertTrue(service.isKerberosEnabled(map));
  
      map.put("core-site", Collections.singletonMap("hadoop.security.authentication", "kerberos"));
      map.put("hdfs-site", Collections.singletonMap("hadoop.security.authentication", "kerberos"));
      Assert.assertTrue(service.isKerberosEnabled(map));
  
    }
  
    @Test
    public void testClusterConfigsUnmappingOnDeleteAllServiceConfigs() throws AmbariException {
      String serviceName = "HDFS";
      Service s = serviceFactory.createNew(cluster, serviceName, repositoryVersion);
      cluster.addService(s);
  
      Service service = cluster.getService(serviceName);
  
      // add some cluster and service configs
      Config config1 = configFactory.createNew(cluster, "hdfs-site", "version1",
          new HashMap<String, String>() {{ put("a", "b"); }}, new HashMap<>());
      cluster.addDesiredConfig("admin", Collections.singleton(config1));
  
      Config config2 = configFactory.createNew(cluster, "hdfs-site", "version2",
          new HashMap<String, String>() {{ put("a", "b"); }}, new HashMap<>());
      cluster.addDesiredConfig("admin", Collections.singleton(config2));
  
  
      // check all cluster configs are mapped
      Collection<ClusterConfigEntity> clusterConfigEntities = cluster.getClusterEntity().getClusterConfigEntities();
      for (ClusterConfigEntity clusterConfigEntity : clusterConfigEntities) {
        assertFalse(clusterConfigEntity.isUnmapped());
      }
  
      ((ServiceImpl)service).deleteAllServiceConfigs();
  
      // check all cluster configs are unmapped
      clusterConfigEntities = cluster.getClusterEntity().getClusterConfigEntities();
      for (ClusterConfigEntity clusterConfigEntity : clusterConfigEntities) {
        assertTrue(clusterConfigEntity.isUnmapped());
      }
    }
  
    @Test
    public void testDeleteAllServiceConfigGroups() throws AmbariException {
      String serviceName = "HDFS";
      Service s = serviceFactory.createNew(cluster, serviceName, repositoryVersion);
      cluster.addService(s);
  
      Service service = cluster.getService(serviceName);
  
      ConfigGroup configGroup1 = createNiceMock(ConfigGroup.class);
      ConfigGroup configGroup2 = createNiceMock(ConfigGroup.class);
  
      expect(configGroup1.getId()).andReturn(1L).anyTimes();
      expect(configGroup2.getId()).andReturn(2L).anyTimes();
  
      expect(configGroup1.getServiceName()).andReturn(serviceName).anyTimes();
      expect(configGroup2.getServiceName()).andReturn(serviceName).anyTimes();
  
      expect(configGroup1.getClusterName()).andReturn(cluster.getClusterName()).anyTimes();
      expect(configGroup2.getClusterName()).andReturn(cluster.getClusterName()).anyTimes();
  
      replay(configGroup1, configGroup2);
  
      cluster.addConfigGroup(configGroup1);
      cluster.addConfigGroup(configGroup2);
  
      ((ServiceImpl)service).deleteAllServiceConfigGroups();
  
      assertTrue(MapUtils.isEmpty(cluster.getConfigGroupsByServiceName(serviceName)));
  
      verify(configGroup1, configGroup2);
    }
  
    private void addHostToCluster(String hostname,
                                  String clusterName) throws AmbariException {
      clusters.addHost(hostname);
      Host h = clusters.getHost(hostname);
      h.setIPv4(hostname + "ipv4");
      h.setIPv6(hostname + "ipv6");
  
      Map<String, String> hostAttributes = new HashMap<>();
      hostAttributes.put("os_family", "redhat");
      hostAttributes.put("os_release_version", "6.3");
      h.setHostAttributes(hostAttributes);
  
      clusters.mapHostToCluster(hostname, clusterName);
    }
  }
  