/* * 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.controller.internal; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.net.URI; import java.util.ArrayList; import java.util.Collections; import java.util.EnumSet; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.apache.ambari.server.AmbariException; import org.apache.ambari.server.StaticallyInject; import org.apache.ambari.server.api.resources.OperatingSystemResourceDefinition; import org.apache.ambari.server.api.resources.RepositoryResourceDefinition; import org.apache.ambari.server.api.services.AmbariMetaInfo; import org.apache.ambari.server.configuration.ComponentSSLConfiguration; import org.apache.ambari.server.configuration.Configuration; import org.apache.ambari.server.controller.spi.NoSuchParentResourceException; import org.apache.ambari.server.controller.spi.NoSuchResourceException; import org.apache.ambari.server.controller.spi.Predicate; import org.apache.ambari.server.controller.spi.Request; import org.apache.ambari.server.controller.spi.RequestStatus; import org.apache.ambari.server.controller.spi.Resource; import org.apache.ambari.server.controller.spi.ResourceAlreadyExistsException; import org.apache.ambari.server.controller.spi.SystemException; import org.apache.ambari.server.controller.spi.UnsupportedPropertyException; import org.apache.ambari.server.controller.utilities.PropertyHelper; import org.apache.ambari.server.orm.dao.RepositoryVersionDAO; import org.apache.ambari.server.orm.dao.StackDAO; import org.apache.ambari.server.orm.entities.OperatingSystemEntity; import org.apache.ambari.server.orm.entities.RepositoryEntity; import org.apache.ambari.server.orm.entities.RepositoryVersionEntity; import org.apache.ambari.server.orm.entities.StackEntity; import org.apache.ambari.server.security.authorization.ResourceType; import org.apache.ambari.server.security.authorization.RoleAuthorization; import org.apache.ambari.server.stack.RepoUtil; import org.apache.ambari.server.state.RepositoryInfo; import org.apache.ambari.server.state.RepositoryType; import org.apache.ambari.server.state.StackId; import org.apache.ambari.server.state.StackInfo; import org.apache.ambari.server.state.repository.VersionDefinitionXml; import org.apache.ambari.server.state.stack.upgrade.RepositoryVersionHelper; import org.apache.commons.codec.binary.Base64; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.BooleanUtils; import org.apache.commons.lang.ObjectUtils; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.math.NumberUtils; import org.codehaus.jackson.node.ArrayNode; import org.codehaus.jackson.node.JsonNodeFactory; import org.codehaus.jackson.node.ObjectNode; import com.google.common.collect.ListMultimap; import com.google.common.collect.Sets; import com.google.inject.Inject; import com.google.inject.Provider; /** * The {@link VersionDefinitionResourceProvider} class deals with managing Version Definition * files. */ @StaticallyInject public class VersionDefinitionResourceProvider extends AbstractAuthorizedResourceProvider { public static final String VERSION_DEF = "VersionDefinition"; public static final String VERSION_DEF_BASE64_PROPERTY = "version_base64"; public static final String VERSION_DEF_STACK_NAME = "VersionDefinition/stack_name"; public static final String VERSION_DEF_STACK_VERSION = "VersionDefinition/stack_version"; protected static final String VERSION_DEF_ID = "VersionDefinition/id"; protected static final String VERSION_DEF_TYPE_PROPERTY_ID = "VersionDefinition/type"; protected static final String VERSION_DEF_DEFINITION_URL = "VersionDefinition/version_url"; protected static final String VERSION_DEF_AVAILABLE_DEFINITION = "VersionDefinition/available"; protected static final String VERSION_DEF_DEFINITION_BASE64 = PropertyHelper.getPropertyId(VERSION_DEF, VERSION_DEF_BASE64_PROPERTY); protected static final String VERSION_DEF_FULL_VERSION = "VersionDefinition/repository_version"; protected static final String VERSION_DEF_RELEASE_VERSION = "VersionDefinition/release/version"; protected static final String VERSION_DEF_RELEASE_BUILD = "VersionDefinition/release/build"; protected static final String VERSION_DEF_RELEASE_NOTES = "VersionDefinition/release/notes"; protected static final String VERSION_DEF_RELEASE_COMPATIBLE_WITH = "VersionDefinition/release/compatible_with"; protected static final String VERSION_DEF_AVAILABLE_SERVICES = "VersionDefinition/services"; protected static final String VERSION_DEF_STACK_SERVICES = "VersionDefinition/stack_services"; protected static final String VERSION_DEF_STACK_DEFAULT = "VersionDefinition/stack_default"; protected static final String VERSION_DEF_STACK_REPO_UPDATE_LINK_EXISTS = "VersionDefinition/stack_repo_update_link_exists"; protected static final String VERSION_DEF_DISPLAY_NAME = "VersionDefinition/display_name"; protected static final String VERSION_DEF_VALIDATION = "VersionDefinition/validation"; protected static final String SHOW_AVAILABLE = "VersionDefinition/show_available"; // !!! for convenience, bring over jdk compatibility information from the stack protected static final String VERSION_DEF_MIN_JDK = "VersionDefinition/min_jdk"; protected static final String VERSION_DEF_MAX_JDK = "VersionDefinition/max_jdk"; public static final String DIRECTIVE_SKIP_URL_CHECK = "skip_url_check"; public static final String SUBRESOURCE_OPERATING_SYSTEMS_PROPERTY_ID = new OperatingSystemResourceDefinition().getPluralName(); @Inject private static RepositoryVersionDAO s_repoVersionDAO; @Inject private static Provider<AmbariMetaInfo> s_metaInfo; @Inject private static Provider<RepositoryVersionHelper> s_repoVersionHelper; @Inject private static StackDAO s_stackDAO; @Inject private static Configuration s_configuration; /** * Key property ids */ private static final Set<String> PK_PROPERTY_IDS = Sets.newHashSet( VERSION_DEF_ID, VERSION_DEF_STACK_NAME, VERSION_DEF_STACK_VERSION, VERSION_DEF_FULL_VERSION); /** * The property ids for an version definition resource. */ private static final Set<String> PROPERTY_IDS = Sets.newHashSet( VERSION_DEF_ID, VERSION_DEF_TYPE_PROPERTY_ID, VERSION_DEF_DEFINITION_URL, VERSION_DEF_DEFINITION_BASE64, VERSION_DEF_AVAILABLE_DEFINITION, VERSION_DEF_STACK_NAME, VERSION_DEF_STACK_VERSION, VERSION_DEF_FULL_VERSION, VERSION_DEF_RELEASE_NOTES, VERSION_DEF_RELEASE_COMPATIBLE_WITH, VERSION_DEF_RELEASE_VERSION, VERSION_DEF_RELEASE_BUILD, VERSION_DEF_AVAILABLE_SERVICES, VERSION_DEF_STACK_SERVICES, VERSION_DEF_STACK_DEFAULT, VERSION_DEF_STACK_REPO_UPDATE_LINK_EXISTS, VERSION_DEF_DISPLAY_NAME, VERSION_DEF_VALIDATION, VERSION_DEF_MIN_JDK, VERSION_DEF_MAX_JDK, SUBRESOURCE_OPERATING_SYSTEMS_PROPERTY_ID, SHOW_AVAILABLE); /** * The key property ids for an version definition resource. */ private static final Map<Resource.Type, String> KEY_PROPERTY_IDS = Collections.singletonMap(Resource.Type.VersionDefinition, VERSION_DEF_ID); /** * Constructor. */ VersionDefinitionResourceProvider() { super(PROPERTY_IDS, KEY_PROPERTY_IDS); setRequiredCreateAuthorizations(EnumSet.of(RoleAuthorization.AMBARI_MANAGE_STACK_VERSIONS)); setRequiredGetAuthorizations(EnumSet.of(RoleAuthorization.AMBARI_MANAGE_STACK_VERSIONS)); } @Override protected RequestStatus createResourcesAuthorized(final Request request) throws SystemException, UnsupportedPropertyException, ResourceAlreadyExistsException, NoSuchParentResourceException { Set<Map<String, Object>> requestProperties = request.getProperties(); if (requestProperties.size() > 1) { throw new IllegalArgumentException("Cannot process more than one file per request"); } final Map<String, Object> properties = requestProperties.iterator().next(); if (!properties.containsKey(VERSION_DEF_DEFINITION_URL) && !properties.containsKey(VERSION_DEF_DEFINITION_BASE64) && !properties.containsKey(VERSION_DEF_AVAILABLE_DEFINITION)) { throw new IllegalArgumentException(String.format("Creation method is not known. %s or %s is required or upload the file directly", VERSION_DEF_DEFINITION_URL, VERSION_DEF_AVAILABLE_DEFINITION)); } if (properties.containsKey(VERSION_DEF_DEFINITION_URL) && properties.containsKey(VERSION_DEF_DEFINITION_BASE64)) { throw new IllegalArgumentException(String.format("Specify ONLY the url with %s or upload the file directly", VERSION_DEF_DEFINITION_URL)); } final String definitionUrl = (String) properties.get(VERSION_DEF_DEFINITION_URL); final String definitionBase64 = (String) properties.get(VERSION_DEF_DEFINITION_BASE64); final String definitionName = (String) properties.get(VERSION_DEF_AVAILABLE_DEFINITION); final Set<String> validations = new HashSet<>(); final boolean dryRun = request.isDryRunRequest(); final boolean skipUrlCheck; if (null != request.getRequestInfoProperties()) { skipUrlCheck = BooleanUtils.toBoolean(request.getRequestInfoProperties().get(DIRECTIVE_SKIP_URL_CHECK)); } else { skipUrlCheck = false; } XmlHolder xmlHolder = createResources(new Command<XmlHolder>() { @Override public XmlHolder invoke() throws AmbariException { XmlHolder holder = null; if (null != definitionUrl) { holder = loadXml(definitionUrl); } else if (null != definitionBase64) { holder = loadXml(Base64.decodeBase64(definitionBase64)); } else if (null != definitionName) { VersionDefinitionXml xml = s_metaInfo.get().getVersionDefinition(definitionName); if (null == xml) { throw new AmbariException(String.format("Version %s not found", definitionName)); } holder = new XmlHolder(); holder.xml = xml; try { holder.xmlString = xml.toXml(); } catch (Exception e) { throw new AmbariException(String.format("The available repository %s does not serialize", definitionName), e); } } else { throw new AmbariException("Cannot determine creation method"); } // !!! must be in this anonymous method because things throw AmbariException toRepositoryVersionEntity(holder); try { RepositoryVersionResourceProvider.validateRepositoryVersion(s_repoVersionDAO, s_metaInfo.get(), holder.entity, skipUrlCheck); } catch (AmbariException e) { if (dryRun) { validations.add(e.getMessage()); } else { throw e; } } checkForParent(holder); return holder; } }); if (StringUtils.isNotBlank(ObjectUtils.toString(properties.get(VERSION_DEF_DISPLAY_NAME)))) { xmlHolder.xml.release.display = properties.get(VERSION_DEF_DISPLAY_NAME).toString(); xmlHolder.entity.setDisplayName(properties.get(VERSION_DEF_DISPLAY_NAME).toString()); // !!! also set the version string to the name for uniqueness reasons. this is // getting replaced during install packages anyway. this is just for the entity, the // VDF should stay the same. xmlHolder.entity.setVersion(properties.get(VERSION_DEF_DISPLAY_NAME).toString()); } if (s_repoVersionDAO.findByDisplayName(xmlHolder.entity.getDisplayName()) != null) { String err = String.format("Repository version with name %s already exists.", xmlHolder.entity.getDisplayName()); if (dryRun) { validations.add(err); } else { throw new IllegalArgumentException(err); } } if (s_repoVersionDAO.findByStackAndVersion(xmlHolder.entity.getStack(), xmlHolder.entity.getVersion()) != null) { String err = String.format("Repository version for stack %s and version %s already exists.", xmlHolder.entity.getStackId(), xmlHolder.entity.getVersion()); if (dryRun) { validations.add(err); } else { throw new IllegalArgumentException(err); } } final Resource res; if (dryRun) { // !!! dry runs imply that the whole entity should be provided. this is usually // done via sub-resources, but that model breaks down since we don't have a saved // entity yet Set<String> ids = Sets.newHashSet( VERSION_DEF_TYPE_PROPERTY_ID, VERSION_DEF_FULL_VERSION, VERSION_DEF_RELEASE_BUILD, VERSION_DEF_RELEASE_COMPATIBLE_WITH, VERSION_DEF_RELEASE_NOTES, VERSION_DEF_RELEASE_VERSION, VERSION_DEF_DISPLAY_NAME, VERSION_DEF_AVAILABLE_SERVICES, VERSION_DEF_VALIDATION, VERSION_DEF_MIN_JDK, VERSION_DEF_MAX_JDK, VERSION_DEF_STACK_SERVICES); res = toResource(null, xmlHolder.xml, ids, validations); // !!! if the definition name is not null, it can only be from available if (null != definitionName) { res.setProperty(SHOW_AVAILABLE, true); } // !!! for dry run, make sure the version is the entity display setResourceProperty(res, VERSION_DEF_FULL_VERSION, xmlHolder.entity.getVersion(), ids); addSubresources(res, xmlHolder.entity); } else { s_repoVersionDAO.create(xmlHolder.entity); res = toResource(xmlHolder.entity, Collections.<String>emptySet()); notifyCreate(Resource.Type.VersionDefinition, request); } RequestStatusImpl status = new RequestStatusImpl(null, Collections.singleton(res)); return status; } @Override public Set<Resource> getResources(Request request, Predicate predicate) throws SystemException, UnsupportedPropertyException, NoSuchResourceException, NoSuchParentResourceException { Set<Resource> results = new HashSet<>(); Set<String> requestPropertyIds = getRequestPropertyIds(request, predicate); Set<Map<String, Object>> propertyMaps = getPropertyMaps(predicate); if (propertyMaps.isEmpty()) { List<RepositoryVersionEntity> versions = s_repoVersionDAO.findAllDefinitions(); for (RepositoryVersionEntity entity : versions) { results.add(toResource(entity, requestPropertyIds)); } } else { for (Map<String, Object> propertyMap : propertyMaps) { if (propertyMap.containsKey(SHOW_AVAILABLE) && Boolean.parseBoolean(propertyMap.get(SHOW_AVAILABLE).toString())) { for (Entry<String, VersionDefinitionXml> entry : s_metaInfo.get().getVersionDefinitions().entrySet()) { Resource res = toResource(entry.getKey(), entry.getValue(), requestPropertyIds, null); res.setProperty(SHOW_AVAILABLE, true); results.add(res); } } else { String id = (String) propertyMap.get(VERSION_DEF_ID); if (null != id) { if (NumberUtils.isDigits(id)) { RepositoryVersionEntity entity = s_repoVersionDAO.findByPK(Long.parseLong(id)); if (null != entity) { results.add(toResource(entity, requestPropertyIds)); } } else { VersionDefinitionXml xml = s_metaInfo.get().getVersionDefinition(id); if (null == xml) { throw new NoSuchResourceException(String.format("Could not find version %s", id)); } Resource res = toResource(id, xml, requestPropertyIds, null); res.setProperty(SHOW_AVAILABLE, true); results.add(res); } } else { List<RepositoryVersionEntity> versions = s_repoVersionDAO.findAllDefinitions(); for (RepositoryVersionEntity entity : versions) { results.add(toResource(entity, requestPropertyIds)); } } } } } return results; } @Override protected RequestStatus updateResourcesAuthorized(final Request request, Predicate predicate) throws SystemException, UnsupportedPropertyException, NoSuchResourceException, NoSuchParentResourceException { throw new SystemException("Cannot update Version Definitions"); } @Override protected RequestStatus deleteResourcesAuthorized(Request request, Predicate predicate) throws SystemException, UnsupportedPropertyException, NoSuchResourceException, NoSuchParentResourceException { throw new SystemException("Cannot delete Version Definitions"); } /** * In the case of a patch, check if there is a parent repo. */ private void checkForParent(XmlHolder holder) throws AmbariException { RepositoryVersionEntity entity = holder.entity; if (entity.getType() != RepositoryType.PATCH) { return; } List<RepositoryVersionEntity> entities = s_repoVersionDAO.findByStack(entity.getStackId()); if (entities.isEmpty()) { throw new IllegalArgumentException(String.format("Patch %s was uploaded, but there are no repositories for %s", entity.getVersion(), entity.getStackId().toString())); } List<RepositoryVersionEntity> matching = new ArrayList<>(); boolean emptyCompatible = StringUtils.isBlank(holder.xml.release.compatibleWith); for (RepositoryVersionEntity candidate : entities) { String baseVersion = candidate.getVersion(); if (baseVersion.lastIndexOf('-') > -1) { baseVersion = baseVersion.substring(0, baseVersion.lastIndexOf('-')); } if (emptyCompatible) { if (baseVersion.equals(holder.xml.release.version)) { matching.add(candidate); } } else { if (baseVersion.matches(holder.xml.release.compatibleWith)) { matching.add(candidate); } } } if (matching.isEmpty()) { String format = "No versions matched pattern %s"; throw new IllegalArgumentException(String.format(format, emptyCompatible ? holder.xml.release.version : holder.xml.release.compatibleWith)); } else if (matching.size() > 1) { Set<String> versions= new HashSet<>(); for (RepositoryVersionEntity match : matching) { versions.add(match.getVersion()); } throw new IllegalArgumentException(String.format("More than one repository matches patch %s: %s", entity.getVersion(), StringUtils.join(versions, ", "))); } RepositoryVersionEntity parent = matching.get(0); entity.setParent(parent); } @Override protected Set<String> getPKPropertyIds() { return PK_PROPERTY_IDS; } @Override protected ResourceType getResourceType(Request request, Predicate predicate) { return ResourceType.AMBARI; } /** * Load the xml data from a posted Base64 stream * @param decoded the decoded Base64 data * @return the XmlHolder instance * @throws AmbariException */ private XmlHolder loadXml(byte[] decoded) { XmlHolder holder = new XmlHolder(); try { holder.xmlString = new String(decoded, "UTF-8"); } catch (UnsupportedEncodingException e) { holder.xmlString = new String(decoded); } try { holder.xml = VersionDefinitionXml.load(holder.xmlString); } catch (Exception e) { throw new IllegalArgumentException(e); } return holder; } /** * Load the xml data from a url * @param definitionUrl * @return the XmlHolder instance * @throws AmbariException */ private XmlHolder loadXml(String definitionUrl) throws AmbariException { XmlHolder holder = new XmlHolder(); holder.url = definitionUrl; int connectTimeout = s_configuration.getVersionDefinitionConnectTimeout(); int readTimeout = s_configuration.getVersionDefinitionReadTimeout(); try { URI uri = new URI(definitionUrl); InputStream stream = null; if (uri.getScheme().equalsIgnoreCase("file")) { stream = uri.toURL().openStream(); } else { URLStreamProvider provider = new URLStreamProvider(connectTimeout, readTimeout, ComponentSSLConfiguration.instance()); stream = provider.readFrom(definitionUrl); } holder.xmlString = IOUtils.toString(stream, "UTF-8"); holder.xml = VersionDefinitionXml.load(holder.xmlString); } catch (Exception e) { String err = String.format("Could not load url from %s. %s", definitionUrl, e.getMessage()); throw new AmbariException(err, e); } return holder; } /** * Transforms a XML version defintion to an entity * * @throws AmbariException if some properties are missing or json has incorrect structure */ protected void toRepositoryVersionEntity(XmlHolder holder) throws AmbariException { // !!! TODO validate parsed object graph RepositoryVersionEntity entity = new RepositoryVersionEntity(); StackId stackId = new StackId(holder.xml.release.stackId); StackEntity stackEntity = s_stackDAO.find(stackId.getStackName(), stackId.getStackVersion()); entity.setStack(stackEntity); List<RepositoryInfo> repos = holder.xml.repositoryInfo.getRepositories(); // Add service repositories (these are not contained by the VDF but are there in the stack model) ListMultimap<String, RepositoryInfo> stackReposByOs = s_metaInfo.get().getStack(stackId.getStackName(), stackId.getStackVersion()).getRepositoriesByOs(); repos.addAll(RepoUtil.getServiceRepos(repos, stackReposByOs)); entity.setOperatingSystems(s_repoVersionHelper.get().serializeOperatingSystems(repos)); entity.setVersion(holder.xml.release.getFullVersion()); entity.setDisplayName(stackId, holder.xml.release); entity.setType(holder.xml.release.repositoryType); entity.setVersionUrl(holder.url); entity.setVersionXml(holder.xmlString); entity.setVersionXsd(holder.xml.xsdLocation); holder.entity = entity; } /** * Converts a version definition to a resource * @param id the definition id * @param xml the version definition xml * @param requestedIds the requested ids * @return the resource * @throws SystemException */ private Resource toResource(String id, VersionDefinitionXml xml, Set<String> requestedIds, Set<String> validations) throws SystemException { Resource resource = new ResourceImpl(Resource.Type.VersionDefinition); resource.setProperty(VERSION_DEF_ID, id); StackId stackId = new StackId(xml.release.stackId); // !!! these are needed for href resource.setProperty(VERSION_DEF_STACK_NAME, stackId.getStackName()); resource.setProperty(VERSION_DEF_STACK_VERSION, stackId.getStackVersion()); StackInfo stack = null; try { stack = s_metaInfo.get().getStack(stackId.getStackName(), stackId.getStackVersion()); } catch (AmbariException e) { throw new SystemException(String.format("Could not load stack %s", stackId)); } setResourceProperty(resource, VERSION_DEF_TYPE_PROPERTY_ID, xml.release.repositoryType, requestedIds); setResourceProperty(resource, VERSION_DEF_FULL_VERSION, xml.release.getFullVersion(), requestedIds); setResourceProperty(resource, VERSION_DEF_RELEASE_BUILD, xml.release.build, requestedIds); setResourceProperty(resource, VERSION_DEF_RELEASE_COMPATIBLE_WITH, xml.release.compatibleWith, requestedIds); setResourceProperty(resource, VERSION_DEF_RELEASE_NOTES, xml.release.releaseNotes, requestedIds); setResourceProperty(resource, VERSION_DEF_RELEASE_VERSION, xml.release.version, requestedIds); setResourceProperty(resource, VERSION_DEF_STACK_DEFAULT, xml.isStackDefault(), requestedIds); setResourceProperty(resource, VERSION_DEF_STACK_REPO_UPDATE_LINK_EXISTS, (stack.getRepositoryXml().getLatestURI() != null), requestedIds); setResourceProperty(resource, VERSION_DEF_DISPLAY_NAME, xml.release.display, requestedIds); if (null != validations) { setResourceProperty(resource, VERSION_DEF_VALIDATION, validations, requestedIds); } setResourceProperty(resource, VERSION_DEF_AVAILABLE_SERVICES, xml.getAvailableServices(stack), requestedIds); setResourceProperty(resource, VERSION_DEF_STACK_SERVICES, xml.getStackServices(stack), requestedIds); setResourceProperty(resource, VERSION_DEF_MIN_JDK, stack.getMinJdk(), requestedIds); setResourceProperty(resource, VERSION_DEF_MAX_JDK, stack.getMaxJdk(), requestedIds); return resource; } /** * Convert the given {@link RepositoryVersionEntity} to a {@link Resource}. * * @param entity * the entity to convert. * @param requestedIds * the properties that were requested or {@code null} for all. * @return the resource representation of the entity (never {@code null}). */ private Resource toResource(RepositoryVersionEntity entity, Set<String> requestedIds) throws SystemException { Resource resource = new ResourceImpl(Resource.Type.VersionDefinition); resource.setProperty(VERSION_DEF_ID, entity.getId()); VersionDefinitionXml xml = null; try { xml = entity.getRepositoryXml(); } catch (Exception e) { String msg = String.format("Could not load version definition %s", entity.getId()); throw new SystemException(msg, e); } StackId stackId = new StackId(xml.release.stackId); // !!! these are needed for href resource.setProperty(VERSION_DEF_STACK_NAME, stackId.getStackName()); resource.setProperty(VERSION_DEF_STACK_VERSION, stackId.getStackVersion()); setResourceProperty(resource, VERSION_DEF_TYPE_PROPERTY_ID, entity.getType(), requestedIds); setResourceProperty(resource, VERSION_DEF_DEFINITION_URL, entity.getVersionUrl(), requestedIds); setResourceProperty(resource, VERSION_DEF_FULL_VERSION, entity.getVersion(), requestedIds); setResourceProperty(resource, VERSION_DEF_RELEASE_BUILD, xml.release.build, requestedIds); setResourceProperty(resource, VERSION_DEF_RELEASE_COMPATIBLE_WITH, xml.release.compatibleWith, requestedIds); setResourceProperty(resource, VERSION_DEF_RELEASE_NOTES, xml.release.releaseNotes, requestedIds); setResourceProperty(resource, VERSION_DEF_RELEASE_VERSION, xml.release.version, requestedIds); setResourceProperty(resource, VERSION_DEF_STACK_DEFAULT, xml.isStackDefault(), requestedIds); setResourceProperty(resource, VERSION_DEF_DISPLAY_NAME, xml.release.display, requestedIds); StackInfo stack = null; if (isPropertyRequested(VERSION_DEF_AVAILABLE_SERVICES, requestedIds) || isPropertyRequested(VERSION_DEF_STACK_SERVICES, requestedIds)) { try { stack = s_metaInfo.get().getStack(stackId.getStackName(), stackId.getStackVersion()); } catch (AmbariException e) { throw new SystemException(String.format("Could not load stack %s", stackId)); } } if (null != stack) { setResourceProperty(resource, VERSION_DEF_AVAILABLE_SERVICES, xml.getAvailableServices(stack), requestedIds); setResourceProperty(resource, VERSION_DEF_STACK_SERVICES, xml.getStackServices(stack), requestedIds); setResourceProperty(resource, VERSION_DEF_MIN_JDK, stack.getMinJdk(), requestedIds); setResourceProperty(resource, VERSION_DEF_MAX_JDK, stack.getMaxJdk(), requestedIds); setResourceProperty(resource, VERSION_DEF_STACK_REPO_UPDATE_LINK_EXISTS, (stack.getRepositoryXml().getLatestURI() != null), requestedIds); } return resource; } /** * Provide the dry-run entity with fake sub-resources. These are not queryable by normal API. */ private void addSubresources(Resource res, RepositoryVersionEntity entity) { JsonNodeFactory factory = JsonNodeFactory.instance; ArrayNode subs = factory.arrayNode(); for (OperatingSystemEntity os : entity.getOperatingSystems()) { ObjectNode osBase = factory.objectNode(); ObjectNode osElement = factory.objectNode(); osElement.put(PropertyHelper.getPropertyName(OperatingSystemResourceProvider.OPERATING_SYSTEM_AMBARI_MANAGED_REPOS), os.isAmbariManagedRepos()); osElement.put(PropertyHelper.getPropertyName(OperatingSystemResourceProvider.OPERATING_SYSTEM_OS_TYPE_PROPERTY_ID), os.getOsType()); osElement.put(PropertyHelper.getPropertyName(OperatingSystemResourceProvider.OPERATING_SYSTEM_STACK_NAME_PROPERTY_ID), entity.getStackName()); osElement.put(PropertyHelper.getPropertyName(OperatingSystemResourceProvider.OPERATING_SYSTEM_STACK_VERSION_PROPERTY_ID), entity.getStackVersion()); osBase.put(PropertyHelper.getPropertyCategory(OperatingSystemResourceProvider.OPERATING_SYSTEM_AMBARI_MANAGED_REPOS), osElement); ArrayNode reposArray = factory.arrayNode(); for (RepositoryEntity repo : os.getRepositories()) { ObjectNode repoBase = factory.objectNode(); ObjectNode repoElement = factory.objectNode(); repoElement.put(PropertyHelper.getPropertyName(RepositoryResourceProvider.REPOSITORY_BASE_URL_PROPERTY_ID), repo.getBaseUrl()); repoElement.put(PropertyHelper.getPropertyName(RepositoryResourceProvider.REPOSITORY_OS_TYPE_PROPERTY_ID), os.getOsType()); repoElement.put(PropertyHelper.getPropertyName(RepositoryResourceProvider.REPOSITORY_REPO_ID_PROPERTY_ID), repo.getRepositoryId()); repoElement.put(PropertyHelper.getPropertyName(RepositoryResourceProvider.REPOSITORY_REPO_NAME_PROPERTY_ID), repo.getName()); repoElement.put(PropertyHelper.getPropertyName(RepositoryResourceProvider.REPOSITORY_STACK_NAME_PROPERTY_ID), entity.getStackName()); repoElement.put(PropertyHelper.getPropertyName(RepositoryResourceProvider.REPOSITORY_STACK_VERSION_PROPERTY_ID), entity.getStackVersion()); repoBase.put(PropertyHelper.getPropertyCategory(RepositoryResourceProvider.REPOSITORY_BASE_URL_PROPERTY_ID), repoElement); reposArray.add(repoBase); } osBase.put(new RepositoryResourceDefinition().getPluralName(), reposArray); subs.add(osBase); } res.setProperty(new OperatingSystemResourceDefinition().getPluralName(), subs); } /** * Convenience class to hold the xml String representation, the url, and the parsed object. */ private static class XmlHolder { String url = null; String xmlString = null; VersionDefinitionXml xml = null; RepositoryVersionEntity entity = null; } }