Bläddra i källkod

AMBARI-9028. Create new API endpoints to obtain the stack and stack service kerberos descriptors

John Speidel 10 år sedan
förälder
incheckning
d822bf34e1
14 ändrade filer med 751 tillägg och 3 borttagningar
  1. 14 0
      ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImpl.java
  2. 1 0
      ambari-server/src/main/java/org/apache/ambari/server/api/resources/StackServiceResourceDefinition.java
  3. 1 0
      ambari-server/src/main/java/org/apache/ambari/server/api/resources/StackVersionResourceDefinition.java
  4. 70 0
      ambari-server/src/main/java/org/apache/ambari/server/api/services/StacksService.java
  5. 3 0
      ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AbstractControllerResourceProvider.java
  6. 401 0
      ambari-server/src/main/java/org/apache/ambari/server/controller/internal/StackArtifactResourceProvider.java
  7. 3 1
      ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java
  8. 9 1
      ambari-server/src/test/java/org/apache/ambari/server/api/query/QueryImplTest.java
  9. 40 0
      ambari-server/src/test/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImplTest.java
  10. 65 0
      ambari-server/src/test/java/org/apache/ambari/server/api/resources/StackServiceResourceDefinitionTest.java
  11. 73 0
      ambari-server/src/test/java/org/apache/ambari/server/api/resources/StackVersionResourceDefinitionTest.java
  12. 43 0
      ambari-server/src/test/java/org/apache/ambari/server/api/services/StacksServiceTest.java
  13. 10 0
      ambari-server/src/test/java/org/apache/ambari/server/controller/internal/AbstractControllerResourceProviderTest.java
  14. 18 1
      ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ClusterControllerImplTest.java

+ 14 - 0
ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImpl.java

@@ -342,6 +342,20 @@ public class ResourceInstanceFactoryImpl implements ResourceInstanceFactory {
         resourceDefinition = new SimpleResourceDefinition(Resource.Type.Stage, "stage", "stages", Resource.Type.Task);
         break;
 
+      case StackArtifact:
+        resourceDefinition = new BaseStacksResourceDefinition(Resource.Type.StackArtifact) {
+          @Override
+          public String getPluralName() {
+            return "artifacts";
+          }
+
+          @Override
+          public String getSingularName() {
+            return "artifact";
+          }
+        };
+        break;
+
       default:
         throw new IllegalArgumentException("Unsupported resource type: " + type);
     }

+ 1 - 0
ambari-server/src/main/java/org/apache/ambari/server/api/resources/StackServiceResourceDefinition.java

@@ -50,6 +50,7 @@ public class StackServiceResourceDefinition extends BaseStacksResourceDefinition
     Set<SubResourceDefinition> setChildren = new HashSet<SubResourceDefinition>();
     setChildren.add(new SubResourceDefinition(Resource.Type.StackConfiguration));
     setChildren.add(new SubResourceDefinition(Resource.Type.StackServiceComponent));
+    setChildren.add(new SubResourceDefinition(Type.StackArtifact));
 
     return setChildren;
 

+ 1 - 0
ambari-server/src/main/java/org/apache/ambari/server/api/resources/StackVersionResourceDefinition.java

@@ -53,6 +53,7 @@ public class StackVersionResourceDefinition extends BaseStacksResourceDefinition
     setChildren.add(new SubResourceDefinition(Resource.Type.StackService));
     setChildren.add(new SubResourceDefinition(Resource.Type.StackLevelConfiguration));
     setChildren.add(new SubResourceDefinition(Resource.Type.RepositoryVersion));
+    setChildren.add(new SubResourceDefinition(Type.StackArtifact));
 
     return setChildren;
 

+ 70 - 0
ambari-server/src/main/java/org/apache/ambari/server/api/services/StacksService.java

@@ -139,6 +139,53 @@ public class StacksService extends BaseService {
         createStackServiceResource(stackName, stackVersion, serviceName));
   }
 
