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.stack.upgrade;
  
  import java.util.ArrayList;
  import java.util.Collection;
  import java.util.HashMap;
  import java.util.HashSet;
  import java.util.LinkedList;
  import java.util.List;
  import java.util.Map;
  import java.util.Map.Entry;
  import java.util.Set;
  
  import org.apache.ambari.annotations.Experimental;
  import org.apache.ambari.annotations.ExperimentalFeature;
  import org.apache.ambari.server.AmbariException;
  import org.apache.ambari.server.agent.CommandRepository;
  import org.apache.ambari.server.agent.ExecutionCommand.KeyNames;
  import org.apache.ambari.server.api.services.AmbariMetaInfo;
  import org.apache.ambari.server.configuration.Configuration;
  import org.apache.ambari.server.controller.ActionExecutionContext;
  import org.apache.ambari.server.controller.AmbariManagementController;
  import org.apache.ambari.server.controller.internal.OperatingSystemResourceProvider;
  import org.apache.ambari.server.controller.internal.RepositoryResourceProvider;
  import org.apache.ambari.server.controller.internal.RepositoryVersionResourceProvider;
  import org.apache.ambari.server.controller.spi.SystemException;
  import org.apache.ambari.server.orm.entities.RepoDefinitionEntity;
  import org.apache.ambari.server.orm.entities.RepoOsEntity;
  import org.apache.ambari.server.orm.entities.RepositoryVersionEntity;
  import org.apache.ambari.server.state.Cluster;
  import org.apache.ambari.server.state.Clusters;
  import org.apache.ambari.server.state.Host;
  import org.apache.ambari.server.state.RepositoryInfo;
  import org.apache.ambari.server.state.Service;
  import org.apache.ambari.server.state.ServiceComponent;
  import org.apache.ambari.server.state.ServiceInfo;
  import org.apache.ambari.server.state.ServiceOsSpecific;
  import org.apache.ambari.server.state.StackId;
  import org.apache.ambari.server.state.repository.ClusterVersionSummary;
  import org.apache.ambari.server.state.repository.VersionDefinitionXml;
  import org.apache.ambari.server.state.stack.OsFamily;
  import org.apache.ambari.server.state.stack.RepoTag;
  import org.apache.ambari.spi.RepositoryType;
  import org.apache.ambari.spi.upgrade.UpgradeType;
  import org.apache.commons.lang.StringUtils;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  import com.google.common.collect.ArrayListMultimap;
  import com.google.common.collect.Multimap;
  import com.google.gson.Gson;
  import com.google.gson.JsonArray;
  import com.google.gson.JsonElement;
  import com.google.gson.JsonObject;
  import com.google.gson.JsonParser;
  import com.google.inject.Inject;
  import com.google.inject.Provider;
  import com.google.inject.Singleton;
  
  
  /**
   * Provides helper methods to manage repository versions.
   */
  @Singleton
  public class RepositoryVersionHelper {
  
    private static final Logger LOG = LoggerFactory.getLogger(RepositoryVersionHelper.class);
  
    @Inject
    private Gson gson;
  
    @Inject
    private Provider<AmbariMetaInfo> ami;
  
    @Inject
    private Provider<Configuration> configuration;
  
    @Inject
    private Provider<OsFamily> os_family;
  
    @Inject Provider<Clusters> clusters;
  
  
    /**
     * Checks repo URLs against the current version for the cluster and make
     * adjustments to the Base URL when the current is different.
     *
     * @param cluster {@link Cluster} object
     * @param component resolve {@link RepositoryVersionEntity} for the component, could be {@code null}
     *
     * @return {@link RepositoryVersionEntity} retrieved for component if set or cluster if not
     */
    @Experimental(feature = ExperimentalFeature.PATCH_UPGRADES)
    private RepositoryVersionEntity getRepositoryVersionEntity(Cluster cluster, ServiceComponent component) throws SystemException {
  
      RepositoryVersionEntity repositoryEntity = null;
  
      // !!! try to find the component repo first
      if (null != component) {
        repositoryEntity = component.getDesiredRepositoryVersion();
      } else {
        LOG.info("Service component not passed in, attempt to resolve the repository for cluster {}",
          cluster.getClusterName());
      }
  
      if (null == repositoryEntity && null != component) {
        try {
          Service service = cluster.getService(component.getServiceName());
          repositoryEntity = service.getDesiredRepositoryVersion();
        } catch (AmbariException e) {
          throw new SystemException("Unhandled exception", e);
        }
      }
  
      if (null == repositoryEntity) {
        LOG.info("Cluster {} has no specific Repository Versions.  Using stack-defined values", cluster.getClusterName());
        return null;
      }
  
      return repositoryEntity;
    }
  
    /**
     * Parses operating systems json to a list of entities. Expects json like:
     * <pre>
     * [
     *    {
     *       "repositories":[
     *          {
     *             "Repositories/base_url":"http://s3.amazonaws.com/dev.hortonworks.com/HDP/centos6/2.x/updates/2.2.0.0",
     *             "Repositories/repo_name":"HDP-UTILS",
     *             "Repositories/repo_id":"HDP-UTILS-1.1.0.20"
     *          },
     *          {
     *             "Repositories/base_url":"http://s3.amazonaws.com/dev.hortonworks.com/HDP/centos6/2.x/updates/2.2.0.0",
     *             "Repositories/repo_name":"HDP",
     *             "Repositories/repo_id":"HDP-2.2"
     *          }
     *       ],
     *       "OperatingSystems/os_type":"redhat6"
     *    }
     * ]
     * </pre>
     * @param repositoriesJson operating systems json
     * @return list of operating system entities
     * @throws Exception if any kind of json parsing error happened
     */
    public List<RepoOsEntity> parseOperatingSystems(String repositoriesJson) throws Exception {
      final List<RepoOsEntity> operatingSystems = new ArrayList<>();
      final JsonArray rootJson = new JsonParser().parse(repositoriesJson).getAsJsonArray();
      for (JsonElement operatingSystemJson: rootJson) {
        JsonObject osObj = operatingSystemJson.getAsJsonObject();
  
        final RepoOsEntity operatingSystemEntity = new RepoOsEntity();
  
        operatingSystemEntity.setFamily(osObj.get(OperatingSystemResourceProvider.OPERATING_SYSTEM_OS_TYPE_PROPERTY_ID).getAsString());
  
        if (osObj.has(OperatingSystemResourceProvider.OPERATING_SYSTEM_AMBARI_MANAGED_REPOS)) {
          operatingSystemEntity.setAmbariManaged(osObj.get(
              OperatingSystemResourceProvider.OPERATING_SYSTEM_AMBARI_MANAGED_REPOS).getAsBoolean());
        }
  
        for (JsonElement repositoryElement: osObj.get(RepositoryVersionResourceProvider.SUBRESOURCE_REPOSITORIES_PROPERTY_ID).getAsJsonArray()) {
          final RepoDefinitionEntity repositoryEntity = new RepoDefinitionEntity();
          final JsonObject repositoryJson = repositoryElement.getAsJsonObject();
          repositoryEntity.setBaseUrl(repositoryJson.get(RepositoryResourceProvider.REPOSITORY_BASE_URL_PROPERTY_ID).getAsString());
          repositoryEntity.setRepoName(repositoryJson.get(RepositoryResourceProvider.REPOSITORY_REPO_NAME_PROPERTY_ID).getAsString());
          repositoryEntity.setRepoID(repositoryJson.get(RepositoryResourceProvider.REPOSITORY_REPO_ID_PROPERTY_ID).getAsString());
          if (repositoryJson.get(RepositoryResourceProvider.REPOSITORY_DISTRIBUTION_PROPERTY_ID) != null) {
            repositoryEntity.setDistribution(repositoryJson.get(RepositoryResourceProvider.REPOSITORY_DISTRIBUTION_PROPERTY_ID).getAsString());
          }
          if (repositoryJson.get(RepositoryResourceProvider.REPOSITORY_COMPONENTS_PROPERTY_ID) != null) {
            repositoryEntity.setComponents(repositoryJson.get(RepositoryResourceProvider.REPOSITORY_COMPONENTS_PROPERTY_ID).getAsString());
          }
          if (repositoryJson.get(RepositoryResourceProvider.REPOSITORY_MIRRORS_LIST_PROPERTY_ID) != null) {
            repositoryEntity.setMirrors(repositoryJson.get(RepositoryResourceProvider.REPOSITORY_MIRRORS_LIST_PROPERTY_ID).getAsString());
          }
          if (repositoryJson.getAsJsonObject().get(RepositoryResourceProvider.REPOSITORY_UNIQUE_PROPERTY_ID) != null) {
            repositoryEntity.setUnique(repositoryJson.getAsJsonObject().get(RepositoryResourceProvider.REPOSITORY_UNIQUE_PROPERTY_ID).getAsBoolean());
          }
          if (repositoryJson.get(RepositoryResourceProvider.REPOSITORY_APPLICABLE_SERVICES_PROPERTY_ID) != null) {
            List<String> applicableServices = new LinkedList<>();
            JsonArray jsonArray = repositoryJson.get(RepositoryResourceProvider.REPOSITORY_APPLICABLE_SERVICES_PROPERTY_ID).getAsJsonArray();
            for (JsonElement je : jsonArray) {
              applicableServices.add(je.getAsString());
            }
            repositoryEntity.setApplicableServices(applicableServices);
          }
  
          if (null != repositoryJson.get(RepositoryResourceProvider.REPOSITORY_TAGS_PROPERTY_ID)) {
            Set<RepoTag> tags = new HashSet<>();
  
            JsonArray jsonArray = repositoryJson.get(RepositoryResourceProvider.REPOSITORY_TAGS_PROPERTY_ID).getAsJsonArray();
            for(JsonElement je : jsonArray) {
              tags.add(RepoTag.valueOf(je.getAsString()));
            }
            repositoryEntity.setTags(tags);
          }
  
          operatingSystemEntity.addRepoDefinition(repositoryEntity);
        }
        operatingSystems.add(operatingSystemEntity);
      }
      return operatingSystems;
    }
  
    public List<RepoOsEntity> createRepoOsEntities(List<RepositoryInfo> repositories) {
  
      List<RepoOsEntity> repoOsEntities = new ArrayList<>();
      final Multimap<String, RepositoryInfo> operatingSystems = ArrayListMultimap.create();
      for (RepositoryInfo repository : repositories) {
        operatingSystems.put(repository.getOsType(), repository);
      }
      for (Entry<String, Collection<RepositoryInfo>> operatingSystem : operatingSystems.asMap().entrySet()) {
        RepoOsEntity operatingSystemEntity = new RepoOsEntity();
        List<RepoDefinitionEntity> repositoriesList = new ArrayList<>();
        for (RepositoryInfo repository : operatingSystem.getValue()) {
          RepoDefinitionEntity repositoryDefinition = new RepoDefinitionEntity();
          repositoryDefinition.setBaseUrl(repository.getBaseUrl());
          repositoryDefinition.setRepoName(repository.getRepoName());
          repositoryDefinition.setRepoID(repository.getRepoId());
          repositoryDefinition.setDistribution(repository.getDistribution());
          repositoryDefinition.setComponents(repository.getComponents());
          repositoryDefinition.setMirrors(repository.getMirrorsList());
          repositoryDefinition.setUnique(repository.isUnique());
  
          repositoryDefinition.setTags(repository.getTags());
          repositoryDefinition.setApplicableServices(repository.getApplicableServices());
  
          repositoriesList.add(repositoryDefinition);
          operatingSystemEntity.setAmbariManaged(repository.isAmbariManagedRepositories());
        }
        operatingSystemEntity.addRepoDefinitionEntities(repositoriesList);
        operatingSystemEntity.setFamily(operatingSystem.getKey());
        repoOsEntities.add(operatingSystemEntity);
      }
      return repoOsEntities;
    }
  
    /**
     * Scans the given stack for upgrade packages which can be applied to update the cluster to given repository version.
     *
     * @param stackName stack name
     * @param stackVersion stack version
     * @param repositoryVersion target repository version
     * @param upgradeType if not {@code null} null, will only return upgrade packs whose type matches.
     * @return upgrade pack name
     * @throws AmbariException if no upgrade packs suit the requirements
     */
    public String getUpgradePackageName(String stackName, String stackVersion, String repositoryVersion, UpgradeType upgradeType) throws AmbariException {
      final Map<String, UpgradePack> upgradePacks = ami.get().getUpgradePacks(stackName, stackVersion);
      for (UpgradePack upgradePack : upgradePacks.values()) {
        final String upgradePackName = upgradePack.getName();
  
        if (null != upgradeType && upgradePack.getType() != upgradeType) {
          continue;
        }
  
        // check that upgrade pack has <target> node
        if (StringUtils.isBlank(upgradePack.getTarget())) {
          LOG.error("Upgrade pack " + upgradePackName + " is corrupted, it should contain <target> node");
          continue;
        }
        if (upgradePack.canBeApplied(repositoryVersion)) {
          return upgradePackName;
        }
      }
      throw new AmbariException("There were no suitable upgrade packs for stack " + stackName + " " + stackVersion +
          ((null != upgradeType) ? " and upgrade type " + upgradeType : ""));
    }
  
    /**
     * Build the role parameters for an install command.
     *
     * @param amc           the management controller.  Tests don't use the same instance that gets injected.
     * @param repoVersion   the repository version
     * @param osFamily      the os family
     * @param servicesOnHost the set of services to check for packages
     * @return a Map<String, String> to use in
     */
    public Map<String, String> buildRoleParams(AmbariManagementController amc, RepositoryVersionEntity repoVersion, String osFamily, Set<String> servicesOnHost)
      throws SystemException {
  
      StackId stackId = repoVersion.getStackId();
  
      List<ServiceOsSpecific.Package> packages = new ArrayList<>();
  
      for (String serviceName : servicesOnHost) {
        ServiceInfo info;
  
        try {
          if (ami.get().isServiceRemovedInStack(stackId.getStackName(), stackId.getStackVersion(), serviceName)) {
            LOG.info(String.format("%s has been removed from stack %s-%s. Skip calculating its installation packages", stackId.getStackName(), stackId.getStackVersion(), serviceName));
            continue; //No need to calculate install packages for removed services
          }
  
          info = ami.get().getService(stackId.getStackName(), stackId.getStackVersion(), serviceName);
        } catch (AmbariException e) {
          throw new SystemException(String.format("Cannot obtain stack information for %s-%s", stackId.getStackName(), stackId.getStackVersion()), e);
        }
  
        List<ServiceOsSpecific.Package> packagesForService = amc.getPackagesForServiceHost(info,
          new HashMap<>(), osFamily);
  
        List<String> blacklistedPackagePrefixes = configuration.get().getRollingUpgradeSkipPackagesPrefixes();
  
        for (ServiceOsSpecific.Package aPackage : packagesForService) {
          if (!aPackage.getSkipUpgrade()) {
            boolean blacklisted = false;
            for (String prefix : blacklistedPackagePrefixes) {
              if (aPackage.getName().startsWith(prefix)) {
                blacklisted = true;
                break;
              }
            }
            if (! blacklisted) {
              packages.add(aPackage);
            }
          }
        }
      }
  
      Map<String, String> roleParams = new HashMap<>();
      roleParams.put("stack_id", stackId.getStackId());
      // !!! TODO make roleParams <String, Object> so we don't have to do this awfulness.
      roleParams.put(KeyNames.PACKAGE_LIST, gson.toJson(packages));
  
      return roleParams;
    }
  
  
    /**
     * Return repositories available for target os version on host based on {@code repoVersion} repository definition
     * @param host target {@link Host} for providing repositories list
     * @param repoVersion {@link RepositoryVersionEntity} version definition with all available repositories
     *
     * @return {@link RepoOsEntity} with available repositories for host
     * @throws SystemException if no repository available for target {@link Host}
     */
    public RepoOsEntity getOSEntityForHost(Host host, RepositoryVersionEntity repoVersion) throws SystemException {
      String osFamily = host.getOsFamily();
      RepoOsEntity osEntity = null;
      for (RepoOsEntity operatingSystem : repoVersion.getRepoOsEntities()) {
        if (osFamily.equals(operatingSystem.getFamily())) {
          osEntity = operatingSystem;
          break;
        }
      }
  
      if (null == osEntity) {
        throw new SystemException(String.format("Operating System matching %s could not be found",
          osFamily));
      }
  
      return osEntity;
    }
  
    /**
     * Adds a command repository to the action context
     * @param osEntity      the OS family
     */
    public CommandRepository getCommandRepository(final RepositoryVersionEntity repoVersion,
                                                  final RepoOsEntity osEntity) throws SystemException {
  
      final CommandRepository commandRepo = new CommandRepository();
      final boolean sysPreppedHost = configuration.get().areHostsSysPrepped().equalsIgnoreCase("true");
  
      if (null == repoVersion) {
        throw new SystemException("Repository version entity is not provided");
      }
  
      commandRepo.setRepositories(osEntity.getFamily(), osEntity.getRepoDefinitionEntities());
      commandRepo.setRepoVersion(repoVersion.getVersion());
      commandRepo.setRepositoryVersionId(repoVersion.getId());
      commandRepo.setResolved(repoVersion.isResolved());
      commandRepo.setStackName(repoVersion.getStackId().getStackName());
      commandRepo.getFeature().setPreInstalled(configuration.get().areHostsSysPrepped());
      commandRepo.getFeature().setIsScoped(!sysPreppedHost);
  
      if (!osEntity.isAmbariManaged()) {
        commandRepo.setNonManaged();
      } else {
        if (repoVersion.isLegacy()){
          commandRepo.setLegacyRepoFileName(repoVersion.getStackName(), repoVersion.getVersion());
          commandRepo.setLegacyRepoId(repoVersion.getVersion());
          commandRepo.getFeature().setIsScoped(false);
        } else {
          commandRepo.setRepoFileName(repoVersion.getStackName(), repoVersion.getId());
          commandRepo.setUniqueSuffix(String.format("-repo-%s", repoVersion.getId()));
        }
      }
  
      if (configuration.get().arePackagesLegacyOverridden()) {
        LOG.warn("Legacy override option is turned on, disabling CommandRepositoryFeature.scoped feature");
        commandRepo.getFeature().setIsScoped(false);
      }
      return commandRepo;
    }
  
  
    /**
     * Builds repository information for inclusion in a command.  This replaces escaping json on
     * a command.
     *
     * @param cluster the cluster
     * @param host    the host
     * @param component {@link ServiceComponent} object, could be null to return service-related repository
     * @return  the command repository
     * @throws SystemException
     */
    @Experimental(feature=ExperimentalFeature.PATCH_UPGRADES)
    public CommandRepository getCommandRepository(final Cluster cluster, ServiceComponent component, final Host host)
      throws SystemException {
  
      RepositoryVersionEntity repoVersion = getRepositoryVersionEntity(cluster, component);
      RepoOsEntity osEntity = getOSEntityForHost(host, repoVersion);
  
      return getCommandRepository(repoVersion, osEntity);
    }
  
    @Experimental(feature=ExperimentalFeature.PATCH_UPGRADES)
    public CommandRepository getCommandRepository(final Cluster cluster, final Service service, final Host host,
                                                  final String componentName) throws AmbariException {
      CommandRepository commandRepository = null;
      try {
        if (null != componentName) {
          ServiceComponent serviceComponent = service.getServiceComponent(componentName);
          commandRepository = getCommandRepository(cluster, serviceComponent, host);
        } else {
          RepositoryVersionEntity repoVersion = service.getDesiredRepositoryVersion();
          RepoOsEntity osEntity = getOSEntityForHost(host, repoVersion);
          commandRepository = getCommandRepository(repoVersion, osEntity);
        }
      } catch (SystemException e){
        LOG.debug("Unable to find command repository with a correct operating system for host {}",
          host, e);
      }
  
      return commandRepository;
    }
  
  
    /**
     * This method builds and adds repo infoto hostLevelParams of action
     *
     * @param cluster cluster to which host level params belongs
     * @param actionContext context of the action. Must be not {@code null}
     * @param repositoryVersion repository version entity to use
     * @param hostLevelParams hasgmap with host level params. Must be not {@code null}
     * @param hostName host name to which add repo onfo
     * @throws AmbariException
     */
    @Deprecated
    public void addRepoInfoToHostLevelParams(final Cluster cluster, final ActionExecutionContext actionContext,
                                             final RepositoryVersionEntity repositoryVersion, final Map<String, String> hostLevelParams,
                                             final String hostName) throws AmbariException {
  
      // if the repo is null, see if any values from the context should go on the
      // host params and then return
      if (null == repositoryVersion) {
        // see if the action context has a repository set to use for the command
        if (null != actionContext.getStackId()) {
          StackId stackId = actionContext.getStackId();
          hostLevelParams.put(KeyNames.STACK_NAME, stackId.getStackName());
          hostLevelParams.put(KeyNames.STACK_VERSION, stackId.getStackVersion());
        }
  
        return;
      } else {
        StackId stackId = repositoryVersion.getStackId();
        hostLevelParams.put(KeyNames.STACK_NAME, stackId.getStackName());
        hostLevelParams.put(KeyNames.STACK_VERSION, stackId.getStackVersion());
      }
    }
  
    /**
     * Executed by two different representations of repos.  When we are comfortable with the new
     * implementation, this may be removed and called inline in {@link #getCommandRepository(Cluster, ServiceComponent, Host)}
     *
     * @param cluster   the cluster to isolate the stack
     * @param component the component
     * @param host      used to resolve the family for the repositories
     * @return JsonArray the type as defined by the supplied {@code function}.
     * @throws SystemException
     */
    @Deprecated
    private JsonArray getBaseUrls(Cluster cluster, ServiceComponent component, Host host) throws SystemException {
  
      String hostOsType = host.getOsType();
      String hostOsFamily = host.getOsFamily();
      String hostName = host.getHostName();
  
      StackId stackId = component.getDesiredStackId();
      Map<String, List<RepositoryInfo>> repos;
  
      try {
        repos = ami.get().getRepository(stackId.getStackName(), stackId.getStackVersion());
      }catch (AmbariException e) {
        throw new SystemException("Unhandled exception", e);
      }
  
      String family = os_family.get().find(hostOsType);
      if (null == family) {
        family = hostOsFamily;
      }
  
      final List<RepositoryInfo> repoInfoList;
  
      // !!! check for the most specific first
      if (repos.containsKey(hostOsType)) {
        repoInfoList = repos.get(hostOsType);
      } else if (null != family && repos.containsKey(family)) {
        repoInfoList = repos.get(family);
      } else {
        repoInfoList = null;
        LOG.warn("Could not retrieve repo information for host"
          + ", hostname=" + hostName
          + ", clusterName=" + cluster.getClusterName()
          + ", stackInfo=" + stackId.getStackId());
      }
  
      return (null == repoInfoList) ? null : (JsonArray) gson.toJsonTree(repoInfoList);
    }
  
  
    /**
     * Adds a command repository to the action context
     * @param context       the context
     * @param osEntity      the OS family
     */
    public void addCommandRepositoryToContext(ActionExecutionContext context,
        final RepositoryVersionEntity repoVersion, RepoOsEntity osEntity) throws SystemException {
  
      final CommandRepository commandRepo = getCommandRepository(repoVersion, osEntity);
  
      ClusterVersionSummary summary = null;
  
      if (RepositoryType.STANDARD != repoVersion.getType()) {
        try {
          final Cluster cluster = clusters.get().getCluster(context.getClusterName());
  
          VersionDefinitionXml xml = repoVersion.getRepositoryXml();
          summary = xml.getClusterSummary(cluster, ami.get());
        } catch (Exception e) {
          LOG.warn("Could not determine repository from %s/%s.  Will not pass cluster version.");
        }
      }
  
      final ClusterVersionSummary clusterSummary = summary;
  
      context.addVisitor(command -> {
        if (null == command.getRepositoryFile()) {
          command.setRepositoryFile(commandRepo);
        }
  
        if (null != clusterSummary) {
          Map<String, Object> params = command.getRoleParameters();
          if (null == params) {
            params = new HashMap<>();
            command.setRoleParameters(params);
          }
          params.put(KeyNames.CLUSTER_VERSION_SUMMARY, clusterSummary);
        }
  
      });
    }
  
    /** Get repository info given a cluster and host.
     *
     * @param cluster  the cluster
     * @param host     the host
     *
     * @return the repo info
     *
     * @throws AmbariException if the repository information can not be obtained
     */
    public String getRepoInfoString(Cluster cluster, ServiceComponent component, Host host) throws AmbariException, SystemException {
      return gson.toJson(getCommandRepository(cluster, component, host));
    }
  }
  