blob: 6c13a13f17c8160df2abd299a132615ecddd75cb [file] [log] [blame]
/*
* 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;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.annotation.Nullable;
import org.apache.ambari.annotations.Experimental;
import org.apache.ambari.annotations.ExperimentalFeature;
import org.apache.ambari.server.AmbariException;
import org.apache.ambari.server.controller.RepositoryResponse;
import org.apache.ambari.server.orm.entities.RepoDefinitionEntity;
import org.apache.ambari.server.orm.entities.RepoOsEntity;
import org.apache.ambari.server.state.RepositoryInfo;
import org.apache.ambari.server.state.stack.RepositoryXml;
import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimaps;
import com.google.common.collect.Sets;
/**
* Utility functions for repository related tasks.
*/
public class RepoUtil {
/**
* logger instance
*/
private final static Logger LOG = LoggerFactory.getLogger(RepoUtil.class);
/**
* repository directory name
*/
final static String REPOSITORY_FOLDER_NAME = "repos";
/**
* repository file name
*/
final static String REPOSITORY_FILE_NAME = "repoinfo.xml";
private static final Function<RepoDefinitionEntity, String> REPO_ENTITY_TO_NAME = new Function<RepoDefinitionEntity, String>() {
@Override
public String apply(@Nullable RepoDefinitionEntity input) {
return input.getRepoName();
}
};
/**
* Parses the repository file for a stack/service if exists.
*
* @param directory stack/service base directory
* @param subDirs stack/service directory sub directories
* @param unmarshaller {@link ModuleFileUnmarshaller}, needed to parse repo XML
* @throws AmbariException if unable to parse the repository file
* @return The directory containing the repo file and the parsed repo file (if exists)
*/
public static RepositoryFolderAndXml parseRepoFile(File directory,
Collection<String> subDirs,
ModuleFileUnmarshaller unmarshaller) {
File repositoryFile = null;
String repoDir = null;
RepositoryXml repoFile = null;
if (subDirs.contains(REPOSITORY_FOLDER_NAME)) {
repoDir = directory.getAbsolutePath() + File.separator + REPOSITORY_FOLDER_NAME;
repositoryFile = new File(directory.getPath()+ File.separator +
REPOSITORY_FOLDER_NAME + File.separator + REPOSITORY_FILE_NAME);
if (repositoryFile.exists()) {
try {
repoFile = unmarshaller.unmarshal(RepositoryXml.class, repositoryFile);
} catch (Exception e) {
repoFile = new RepositoryXml();
repoFile.setValid(false);
String msg = "Unable to parse repo file at location: " +
repositoryFile.getAbsolutePath();
repoFile.addError(msg);
LOG.warn(msg);
}
}
}
return new RepositoryFolderAndXml(Optional.fromNullable(repoDir), Optional.fromNullable(repoFile));
}
/**
* Checks the passed {@code operatingSystems} parameter if it contains all repositories from the stack model. If a
* repository is present in the stack model but missing in the operating system entity list, it is considered a
* service repository and will be added.
* @param operatingSystems - A list of OperatingSystemEntity objects extracted from a RepositoryVersionEntity
* @param stackReposByOs - Stack repositories loaded from the disk (including service repositories), grouped by os.
* @return {@code true} if there were added repositories
*/
@Experimental(feature = ExperimentalFeature.CUSTOM_SERVICE_REPOS,
comment = "Remove logic for handling custom service repos after enabling multi-mpack cluster deployment")
public static boolean addServiceReposToOperatingSystemEntities(List<RepoOsEntity> operatingSystems,
ListMultimap<String, RepositoryInfo> stackReposByOs) {
Set<String> addedRepos = new HashSet<>();
for (RepoOsEntity os : operatingSystems) {
List<RepositoryInfo> serviceReposForOs = stackReposByOs.get(os.getFamily());
ImmutableSet<String> repoNames = ImmutableSet.copyOf(Lists.transform(os.getRepoDefinitionEntities(), REPO_ENTITY_TO_NAME));
for (RepositoryInfo repoInfo : serviceReposForOs)
if (!repoNames.contains(repoInfo.getRepoName())) {
os.addRepoDefinition(toRepositoryEntity(repoInfo));
addedRepos.add(String.format("%s (%s)", repoInfo.getRepoId(), os.getFamily()));
}
}
LOG.info("Added {} service repos: {}", addedRepos.size(),Iterables.toString(addedRepos));
return CollectionUtils.isNotEmpty(addedRepos);
}
/**
* Given a list of VDF repositorie and stack repositories (grouped by os) returns the service repositories.
* A repository is considered a service repo if present in the stack model but missing in the VDF (check is performed
* by repository name, per operating system).
* @param vdfRepos the repositories coming from a version definition
* @param stackReposByOs the repositories in the stack model (loaded from disks)
* @return A list of service repositories
*/
@Experimental(feature = ExperimentalFeature.CUSTOM_SERVICE_REPOS,
comment = "Remove logic for handling custom service repos after enabling multi-mpack cluster deployment")
public static List<RepositoryInfo> getServiceRepos(List<RepositoryInfo> vdfRepos,
ListMultimap<String, RepositoryInfo> stackReposByOs) {
Set<String> serviceRepoIds = new HashSet<>();
List<RepositoryInfo> serviceRepos = new ArrayList<>();
ListMultimap<String, RepositoryInfo> vdfReposByOs = Multimaps.index(vdfRepos, RepositoryInfo.GET_OSTYPE_FUNCTION);
for(String os: vdfReposByOs.keySet()) {
Set<String> vdfRepoNames = Sets.newHashSet(
Lists.transform(vdfReposByOs.get(os), RepositoryInfo.GET_REPO_NAME_FUNCTION));
for (RepositoryInfo repo: stackReposByOs.get(os)) {
if (!vdfRepoNames.contains(repo.getRepoName())) {
serviceRepos.add(repo);
serviceRepoIds.add(repo.getRepoId());
}
}
}
LOG.debug("Found {} service repos: {}", serviceRepoIds.size(),Iterables.toString(serviceRepoIds));
return serviceRepos;
}
/**
* Convert a list of {@link RepositoryInfo} objects to a lost of {@link RepositoryResponse} objects
* @param repositoryInfos the list of repository infos
* @param versionDefinitionId the version definition id
* @param stackName the stack name
* @param stackVersion the stack version
* @return a list of repository responses
*/
public static List<RepositoryResponse> asResponses(List<RepositoryInfo> repositoryInfos,
@Nullable String versionDefinitionId,
@Nullable String stackName,
@Nullable String stackVersion) {
List<RepositoryResponse> responses = new ArrayList<>(repositoryInfos.size());
for (RepositoryInfo repoInfo: repositoryInfos) {
RepositoryResponse response = repoInfo.convertToResponse();
response.setVersionDefinitionId(versionDefinitionId);
response.setStackName(stackName);
response.setStackVersion(stackVersion);
responses.add(response);
}
return responses;
}
@Experimental(feature = ExperimentalFeature.CUSTOM_SERVICE_REPOS,
comment = "Remove logic for handling custom service repos after enabling multi-mpack cluster deployment")
private static RepoDefinitionEntity toRepositoryEntity(RepositoryInfo repoInfo) {
RepoDefinitionEntity re = new RepoDefinitionEntity();
re.setBaseUrl(repoInfo.getBaseUrl());
re.setRepoName(repoInfo.getRepoName());
re.setRepoID(repoInfo.getRepoId());
re.setDistribution(repoInfo.getDistribution());
re.setComponents(repoInfo.getComponents());
re.setTags(repoInfo.getTags());
re.setApplicableServices(repoInfo.getApplicableServices());
return re;
}
}
/**
* Value class for a pair of repository folder and parsed repository XML.
*/
class RepositoryFolderAndXml {
final Optional<String> repoDir;
final Optional<RepositoryXml> repoXml;
/**
* @param repoDir Path to the repository directory (optional)
* @param repoXml Parsed repository XML (optional)
*/
public RepositoryFolderAndXml(Optional<String> repoDir, Optional<RepositoryXml> repoXml) {
this.repoDir = repoDir;
this.repoXml = repoXml;
}
}