+  @GET
+  @Path("{stackName}/versions/{stackVersion}/artifacts")
+  @Produces("text/plain")
+  public Response getStackArtifacts(String body, @Context HttpHeaders headers,
+                                              @Context UriInfo ui, @PathParam("stackName") String stackName,
+                                              @PathParam("stackVersion") String stackVersion) {
+
+    return handleRequest(headers, body, new StackUriInfo(ui), Request.Type.GET,
+        createStackArtifactsResource(stackName, stackVersion, null));
+  }
+
+  @GET
+  @Path("{stackName}/versions/{stackVersion}/artifacts/{artifactName}")
+  @Produces("text/plain")
+  public Response getStackArtifact(String body, @Context HttpHeaders headers,
+                                   @Context UriInfo ui, @PathParam("stackName") String stackName,
+                                   @PathParam("stackVersion") String stackVersion,
+                                   @PathParam("artifactName") String artifactName) {
+
+    return handleRequest(headers, body, new StackUriInfo(ui), Request.Type.GET,
+        createStackArtifactsResource(stackName, stackVersion, artifactName));
+  }
+
+  @GET
+  @Path("{stackName}/versions/{stackVersion}/services/{serviceName}/artifacts")
+  @Produces("text/plain")
+  public Response getStackServiceArtifacts(String body, @Context HttpHeaders headers,
+                                  @Context UriInfo ui, @PathParam("stackName") String stackName,
+                                  @PathParam("stackVersion") String stackVersion,
+                                  @PathParam("serviceName") String serviceName) {
+
+    return handleRequest(headers, body, new StackUriInfo(ui), Request.Type.GET,
+        createStackServiceArtifactsResource(stackName, stackVersion, serviceName, null));
+  }
+
+  @GET
+  @Path("{stackName}/versions/{stackVersion}/services/{serviceName}/artifacts/{artifactName}")
+  @Produces("text/plain")
+  public Response getStackServiceArtifact(String body, @Context HttpHeaders headers,
+                                           @Context UriInfo ui, @PathParam("stackName") String stackName,
+                                           @PathParam("stackVersion") String stackVersion,
+                                           @PathParam("serviceName") String serviceName,
+                                           @PathParam("artifactName") String artifactName) {
+
+    return handleRequest(headers, body, new StackUriInfo(ui), Request.Type.GET,
+        createStackServiceArtifactsResource(stackName, stackVersion, serviceName, artifactName));
+  }
 
   @GET
   @Path("{stackName}/versions/{stackVersion}/services/{serviceName}/configurations")
@@ -316,6 +363,29 @@ public class StacksService extends BaseService {
     return createResource(Resource.Type.StackLevelConfiguration, mapIds);
   }
 
