Repository: ambari Updated Branches: refs/heads/trunk e2b3f4deb -> d902509f7
AMBARI-9142. Create new API endpoints for cluster and service kerberos descriptors Project: http://git-wip-us.apache.org/repos/asf/ambari/repo Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/d902509f Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/d902509f Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/d902509f Branch: refs/heads/trunk Commit: d902509f755701285bda9bc23b51bd2e58064133 Parents: e2b3f4d Author: John Speidel <[email protected]> Authored: Wed Jan 14 22:34:49 2015 -0500 Committer: John Speidel <[email protected]> Committed: Thu Jan 15 13:27:42 2015 -0500 ---------------------------------------------------------------------- .../resources/ClusterResourceDefinition.java | 2 + .../resources/ResourceInstanceFactoryImpl.java | 4 + .../resources/ServiceResourceDefinition.java | 3 + .../server/api/services/ClusterService.java | 95 ++- .../server/api/services/ServiceService.java | 92 ++- .../internal/ArtifactResourceProvider.java | 695 +++++++++++++++++++ .../internal/DefaultProviderModule.java | 2 + .../ambari/server/controller/spi/Resource.java | 4 +- .../ambari/server/orm/dao/ArtifactDAO.java | 109 +++ .../server/orm/entities/ArtifactEntity.java | 137 ++++ .../server/orm/entities/ArtifactEntityPK.java | 98 +++ .../server/upgrade/UpgradeCatalog200.java | 10 + .../main/resources/Ambari-DDL-MySQL-CREATE.sql | 6 + .../main/resources/Ambari-DDL-Oracle-CREATE.sql | 6 + .../Ambari-DDL-Postgres-EMBEDDED-CREATE.sql | 7 + .../src/main/resources/META-INF/persistence.xml | 4 +- .../api/query/render/DefaultRendererTest.java | 3 +- .../api/query/render/MinimalRendererTest.java | 3 +- .../ClusterResourceDefinitionTest.java | 3 +- .../ResourceInstanceFactoryImplTest.java | 10 + .../ServiceResourceDefinitionTest.java | 64 ++ .../internal/ArtifactResourceProviderTest.java | 229 ++++++ .../server/upgrade/UpgradeCatalog200Test.java | 37 + 23 files changed, 1616 insertions(+), 7 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/d902509f/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ClusterResourceDefinition.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ClusterResourceDefinition.java b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ClusterResourceDefinition.java index 422cf1c..9b744d0 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ClusterResourceDefinition.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ClusterResourceDefinition.java @@ -70,6 +70,8 @@ public class ClusterResourceDefinition extends BaseStacksResourceDefinition { setChildren.add(new SubResourceDefinition(Resource.Type.AlertDefinition)); setChildren.add(new SubResourceDefinition(Resource.Type.Alert)); setChildren.add(new SubResourceDefinition(Resource.Type.ClusterStackVersion)); + //todo: dynamic sub-resource definition + setChildren.add(new SubResourceDefinition(Resource.Type.Artifact)); return setChildren; } http://git-wip-us.apache.org/repos/asf/ambari/blob/d902509f/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImpl.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImpl.java index a75729d..fc28c13 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImpl.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImpl.java @@ -356,6 +356,10 @@ public class ResourceInstanceFactoryImpl implements ResourceInstanceFactory { }; break; + case Artifact: + resourceDefinition = new SimpleResourceDefinition(Resource.Type.Artifact, "artifact", "artifacts"); + break; + default: throw new IllegalArgumentException("Unsupported resource type: " + type); } http://git-wip-us.apache.org/repos/asf/ambari/blob/d902509f/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ServiceResourceDefinition.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ServiceResourceDefinition.java b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ServiceResourceDefinition.java index be8e0b4..9abf3a7 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ServiceResourceDefinition.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ServiceResourceDefinition.java @@ -50,6 +50,9 @@ public class ServiceResourceDefinition extends BaseResourceDefinition { Set<SubResourceDefinition> subs = new HashSet<SubResourceDefinition>(); subs.add(new SubResourceDefinition(Resource.Type.Component)); subs.add(new SubResourceDefinition(Resource.Type.Alert)); + //todo: dynamic sub-resource definition + subs.add(new SubResourceDefinition(Resource.Type.Artifact)); + return subs; } } http://git-wip-us.apache.org/repos/asf/ambari/blob/d902509f/ambari-server/src/main/java/org/apache/ambari/server/api/services/ClusterService.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/services/ClusterService.java b/ambari-server/src/main/java/org/apache/ambari/server/api/services/ClusterService.java index ead49ca..5f44b2f 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/api/services/ClusterService.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/api/services/ClusterService.java @@ -19,6 +19,8 @@ package org.apache.ambari.server.api.services; import java.util.Collections; +import java.util.HashMap; +import java.util.Map; import javax.ws.rs.DELETE; import javax.ws.rs.GET; @@ -170,6 +172,81 @@ public class ClusterService extends BaseService { } /** + * Handles: GET /clusters/{clusterID}/artifacts + * Get all artifacts associated with the cluster. + * + * @param body request body + * @param headers http headers + * @param ui uri info + * @param clusterName cluster name + * + * @return artifact collection resource representation + */ + @GET + @Path("{clusterName}/artifacts") + @Produces("text/plain") + public Response getClusterArtifacts(String body, + @Context HttpHeaders headers, + @Context UriInfo ui, + @PathParam("clusterName") String clusterName) { + + hasPermission(Request.Type.GET, clusterName); + return handleRequest(headers, body, ui, Request.Type.GET, + createArtifactResource(clusterName, null)); + } + + /** + * Handles: GET /clusters/{clusterID}/artifacts/{artifactName} + * Get an artifact resource instance. + * + * @param body request body + * @param headers http headers + * @param ui uri info + * @param clusterName cluster name + * @param artifactName artifact name + * + * @return artifact instance resource representation + */ + @GET + @Path("{clusterName}/artifacts/{artifactName}") + @Produces("text/plain") + public Response getClusterArtifact(String body, + @Context HttpHeaders headers, + @Context UriInfo ui, + @PathParam("clusterName") String clusterName, + @PathParam("artifactName") String artifactName) { + + hasPermission(Request.Type.GET, clusterName); + return handleRequest(headers, body, ui, Request.Type.GET, + createArtifactResource(clusterName, artifactName)); + } + + /** + * Handles: POST /clusters/{clusterID}/artifacts/{artifactName} + * Create a cluster artifact. + * + * @param body request body + * @param headers http headers + * @param ui uri info + * @param clusterName cluster name + * @param artifactName artifact name + * @return + */ + @POST + @Path("{clusterName}/artifacts/{artifactName}") + @Produces("text/plain") + public Response createClusterArtifact(String body, + @Context HttpHeaders headers, + @Context UriInfo ui, + @PathParam("clusterName") String clusterName, + @PathParam("artifactName") String artifactName) { + + hasPermission(Request.Type.POST, clusterName); + return handleRequest(headers, body, ui, Request.Type.POST, + createArtifactResource(clusterName, artifactName)); + } + + /** * Get the hosts sub-resource * * @param request the request @@ -434,7 +511,7 @@ public class ClusterService extends BaseService { * Gets the services for upgrades. * * @param request the request - * @param cluserName the cluster name + * @param clusterName the cluster name * * @return the upgrade services */ @@ -476,6 +553,22 @@ public class ClusterService extends BaseService { } /** + * Create an artifact resource instance. + * + * @param clusterName cluster name + * @param artifactName artifact name + * + * @return an artifact resource instance + */ + ResourceInstance createArtifactResource(String clusterName, String artifactName) { + Map<Resource.Type, String> mapIds = new HashMap<Resource.Type, String>(); + mapIds.put(Resource.Type.Cluster, clusterName); + mapIds.put(Resource.Type.Artifact, artifactName); + + return createResource(Resource.Type.Artifact, mapIds); + } + + /** * Determine whether or not the access specified by the given request type is * permitted for the current user on the cluster resource identified by the * given cluster name. http://git-wip-us.apache.org/repos/asf/ambari/blob/d902509f/ambari-server/src/main/java/org/apache/ambari/server/api/services/ServiceService.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/services/ServiceService.java b/ambari-server/src/main/java/org/apache/ambari/server/api/services/ServiceService.java index a51b50b..8a2642b 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/api/services/ServiceService.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/api/services/ServiceService.java @@ -200,6 +200,79 @@ public class ServiceService extends BaseService { } /** + * Handles: POST /clusters/{clusterId}/services/{serviceId}/artifacts/{artifactName} + * Create a service artifact instance. + * + * @param body http body + * @param headers http headers + * @param ui uri info + * @param serviceName service name + * @param artifactName artifact name + * + * @return information regarding the created artifact + */ + @POST + @Path("{serviceName}/artifacts/{artifactName}") + @Produces("text/plain") + public Response createArtifact(String body, + @Context HttpHeaders headers, + @Context UriInfo ui, + @PathParam("serviceName") String serviceName, + @PathParam("artifactName") String artifactName) { + + return handleRequest(headers, body, ui, Request.Type.POST, + createArtifactResource(m_clusterName, serviceName, artifactName)); + } + + /** + * Handles: GET /clusters/{clusterId}/services/{serviceId}/artifacts + * Get all service artifacts. + * + * @param body http body + * @param headers http headers + * @param ui uri info + * @param serviceName service name + * + * @return artifact collection resource representation + */ + @GET + @Path("{serviceName}/artifacts") + @Produces("text/plain") + public Response getArtifacts(String body, + @Context HttpHeaders headers, + @Context UriInfo ui, + @PathParam("serviceName") String serviceName) { + + return handleRequest(headers, body, ui, Request.Type.GET, + createArtifactResource(m_clusterName, serviceName, null)); + } + + /** + * Handles: GET /clusters/{clusterId}/services/{serviceId}/artifacts/{artifactName} + * Gat a service artifact instance. + * + * @param body http body + * @param headers http headers + * @param ui uri info + * @param serviceName service name + * @param artifactName artifact name + * + * @return artifact instance resource representation + */ + @GET + @Path("{serviceName}/artifacts/{artifactName}") + @Produces("text/plain") + public Response getArtifact(String body, + @Context HttpHeaders headers, + @Context UriInfo ui, + @PathParam("serviceName") String serviceName, + @PathParam("artifactName") String artifactName) { + + return handleRequest(headers, body, ui, Request.Type.GET, + createArtifactResource(m_clusterName, serviceName, artifactName)); + } + + /** * Gets the alert history service * * @param request @@ -220,7 +293,6 @@ public class ServiceService extends BaseService { /** * Create a service resource instance. * - * * @param clusterName cluster name * @param serviceName service name * @@ -233,4 +305,22 @@ public class ServiceService extends BaseService { return createResource(Resource.Type.Service, mapIds); } + + /** + * Create an artifact resource instance. + * + * @param clusterName cluster name + * @param serviceName service name + * @param artifactName artifact name + * + * @return an artifact resource instance + */ + ResourceInstance createArtifactResource(String clusterName, String serviceName, String artifactName) { + Map<Resource.Type,String> mapIds = new HashMap<Resource.Type, String>(); + mapIds.put(Resource.Type.Cluster, clusterName); + mapIds.put(Resource.Type.Service, serviceName); + mapIds.put(Resource.Type.Artifact, artifactName); + + return createResource(Resource.Type.Artifact, mapIds); + } } http://git-wip-us.apache.org/repos/asf/ambari/blob/d902509f/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ArtifactResourceProvider.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ArtifactResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ArtifactResourceProvider.java new file mode 100644 index 0000000..b3eb159 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ArtifactResourceProvider.java @@ -0,0 +1,695 @@ +/** + * 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 com.google.inject.Inject; +import org.apache.ambari.server.AmbariException; +import org.apache.ambari.server.DuplicateResourceException; +import org.apache.ambari.server.ObjectNotFoundException; +import org.apache.ambari.server.ParentObjectNotFoundException; +import org.apache.ambari.server.StaticallyInject; +import org.apache.ambari.server.controller.AmbariManagementController; +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.ArtifactDAO; +import org.apache.ambari.server.orm.entities.ArtifactEntity; +import org.apache.ambari.server.state.Cluster; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; + +/** + * Provider for cluster artifacts. + * Artifacts contain an artifact name as the PK and artifact data in the form of + * a map which is the content of the artifact. + * <p> + * An example of an artifact is a kerberos descriptor. + */ +//todo: implement ExtendedResourceProvider??? +@StaticallyInject +public class ArtifactResourceProvider extends AbstractResourceProvider { + /** + * artifact name + */ + public static final String ARTIFACT_NAME_PROPERTY = + PropertyHelper.getPropertyId("Artifacts", "artifact_name"); + + /** + * artifact data + */ + public static final String ARTIFACT_DATA_PROPERTY = "artifact_data"; + + /** + * primary key fields + */ + private static Set<String> pkPropertyIds = new HashSet<String>(); + + /** + * map of resource type to fk field + */ + private static Map<Resource.Type, String> keyPropertyIds = + new HashMap<Resource.Type, String>(); + + /** + * resource properties + */ + private static Set<String> propertyIds = new HashSet<String>(); + + /** + * map of resource type to type registration + */ + private static final Map<Resource.Type, TypeRegistration> typeRegistrations = + new HashMap<Resource.Type, TypeRegistration>(); + + /** + * map of foreign key field to type registration + */ + private static final Map<String, TypeRegistration> typeRegistrationsByFK = + new HashMap<String, TypeRegistration>(); + + /** + * map of short foreign key field to type registration + */ + private static final Map<String, TypeRegistration> typeRegistrationsByShortFK = + new HashMap<String, TypeRegistration>(); + + /** + * artifact data access object + */ + @Inject + private static ArtifactDAO artifactDAO; + + + /** + * set resource properties, pk and fk's + */ + static { + // resource properties + propertyIds.add(ARTIFACT_NAME_PROPERTY); + propertyIds.add(ARTIFACT_DATA_PROPERTY); + + // pk property + pkPropertyIds.add(ARTIFACT_NAME_PROPERTY); + + // key properties + keyPropertyIds.put(Resource.Type.Artifact, ARTIFACT_NAME_PROPERTY); + + //todo: external registration + // cluster registration + ClusterTypeRegistration clusterTypeRegistration = new ClusterTypeRegistration(); + typeRegistrations.put(clusterTypeRegistration.getType(), clusterTypeRegistration); + + //service registration + ServiceTypeRegistration serviceTypeRegistration = new ServiceTypeRegistration(); + typeRegistrations.put(serviceTypeRegistration.getType(), serviceTypeRegistration); + + //todo: detect resource type and fk name collisions during registration + for (TypeRegistration registration: typeRegistrations.values()) { + String fkProperty = registration.getFKPropertyName(); + keyPropertyIds.put(registration.getType(), fkProperty); + propertyIds.add(fkProperty); + + typeRegistrationsByFK.put(fkProperty, registration); + typeRegistrationsByShortFK.put(registration.getShortFKPropertyName(), registration); + + for (Map.Entry<Resource.Type, String> ancestor : registration.getForeignKeyInfo().entrySet()) { + Resource.Type ancestorType = ancestor.getKey(); + if (! keyPropertyIds.containsKey(ancestorType)) { + String ancestorFK = ancestor.getValue(); + keyPropertyIds.put(ancestorType, ancestorFK); + propertyIds.add(ancestorFK); + } + } + } + } + + /** + * Constructor. + * + * @param controller management controller + */ + @Inject + protected ArtifactResourceProvider(AmbariManagementController controller) { + super(propertyIds, keyPropertyIds); + + for (TypeRegistration typeRegistration : typeRegistrations.values()) { + typeRegistration.setManagementController(controller); + } + } + + @Override + protected Set<String> getPKPropertyIds() { + return pkPropertyIds; + } + + @Override + public RequestStatus createResources(Request request) + throws SystemException, + UnsupportedPropertyException, + ResourceAlreadyExistsException, + NoSuchParentResourceException { + + for (Map<String, Object> properties : request.getProperties()) { + createResources(getCreateCommand(properties)); + } + notifyCreate(Resource.Type.Artifact, request); + + return getRequestStatus(null); + } + + @Override + public Set<Resource> getResources(Request request, Predicate predicate) + throws SystemException, + UnsupportedPropertyException, + NoSuchResourceException, + NoSuchParentResourceException { + + Set<Map<String, Object>> requestProps = getPropertyMaps(predicate); + Set<Resource> resources = new LinkedHashSet<Resource>(); + + for (Map<String, Object> props : requestProps) { + resources.addAll(getResources(getGetCommand(request, predicate, props))); + } + + if (resources.isEmpty() && isInstanceRequest(requestProps)) { + throw new NoSuchResourceException( + "The requested resource doesn't exist: Artifact not found, " + predicate); + } + return resources; + } + + @Override + public RequestStatus updateResources(Request request, Predicate predicate) + throws SystemException, + UnsupportedPropertyException, + NoSuchResourceException, + NoSuchParentResourceException { + + throw new UnsupportedOperationException("Update not currently supported for Artifact resources"); + } + + @Override + public RequestStatus deleteResources(Predicate predicate) + throws SystemException, + UnsupportedPropertyException, + NoSuchResourceException, + NoSuchParentResourceException { + + throw new UnsupportedOperationException("Delete not currently supported for Artifact resources"); + } + + /** + * Create a command to create the resource. + * + * @param properties request properties + * + * @return a new create command + */ + private Command<Void> getCreateCommand(final Map<String, Object> properties) { + return new Command<Void>() { + @Override + public Void invoke() throws AmbariException { + // ensure that parent exists + validateParent(properties); + + String artifactName = String.valueOf(properties.get(ARTIFACT_NAME_PROPERTY)); + TreeMap<String, String> foreignKeyMap = createForeignKeyMap(properties); + + if (artifactDAO.findByNameAndForeignKeys(artifactName, foreignKeyMap) != null) { + throw new DuplicateResourceException(String.format( + "Attempted to create an artifact which already exists, artifact_name='%s', foreign_keys='%s'", + artifactName, getRequestForeignKeys(properties))); + } + + LOG.debug("Creating Artifact Resource with name '{}'. Parent information: {}", + artifactName, getRequestForeignKeys(properties)); + + artifactDAO.create(toEntity(properties)); + + return null; + } + }; + } + + /** + * Create a command to get the requested resources. + * + * @param properties request properties + * + * @return a new get command + */ + private Command<Set<Resource>> getGetCommand(final Request request, + final Predicate predicate, + final Map<String, Object> properties) { + return new Command<Set<Resource>>() { + @Override + public Set<Resource> invoke() throws AmbariException { + String name = (String) properties.get(ARTIFACT_NAME_PROPERTY); + validateParent(properties); + + Set<Resource> matchingResources = new HashSet<Resource>(); + TreeMap<String, String> foreignKeys = createForeignKeyMap(properties); + Set<String> requestPropertyIds = getRequestPropertyIds(request, predicate); + if (name != null) { + // find instance using name and foreign keys + ArtifactEntity entity = artifactDAO.findByNameAndForeignKeys(name, foreignKeys); + if (entity != null) { + Resource instance = (toResource(entity, requestPropertyIds)); + if (predicate.evaluate(instance)) { + matchingResources.add(instance); + } + } + } else { + // find collection using foreign keys only + List<ArtifactEntity> results = artifactDAO.findByForeignKeys(foreignKeys); + for (ArtifactEntity entity : results) { + Resource resource = toResource(entity, requestPropertyIds); + if (predicate.evaluate(resource)) { + matchingResources.add(resource); + } + } + } + return matchingResources; + } + }; + } + + /** + * Validate that parent resources exist. + * + * @param properties request properties + * + * @throws ParentObjectNotFoundException if the parent resource doesn't exist + * @throws AmbariException if an error occurred while attempting to validate the parent + */ + private void validateParent(Map<String, Object> properties) throws AmbariException { + Resource.Type parentType = getRequestType(properties); + if (! typeRegistrations.get(parentType).instanceExists(keyPropertyIds, properties)) { + throw new ParentObjectNotFoundException(String.format( + "Parent resource doesn't exist: %s", getRequestForeignKeys(properties))); + } + } + + /** + * Get the type of the parent resource from the request properties. + * + * @param properties request properties + * + * @return the parent resource type based on the request properties + * + * @throws AmbariException if unable to determine the parent resource type + */ + private Resource.Type getRequestType(Map<String, Object> properties) throws AmbariException { + Set<String> requestFKs = getRequestForeignKeys(properties).keySet(); + for (TypeRegistration registration : typeRegistrations.values()) { + Collection<String> typeFKs = new HashSet<String>(registration.getForeignKeyInfo().values()); + typeFKs.add(registration.getFKPropertyName()); + if (requestFKs.equals(typeFKs)) { + return registration.getType(); + } + } + throw new AmbariException("Couldn't determine resource type based on request properties"); + } + + /** + * Get a map of foreign key to value for the given request properties. + * The foreign key map will only include the foreign key properties which + * are included in the request properties. This is useful for reporting + * errors back to the user. + * . + * @param properties request properties + * + * @return map of foreign key to value for the provided request properties + */ + private Map<String, String> getRequestForeignKeys(Map<String, Object> properties) { + Map<String, String> requestFKs = new HashMap<String, String>(); + for (String property : properties.keySet()) { + if (! property.equals(ARTIFACT_NAME_PROPERTY) && ! property.startsWith(ARTIFACT_DATA_PROPERTY)) { + requestFKs.put(property, String.valueOf(properties.get(property))); + } + } + return requestFKs; + } + + /** + * Convert a map of properties to an artifact entity. + * + * @param properties property map + * + * @return new artifact entity + */ + private ArtifactEntity toEntity(Map<String, Object> properties) + throws AmbariException { + + String name = (String) properties.get(ARTIFACT_NAME_PROPERTY); + if (name == null || name.isEmpty()) { + throw new IllegalArgumentException("Artifact name must be provided"); + } + + ArtifactEntity artifact = new ArtifactEntity(); + artifact.setArtifactName(name); + Map<String, Object> dataMap = new HashMap<String, Object>(); + for (Map.Entry<String, Object> entry : properties.entrySet()) { + String key = entry.getKey(); + //todo: should we handle scalar value? + if (key.startsWith(ARTIFACT_DATA_PROPERTY)) { + dataMap.put(key.split("/")[1], entry.getValue()); + } + } + artifact.setArtifactData(dataMap); + artifact.setForeignKeys(createForeignKeyMap(properties)); + + return artifact; + } + + /** + * Create a map of foreign keys and values which can be persisted. + * This map will include the short fk names of the key properties as well + * as the 'persist id' representation of the value which is returned + * by the type registration. + * + * @param properties request properties + * @return an ordered map of key name to value + * + * @throws AmbariException an unexpected exception occurred + */ + private TreeMap<String, String> createForeignKeyMap(Map<String, Object> properties) throws AmbariException { + TreeMap<String, String> foreignKeys = new TreeMap<String, String>(); + for (String keyProperty : keyPropertyIds.values()) { + if (! keyProperty.equals(ARTIFACT_NAME_PROPERTY)) { + String origValue = (String) properties.get(keyProperty); + if (origValue != null && ! origValue.isEmpty()) { + TypeRegistration typeRegistration = typeRegistrationsByFK.get(keyProperty); + foreignKeys.put(typeRegistration.getShortFKPropertyName(), typeRegistration.toPersistId(origValue)); + } + } + } + return foreignKeys; + } + + /** + * Create a resource instance from an artifact entity. + * This will convert short fk property names to the full property name as well + * as converting the value from the 'persist id' representation which is written + * to the database. + * + * @param entity artifact entity + * @param requestedIds requested id's + * + * @return a new resource instance for the given artifact entity + */ + private Resource toResource(ArtifactEntity entity, Set<String> requestedIds) throws AmbariException { + Resource resource = new ResourceImpl(Resource.Type.Artifact); + setResourceProperty(resource, ARTIFACT_NAME_PROPERTY, entity.getArtifactName(), requestedIds); + setResourceProperty(resource, ARTIFACT_DATA_PROPERTY, entity.getArtifactData(), requestedIds); + + for (Map.Entry<String, String> entry : entity.getForeignKeys().entrySet()) { + TypeRegistration typeRegistration = typeRegistrationsByShortFK.get(entry.getKey()); + setResourceProperty(resource, typeRegistration.getFKPropertyName(), + typeRegistration.fromPersistId(entry.getValue()), requestedIds); + } + return resource; + } + + /** + * Determine if the request was for an instance resource. + * + * @param requestProps request properties + * + * @return true if the request was for a specific instance, false otherwise + */ + private boolean isInstanceRequest(Set<Map<String, Object>> requestProps) { + return requestProps.size() == 1 && + requestProps.iterator().next().get(ARTIFACT_NAME_PROPERTY) != null; + } + + //todo: when static registration is changed to external registration, this interface + //todo: should be extracted as a first class interface. + /** + * Used to register a dynamic sub-resource with an existing resource type. + */ + public static interface TypeRegistration { + /** + * Allows the management controller to be set on the registration. + * This is called as part of the registration process. + * For registrations that need access to the management controller, + * they should assign this controller to a member field. + * + * @param controller management controller + */ + public void setManagementController(AmbariManagementController controller); + + /** + * Get the type of the registering resource. + * + * @return type of the register resource + */ + public Resource.Type getType(); + + /** + * Full foreign key property name to use in the artifact resource. + * At this time, all foreign key properties should be in the "Artifacts" category. + * + * @return the absolute foreign key property name. + * For example: "Artifacts/cluster_name + */ + //todo: use relative property names + public String getFKPropertyName(); + + /** + * Shortened foreign key name that is written to the database. + * This name doesn't need to be in any category but must be unique + * across all registrations. + * + * @return short fk name. For example: "cluster_name" + */ + public String getShortFKPropertyName(); + + /** + * Convert the foreign key value to a value that is persisted to the database. + * In most cases this will be the original value. + * <p> + * An example of when this will be different is when the fk value value needs + * to be converted to the unique id for the resource. + * <p> + * For example, the cluster_name to the cluster_id. + * <p> + * This returned value will later be converted back to the normal form via + * {@link #fromPersistId(String)}. + * + * @param value normal form of the fk value used by the api + * + * @return persist id form of the fk value + * + * @throws AmbariException if unable to convert the value + */ + public String toPersistId(String value) throws AmbariException; + + /** + * Convert the persist id form of the foreign key which is written to the database + * to the form used by the api. In most cases, this will be the same. + * <p> + * This method takes the value returned from {@link #toPersistId(String)} and converts + * it back to the original value which is used by the api. + * <p> + * An example of this is the converting the cluster name to the cluster id in + * {@link #toPersistId(String)} and then back to the cluster name by this method. The + * api always uses the cluster name so we wouldn't want to return the id back as the + * value for a cluster_name foreign key. + * + * @param value persist id form of the fk value + * + * @return normal form of the fk value used by the api + * + * @throws AmbariException if unable to convert the value + */ + public String fromPersistId(String value) throws AmbariException; + + /** + * Get a map of ancestor type to foreign key. + * <p> + * <b>Note: Currently, if a parent resource has also registered the same dynamic resource, + * the foreign key name used here has to match the value returned by the parent resource + * in {@link #getFKPropertyName()}</b> + * + * @return map of ancestor type to foreign key + */ + //todo: look at the need to use the same name as specified by ancestors + public Map<Resource.Type, String> getForeignKeyInfo(); + + /** + * Determine if the instance identified by the provided properties exists. + * + * @param keyMap map of resource type to foreign key properties + * @param properties request properties + * + * @return true if the resource instance exists, false otherwise + * + * @throws AmbariException an exception occurs trying to determine if the instance exists + */ + public boolean instanceExists(Map<Resource.Type, String> keyMap, + Map<String, Object> properties) throws AmbariException; + } + + + //todo: Registration should be done externally and these implementations should be moved + //todo: to a location where the registering resource definition has access to them. + /** + * Cluster resource registration. + */ + private static class ClusterTypeRegistration implements TypeRegistration { + /** + * management controller instance + */ + private AmbariManagementController controller = null; + + /** + * cluster name property name + */ + private static final String CLUSTER_NAME = PropertyHelper.getPropertyId("Artifacts", "cluster_name"); + + @Override + public void setManagementController(AmbariManagementController controller) { + this.controller = controller; + } + + @Override + public Resource.Type getType() { + return Resource.Type.Cluster; + } + + @Override + public String getFKPropertyName() { + return CLUSTER_NAME; + } + + @Override + public String getShortFKPropertyName() { + return "cluster"; + } + + @Override + public String toPersistId(String value) throws AmbariException { + return String.valueOf(controller.getClusters().getCluster(value).getClusterId()); + } + + @Override + public String fromPersistId(String value) throws AmbariException { + return controller.getClusters().getClusterById(Long.valueOf(value)).getClusterName(); + } + + @Override + public Map<Resource.Type, String> getForeignKeyInfo() { + return Collections.emptyMap(); + } + + @Override + public boolean instanceExists(Map<Resource.Type, String> keyMap, + Map<String, Object> properties) throws AmbariException { + try { + String clusterName = String.valueOf(properties.get(CLUSTER_NAME)); + controller.getClusters().getCluster(clusterName); + return true; + } catch (ObjectNotFoundException e) { + // doesn't exist + } + return false; + } + } + + /** + * Service resource registration. + */ + private static class ServiceTypeRegistration implements TypeRegistration { + /** + * management controller instance + */ + private AmbariManagementController controller = null; + + /** + * service name property name + */ + private static final String SERVICE_NAME = PropertyHelper.getPropertyId("Artifacts", "service_name"); + + @Override + public void setManagementController(AmbariManagementController controller) { + this.controller = controller; + } + + @Override + public Resource.Type getType() { + return Resource.Type.Service; + } + + @Override + public String getFKPropertyName() { + return SERVICE_NAME; + } + + @Override + public String getShortFKPropertyName() { + return "service"; + } + + @Override + public String toPersistId(String value) { + return value; + } + + @Override + public String fromPersistId(String value) { + return value; + } + + @Override + public Map<Resource.Type, String> getForeignKeyInfo() { + return Collections.singletonMap(Resource.Type.Cluster, "Artifacts/cluster_name"); + } + + @Override + public boolean instanceExists(Map<Resource.Type, String> keyMap, + Map<String, Object> properties) throws AmbariException { + + String clusterName = String.valueOf(properties.get(keyMap.get(Resource.Type.Cluster))); + try { + Cluster cluster = controller.getClusters().getCluster(clusterName); + cluster.getService(String.valueOf(properties.get(SERVICE_NAME))); + return true; + } catch (ObjectNotFoundException e) { + // doesn't exist + } + return false; + } + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/d902509f/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/DefaultProviderModule.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/DefaultProviderModule.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/DefaultProviderModule.java index be2a9ad..b0e23e9 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/DefaultProviderModule.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/DefaultProviderModule.java @@ -114,6 +114,8 @@ public class DefaultProviderModule extends AbstractProviderModule { return new OperatingSystemResourceProvider(managementController); case Repository: return new RepositoryResourceProvider(managementController); + case Artifact: + return new ArtifactResourceProvider(managementController); default: return AbstractControllerResourceProvider.getResourceProvider(type, propertyIds, http://git-wip-us.apache.org/repos/asf/ambari/blob/d902509f/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java index 740f37e..aa5cf64 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java @@ -136,7 +136,8 @@ public interface Resource { UpgradeItem, PreUpgradeCheck, Stage, - StackArtifact; + StackArtifact, + Artifact; /** * Get the {@link Type} that corresponds to this InternalType. @@ -234,6 +235,7 @@ public interface Resource { public static final Type PreUpgradeCheck = InternalType.PreUpgradeCheck.getType(); public static final Type Stage = InternalType.Stage.getType(); public static final Type StackArtifact = InternalType.StackArtifact.getType(); + public static final Type Artifact = InternalType.Artifact.getType(); /** * The type name. http://git-wip-us.apache.org/repos/asf/ambari/blob/d902509f/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ArtifactDAO.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ArtifactDAO.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ArtifactDAO.java new file mode 100644 index 0000000..4b7cf25 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ArtifactDAO.java @@ -0,0 +1,109 @@ +/** + * 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.orm.dao; + + +import com.google.inject.Inject; +import com.google.inject.Provider; +import com.google.inject.Singleton; +import com.google.inject.persist.Transactional; +import org.apache.ambari.server.orm.RequiresSession; +import org.apache.ambari.server.orm.entities.ArtifactEntity; + +import javax.persistence.EntityManager; +import javax.persistence.NoResultException; +import javax.persistence.TypedQuery; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + + +/** + * Cluster Artifact Data Access Object. + */ +@Singleton +public class ArtifactDAO { + /** + * JPA entity manager + */ + @Inject + Provider<EntityManager> entityManagerProvider; + + /** + * Find an artifact with the given name and foreign keys. + * + * @param artifactName name of artifact to find + * @param foreignKeys foreign keys of artifact as json representation of + * a map of key properties to values + * + * @return a matching artifact or null + */ + @RequiresSession + public ArtifactEntity findByNameAndForeignKeys(String artifactName, TreeMap<String, String> foreignKeys) { + //todo: need to update PK in DB + TypedQuery<ArtifactEntity> query = entityManagerProvider.get() + .createNamedQuery("artifactByNameAndForeignKeys", ArtifactEntity.class); + query.setParameter("artifactName", artifactName); + query.setParameter("foreignKeys", ArtifactEntity.serializeForeignKeys(foreignKeys)); + + try { + return query.getSingleResult(); + } catch (NoResultException ignored) { + return null; + } + } + + /** + * Find all artifacts for the specified foreign keys. + * + * @param foreignKeys foreign keys of artifact as json representation of + * a map of key properties to values + * + * @return all artifacts for the specified foreign keys or an empty List + */ + @RequiresSession + public List<ArtifactEntity> findByForeignKeys(TreeMap<String, String> foreignKeys) { + TypedQuery<ArtifactEntity> query = entityManagerProvider.get(). + createNamedQuery("artifactByForeignKeys", ArtifactEntity.class); + query.setParameter("foreignKeys", ArtifactEntity.serializeForeignKeys(foreignKeys)); + + return query.getResultList(); + } + + /** + * Refresh the state of the instance from the database, + * overwriting changes made to the entity, if any. + * + * @param entity entity to refresh + */ + @Transactional + public void refresh(ArtifactEntity entity) { + entityManagerProvider.get().refresh(entity); + } + + /** + * Make an instance managed and persistent. + * + * @param entity entity to persist + */ + @Transactional + public void create(ArtifactEntity entity) { + entityManagerProvider.get().persist(entity); + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/d902509f/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ArtifactEntity.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ArtifactEntity.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ArtifactEntity.java new file mode 100644 index 0000000..849a938 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ArtifactEntity.java @@ -0,0 +1,137 @@ +/** + * 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.orm.entities; + +import com.google.gson.Gson; + +import javax.persistence.Basic; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.IdClass; +import javax.persistence.NamedQueries; +import javax.persistence.NamedQuery; +import javax.persistence.Table; +import javax.persistence.Transient; +import java.util.Collections; +import java.util.Map; +import java.util.TreeMap; + +/** + * Entity representing an Artifact. + */ +@IdClass(ArtifactEntityPK.class) +@Table(name = "artifact") +@NamedQueries({ + @NamedQuery(name = "artifactByNameAndForeignKeys", + query = "SELECT artifact FROM ArtifactEntity artifact " + + "WHERE artifact.artifactName=:artifactName AND artifact.foreignKeys=:foreignKeys"), + @NamedQuery(name = "artifactByForeignKeys", + query = "SELECT artifact FROM ArtifactEntity artifact " + + "WHERE artifact.foreignKeys=:foreignKeys") +}) + +@Entity +public class ArtifactEntity { + @Id + @Column(name = "artifact_name", nullable = false, insertable = true, updatable = false, unique = true) + private String artifactName; + + @Id + @Column(name = "foreign_keys", nullable = false, insertable = true, updatable = false) + @Basic + private String foreignKeys; + + @Column(name = "artifact_data", nullable = false, insertable = true, updatable = false) + @Basic + private String artifactData; + + @Transient + private static final Gson jsonSerializer = new Gson(); + + + /** + * Get the artifact name. + * + * @return artifact name + */ + public String getArtifactName() { + return artifactName; + } + + /** + * Set the artifact name. + * + * @param artifactName the artifact name + */ + public void setArtifactName(String artifactName) { + this.artifactName = artifactName; + } + + /** + * Set the artifact data by specifying a map that is then + * converted to a json string. + * + * @param artifactData artifact data map + */ + public void setArtifactData(Map<String, Object> artifactData) { + this.artifactData = jsonSerializer.toJson(artifactData); + } + + /** + * Get the artifact data as a map + * + * @return artifact data as a map + */ + public Map<String, Object> getArtifactData() { + return jsonSerializer.<Map<String, Object>>fromJson( + artifactData, Map.class); + } + + /** + * Set the foreign keys. + * + * @param foreignKeys ordered map of foreign key property names to values + */ + public void setForeignKeys(TreeMap<String, String> foreignKeys) { + this.foreignKeys = serializeForeignKeys(foreignKeys); + } + + /** + * Get the foreign keys. + * + * @return foreign key map of property name to value + */ + public Map<String, String> getForeignKeys() { + return foreignKeys == null ? + Collections.<String, String>emptyMap() : + jsonSerializer.<Map<String, String>>fromJson(foreignKeys, Map.class); + } + + /** + * Serialize a map of foreign keys to a string. + * + * @param foreignKeys map of foreign keys to values + * + * @return string representation of the foreign keys map + */ + public static String serializeForeignKeys(TreeMap<String, String> foreignKeys) { + return jsonSerializer.toJson(foreignKeys); + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/d902509f/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ArtifactEntityPK.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ArtifactEntityPK.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ArtifactEntityPK.java new file mode 100644 index 0000000..67b52c9 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ArtifactEntityPK.java @@ -0,0 +1,98 @@ +/** + * 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.orm.entities; + +import javax.persistence.Column; +import javax.persistence.Id; + +/** + * Composite primary key for ArtifactEntity. + */ +public class ArtifactEntityPK { + @Id + @Column(name = "artifact_name", nullable = false, insertable = true, updatable = false) + private String artifactName; + + @Id + @Column(name = "foreign_keys", nullable = false, insertable = true, updatable = false) + private String foreignKeys; + + /** + * Constructor. + * + @param artifactName artifact name + @param foreignKeys foreign key information + */ + public ArtifactEntityPK(String artifactName, String foreignKeys) { + this.artifactName = artifactName; + this.foreignKeys = foreignKeys; + } + + /** + * Get the name of the associated artifact. + * + * @return artifact name + */ + public String getArtifactName() { + return artifactName; + } + + /** + * Set the name of the associated artifact. + * + * @param name artifact name + */ + public void setArtifactName(String name) { + artifactName = name; + } + + /** + * Get the foreign key information of the associated artifact. + * + * @return foreign key information + */ + public String getForeignKeys() { + return foreignKeys; + } + + /** + * Set the foreign key information of the associated artifact. + * + * @param foreignKeys foreign key information + */ + public void setForeignKeys(String foreignKeys) { + this.foreignKeys = foreignKeys; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ArtifactEntityPK that = (ArtifactEntityPK) o; + + return this.artifactName.equals(that.artifactName) && + this.foreignKeys.equals(that.foreignKeys); + } + + @Override + public int hashCode() { + return 31 * artifactName.hashCode() + foreignKeys.hashCode(); + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/d902509f/ambari-server/src/main/java/org/apache/ambari/server/upgrade/UpgradeCatalog200.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/upgrade/UpgradeCatalog200.java b/ambari-server/src/main/java/org/apache/ambari/server/upgrade/UpgradeCatalog200.java index f7bd080..3a043b9 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/upgrade/UpgradeCatalog200.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/upgrade/UpgradeCatalog200.java @@ -63,6 +63,7 @@ public class UpgradeCatalog200 extends AbstractUpgradeCatalog { private static final String ALERT_TARGET_TABLE = "alert_target"; private static final String ALERT_TARGET_STATES_TABLE = "alert_target_states"; private static final String ALERT_CURRENT_TABLE = "alert_current"; + private static final String ARTIFACT_TABLE = "artifact"; /** * {@inheritDoc} @@ -107,6 +108,7 @@ public class UpgradeCatalog200 extends AbstractUpgradeCatalog { protected void executeDDLUpdates() throws AmbariException, SQLException { prepareRollingUpgradesDDL(); executeAlertDDLUpdates(); + createArtifactTable(); // add security_state to various tables dbAccessor.addColumn("hostcomponentdesiredstate", new DBColumnInfo( @@ -255,6 +257,14 @@ public class UpgradeCatalog200 extends AbstractUpgradeCatalog { dbAccessor.executeQuery("INSERT INTO ambari_sequences(sequence_name, sequence_value) VALUES('upgrade_item_id_seq', 0)", false); } + private void createArtifactTable() throws SQLException { + ArrayList<DBColumnInfo> columns = new ArrayList<DBColumnInfo>(); + columns.add(new DBColumnInfo("artifact_name", String.class, 255, null, false)); + columns.add(new DBColumnInfo("foreign_keys", String.class, null, null, false)); + columns.add(new DBColumnInfo("artifact_data", char[].class, null, null, false)); + dbAccessor.createTable(ARTIFACT_TABLE, columns, "artifact_name", "foreign_keys"); + } + // ----- UpgradeCatalog ---------------------------------------------------- /** http://git-wip-us.apache.org/repos/asf/ambari/blob/d902509f/ambari-server/src/main/resources/Ambari-DDL-MySQL-CREATE.sql ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/resources/Ambari-DDL-MySQL-CREATE.sql b/ambari-server/src/main/resources/Ambari-DDL-MySQL-CREATE.sql index 6f3f094..972053c 100644 --- a/ambari-server/src/main/resources/Ambari-DDL-MySQL-CREATE.sql +++ b/ambari-server/src/main/resources/Ambari-DDL-MySQL-CREATE.sql @@ -517,6 +517,12 @@ CREATE TABLE repo_version ( PRIMARY KEY(repo_version_id) ); +CREATE TABLE artifact ( + artifact_name VARCHAR(255) NOT NULL, + foreign_keys LONGTEXT NOT NULL, + artifact_data VARCHAR(4096) NOT NULL, + PRIMARY KEY(artifact_name, foreign_keys)); + -- altering tables by creating unique constraints---------- ALTER TABLE users ADD CONSTRAINT UNQ_users_0 UNIQUE (user_name, ldap_user); ALTER TABLE groups ADD CONSTRAINT UNQ_groups_0 UNIQUE (group_name, ldap_group); http://git-wip-us.apache.org/repos/asf/ambari/blob/d902509f/ambari-server/src/main/resources/Ambari-DDL-Oracle-CREATE.sql ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/resources/Ambari-DDL-Oracle-CREATE.sql b/ambari-server/src/main/resources/Ambari-DDL-Oracle-CREATE.sql index 11d5de3..0117fed 100644 --- a/ambari-server/src/main/resources/Ambari-DDL-Oracle-CREATE.sql +++ b/ambari-server/src/main/resources/Ambari-DDL-Oracle-CREATE.sql @@ -507,6 +507,12 @@ CREATE TABLE repo_version ( PRIMARY KEY(repo_version_id) ); +CREATE TABLE artifact ( + artifact_name VARCHAR2(255) NOT NULL, + foreign_keys CLOB NOT NULL, + artifact_data VARCHAR2(4096) NOT NULL, + PRIMARY KEY(artifact_name, foreign_keys)); + --------altering tables by creating unique constraints---------- ALTER TABLE users ADD CONSTRAINT UNQ_users_0 UNIQUE (user_name, ldap_user); ALTER TABLE groups ADD CONSTRAINT UNQ_groups_0 UNIQUE (group_name, ldap_group); http://git-wip-us.apache.org/repos/asf/ambari/blob/d902509f/ambari-server/src/main/resources/Ambari-DDL-Postgres-EMBEDDED-CREATE.sql ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/resources/Ambari-DDL-Postgres-EMBEDDED-CREATE.sql b/ambari-server/src/main/resources/Ambari-DDL-Postgres-EMBEDDED-CREATE.sql index 2ae175b..7c2d7ae 100644 --- a/ambari-server/src/main/resources/Ambari-DDL-Postgres-EMBEDDED-CREATE.sql +++ b/ambari-server/src/main/resources/Ambari-DDL-Postgres-EMBEDDED-CREATE.sql @@ -449,6 +449,13 @@ GRANT ALL PRIVILEGES ON TABLE ambari.hostgroup_component TO :username; GRANT ALL PRIVILEGES ON TABLE ambari.blueprint_configuration TO :username; GRANT ALL PRIVILEGES ON TABLE ambari.hostgroup_configuration TO :username; +CREATE TABLE ambari.artifact ( + artifact_name VARCHAR(255) NOT NULL, + artifact_data TEXT NOT NULL, + foreign_keys VARCHAR(4096) NOT NULL, + PRIMARY KEY (artifact_name, foreign_keys)); +GRANT ALL PRIVILEGES ON TABLE ambari.artifact TO :username; + CREATE TABLE ambari.viewmain ( view_name VARCHAR(255) NOT NULL, label VARCHAR(255), http://git-wip-us.apache.org/repos/asf/ambari/blob/d902509f/ambari-server/src/main/resources/META-INF/persistence.xml ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/resources/META-INF/persistence.xml b/ambari-server/src/main/resources/META-INF/persistence.xml index dc49b7c..07bd67d 100644 --- a/ambari-server/src/main/resources/META-INF/persistence.xml +++ b/ambari-server/src/main/resources/META-INF/persistence.xml @@ -75,8 +75,10 @@ <class>org.apache.ambari.server.orm.entities.ViewInstancePropertyEntity</class> <class>org.apache.ambari.server.orm.entities.ViewParameterEntity</class> <class>org.apache.ambari.server.orm.entities.ViewResourceEntity</class> + <class>org.apache.ambari.server.orm.entities.ArtifactEntity</class> - <properties> + + <properties> <!--<property name="javax.persistence.jdbc.url" value="jdbc:postgresql://localhost/ambari" />--> <!--<property name="javax.persistence.jdbc.driver" value="org.postgresql.Driver" />--> <property name="eclipselink.cache.size.default" value="10000" /> http://git-wip-us.apache.org/repos/asf/ambari/blob/d902509f/ambari-server/src/test/java/org/apache/ambari/server/api/query/render/DefaultRendererTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/api/query/render/DefaultRendererTest.java b/ambari-server/src/test/java/org/apache/ambari/server/api/query/render/DefaultRendererTest.java index 3e0617e..4981a67 100644 --- a/ambari-server/src/test/java/org/apache/ambari/server/api/query/render/DefaultRendererTest.java +++ b/ambari-server/src/test/java/org/apache/ambari/server/api/query/render/DefaultRendererTest.java @@ -51,6 +51,7 @@ public class DefaultRendererTest { // schema expectations expect(schemaFactory.getSchema(Resource.Type.Component)).andReturn(schema).anyTimes(); expect(schemaFactory.getSchema(Resource.Type.Alert)).andReturn(schema).anyTimes(); + expect(schemaFactory.getSchema(Resource.Type.Artifact)).andReturn(schema).anyTimes(); expect(schema.getKeyPropertyId(Resource.Type.Component)).andReturn("ServiceComponentInfo/component_name").anyTimes(); expect(schema.getKeyPropertyId(Resource.Type.Service)).andReturn("ServiceComponentInfo/service_name").anyTimes(); @@ -64,7 +65,7 @@ public class DefaultRendererTest { TreeNode<Set<String>> propertyTree = renderer.finalizeProperties(queryTree, false); // no properties should have been added assertTrue(propertyTree.getObject().isEmpty()); - assertEquals(2, propertyTree.getChildren().size()); + assertEquals(3, propertyTree.getChildren().size()); TreeNode<Set<String>> componentNode = propertyTree.getChild("Component"); assertEquals(2, componentNode.getObject().size()); http://git-wip-us.apache.org/repos/asf/ambari/blob/d902509f/ambari-server/src/test/java/org/apache/ambari/server/api/query/render/MinimalRendererTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/api/query/render/MinimalRendererTest.java b/ambari-server/src/test/java/org/apache/ambari/server/api/query/render/MinimalRendererTest.java index 9b03c51..37bf33c 100644 --- a/ambari-server/src/test/java/org/apache/ambari/server/api/query/render/MinimalRendererTest.java +++ b/ambari-server/src/test/java/org/apache/ambari/server/api/query/render/MinimalRendererTest.java @@ -58,6 +58,7 @@ public class MinimalRendererTest { // schema expectations expect(schemaFactory.getSchema(Resource.Type.Component)).andReturn(schema).anyTimes(); expect(schemaFactory.getSchema(Resource.Type.Alert)).andReturn(schema).anyTimes(); + expect(schemaFactory.getSchema(Resource.Type.Artifact)).andReturn(schema).anyTimes(); expect(schema.getKeyPropertyId(Resource.Type.Component)).andReturn("ServiceComponentInfo/component_name").anyTimes(); replay(schemaFactory, schema); @@ -70,7 +71,7 @@ public class MinimalRendererTest { TreeNode<Set<String>> propertyTree = renderer.finalizeProperties(queryTree, false); // no properties should have been added assertTrue(propertyTree.getObject().isEmpty()); - assertEquals(2, propertyTree.getChildren().size()); + assertEquals(3, propertyTree.getChildren().size()); TreeNode<Set<String>> componentNode = propertyTree.getChild("Component"); assertEquals(1, componentNode.getObject().size()); http://git-wip-us.apache.org/repos/asf/ambari/blob/d902509f/ambari-server/src/test/java/org/apache/ambari/server/api/resources/ClusterResourceDefinitionTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/api/resources/ClusterResourceDefinitionTest.java b/ambari-server/src/test/java/org/apache/ambari/server/api/resources/ClusterResourceDefinitionTest.java index 6d897b2..cb68dfa 100644 --- a/ambari-server/src/test/java/org/apache/ambari/server/api/resources/ClusterResourceDefinitionTest.java +++ b/ambari-server/src/test/java/org/apache/ambari/server/api/resources/ClusterResourceDefinitionTest.java @@ -50,7 +50,7 @@ public class ClusterResourceDefinitionTest { ResourceDefinition resource = new ClusterResourceDefinition(); Set<SubResourceDefinition> subResources = resource.getSubResourceDefinitions(); - assertEquals(11, subResources.size()); + assertEquals(12, subResources.size()); assertTrue(includesType(subResources, Resource.Type.Service)); assertTrue(includesType(subResources, Resource.Type.Host)); assertTrue(includesType(subResources, Resource.Type.Configuration)); @@ -62,6 +62,7 @@ public class ClusterResourceDefinitionTest { assertTrue(includesType(subResources, Resource.Type.ClusterPrivilege)); assertTrue(includesType(subResources, Resource.Type.Alert)); assertTrue(includesType(subResources, Resource.Type.ClusterStackVersion)); + assertTrue(includesType(subResources, Resource.Type.Artifact)); } @Test http://git-wip-us.apache.org/repos/asf/ambari/blob/d902509f/ambari-server/src/test/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImplTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImplTest.java b/ambari-server/src/test/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImplTest.java index b5cfcc3..0daffce 100644 --- a/ambari-server/src/test/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImplTest.java +++ b/ambari-server/src/test/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImplTest.java @@ -37,4 +37,14 @@ public class ResourceInstanceFactoryImplTest { assertEquals("artifacts", resourceDefinition.getPluralName()); assertEquals(Resource.Type.StackArtifact, resourceDefinition.getType()); } + + @Test + public void testGetArtifactDefinition() { + ResourceDefinition resourceDefinition = ResourceInstanceFactoryImpl.getResourceDefinition( + Resource.Type.Artifact, null); + + assertEquals("artifact", resourceDefinition.getSingularName()); + assertEquals("artifacts", resourceDefinition.getPluralName()); + assertEquals(Resource.Type.Artifact, resourceDefinition.getType()); + } } http://git-wip-us.apache.org/repos/asf/ambari/blob/d902509f/ambari-server/src/test/java/org/apache/ambari/server/api/resources/ServiceResourceDefinitionTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/api/resources/ServiceResourceDefinitionTest.java b/ambari-server/src/test/java/org/apache/ambari/server/api/resources/ServiceResourceDefinitionTest.java new file mode 100644 index 0000000..013c9b9 --- /dev/null +++ b/ambari-server/src/test/java/org/apache/ambari/server/api/resources/ServiceResourceDefinitionTest.java @@ -0,0 +1,64 @@ +/** + * 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.api.resources; + +import org.apache.ambari.server.controller.spi.Resource; +import org.junit.Test; + +import java.util.Set; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +/** + * ServiceResourceDefinition unit tests. + */ +public class ServiceResourceDefinitionTest { + + @Test + public void testGetPluralName() { + assertEquals("services", new ServiceResourceDefinition().getPluralName()); + } + + @Test + public void testGetSingularName() { + assertEquals("service", new ServiceResourceDefinition().getSingularName()); + } + + @Test + public void testGetSubResourceDefinitions() { + ResourceDefinition resource = new ServiceResourceDefinition(); + Set<SubResourceDefinition> subResources = resource.getSubResourceDefinitions(); + + assertEquals(3, subResources.size()); + assertTrue(includesType(subResources, Resource.Type.Component)); + assertTrue(includesType(subResources, Resource.Type.Alert)); + assertTrue(includesType(subResources, Resource.Type.Artifact)); + } + + private boolean includesType(Set<SubResourceDefinition> resources, Resource.Type type) { + for (SubResourceDefinition subResource : resources) { + if (subResource.getType() == type) { + return true; + } + } + + return false; + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/d902509f/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ArtifactResourceProviderTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ArtifactResourceProviderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ArtifactResourceProviderTest.java new file mode 100644 index 0000000..4ed1a51 --- /dev/null +++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ArtifactResourceProviderTest.java @@ -0,0 +1,229 @@ +/** + * 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 org.apache.ambari.server.controller.AmbariManagementController; +import org.apache.ambari.server.controller.spi.Predicate; +import org.apache.ambari.server.controller.spi.Request; +import org.apache.ambari.server.controller.spi.Resource; +import org.apache.ambari.server.controller.utilities.PredicateBuilder; +import org.apache.ambari.server.orm.dao.ArtifactDAO; +import org.apache.ambari.server.orm.entities.ArtifactEntity; +import org.apache.ambari.server.state.Cluster; +import org.apache.ambari.server.state.Clusters; +import org.easymock.Capture; +import org.junit.Before; +import org.junit.Test; + +import javax.persistence.EntityManager; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; + +import static org.easymock.EasyMock.capture; +import static org.easymock.EasyMock.createMock; +import static org.easymock.EasyMock.createStrictMock; +import static org.easymock.EasyMock.eq; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.replay; +import static org.easymock.EasyMock.reset; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +/** + * ArtifactResourceProvider unit tests. + */ +public class ArtifactResourceProviderTest { + + private ArtifactDAO dao = createStrictMock(ArtifactDAO.class); + private EntityManager em = createStrictMock(EntityManager.class); + private AmbariManagementController controller = createStrictMock(AmbariManagementController.class); + private Request request = createStrictMock(Request.class); + private Clusters clusters = createStrictMock(Clusters.class); + private Cluster cluster = createStrictMock(Cluster.class); + private ArtifactEntity entity = createMock(ArtifactEntity.class); + private ArtifactEntity entity2 = createMock(ArtifactEntity.class); + + ArtifactResourceProvider resourceProvider; + + @Before + public void setUp() throws Exception { + reset(dao, em, controller, request, clusters, cluster, entity, entity2); + resourceProvider = new ArtifactResourceProvider(controller); + setPrivateField(resourceProvider, "artifactDAO", dao); + } + + @Test + public void testGetResources_instance() throws Exception { + Set<String> propertyIds = new HashSet<String>(); + TreeMap<String, String> foreignKeys = new TreeMap<String, String>(); + foreignKeys.put("cluster", "500"); + + Map<String, Object> artifact_data = Collections.<String, Object>singletonMap("foo", "bar"); + + Map<String, String> responseForeignKeys = new HashMap<String, String>(); + responseForeignKeys.put("cluster", "500"); + + // expectations + expect(controller.getClusters()).andReturn(clusters).anyTimes(); + expect(clusters.getCluster("test-cluster")).andReturn(cluster).anyTimes(); + expect(clusters.getClusterById(500L)).andReturn(cluster).anyTimes(); + expect(cluster.getClusterId()).andReturn(500L).anyTimes(); + expect(cluster.getClusterName()).andReturn("test-cluster").anyTimes(); + + expect(request.getPropertyIds()).andReturn(propertyIds).anyTimes(); + + expect(dao.findByNameAndForeignKeys(eq("test-artifact"), eq(foreignKeys))).andReturn(entity).once(); + expect(entity.getArtifactName()).andReturn("test-artifact").anyTimes(); + expect(entity.getForeignKeys()).andReturn(responseForeignKeys).anyTimes(); + expect(entity.getArtifactData()).andReturn(artifact_data).anyTimes(); + + // end of expectation setting + replay(dao, em, controller, request, clusters, cluster, entity, entity2); + + // test + PredicateBuilder pb = new PredicateBuilder(); + Predicate predicate = pb.begin().property("Artifacts/cluster_name").equals("test-cluster").and(). + property("Artifacts/artifact_name").equals("test-artifact").end().toPredicate(); + + Set<Resource> response = resourceProvider.getResources(request, predicate); + assertEquals(1, response.size()); + Resource resource = response.iterator().next(); + assertEquals("test-artifact", resource.getPropertyValue("Artifacts/artifact_name")); + assertEquals("test-cluster", resource.getPropertyValue("Artifacts/cluster_name")); + assertEquals("bar", resource.getPropertyValue("artifact_data/foo")); + } + + @Test + public void testGetResources_collection() throws Exception { + Set<String> propertyIds = new HashSet<String>(); + TreeMap<String, String> foreignKeys = new TreeMap<String, String>(); + foreignKeys.put("cluster", "500"); + + List<ArtifactEntity> entities = new ArrayList<ArtifactEntity>(); + entities.add(entity); + entities.add(entity2); + + Map<String, Object> artifact_data = Collections.<String, Object>singletonMap("foo", "bar"); + Map<String, Object> artifact_data2 = Collections.<String, Object>singletonMap("foo2", "bar2"); + + Map<String, String> responseForeignKeys = new HashMap<String, String>(); + responseForeignKeys.put("cluster", "500"); + + // expectations + expect(controller.getClusters()).andReturn(clusters).anyTimes(); + expect(clusters.getCluster("test-cluster")).andReturn(cluster).anyTimes(); + expect(clusters.getClusterById(500L)).andReturn(cluster).anyTimes(); + expect(cluster.getClusterId()).andReturn(500L).anyTimes(); + expect(cluster.getClusterName()).andReturn("test-cluster").anyTimes(); + + expect(request.getPropertyIds()).andReturn(propertyIds).anyTimes(); + + expect(dao.findByForeignKeys(eq(foreignKeys))).andReturn(entities).anyTimes(); + expect(entity.getArtifactName()).andReturn("test-artifact").anyTimes(); + expect(entity.getForeignKeys()).andReturn(responseForeignKeys).anyTimes(); + expect(entity.getArtifactData()).andReturn(artifact_data).anyTimes(); + expect(entity2.getArtifactName()).andReturn("test-artifact2").anyTimes(); + expect(entity2.getForeignKeys()).andReturn(responseForeignKeys).anyTimes(); + expect(entity2.getArtifactData()).andReturn(artifact_data2).anyTimes(); + + // end of expectation setting + replay(dao, em, controller, request, clusters, cluster, entity, entity2); + + // test + PredicateBuilder pb = new PredicateBuilder(); + Predicate predicate = pb.begin().property("Artifacts/cluster_name").equals("test-cluster").end().toPredicate(); + + Set<Resource> response = resourceProvider.getResources(request, predicate); + assertEquals(2, response.size()); + + boolean artifact1Returned = false; + boolean artifact2Returned = false; + for (Resource resource : response) { + if (resource.getPropertyValue("Artifacts/artifact_name").equals("test-artifact")) { + artifact1Returned = true; + assertEquals("bar", resource.getPropertyValue("artifact_data/foo")); + assertEquals("test-cluster", resource.getPropertyValue("Artifacts/cluster_name")); + } else if (resource.getPropertyValue("Artifacts/artifact_name").equals("test-artifact2")) { + artifact2Returned = true; + assertEquals("bar2", resource.getPropertyValue("artifact_data/foo2")); + assertEquals("test-cluster", resource.getPropertyValue("Artifacts/cluster_name")); + } else { + fail("unexpected artifact name"); + } + } + assertTrue(artifact1Returned); + assertTrue(artifact2Returned); + } + + @Test + public void testCreateResource() throws Exception { + Capture<ArtifactEntity> createEntityCapture = new Capture<ArtifactEntity>(); + + Map<String, Object> artifact_data = Collections.<String, Object>singletonMap("foo", "bar"); + + TreeMap<String, String> foreignKeys = new TreeMap<String, String>(); + foreignKeys.put("cluster", "500"); + + Map<String, Object> properties = new HashMap<String, Object>(); + properties.put("Artifacts/artifact_name", "test-artifact"); + properties.put("Artifacts/cluster_name", "test-cluster"); + properties.put("artifact_data/foo", "bar"); + Set<Map<String, Object>> requestProperties = Collections.singleton(properties); + + // expectations + expect(request.getProperties()).andReturn(requestProperties).anyTimes(); + expect(controller.getClusters()).andReturn(clusters).anyTimes(); + expect(clusters.getCluster("test-cluster")).andReturn(cluster).anyTimes(); + expect(clusters.getClusterById(500L)).andReturn(cluster).anyTimes(); + expect(cluster.getClusterId()).andReturn(500L).anyTimes(); + expect(cluster.getClusterName()).andReturn("test-cluster").anyTimes(); + + // check to see if entity already exists + expect(dao.findByNameAndForeignKeys(eq("test-artifact"), eq(foreignKeys))).andReturn(null).once(); + // create + dao.create(capture(createEntityCapture)); + + // end of expectation setting + replay(dao, em, controller, request, clusters, cluster, entity, entity2); + + resourceProvider.createResources(request); + + ArtifactEntity createEntity = createEntityCapture.getValue(); + assertEquals("test-artifact", createEntity.getArtifactName()); + assertEquals(createEntity.getArtifactData(), artifact_data); + assertEquals(foreignKeys, createEntity.getForeignKeys()); + } + + + private void setPrivateField(Object o, String field, Object value) throws Exception{ + Class<?> c = o.getClass(); + Field f = c.getDeclaredField(field); + f.setAccessible(true); + f.set(o, value); + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/d902509f/ambari-server/src/test/java/org/apache/ambari/server/upgrade/UpgradeCatalog200Test.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/upgrade/UpgradeCatalog200Test.java b/ambari-server/src/test/java/org/apache/ambari/server/upgrade/UpgradeCatalog200Test.java index 6bb8a95..9cf016e 100644 --- a/ambari-server/src/test/java/org/apache/ambari/server/upgrade/UpgradeCatalog200Test.java +++ b/ambari-server/src/test/java/org/apache/ambari/server/upgrade/UpgradeCatalog200Test.java @@ -23,6 +23,7 @@ import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertNull; import static junit.framework.Assert.assertTrue; +import static junit.framework.Assert.fail; import static org.easymock.EasyMock.capture; import static org.easymock.EasyMock.createMockBuilder; import static org.easymock.EasyMock.createNiceMock; @@ -131,6 +132,7 @@ public class UpgradeCatalog200Test { Capture<DBAccessor.DBColumnInfo> valueColumnCapture = new Capture<DBAccessor.DBColumnInfo>(); Capture<DBAccessor.DBColumnInfo> dataValueColumnCapture = new Capture<DBAccessor.DBColumnInfo>(); Capture<List<DBAccessor.DBColumnInfo>> alertTargetStatesCapture = new Capture<List<DBAccessor.DBColumnInfo>>(); + Capture<List<DBAccessor.DBColumnInfo>> artifactCapture = new Capture<List<DBAccessor.DBColumnInfo>>(); Capture<List<DBAccessor.DBColumnInfo>> upgradeCapture = new Capture<List<DBAccessor.DBColumnInfo>>(); Capture<List<DBAccessor.DBColumnInfo>> upgradeGroupCapture = new Capture<List<DBAccessor.DBColumnInfo>>(); @@ -199,6 +201,9 @@ public class UpgradeCatalog200Test { // Upgrade item dbAccessor.createTable(eq("upgrade_item"), capture(upgradeItemCapture), eq("upgrade_item_id")); + // artifact + dbAccessor.createTable(eq("artifact"), capture(artifactCapture), + eq("artifact_name"), eq("foreign_keys")); setViewInstancePropertyExpectations(dbAccessor, valueColumnCapture); setViewInstanceDataExpectations(dbAccessor, dataValueColumnCapture); @@ -264,6 +269,11 @@ public class UpgradeCatalog200Test { verifyViewParameterColumns(viewparameterLabelColumnCapture, viewparameterPlaceholderColumnCapture, viewparameterDefaultValueColumnCapture); + // verify artifact columns + List<DBAccessor.DBColumnInfo> artifactColumns = artifactCapture.getValue(); + testCreateArtifactTable(artifactColumns); + + // Verify capture group sizes assertEquals(7, clusterVersionCapture.getValue().size()); assertEquals(4, hostVersionCapture.getValue().size()); @@ -529,4 +539,31 @@ public class UpgradeCatalog200Test { assertNull(column.getDefaultValue()); assertTrue(column.isNullable()); } + + /** + * assert artifact table creation + * + * @param artifactColumns artifact table columns + */ + private void testCreateArtifactTable(List<DBColumnInfo> artifactColumns) { + assertEquals(3, artifactColumns.size()); + for (DBColumnInfo column : artifactColumns) { + if (column.getName().equals("artifact_name")) { + assertNull(column.getDefaultValue()); + assertEquals(String.class, column.getType()); + assertEquals(255, (int) column.getLength()); + assertEquals(false, column.isNullable()); + } else if (column.getName().equals("foreign_keys")) { + assertNull(column.getDefaultValue()); + assertEquals(String.class, column.getType()); + assertEquals(false, column.isNullable()); + } else if (column.getName().equals("artifact_data")) { + assertNull(column.getDefaultValue()); + assertEquals(char[].class, column.getType()); + assertEquals(false, column.isNullable()); + } else { + fail("unexpected column name"); + } + } + } }
