Browse Source

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

John Speidel 10 năm trước cách đây
mục cha
commit
d822bf34e1
14 tập tin đã thay đổi với 751 bổ sung3 xóa
  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);
+    }
+  }
 
 }