+  ResourceInstance createStackArtifactsResource(String stackName, String stackVersion, String artifactName) {
+    Map<Resource.Type, String> mapIds = new HashMap<Resource.Type, String>();
+    mapIds.put(Resource.Type.Stack, stackName);
+    mapIds.put(Resource.Type.StackVersion, stackVersion);
+    mapIds.put(Resource.Type.StackArtifact, artifactName);
+
+    return createResource(Resource.Type.StackArtifact, mapIds);
+  }
+
+  ResourceInstance createStackServiceArtifactsResource(String stackName,
+                                                       String stackVersion,
+                                                       String serviceName,
+                                                       String artifactName) {
+
+    Map<Resource.Type, String> mapIds = new HashMap<Resource.Type, String>();
+    mapIds.put(Resource.Type.Stack, stackName);
+    mapIds.put(Resource.Type.StackVersion, stackVersion);
+    mapIds.put(Resource.Type.StackService, serviceName);
+    mapIds.put(Resource.Type.StackArtifact, artifactName);
+
+    return createResource(Resource.Type.StackArtifact, mapIds);
+  }
+
   ResourceInstance createStackResource(String stackName) {
 
     return createResource(Resource.Type.Stack,

+ 3 - 0
ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AbstractControllerResourceProvider.java

@@ -153,6 +153,9 @@ public abstract class AbstractControllerResourceProvider extends AbstractResourc
         return new ClientConfigResourceProvider(propertyIds, keyPropertyIds, managementController);
       case RepositoryVersion:
         return resourceProviderFactory.getRepositoryVersionResourceProvider();
+      case StackArtifact:
+        return new StackArtifactResourceProvider(managementController);
+
       default:
         throw new IllegalArgumentException("Unknown type " + type);
     }

+ 401 - 0
ambari-server/src/main/java/org/apache/ambari/server/controller/internal/StackArtifactResourceProvider.java

@@ -0,0 +1,401 @@
+/**
+ * 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.AmbariException;
+import org.apache.ambari.server.StackAccessException;
+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.state.ServiceInfo;
+import org.apache.ambari.server.state.StackInfo;
+import org.apache.ambari.server.state.kerberos.KerberosDescriptor;
+import org.apache.ambari.server.state.kerberos.KerberosServiceDescriptor;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Provider for stack and stack service 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.
+ * <p>
+ * Stack artifacts are part of the stack definition and therefore can't
+ * be created, updated or deleted.
+ */
+public class StackArtifactResourceProvider extends AbstractControllerResourceProvider {
+  /**
+   * stack name
+   */
+  public static final String STACK_NAME_PROPERTY_ID =
+      PropertyHelper.getPropertyId("Artifacts", "stack_name");
+
+  /**
+   * stack version
+   */
+  public static final String STACK_VERSION_PROPERTY_ID =
+      PropertyHelper.getPropertyId("Artifacts", "stack_version");
+
+  /**
+   * stack service name
+   */
+  public static final String STACK_SERVICE_NAME_PROPERTY_ID =
+      PropertyHelper.getPropertyId("Artifacts", "service_name");
+
+  /**
+   * artifact name
+   */
+  public static final String ARTIFACT_NAME_PROPERTY_ID =
+      PropertyHelper.getPropertyId("Artifacts", "artifact_name");
+
+  /**
+   * artifact data
+   */
+  public static final String ARTIFACT_DATA_PROPERTY_ID = "artifact_data";
+
+  /**
+   * primary key fields
+   */
+  public static Set<String> pkPropertyIds = new HashSet<String>();
+
+  /**
+   * map of resource type to fk field
+   */
+  public static Map<Resource.Type, String> keyPropertyIds =
+      new HashMap<Resource.Type, String>();
+
+  /**
+   * resource properties
+   */
+  public static Set<String> propertyIds = new HashSet<String>();
+
+  /**
+   * name of the kerberos descriptor artifact.
+   */
+  public static final String KERBEROS_DESCRIPTOR_NAME = "kerberos_descriptor";
+
+
+  /**
+   * set resource properties, pk and fk's
+   */
+  static {
+    // resource properties
+    propertyIds.add(STACK_NAME_PROPERTY_ID);
+    propertyIds.add(STACK_VERSION_PROPERTY_ID);
+    propertyIds.add(STACK_SERVICE_NAME_PROPERTY_ID);
+    propertyIds.add(ARTIFACT_NAME_PROPERTY_ID);
+    propertyIds.add(ARTIFACT_DATA_PROPERTY_ID);
+
+    // pk property
+    pkPropertyIds.add(ARTIFACT_NAME_PROPERTY_ID);
+
+    // fk properties
+    keyPropertyIds.put(Resource.Type.StackArtifact, ARTIFACT_NAME_PROPERTY_ID);
+    keyPropertyIds.put(Resource.Type.Stack, STACK_NAME_PROPERTY_ID);
+    keyPropertyIds.put(Resource.Type.StackVersion, STACK_VERSION_PROPERTY_ID);
+    keyPropertyIds.put(Resource.Type.StackService, STACK_SERVICE_NAME_PROPERTY_ID);
+  }
+
+  /**
+   * Constructor.
+   *
+   * @param managementController ambari controller
+   */
+  protected StackArtifactResourceProvider(AmbariManagementController managementController) {
+    super(propertyIds, keyPropertyIds, managementController);
+  }
+
+  @Override
+  public Set<Resource> getResources(Request request, Predicate predicate)
+      throws SystemException,
+             UnsupportedPropertyException,
+             NoSuchResourceException,
+             NoSuchParentResourceException {
+
+    Set<Resource> resources = new HashSet<Resource>();
+
+    resources.addAll(getKerberosDescriptors(request, predicate));
+    // add other artifacts types here
+
+    if (resources.isEmpty()) {
+      throw new NoSuchResourceException(
+          "The requested resource doesn't exist: Artifact not found, " + predicate);
+    }
+
+    return resources;
+  }
+
+  @Override
+  protected Set<String> getPKPropertyIds() {
+    return pkPropertyIds;
+  }
+
+  @Override
+  public RequestStatus createResources(Request request)
+      throws SystemException,
+      UnsupportedPropertyException,
+      ResourceAlreadyExistsException,
+      NoSuchParentResourceException {
+
+    throw new UnsupportedOperationException("Creating stack artifacts is not supported");
+  }
+
+  @Override
+  public RequestStatus updateResources(Request request, Predicate predicate)
+      throws SystemException,
+             UnsupportedPropertyException,
+             NoSuchResourceException,
+             NoSuchParentResourceException {
+
+    throw new UnsupportedOperationException("Updating of stack artifacts is not supported");
+  }
+
+  @Override
+  public RequestStatus deleteResources(Predicate predicate)
+      throws SystemException,
+             UnsupportedPropertyException,
+             NoSuchResourceException,
+             NoSuchParentResourceException {
+
+    throw new UnsupportedOperationException("Deletion of stack artifacts is not supported");
+  }
+
+  /**
+   * Get all stack and stack service descriptor resources.
+   *
+   * @param request    user request
+   * @param predicate  request predicate
+   *
+   * @return set of all stack related kerberos descriptor resources; will not return null
+   *
+   * @throws SystemException                if an unexpected exception occurs
+   * @throws UnsupportedPropertyException   if an unsupported property was requested
+   * @throws NoSuchParentResourceException  if a specified parent resource doesn't exist
+   * @throws NoSuchResourceException        if the requested resource doesn't exist
+   */
+  private Set<Resource> getKerberosDescriptors(Request request, Predicate predicate)
+      throws SystemException,
+             UnsupportedPropertyException,
+             NoSuchParentResourceException,
+             NoSuchResourceException {
+
+    Set<Resource> resources = new HashSet<Resource>();
+
+    for (Map<String, Object> properties : getPropertyMaps(predicate)) {
+      String artifactName = (String) properties.get(ARTIFACT_NAME_PROPERTY_ID);
+      if (artifactName == null || artifactName.equals(KERBEROS_DESCRIPTOR_NAME)) {
+        String stackName = (String) properties.get(STACK_NAME_PROPERTY_ID);
+        String stackVersion = (String) properties.get(STACK_VERSION_PROPERTY_ID);
+        String stackService = (String) properties.get(STACK_SERVICE_NAME_PROPERTY_ID);
+
+        Map<String, Object> descriptor;
+        try {
+          descriptor = getKerberosDescriptor(stackName, stackVersion, stackService);
+        } catch (IOException e) {
+          LOG.error("Unable to process Kerberos Descriptor. Properties: " + properties, e);
+          throw new SystemException("An internal exception occurred while attempting to build a Kerberos Descriptor " +
+              "artifact. See ambari server logs for more information", e);
+        }
+
+        if (descriptor != null) {
+          Resource resource = new ResourceImpl(Resource.Type.StackArtifact);
+          Set<String> requestedIds = getRequestPropertyIds(request, predicate);
+          setResourceProperty(resource, ARTIFACT_NAME_PROPERTY_ID, KERBEROS_DESCRIPTOR_NAME, requestedIds);
+          setResourceProperty(resource, ARTIFACT_DATA_PROPERTY_ID, descriptor, requestedIds);
+          setResourceProperty(resource, STACK_NAME_PROPERTY_ID, stackName, requestedIds);
+          setResourceProperty(resource, STACK_VERSION_PROPERTY_ID, stackVersion, requestedIds);
+          if (stackService != null) {
+            setResourceProperty(resource, STACK_SERVICE_NAME_PROPERTY_ID, stackService, requestedIds);
+          }
+          resources.add(resource);
+        }
+      }
+    }
+    return resources;
+  }
+
+  /**
+   * Get a kerberos descriptor.
+   *
+   * @param stackName     stack name
+   * @param stackVersion  stack version
+   * @param serviceName   service name
+   *
+   * @return map of kerberos descriptor data or null if no descriptor exists
+   *
+   * @throws IOException if unable to parse the associated kerberos descriptor file
+   * @throws NoSuchParentResourceException if the parent stack or stack service doesn't exist
+   */
+  private Map<String, Object> getKerberosDescriptor(String stackName, String stackVersion, String serviceName)
+      throws NoSuchParentResourceException, IOException {
+
+      return serviceName == null ?
+          buildStackDescriptor(stackName, stackVersion) :
+          getServiceDescriptor(stackName, stackVersion, serviceName);
+  }
+
+  /**
+   * Build a kerberos descriptor for the specified stack. This descriptor is for the entire stack version
+   * and will contain both the stack descriptor as well as all service descriptors.
+   *
+   * @return map of kerberos descriptor data or null if no descriptor exists
+   *
+   * @throws IOException     if unable to read the kerberos descriptor file
+   * @throws AmbariException if unable to parse the kerberos descriptor file json
+   * @throws NoSuchParentResourceException if the parent stack doesn't exist
+   */
+  private Map<String, Object> buildStackDescriptor(String stackName, String stackVersion)
+      throws NoSuchParentResourceException, IOException {
+
+    KerberosDescriptor kerberosDescriptor = null;
+
+    AmbariManagementController controller = getManagementController();
+    StackInfo stackInfo;
+    try {
+      stackInfo = controller.getAmbariMetaInfo().getStack(stackName, stackVersion);
+    } catch (StackAccessException e) {
+      throw new NoSuchParentResourceException(String.format(
+          "Parent stack resource doesn't exist: stackName='%s', stackVersion='%s'", stackName, stackVersion));
+    }
+
+    Collection<KerberosServiceDescriptor> serviceDescriptors = getServiceDescriptors(stackInfo);
+
+    String kerberosFileLocation = stackInfo.getKerberosDescriptorFileLocation();
+    if (kerberosFileLocation != null) {
+      kerberosDescriptor = KerberosDescriptor.fromFile(new File(kerberosFileLocation));
+    } else if (! serviceDescriptors.isEmpty()) {
+      // service descriptors present with no stack descriptor,
+      // create an empty stack descriptor to hold services
+      kerberosDescriptor = new KerberosDescriptor();
+    }
+
+    if (kerberosDescriptor != null) {
+      for (KerberosServiceDescriptor descriptor : serviceDescriptors) {
+        kerberosDescriptor.putService(descriptor);
+      }
+      return kerberosDescriptor.toMap();
+    } else {
+      return null;
+    }
+  }
+
+  /**
+   * Get the kerberos descriptor for the specified stack service.
+   *
+   * @param stackName     stack name
+   * @param stackVersion  stack version
+   * @param serviceName   service name
+   *
+   * @return map of kerberos descriptor data or null if no descriptor exists
+   *
+   * @throws IOException if unable to read or parse the kerberos descriptor file
+   * @throws NoSuchParentResourceException if the parent stack or stack service doesn't exist
+   */
+  private Map<String, Object> getServiceDescriptor(
+      String stackName, String stackVersion, String serviceName) throws NoSuchParentResourceException, IOException {
+
+    AmbariManagementController controller = getManagementController();
+
+    ServiceInfo serviceInfo;
+    try {
+      serviceInfo = controller.getAmbariMetaInfo().getService(stackName, stackVersion, serviceName);
+    } catch (StackAccessException e) {
+      throw new NoSuchParentResourceException(String.format(
+          "Parent stack/service resource doesn't exist: stackName='%s', stackVersion='%s', serviceName='%s'",
+          stackName, stackVersion, serviceName));
+    }
+    File kerberosFile = serviceInfo.getKerberosDescriptorFile();
+
+    if (kerberosFile != null) {
+      KerberosServiceDescriptor serviceDescriptor = getMatchingServiceDescriptor(
+          KerberosServiceDescriptor.fromFile(kerberosFile), serviceName);
+
+      if (serviceDescriptor != null) {
+        return serviceDescriptor.toMap();
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Get a collection of all service descriptors for the specified stack.
+   *
+   * @param stack  stack name
+   *
+   * @return collection of all service descriptors for the stack; will not return null
+   *
+   * @throws IOException if unable to read or parse a descriptor file
+   */
+  private Collection<KerberosServiceDescriptor> getServiceDescriptors(StackInfo stack) throws IOException {
+    Collection<KerberosServiceDescriptor> serviceDescriptors = new ArrayList<KerberosServiceDescriptor>();
+    for (ServiceInfo service : stack.getServices()) {
+      File descriptorFile = service.getKerberosDescriptorFile();
+      if (descriptorFile != null) {
+        KerberosServiceDescriptor descriptor = getMatchingServiceDescriptor(
+            KerberosServiceDescriptor.fromFile(descriptorFile), service.getName());
+
+        if (descriptor != null) {
+          serviceDescriptors.add(descriptor);
+        }
+      }
+    }
+    return serviceDescriptors;
+  }
+
+  /**
+   * Get the correct service descriptor from an array of service descriptors.
+   * This is necessary because in some cases, multiple stack services are contained in the same
+   * stack metainfo file and all point to the same kerberos descriptor.
+   * This should be fixed in the stack to only return the matching descriptor, not all descriptors.
+   * When/If these changes are made in the stack, this method will go away as only the correct descriptor
+   * will be returned for a given service.
+   *
+   * @param descriptors  array of service descriptors
+   * @param serviceName  service name
+   *
+   * @return the service descriptor which correlates to the specified service or null if no match is made
+   */
+  private KerberosServiceDescriptor getMatchingServiceDescriptor(KerberosServiceDescriptor[] descriptors,
+                                                                 String serviceName) {
+    KerberosServiceDescriptor match = null;
+    for (KerberosServiceDescriptor descriptor : descriptors) {
+      if (descriptor.getName().equals(serviceName)) {
+        match = descriptor;
+        break;
+      }
+    }
+    return match;
+  }
+}

+ 3 - 1
ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java

@@ -135,7 +135,8 @@ public interface Resource {
     UpgradeGroup,
     UpgradeItem,
     PreUpgradeCheck,
-    Stage;
+    Stage,
+    StackArtifact;
 
     /**
      * Get the {@link Type} that corresponds to this InternalType.
@@ -232,6 +233,7 @@ public interface Resource {
     public static final Type UpgradeItem = InternalType.UpgradeItem.getType();
     public static final Type PreUpgradeCheck = InternalType.PreUpgradeCheck.getType();
     public static final Type Stage = InternalType.Stage.getType();
+    public static final Type StackArtifact = InternalType.StackArtifact.getType();
 
     /**
      * The type name.

+ 9 - 1
ambari-server/src/test/java/org/apache/ambari/server/api/query/QueryImplTest.java

@@ -275,7 +275,8 @@ public class QueryImplTest {
     Assert.assertEquals("StackVersion:1", versionNode.getName());
     Assert.assertEquals(Resource.Type.StackVersion, versionNode.getObject().getType());
 
-    Assert.assertEquals(4, versionNode.getChildren().size());
+    Assert.assertEquals(5, versionNode.getChildren().size());
+
     TreeNode<Resource> opSystemsNode = versionNode.getChild("operatingSystems");
     Assert.assertEquals(3, opSystemsNode.getChildren().size());
 
@@ -296,6 +297,13 @@ public class QueryImplTest {
     Assert.assertEquals("centos5", repositoryResource.getPropertyValue("Repositories/os_type"));
     Assert.assertEquals("1.2.1", repositoryResource.getPropertyValue("Repositories/stack_version"));
     Assert.assertEquals("HDP", repositoryResource.getPropertyValue("Repositories/stack_name"));
+
+    TreeNode<Resource> artifactsNode = versionNode.getChild("artifacts");
+    Assert.assertEquals(1, artifactsNode.getChildren().size());
+
+    TreeNode<Resource> artifactNode = artifactsNode.getChild("StackArtifact:1");
+    Assert.assertEquals("StackArtifact:1", artifactNode.getName());
+    Assert.assertEquals(Resource.Type.StackArtifact, artifactNode.getObject().getType());
   }
 
   @Test

+ 40 - 0
ambari-server/src/test/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImplTest.java

@@ -0,0 +1,40 @@
+/**
+ * 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 static org.junit.Assert.assertEquals;
+
+/**
+ * ResourceInstanceFactoryImpl unit tests.
+ */
+public class ResourceInstanceFactoryImplTest {
+
+  @Test
+  public void testGetStackArtifactDefinition() {
+    ResourceDefinition resourceDefinition = ResourceInstanceFactoryImpl.getResourceDefinition(
+        Resource.Type.StackArtifact, null);
+
+    assertEquals("artifact", resourceDefinition.getSingularName());
+    assertEquals("artifacts", resourceDefinition.getPluralName());
+    assertEquals(Resource.Type.StackArtifact, resourceDefinition.getType());
+  }
+}

+ 65 - 0
ambari-server/src/test/java/org/apache/ambari/server/api/resources/StackServiceResourceDefinitionTest.java

@@ -0,0 +1,65 @@
+/**
+ * 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 junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertTrue;
+
+/**
+ * StackServiceResourceDefinition unit tests
+ */
+public class StackServiceResourceDefinitionTest {
+  @Test
+  public void testDefinitionNames() {
+    ResourceDefinition def = new StackServiceResourceDefinition();
+    assertEquals("stackService", def.getSingularName());
+    assertEquals("stackServices", def.getPluralName());
+  }
+
+  @Test
+  public void testGetSubResourceDefinitions() {
+    ResourceDefinition def = new StackServiceResourceDefinition();
+
+    Set<SubResourceDefinition> subResources = def.getSubResourceDefinitions();
+    assertEquals(3, subResources.size());
+
+    boolean configReturned = false;
+    boolean componentReturned = false;
+    boolean artifactReturned = false;
+
+    for (SubResourceDefinition subResource : subResources) {
+      Resource.Type type = subResource.getType();
+      if (type.equals(Resource.Type.StackConfiguration)) {
+        configReturned = true;
+      } else if (type.equals(Resource.Type.StackServiceComponent)) {
+        componentReturned = true;
+      } else if (type.equals(Resource.Type.StackArtifact)) {
+        artifactReturned = true;
+      }
+    }
+    assertTrue(configReturned);
+    assertTrue(componentReturned);
+    assertTrue(artifactReturned);
+  }
+}

+ 73 - 0
ambari-server/src/test/java/org/apache/ambari/server/api/resources/StackVersionResourceDefinitionTest.java

@@ -0,0 +1,73 @@
+/**
+ * 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 junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertTrue;
+
+/**
+ * StackVersionResourceDefinition unit tests
+ */
+public class StackVersionResourceDefinitionTest {
+  @Test
+  public void testDefinitionNames() {
+    ResourceDefinition def = new StackVersionResourceDefinition();
+    assertEquals("version", def.getSingularName());
+    assertEquals("versions", def.getPluralName());
+  }
+
+  @Test
+  public void testGetSubResourceDefinitions() {
+    ResourceDefinition def = new StackVersionResourceDefinition();
+
+    Set<SubResourceDefinition> subResources = def.getSubResourceDefinitions();
+    assertEquals(5, subResources.size());
+
+    boolean operatingSystemFound = false;
+    boolean serviceFound = false;
+    boolean configFound = false;
+    boolean repoFound = false;
+    boolean artifactReturned = false;
+
+    for (SubResourceDefinition subResource : subResources) {
+      Resource.Type type = subResource.getType();
+      if (type.equals(Resource.Type.OperatingSystem)) {
+        operatingSystemFound = true;
+      } else if (type.equals(Resource.Type.StackService)) {
+        serviceFound = true;
+      } else if (type.equals(Resource.Type.StackLevelConfiguration)) {
+        configFound = true;
+      }else if (type.equals(Resource.Type.RepositoryVersion)) {
+        repoFound = true;
+      }else if (type.equals(Resource.Type.StackArtifact)) {
+        artifactReturned = true;
+      }
+    }
+    assertTrue(operatingSystemFound);
+    assertTrue(serviceFound);
+    assertTrue(configFound);
+    assertTrue(repoFound);
+    assertTrue(artifactReturned);
+  }
+}

+ 43 - 0
ambari-server/src/test/java/org/apache/ambari/server/api/services/StacksServiceTest.java

@@ -21,7 +21,11 @@ package org.apache.ambari.server.api.services;
 import org.apache.ambari.server.api.resources.ResourceInstance;
 import org.apache.ambari.server.api.services.parsers.RequestBodyParser;
 import org.apache.ambari.server.api.services.serializers.ResultSerializer;
+
+import javax.ws.rs.PathParam;
+import javax.ws.rs.core.Context;
 import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriInfo;
 
 import java.lang.reflect.Method;
@@ -107,6 +111,33 @@ public class StacksServiceTest extends BaseServiceTest {
     args = new Object[] {null, getHttpHeaders(), getUriInfo(), "stackName", "stackVersion", "service-name"};
     listInvocations.add(new ServiceTestInvocation(Request.Type.GET, service, m, args, null));
 
+    //get stack artifacts
+    service = new TestStacksService("stackName", null);
+    m = service.getClass().getMethod("getStackArtifacts", String.class, HttpHeaders.class, UriInfo.class, String.class, String.class);
+    args = new Object[] {null, getHttpHeaders(), getUriInfo(), "stackName", "stackVersion"};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.GET, service, m, args, null));
+
+    //get stack artifact
+    service = new TestStacksService("stackName", null);
+    m = service.getClass().getMethod("getStackArtifact", String.class, HttpHeaders.class, UriInfo.class, String.class,
+        String.class, String.class);
+    args = new Object[] {null, getHttpHeaders(), getUriInfo(), "stackName", "stackVersion", "artifact-name"};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.GET, service, m, args, null));
+
+    //get stack service artifacts
+    service = new TestStacksService("stackName", "stackVersion");
+    m = service.getClass().getMethod("getStackServiceArtifacts", String.class, HttpHeaders.class, UriInfo.class,
+        String.class, String.class, String.class);
+    args = new Object[] {null, getHttpHeaders(), getUriInfo(), "stackName", "stackVersion", "service-name"};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.GET, service, m, args, null));
+
+    //get stack service artifact
+    service = new TestStacksService("stackName", "stackVersion");
+    m = service.getClass().getMethod("getStackServiceArtifact", String.class, HttpHeaders.class, UriInfo.class,
+        String.class, String.class, String.class, String.class);
+    args = new Object[] {null, getHttpHeaders(), getUriInfo(), "stackName", "stackVersion", "service-name", "artifact-name"};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.GET, service, m, args, null));
+
     return listInvocations;
   }
 
@@ -139,16 +170,28 @@ public class StacksServiceTest extends BaseServiceTest {
       return getTestResource();
     }
 
+    @Override
     ResourceInstance createStackConfigurationResource(String stackName,
         String stackVersion, String serviceName, String propertyName) {
       return getTestResource();
     }
 
+    @Override
     ResourceInstance createStackServiceComponentResource(String stackName,
         String stackVersion, String serviceName, String componentName) {
       return getTestResource();
     }
 
+    @Override
+    ResourceInstance createStackArtifactsResource(String stackName, String stackVersion, String artifactName) {
+      return getTestResource();
+    }
+
+    @Override
+    ResourceInstance createStackServiceArtifactsResource(String stackName, String stackVersion, String serviceName, String artifactName) {
+      return getTestResource();
+    }
+
     @Override
     RequestFactory getRequestFactory() {
       return getTestRequestFactory();

+ 10 - 0
ambari-server/src/test/java/org/apache/ambari/server/controller/internal/AbstractControllerResourceProviderTest.java

@@ -35,6 +35,7 @@ import static org.easymock.EasyMock.createMock;
 import static org.easymock.EasyMock.createNiceMock;
 import static org.easymock.EasyMock.expect;
 import static org.easymock.EasyMock.replay;
+import static org.junit.Assert.assertEquals;
 
 /**
  * Abstract controller resource provider test.
@@ -75,4 +76,13 @@ public class AbstractControllerResourceProviderTest {
     Assert.assertTrue(provider instanceof ServiceResourceProvider);
   }
 
+  @Test
+  public void testGetStackArtifactResourceProvider() {
+    AmbariManagementController managementController = createMock(AmbariManagementController.class);
+
+    ResourceProvider provider = AbstractControllerResourceProvider.getResourceProvider(
+        Resource.Type.StackArtifact, null, null, managementController);
+
+    assertEquals(StackArtifactResourceProvider.class, provider.getClass());
   }
+}

+ 18 - 1
ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ClusterControllerImplTest.java

@@ -978,6 +978,7 @@ public class ClusterControllerImplTest {
       providers.put(Resource.Type.OperatingSystem, new TestOperatingSystemResourceProvider());
       providers.put(Resource.Type.Repository, new TestRepositoryResourceProvider());
       providers.put(Resource.Type.RepositoryVersion, new TestRepositoryVersionResourceProvider());
+      providers.put(Type.StackArtifact, new TestStackArtifactResourceProvider());
     }
 
     @Override
@@ -1250,7 +1251,23 @@ public class ClusterControllerImplTest {
 
       return getResources(Resource.Type.RepositoryVersion, predicate, "RepositoriVersions/id", keyPropertyValues);
     }
-  }  
+  }
+
+  private static class TestStackArtifactResourceProvider extends TestResourceProvider {
+    private TestStackArtifactResourceProvider() {
+      super(StackArtifactResourceProvider.propertyIds, StackArtifactResourceProvider.keyPropertyIds);
+    }
+
+    @Override
+    public Set<Resource> getResources(Request request, Predicate predicate)
+        throws SystemException, UnsupportedPropertyException, NoSuchResourceException, NoSuchParentResourceException {
+      Set<String> keyPropertyValues = new LinkedHashSet<String>();
+
+      keyPropertyValues.add("kerberos_descriptor");
+
+      return getResources(Type.StackArtifact, predicate, "Artifacts/artifact_name", keyPropertyValues);
+    }
+  }
 
 }