You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jclouds.apache.org by za...@apache.org on 2015/04/29 19:12:43 UTC

[1/2] jclouds-labs-openstack git commit: adding: StackApi Stack and StackResource

Repository: jclouds-labs-openstack
Updated Branches:
  refs/heads/master ea1e72905 -> ce4d93f82


http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/ce4d93f8/openstack-heat/src/test/resources/stack_list_response.json
----------------------------------------------------------------------
diff --git a/openstack-heat/src/test/resources/stack_list_response.json b/openstack-heat/src/test/resources/stack_list_response.json
new file mode 100644
index 0000000..66d2532
--- /dev/null
+++ b/openstack-heat/src/test/resources/stack_list_response.json
@@ -0,0 +1,19 @@
+{
+    "stacks": [
+        {
+            "creation_time": "2014-06-03T20:59:46Z",
+            "description": "sample stack",
+            "id": "3095aefc-09fb-4bc7-b1f0-f21a304e864c",
+            "links": [
+                {
+                    "href": "http://192.168.123.200:8004/v1/eb1c63a4f77141548385f113a28f0f52/stacks/simple_stack/3095aefc-09fb-4bc7-b1f0-f21a304e864c",
+                    "rel": "self"
+                }
+            ],
+            "stack_name": "simple_stack",
+            "stack_status": "CREATE_COMPLETE",
+            "stack_status_reason": "Stack CREATE completed successfully",
+            "updated_time": "2014-06-03T20:59:46Z"
+        }
+    ]
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/ce4d93f8/openstack-heat/src/test/resources/stack_resources_get_response.json
----------------------------------------------------------------------
diff --git a/openstack-heat/src/test/resources/stack_resources_get_response.json b/openstack-heat/src/test/resources/stack_resources_get_response.json
new file mode 100644
index 0000000..0286dbd
--- /dev/null
+++ b/openstack-heat/src/test/resources/stack_resources_get_response.json
@@ -0,0 +1,23 @@
+{
+    "resource": {
+        "resource_name": "cinder_volume",
+        "description": "",
+        "links": [
+            {
+                "href": "http://10.20.20.11:8004/v1/ed902e7377e340a7a0d78e1dfeb15c62/stacks/StackIT_1424281004155/c749952a-dde7-4aba-83d1-f360217a93e0/resources/cinder_volume",
+                "rel": "self"
+            },
+            {
+                "href": "http://10.20.20.11:8004/v1/ed902e7377e340a7a0d78e1dfeb15c62/stacks/StackIT_1424281004155/c749952a-dde7-4aba-83d1-f360217a93e0",
+                "rel": "stack"
+            }
+        ],
+        "logical_resource_id": "cinder_volume",
+        "resource_status": "CREATE_COMPLETE",
+        "updated_time": "2015-02-18T17:36:45Z",
+        "required_by": [ ],
+        "resource_status_reason": "state changed",
+        "physical_resource_id": "f59b7a86-aee0-4f62-b190-606c4e08da7c",
+        "resource_type": "OS::Cinder::Volume"
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/ce4d93f8/openstack-heat/src/test/resources/stack_resources_list_response.json
----------------------------------------------------------------------
diff --git a/openstack-heat/src/test/resources/stack_resources_list_response.json b/openstack-heat/src/test/resources/stack_resources_list_response.json
new file mode 100644
index 0000000..32b842c
--- /dev/null
+++ b/openstack-heat/src/test/resources/stack_resources_list_response.json
@@ -0,0 +1,24 @@
+{
+    "resources": [
+        {
+            "resource_name": "cinder_volume",
+            "links": [
+                {
+                    "href": "http://10.20.20.11:8004/v1/ed902e7377e340a7a0d78e1dfeb15c62/stacks/StackIT_1424281004155/c749952a-dde7-4aba-83d1-f360217a93e0/resources/cinder_volume",
+                    "rel": "self"
+                },
+                {
+                    "href": "http://10.20.20.11:8004/v1/ed902e7377e340a7a0d78e1dfeb15c62/stacks/StackIT_1424281004155/c749952a-dde7-4aba-83d1-f360217a93e0",
+                    "rel": "stack"
+                }
+            ],
+            "logical_resource_id": "cinder_volume",
+            "resource_status_reason": "state changed",
+            "updated_time": "2015-02-18T17:36:45Z",
+            "required_by": [ ],
+            "resource_status": "CREATE_COMPLETE",
+            "physical_resource_id": "f59b7a86-aee0-4f62-b190-606c4e08da7c",
+            "resource_type": "OS::Cinder::Volume"
+        }
+    ]
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/ce4d93f8/openstack-heat/src/test/resources/stack_with_environment_and_files.json
----------------------------------------------------------------------
diff --git a/openstack-heat/src/test/resources/stack_with_environment_and_files.json b/openstack-heat/src/test/resources/stack_with_environment_and_files.json
new file mode 100644
index 0000000..50934f8
--- /dev/null
+++ b/openstack-heat/src/test/resources/stack_with_environment_and_files.json
@@ -0,0 +1,21 @@
+{
+    "environment": {
+        "resource_registry": {
+            "ALU::LCP::VolumeB": "VolumeB.template.yaml"
+        }
+    },
+
+    "template": {
+
+        "heat_template_version": "2013-05-23",
+
+        "resources": {
+            "ALU-LCP-OAMB": {
+                "properties": {
+                    "storage_size": 4
+                },
+                "type": "ALU::LCP::VolumeB"
+            }
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/ce4d93f8/openstack-heat/src/test/resources/stack_with_parameters.json
----------------------------------------------------------------------
diff --git a/openstack-heat/src/test/resources/stack_with_parameters.json b/openstack-heat/src/test/resources/stack_with_parameters.json
new file mode 100644
index 0000000..6ace6b5
--- /dev/null
+++ b/openstack-heat/src/test/resources/stack_with_parameters.json
@@ -0,0 +1,25 @@
+{
+    "template": {
+        "heat_template_version": "2013-05-23",
+
+        "description": "Simple template to deploy a single compute instance",
+
+        "parameters": {
+            "key_name": {
+                "type": "string",
+                "label": "Key Name",
+                "description": "Name of key-pair to be used for compute instance"
+            }
+        },
+
+        "resources": {
+            "my_instance": {
+                "type": "OS::Nova::KeyPair",
+                "properties": {
+                    "name": { "get_param": "key_name" }
+                }
+            }
+
+        }
+    }
+}
\ No newline at end of file


[2/2] jclouds-labs-openstack git commit: adding: StackApi Stack and StackResource

Posted by za...@apache.org.
adding: StackApi Stack and StackResource


Project: http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/repo
Commit: http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/commit/ce4d93f8
Tree: http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/tree/ce4d93f8
Diff: http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/diff/ce4d93f8

Branch: refs/heads/master
Commit: ce4d93f82c0210a5a58789cee8a7ff8fd8f9817d
Parents: ea1e729
Author: lbortman <li...@alcatel-lucent.com>
Authored: Wed Apr 8 11:05:19 2015 +0300
Committer: Zack Shoylev <za...@rackspace.com>
Committed: Wed Apr 29 12:09:08 2015 -0500

----------------------------------------------------------------------
 openstack-heat/pom.xml                          |  13 +-
 .../org/jclouds/openstack/heat/v1/HeatApi.java  |   8 +
 .../jclouds/openstack/heat/v1/domain/Stack.java | 150 +++++
 .../openstack/heat/v1/domain/StackResource.java |  95 ++++
 .../heat/v1/domain/StackResourceStatus.java     |  57 ++
 .../openstack/heat/v1/domain/StackStatus.java   |  57 ++
 .../openstack/heat/v1/features/StackApi.java    | 123 +++++
 .../openstack/heat/v1/options/CreateStack.java  | 185 +++++++
 .../heat/v1/options/ListStackOptions.java       | 243 +++++++++
 .../openstack/heat/v1/options/UpdateStack.java  | 116 ++++
 .../heat/v1/features/StackApiLiveTest.java      | 321 +++++++++++
 .../heat/v1/features/StackApiMockTest.java      | 542 +++++++++++++++++++
 .../heat/v1/options/ListStackOptionsTest.java   | 109 ++++
 .../src/test/resources/create_stack.json        |  11 +
 .../test/resources/files_for_atck_template.json |  33 ++
 .../src/test/resources/resources_metadata.json  |   6 +
 .../src/test/resources/simple_stack.json        |  21 +
 .../src/test/resources/stack_get_response.json  |  27 +
 .../src/test/resources/stack_list_response.json |  19 +
 .../resources/stack_resources_get_response.json |  23 +
 .../stack_resources_list_response.json          |  24 +
 .../stack_with_environment_and_files.json       |  21 +
 .../test/resources/stack_with_parameters.json   |  25 +
 23 files changed, 2226 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/ce4d93f8/openstack-heat/pom.xml
----------------------------------------------------------------------
diff --git a/openstack-heat/pom.xml b/openstack-heat/pom.xml
index 5d995f4..afe7870 100644
--- a/openstack-heat/pom.xml
+++ b/openstack-heat/pom.xml
@@ -96,14 +96,21 @@
       <scope>test</scope>
     </dependency>
     <dependency>
+      <groupId>com.google.auto.service</groupId>
+      <artifactId>auto-service</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
       <groupId>com.google.auto.value</groupId>
       <artifactId>auto-value</artifactId>
+      <version>1.0</version>
       <scope>provided</scope>
     </dependency>
     <dependency>
-      <groupId>com.google.auto.service</groupId>
-      <artifactId>auto-service</artifactId>
-      <scope>provided</scope>
+      <groupId>com.googlecode.json-simple</groupId>
+      <artifactId>json-simple</artifactId>
+      <version>1.1</version>
+      <scope>test</scope>
     </dependency>
   </dependencies>
 

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/ce4d93f8/openstack-heat/src/main/java/org/jclouds/openstack/heat/v1/HeatApi.java
----------------------------------------------------------------------
diff --git a/openstack-heat/src/main/java/org/jclouds/openstack/heat/v1/HeatApi.java b/openstack-heat/src/main/java/org/jclouds/openstack/heat/v1/HeatApi.java
index e1f9e25..9197b5a 100644
--- a/openstack-heat/src/main/java/org/jclouds/openstack/heat/v1/HeatApi.java
+++ b/openstack-heat/src/main/java/org/jclouds/openstack/heat/v1/HeatApi.java
@@ -22,6 +22,7 @@ import java.util.Set;
 import org.jclouds.location.Region;
 import org.jclouds.location.functions.RegionToEndpoint;
 import org.jclouds.openstack.heat.v1.features.ResourceApi;
+import org.jclouds.openstack.heat.v1.features.StackApi;
 import org.jclouds.rest.annotations.Delegate;
 import org.jclouds.rest.annotations.EndpointParam;
 
@@ -42,4 +43,11 @@ public interface HeatApi extends Closeable {
     */
    @Delegate
    ResourceApi getResourceApi(@EndpointParam(parser = RegionToEndpoint.class) String region);
+
+   /**
+    * Provides access to Stack features.
+    */
+   @Delegate
+   StackApi getStackApi(@EndpointParam(parser = RegionToEndpoint.class) String region);
 }
+

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/ce4d93f8/openstack-heat/src/main/java/org/jclouds/openstack/heat/v1/domain/Stack.java
----------------------------------------------------------------------
diff --git a/openstack-heat/src/main/java/org/jclouds/openstack/heat/v1/domain/Stack.java b/openstack-heat/src/main/java/org/jclouds/openstack/heat/v1/domain/Stack.java
new file mode 100644
index 0000000..9b43eb8
--- /dev/null
+++ b/openstack-heat/src/main/java/org/jclouds/openstack/heat/v1/domain/Stack.java
@@ -0,0 +1,150 @@
+/*
+ * 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.jclouds.openstack.heat.v1.domain;
+
+import autovalue.shaded.com.google.common.common.collect.ImmutableMap;
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import org.jclouds.javax.annotation.Nullable;
+import org.jclouds.json.SerializedNames;
+import org.jclouds.openstack.v2_0.domain.Link;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Representation of an OpenStack Heat Stack.
+ */
+@AutoValue
+public abstract class Stack {
+
+   /**
+    * @return Specifies the Stack ID . The value is a UUID, such as 96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0.
+    */
+   public abstract String getId();
+
+   /**
+    * @return Specifies the name of the stack
+    */
+   @Nullable public abstract String getName();
+
+   /**
+    * @return the template description of this Stack.
+    */
+   @Nullable public abstract String getTemplateDescription();
+
+   /**
+    * @return the description of this Stack.
+    */
+   @Nullable public abstract String getDescription();
+
+   /**
+    * @return the owner of this Stack.
+    */
+   @Nullable public abstract String getOwner();
+
+   /**
+    * @return the project ID of the stack
+    */
+   @Nullable public abstract String getProject();
+
+   /**
+    * @return the parameters of this Stack.
+    */
+   @Nullable public abstract Map<String, String> getParameters();
+
+   /**
+    * @return the capabilities of this Stack.
+    */
+   @Nullable public abstract Set<String> getCapabilities();
+
+   /**
+    * @return the outputs of this Stack.
+    */
+   @Nullable public abstract List<String> getOutputs();
+
+   /**
+    * @return the notification topics of this Stack.
+    */
+   @Nullable public abstract List<String> getNotificationTopics();
+
+   /**
+    * @return the status of this Stack.
+    */
+   @Nullable public abstract StackStatus getStatus();
+
+   /**
+    * @return the status reason of this Stack.
+    */
+   @Nullable public abstract String getSatusReason();
+
+   /**
+    * @return the date this Stack was created.
+    */
+   @Nullable public abstract Date getCreated();
+
+   /**
+    * @return the date this Stack was last updated.
+    */
+   @Nullable public abstract Date getUpdated();
+
+   /**
+    * @return the timeout of this Stack (in minutes).
+    */
+   public abstract int getTimeoutMins();
+
+   /**
+    * @return true is disableRollback is true
+    */
+   public abstract boolean isDisableRollback();
+
+   /**
+    * @return Specifies the self-navigating JSON document paths.
+    */
+   public abstract Set<Link> getLinks();
+
+   @SerializedNames({"id", "stack_name", "description", "owner", "capabilities", "parameters", "outputs",
+         "notification_topics", "template_description", "stack_status", "stack_status_reason", "creation_time",
+         "updated_time", "timeout_mins", "disable_rollback", "project", "links"})
+   private static Stack create(String id, String name, String description, String owner, Set<String> capabilities,
+                               Map<String, String> parameters, List<String> outputs, List<String> notificationTopics,
+                               String templateDescription, StackStatus status, String statusReason, Date created, Date updated, int timeoutMins,
+                               boolean disableRollback, String project, Set<Link> links) {
+      return new AutoValue_Stack(
+            id,
+            name,
+            templateDescription,
+            description,
+            owner,
+            project,
+            parameters != null ? ImmutableMap.copyOf(parameters) : null,
+            capabilities != null ? ImmutableSet.copyOf(capabilities) : null,
+            outputs != null ? ImmutableList.copyOf(outputs) : null,
+            notificationTopics != null ? ImmutableList.copyOf(notificationTopics) : null,
+            status,
+            statusReason,
+            created,
+            updated,
+            timeoutMins,
+            disableRollback,
+            ImmutableSet.copyOf(links));
+   }
+}
+

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/ce4d93f8/openstack-heat/src/main/java/org/jclouds/openstack/heat/v1/domain/StackResource.java
----------------------------------------------------------------------
diff --git a/openstack-heat/src/main/java/org/jclouds/openstack/heat/v1/domain/StackResource.java b/openstack-heat/src/main/java/org/jclouds/openstack/heat/v1/domain/StackResource.java
new file mode 100644
index 0000000..5d17764
--- /dev/null
+++ b/openstack-heat/src/main/java/org/jclouds/openstack/heat/v1/domain/StackResource.java
@@ -0,0 +1,95 @@
+/*
+ * 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.jclouds.openstack.heat.v1.domain;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableSet;
+import org.jclouds.javax.annotation.Nullable;
+import org.jclouds.json.SerializedNames;
+import org.jclouds.openstack.v2_0.domain.Link;
+
+import java.util.Date;
+import java.util.Set;
+
+/**
+ * Representation of an OpenStack Heat Stack Resources.
+ */
+@AutoValue
+public abstract class StackResource {
+
+   /**
+    * @return Specifies the name of the stack
+    */
+   public abstract String getName();
+
+   /**
+    * @return logical resource ID.
+    */
+   public abstract String getLogicalResourceId();
+
+   /**
+    * @return physical resource ID.
+    */
+   public abstract String getPhysicalResourceId();
+
+   /**
+    * @return the status
+    */
+   public abstract StackResourceStatus getStatus();
+
+   /**
+    * @return Status reason
+    */
+   public abstract String getStatusReason();
+
+   /**
+    * @return the field required_by
+    */
+   @Nullable public abstract Set<String> getRequiredBy();
+
+   /**
+    * @return the resource type
+    */
+   public abstract String getResourceType();
+
+   /**
+    * @return the update time
+    */
+   public abstract Date getUpdated();
+
+   /**
+    * @return Specifies the self-navigating JSON document paths.
+    */
+   public abstract Set<Link> getLinks();
+
+   @SerializedNames({"resource_name", "logical_resource_id", "resource_status_reason", "updated_time", "required_by", "resource_status", "physical_resource_id", "resource_type", "links"})
+   private static StackResource create(String name, String logicalResourceId, String statusReason, Date updated, @Nullable Set<String> requiredBy,
+                                       StackResourceStatus status, String physicalResourceId, String resourceType, Set<Link> links) {
+      return new AutoValue_StackResource(
+            name,
+            logicalResourceId,
+            physicalResourceId,
+            status,
+            statusReason,
+            requiredBy != null ? ImmutableSet.copyOf(requiredBy) : null,
+            resourceType,
+            updated,
+            ImmutableSet.copyOf(links));
+   }
+
+}
+

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/ce4d93f8/openstack-heat/src/main/java/org/jclouds/openstack/heat/v1/domain/StackResourceStatus.java
----------------------------------------------------------------------
diff --git a/openstack-heat/src/main/java/org/jclouds/openstack/heat/v1/domain/StackResourceStatus.java b/openstack-heat/src/main/java/org/jclouds/openstack/heat/v1/domain/StackResourceStatus.java
new file mode 100644
index 0000000..5f06a3c
--- /dev/null
+++ b/openstack-heat/src/main/java/org/jclouds/openstack/heat/v1/domain/StackResourceStatus.java
@@ -0,0 +1,57 @@
+/*
+ * 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.jclouds.openstack.heat.v1.domain;
+
+
+import com.google.common.base.CaseFormat;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+public enum StackResourceStatus {
+   INIT_COMPLETE, CREATE_IN_PROGRESS, CREATE_COMPLETE, CREATE_FAILED,
+   UPDATE_IN_PROGRESS, UPDATE_COMPLETE, UPDATE_FAILED,
+   DELETE_IN_PROGRESS, DELETE_COMPLETE, DELETE_FAILED,
+   ROLLBACK_IN_PROGRESS, ROLLBACK_COMPLETE, ROLLBACK_FAILED,
+   SUSPEND_IN_PROGRESS, SUSPEND_COMPLETE, SUSPEND_FAILED,
+   RESUME_IN_PROGRESS, RESUME_COMPLETE, RESUME_FAILED,
+   UNRECOGNIZED;
+
+   public String value() {
+      return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_HYPHEN, name());
+   }
+
+   @Override
+   public String toString() {
+      return value();
+   }
+
+   /**
+    * This provides GSON enum support in jclouds.
+    *
+    * @param name The string representation of this enum value.
+    * @return The corresponding enum value.
+    */
+
+   public static StackResourceStatus fromValue(String status) {
+      try {
+         return valueOf(checkNotNull(status, "status"));
+      } catch (IllegalArgumentException e) {
+         return UNRECOGNIZED;
+      }
+   }
+}
+

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/ce4d93f8/openstack-heat/src/main/java/org/jclouds/openstack/heat/v1/domain/StackStatus.java
----------------------------------------------------------------------
diff --git a/openstack-heat/src/main/java/org/jclouds/openstack/heat/v1/domain/StackStatus.java b/openstack-heat/src/main/java/org/jclouds/openstack/heat/v1/domain/StackStatus.java
new file mode 100644
index 0000000..8d318e6
--- /dev/null
+++ b/openstack-heat/src/main/java/org/jclouds/openstack/heat/v1/domain/StackStatus.java
@@ -0,0 +1,57 @@
+/*
+ * 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.jclouds.openstack.heat.v1.domain;
+
+import com.google.common.base.CaseFormat;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+public enum StackStatus {
+   CREATE_IN_PROGRESS, CREATE_COMPLETE, CREATE_FAILED,
+   UPDATE_IN_PROGRESS, UPDATE_COMPLETE, UPDATE_FAILED,
+   DELETE_IN_PROGRESS, DELETE_COMPLETE, DELETE_FAILED,
+   ROLLBACK_IN_PROGRESS, ROLLBACK_COMPLETE, ROLLBACK_FAILED,
+   SUSPEND_IN_PROGRESS, SUSPEND_COMPLETE, SUSPEND_FAILED,
+   RESUME_IN_PROGRESS, RESUME_COMPLETE, RESUME_FAILED,
+   UNRECOGNIZED;
+
+   public String value() {
+      return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_HYPHEN, name());
+   }
+
+   @Override
+   public String toString() {
+      return value();
+   }
+
+   /**
+    * This provides GSON enum support in jclouds.
+    *
+    * @param name The string representation of this enum value.
+    * @return The corresponding enum value.
+    */
+
+   public static StackStatus fromValue(String status) {
+      try {
+         return valueOf(checkNotNull(status, "status"));
+      } catch (IllegalArgumentException e) {
+         return UNRECOGNIZED;
+      } catch (NullPointerException e){
+         return UNRECOGNIZED;
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/ce4d93f8/openstack-heat/src/main/java/org/jclouds/openstack/heat/v1/features/StackApi.java
----------------------------------------------------------------------
diff --git a/openstack-heat/src/main/java/org/jclouds/openstack/heat/v1/features/StackApi.java b/openstack-heat/src/main/java/org/jclouds/openstack/heat/v1/features/StackApi.java
new file mode 100644
index 0000000..271d9a4
--- /dev/null
+++ b/openstack-heat/src/main/java/org/jclouds/openstack/heat/v1/features/StackApi.java
@@ -0,0 +1,123 @@
+/*
+ * 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.jclouds.openstack.heat.v1.features;
+
+import org.jclouds.Fallbacks.EmptyListOnNotFoundOr404;
+import org.jclouds.Fallbacks.EmptyMapOnNotFoundOr404;
+import org.jclouds.Fallbacks.FalseOnNotFoundOr404;
+import org.jclouds.Fallbacks.NullOnNotFoundOr404;
+import org.jclouds.javax.annotation.Nullable;
+import org.jclouds.openstack.heat.v1.domain.Stack;
+import org.jclouds.openstack.heat.v1.domain.StackResource;
+import org.jclouds.openstack.heat.v1.options.CreateStack;
+import org.jclouds.openstack.heat.v1.options.ListStackOptions;
+import org.jclouds.openstack.heat.v1.options.UpdateStack;
+import org.jclouds.openstack.keystone.v2_0.filters.AuthenticateRequest;
+import org.jclouds.rest.annotations.BinderParam;
+import org.jclouds.rest.annotations.Fallback;
+import org.jclouds.rest.annotations.RequestFilters;
+import org.jclouds.rest.annotations.SelectJson;
+import org.jclouds.rest.binders.BindToJsonPayload;
+
+import javax.inject.Named;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.core.MediaType;
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * Provides access to the OpenStack Orchestration (Heat) Stack API features.
+ */
+@RequestFilters(AuthenticateRequest.class)
+@Consumes(MediaType.APPLICATION_JSON)
+@Path("/stacks")
+public interface StackApi {
+
+   @Named("stack:list")
+   @GET
+   @SelectJson("stacks")
+   @Fallback(EmptyListOnNotFoundOr404.class)
+   List<Stack> list();
+
+   @Named("stack:list")
+   @GET
+   @SelectJson("stacks")
+   @Fallback(EmptyListOnNotFoundOr404.class)
+   List<Stack> list(ListStackOptions options);
+
+   @Named("stack:get")
+   @GET
+   @SelectJson("stack")
+   @Path("/{stack_name}/{stack_id}")
+   @Fallback(NullOnNotFoundOr404.class)
+   @Nullable
+   Stack get(@PathParam("stack_name") String name, @PathParam("stack_id") String id);
+
+   @Named("stack:get")
+   @GET
+   @SelectJson("stack")
+   @Path("/{id}")
+   @Fallback(NullOnNotFoundOr404.class)
+   Stack get(@PathParam("id") String stackId);
+
+   @Named("stack:create")
+   @POST
+   @SelectJson("stack")
+   Stack create(@BinderParam(BindToJsonPayload.class) CreateStack options);
+
+   @Named("stack:delete")
+   @DELETE
+   @Path("/{stack_name}/{stack_id}")
+   @Fallback(FalseOnNotFoundOr404.class)
+   boolean delete(@PathParam("stack_name") String name, @PathParam("stack_id") String id);
+
+   @Named("stack:update")
+   @PUT
+   @Path("/{stack_name}/{stack_id}")
+   @Nullable
+   boolean update(@PathParam("stack_name") String name, @PathParam("stack_id") String id, @BinderParam(BindToJsonPayload.class) UpdateStack options);
+
+
+   @Named("stack:list_resources")
+   @GET
+   @SelectJson("resources")
+   @Path("/{stack_name}/{stack_id}/resources")
+   @Fallback(EmptyListOnNotFoundOr404.class)
+   List<StackResource> listStackResources(@PathParam("stack_name") String stackName, @PathParam("stack_id") String stackId);
+
+   @Named("stack:get_resources")
+   @GET
+   @SelectJson("resource")
+   @Path("/{stack_name}/{stack_id}/resources/{resource_name}")
+   @Fallback(NullOnNotFoundOr404.class)
+   StackResource getStackResource(@PathParam("stack_name") String stackName, @PathParam("stack_id") String stackId, @PathParam("resource_name") String name);
+
+   @Named("stack:get_resources_metadata")
+   @GET
+   @SelectJson("metadata")
+   @Path("/{stack_name}/{stack_id}/resources/{resource_name}/metadata")
+   @Fallback(EmptyMapOnNotFoundOr404.class)
+   Map<String, Object> getStackResourceMetadata(@PathParam("stack_name") String stackName, @PathParam("stack_id") String stackId, @PathParam("resource_name") String name);
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/ce4d93f8/openstack-heat/src/main/java/org/jclouds/openstack/heat/v1/options/CreateStack.java
----------------------------------------------------------------------
diff --git a/openstack-heat/src/main/java/org/jclouds/openstack/heat/v1/options/CreateStack.java b/openstack-heat/src/main/java/org/jclouds/openstack/heat/v1/options/CreateStack.java
new file mode 100644
index 0000000..864d090
--- /dev/null
+++ b/openstack-heat/src/main/java/org/jclouds/openstack/heat/v1/options/CreateStack.java
@@ -0,0 +1,185 @@
+/*
+ * 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.jclouds.openstack.heat.v1.options;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableMap;
+import org.jclouds.javax.annotation.Nullable;
+import org.jclouds.json.SerializedNames;
+
+import java.util.Map;
+
+/**
+ * Representation of create stack  options.
+ */
+@AutoValue
+public abstract class CreateStack {
+
+   /**
+    * @see Builder#name(String)
+    */
+    public abstract String getName();
+
+   /**
+    * @see Builder#template(String)
+    */
+   @Nullable public abstract String getTemplate();
+
+   /**
+    * @see Builder#templateUrl(String)
+    */
+   @Nullable public abstract String getTemplateUrl();
+
+   /**
+    * @see Builder#parameters(Map<String, Object>)
+    */
+   @Nullable public abstract Map<String, Object> getParameters();
+
+   /**
+    * @see Builder#disableRollback(boolean)
+    */
+   public abstract boolean isDisableRollback();
+
+   /**
+    * @see Builder#files(Map<String, String>)
+    */
+   @Nullable public abstract Map<String, String> getFiles();
+
+   /**
+    * @see Builder#environment(String)
+    */
+   @Nullable public abstract String getEnvironment();
+
+   public static Builder builder() {
+      return new AutoValue_CreateStack.Builder().disableRollback(true).files(null).environment(null);
+   }
+
+   public Builder toBuilder() {
+      return builder()
+            .name(getName())
+            .template(getTemplate())
+            .templateUrl(getTemplateUrl())
+            .parameters(getParameters())
+            .disableRollback(isDisableRollback())
+            .files(getFiles())
+            .environment(getEnvironment());
+   }
+
+   @SerializedNames({"stack_name", "template", "template_url", "parameters", "disable_rollback", "files", "environment" })
+   private static CreateStack create(String name, @Nullable String template, @Nullable String templateUrl, @Nullable Map<String, Object> parameters, boolean disableRollback, @Nullable Map<String, String> files, @Nullable String environment) {
+      return builder()
+            .name(name)
+            .template(template)
+            .templateUrl(templateUrl)
+            .parameters(parameters)
+            .disableRollback(disableRollback)
+            .files(files)
+            .environment(environment).build();
+   }
+
+   public static final class Builder {
+
+      private String name;
+      private String template;
+      private String templateUrl;
+      private Map<String, Object> parameters;
+      private boolean disableRollback = true;
+      private Map<String, String> files;
+      private String environment;
+
+      Builder() {
+      }
+
+      Builder(CreateStack source) {
+         name(source.getName());
+         template(source.getTemplate());
+         templateUrl(source.getTemplateUrl());
+         parameters(source.getParameters());
+         disableRollback(source.isDisableRollback());
+         files(source.getFiles());
+         environment(source.getEnvironment());
+      }
+
+      /**
+       * @param name - The name of the stack
+       */
+      public Builder name(String name) {
+         this.name = name;
+         return this;
+      }
+
+      /**
+       * @param template - The stack template to instantiate.
+       */
+      public Builder template(String template) {
+         this.template = template;
+         return this;
+      }
+
+      /**
+       * @param templateUrl - A URI to the location containing the stack template to instantiate.
+       */
+      public Builder templateUrl(String templateUrl) {
+         this.templateUrl = templateUrl;
+         return this;
+      }
+
+      /**
+       * @param parameters - The properties for the template
+       */
+      public Builder parameters(Map<String, Object> parameters) {
+         this.parameters = parameters;
+         return this;
+      }
+
+      /**
+       * @param disableRollback - Controls whether a failure during stack creation causes deletion of all previously-created resources in that stack. The default is True
+       */
+      public Builder disableRollback(boolean disableRollback) {
+         this.disableRollback = disableRollback;
+         return this;
+      }
+
+      /**
+       * @param files - The properties for the template
+       */
+      public Builder files(Map<String, String> files) {
+         this.files = files;
+         return this;
+      }
+
+      /**
+       * @param environment - used to affect the runtime behaviour of the template
+       */
+      public Builder environment(String environment) {
+         this.environment = environment;
+         return this;
+      }
+
+      public CreateStack build() {
+         CreateStack result = new AutoValue_CreateStack(
+               this.name,
+               this.template,
+               this.templateUrl,
+               this.parameters != null ? ImmutableMap.copyOf(this.parameters) : null,
+               this.disableRollback,
+               this.files != null ? ImmutableMap.copyOf(this.files) : null,
+               this.environment);
+         return result;
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/ce4d93f8/openstack-heat/src/main/java/org/jclouds/openstack/heat/v1/options/ListStackOptions.java
----------------------------------------------------------------------
diff --git a/openstack-heat/src/main/java/org/jclouds/openstack/heat/v1/options/ListStackOptions.java b/openstack-heat/src/main/java/org/jclouds/openstack/heat/v1/options/ListStackOptions.java
new file mode 100644
index 0000000..9f85b08
--- /dev/null
+++ b/openstack-heat/src/main/java/org/jclouds/openstack/heat/v1/options/ListStackOptions.java
@@ -0,0 +1,243 @@
+/*
+ * 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.jclouds.openstack.heat.v1.options;
+
+import com.google.common.base.CaseFormat;
+import org.jclouds.openstack.heat.v1.domain.StackStatus;
+import org.jclouds.openstack.v2_0.options.PaginationOptions;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+
+/**
+ * Options used to control the amount of detail in the request.
+ *
+ * @see PaginationOptions
+ */
+public class ListStackOptions extends PaginationOptions {
+
+   public static final ListStackOptions NONE = new ListStackOptions();
+
+   public enum SortDirection {
+      ASCENDING("asc"),
+      DESCENDING("desc"),
+      UNRECOGNIZED("unrecognized");
+
+      private String name;
+
+      private SortDirection(String name) {
+         this.name = name;
+      }
+
+      @Override
+      public String toString() {
+         return name;
+      }
+
+      /**
+       * This provides GSON enum support in jclouds.
+       *
+       * @param name The string representation of this enum value.
+       * @return The corresponding enum value.
+       */
+      public static SortDirection fromValue(String direction) {
+         if (direction != null) {
+            for (SortDirection value : SortDirection.values()) {
+               if (direction.equalsIgnoreCase(value.name)) {
+                  return value;
+               }
+            }
+         }
+         return UNRECOGNIZED;
+      }
+
+      public static boolean contains(SortDirection direction) {
+         for (SortDirection dir : SortDirection.values()) {
+            if (dir.equals(direction)) {
+               return true;
+            }
+         }
+         return false;
+      }
+   }
+
+   public enum SortKey {
+      NAME, STATUS, CREATED_AT, UPDATED_AT,
+      UNRECOGNIZED;
+
+      public String value() {
+         return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_UNDERSCORE, name());
+      }
+
+      @Override
+      public String toString() {
+         return value();
+      }
+
+      /**
+       * This provides GSON enum support in jclouds.
+       *
+       * @param name The string representation of this enum value.
+       * @return The corresponding enum value.
+       */
+
+      public static SortKey fromValue(String sortKey) {
+         try {
+            return valueOf(checkNotNull(sortKey, "sortKey"));
+         } catch (IllegalArgumentException e) {
+            return UNRECOGNIZED;
+         }
+      }
+
+      public static boolean contains(String sortKey) {
+         for (SortKey key : SortKey.values()) {
+            if (key.value().equals(sortKey)) {
+               return true;
+            }
+         }
+         return false;
+      }
+
+      public static boolean contains(SortKey sortKey) {
+         for (SortKey key : SortKey.values()) {
+            if (key.equals(sortKey)) {
+               return true;
+            }
+         }
+         return false;
+      }
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public ListStackOptions limit(int limit) {
+      super.limit(limit);
+      return this;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public ListStackOptions marker(String marker) {
+      super.marker(marker);
+      return this;
+   }
+
+   /**
+    * Filters the stack list by the specified status. You can use this filter multiple
+    * times to filter by multiple statuses.
+    */
+   public ListStackOptions status(StackStatus status) {
+      this.queryParameters.put("status", checkNotNull(status.toString(), "status"));
+      return this;
+   }
+
+   /**
+    * Filters the stack list by the specified name.
+    */
+   public ListStackOptions name(String name) {
+      this.queryParameters.put("name", checkNotNull(name, "name"));
+      return this;
+   }
+
+   /**
+    * Return all the stack of all the tenant
+    */
+   public ListStackOptions globalTenant(Boolean globalTenant) {
+      this.queryParameters.put("global_tenant", globalTenant != null ? Boolean.toString(globalTenant) : "false");
+      return this;
+   }
+
+   /**
+    * Sorts the stack list by one of these attributes:
+    * name, status, created_at, or updated_at
+    */
+   public ListStackOptions sortKey(SortKey key) {
+      checkState(SortKey.contains(key), "invalid sort key");
+      this.queryParameters.put("sort_keys", checkNotNull(key.toString(), "key"));
+      return this;
+   }
+
+   /**
+    * The sort direction of the stack list. Either asc (ascending) or desc (descending).
+    */
+   public ListStackOptions sortDirection(SortDirection direction) {
+      checkState(SortDirection.contains(direction), "direction is either asc or desc");
+      this.queryParameters.put("sort_dir", checkNotNull(direction.toString(), "direction"));
+      return this;
+   }
+
+   public static Builder builder() {
+      return new Builder();
+   }
+
+   public static class Builder {
+
+      /**
+       * @see PaginationOptions#limit(int)
+       */
+      public static ListStackOptions limit(int limit) {
+         return new ListStackOptions().limit(limit);
+      }
+
+      /**
+       * @see PaginationOptions#marker(String)
+       */
+      public static ListStackOptions marker(String marker) {
+         return new ListStackOptions().marker(marker);
+      }
+
+      /**
+       * @see ListStackOptions#status(String)
+       */
+      public static ListStackOptions status(StackStatus status) {
+         return new ListStackOptions().status(status);
+      }
+
+      /**
+       * @see ListStackOptions#name(String)
+       */
+      public static ListStackOptions name(String name) {
+         return new ListStackOptions().name(name);
+      }
+
+      /**
+       * @see ListStackOptions#sortKey(String)
+       */
+      public static ListStackOptions sortKey(SortKey key) {
+         return new ListStackOptions().sortKey(key);
+      }
+
+      /**
+       * @see ListStackOptions#sortDirection(String)
+       */
+      public static ListStackOptions sortDirection(SortDirection direction) {
+         return new ListStackOptions().sortDirection(direction);
+      }
+
+      /**
+       * @see ListStackOptions#globalTenant(Boolean
+       */
+      public static ListStackOptions globalTenant(Boolean globalTenant) {
+         return new ListStackOptions().globalTenant(globalTenant);
+      }
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/ce4d93f8/openstack-heat/src/main/java/org/jclouds/openstack/heat/v1/options/UpdateStack.java
----------------------------------------------------------------------
diff --git a/openstack-heat/src/main/java/org/jclouds/openstack/heat/v1/options/UpdateStack.java b/openstack-heat/src/main/java/org/jclouds/openstack/heat/v1/options/UpdateStack.java
new file mode 100644
index 0000000..b1f7640
--- /dev/null
+++ b/openstack-heat/src/main/java/org/jclouds/openstack/heat/v1/options/UpdateStack.java
@@ -0,0 +1,116 @@
+/*
+ * 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.jclouds.openstack.heat.v1.options;
+
+
+import autovalue.shaded.com.google.common.common.collect.ImmutableMap;
+import com.google.auto.value.AutoValue;
+import org.jclouds.javax.annotation.Nullable;
+import org.jclouds.json.SerializedNames;
+
+import java.util.Map;
+
+/**
+ * Representation of updatable options
+ */
+@AutoValue
+public abstract class UpdateStack {
+
+   /**
+    * @see Builder#template(String)
+    */
+   @Nullable public abstract String getTemplate();
+
+   /**
+    * @see Builder#templateUrl(String)
+    */
+   @Nullable public abstract String getTemplateUrl();
+
+   /**
+    * @see Builder#parameters(java.util.Map)
+    */
+   @Nullable public abstract Map<String, Object> getParameters();
+
+   public static Builder builder() {
+      return new AutoValue_UpdateStack.Builder().parameters(null);
+   }
+
+   public Builder toBuilder() {
+      return builder()
+            .template(getTemplate())
+            .templateUrl(getTemplateUrl())
+            .parameters(getParameters());
+   }
+
+   @SerializedNames({"template", "template_url", "parameters"})
+   private static UpdateStack create(@Nullable String template, @Nullable String templateUrl, @Nullable Map<String, Object> parameters) {
+      return builder()
+            .template(template)
+            .templateUrl(templateUrl)
+            .parameters(parameters).build();
+   }
+
+
+   public static final class Builder {
+
+      private String template;
+      private String templateUrl;
+      private Map<String, Object> parameters;
+
+      Builder() {
+      }
+
+      Builder(CreateStack source) {
+         template(source.getTemplate());
+         templateUrl(source.getTemplateUrl());
+         parameters(source.getParameters());
+      }
+
+      /**
+       * @see UpdateStack#getTemplate()
+       */
+      public Builder template(String template) {
+         this.template = template;
+         return this;
+      }
+
+      /**
+       * @see UpdateStack#getTemplateUrl()
+       */
+      public Builder templateUrl(String templateUrl) {
+         this.templateUrl = templateUrl;
+         return this;
+      }
+
+      /**
+       * @see UpdateStack#getParameters()
+       */
+      public Builder parameters(Map<String, Object> parameters) {
+         this.parameters = parameters;
+         return this;
+      }
+
+      public UpdateStack build() {
+         UpdateStack result = new AutoValue_UpdateStack(
+               this.template,
+               this.templateUrl,
+               parameters != null ? ImmutableMap.copyOf(this.parameters) : null);
+         return result;
+      }
+   }
+}
+

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/ce4d93f8/openstack-heat/src/test/java/org/jclouds/openstack/heat/v1/features/StackApiLiveTest.java
----------------------------------------------------------------------
diff --git a/openstack-heat/src/test/java/org/jclouds/openstack/heat/v1/features/StackApiLiveTest.java b/openstack-heat/src/test/java/org/jclouds/openstack/heat/v1/features/StackApiLiveTest.java
new file mode 100644
index 0000000..f9e30a0
--- /dev/null
+++ b/openstack-heat/src/test/java/org/jclouds/openstack/heat/v1/features/StackApiLiveTest.java
@@ -0,0 +1,321 @@
+/*
+ * 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.jclouds.openstack.heat.v1.features;
+
+import com.google.common.base.Predicate;
+import com.google.common.base.Throwables;
+import org.jclouds.openstack.heat.v1.domain.Stack;
+import org.jclouds.openstack.heat.v1.domain.StackResource;
+import org.jclouds.openstack.heat.v1.domain.StackResourceStatus;
+import org.jclouds.openstack.heat.v1.domain.StackStatus;
+import org.jclouds.openstack.heat.v1.internal.BaseHeatApiLiveTest;
+import org.jclouds.openstack.heat.v1.options.CreateStack;
+import org.jclouds.openstack.heat.v1.options.UpdateStack;
+import org.jclouds.util.Strings2;
+import org.json.simple.JSONObject;
+import org.json.simple.parser.JSONParser;
+import org.json.simple.parser.ParseException;
+import org.testng.Assert;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.Test;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.jclouds.util.Predicates2.retry;
+
+/**
+ * Tests parsing and Guice wiring of StackApi
+ */
+@Test(groups = "live", testName = "StackApiLiveTest")
+public class StackApiLiveTest extends BaseHeatApiLiveTest {
+
+   public static final String TEMPLATE_URL = "http://10.5.5.121/Installs/cPaaS/YAML/simple_stack.yaml";
+   protected String stackName = System.getProperty("user.name").replace('.', '-').toLowerCase();
+
+   public void testList() {
+      for (String region : api.getConfiguredRegions()) {
+         StackApi stackApi = api.getStackApi(region);
+
+         List<Stack> stacks = stackApi.list();
+
+         assertThat(stacks).isNotEmpty();
+
+         int oldStackSize = stacks.size();
+         CreateStack createStack = CreateStack.builder().name(getName()).templateUrl(TEMPLATE_URL).build();
+         Stack stack = stackApi.create(createStack);
+         assertThat(stack).isNotNull();
+         stacks = stackApi.list();
+         assertThat(stacks.size()).isEqualTo(oldStackSize + 1);
+      }
+   }
+
+
+   public void testGetStackWitnNameAndID() {
+      for (String region : api.getConfiguredRegions()) {
+         StackApi stackApi = api.getStackApi(region);
+
+         List<Stack> stacks = stackApi.list();
+         assertThat(stacks).isNotNull();
+
+         Stack stack = stackApi.get(stacks.get(0).getName(), stacks.get(0).getId());
+         assertThat(stack).isNotNull();
+         assertThat(stack.getId()).isEqualTo(stacks.get(0).getId());
+      }
+   }
+
+   public void testGetStack() {
+      for (String region : api.getConfiguredRegions()) {
+         StackApi stackApi = api.getStackApi(region);
+
+         List<Stack> stacks = stackApi.list();
+         assertThat(stacks).isNotNull();
+
+         Stack stack = stackApi.get(stacks.get(0).getId());
+         assertThat(stack).isNotNull();
+         assertThat(stack.getId()).isEqualTo(stacks.get(0).getId());
+      }
+   }
+
+   public void testCreateWithTempletUrl() {
+      for (String region : api.getConfiguredRegions()) {
+         StackApi stackApi = api.getStackApi(region);
+         CreateStack createStack = CreateStack.builder().name(getName()).templateUrl(TEMPLATE_URL).build();
+         Stack stack = stackApi.create(createStack);
+         assertThat(stack).isNotNull();
+         assertThat(stack.getId()).isNotEmpty();
+
+      }
+   }
+
+   public void testDeleteStack() {
+      for (String region : api.getConfiguredRegions()) {
+         StackApi stackApi = api.getStackApi(region);
+         String stackName = getName();
+         CreateStack createStack = CreateStack.builder().name(stackName).templateUrl(TEMPLATE_URL).build();
+         Stack stack = stackApi.create(createStack);
+         assertThat(stack).isNotNull();
+         assertThat(stack.getId()).isNotEmpty();
+         assertThat(stackApi.delete(stackName, stack.getId())).isTrue();
+         Stack stackAfterDelete = stackApi.get(stackName, stack.getId());
+         assertThat(stackAfterDelete).isNotNull();
+      }
+   }
+
+   public void testCreateWithDisableRollback() {
+      for (String region : api.getConfiguredRegions()) {
+         StackApi stackApi = api.getStackApi(region);
+         CreateStack createStack = CreateStack.builder().name(getName()).templateUrl(TEMPLATE_URL).disableRollback(false).build();
+         Stack stack = stackApi.create(createStack);
+         assertThat(stack).isNotNull();
+         assertThat(stack.getId()).isNotEmpty();
+         assertThat(stack.isDisableRollback()).isFalse();
+
+      }
+   }
+
+   public void testCreateWithTemplate() throws ParseException {
+      for (String region : api.getConfiguredRegions()) {
+         StackApi stackApi = api.getStackApi(region);
+         JSONParser parser = new JSONParser();
+         Object obj = parser.parse(stringFromResource("/simple_stack.json"));
+
+         JSONObject jsonObject = (JSONObject) obj;
+
+         CreateStack createStack = CreateStack.builder().name(getName()).template(String.valueOf(jsonObject.get("template"))).build();
+         Stack stack = stackApi.create(createStack);
+         assertThat(stack).isNotNull();
+         assertThat(stack.getId()).isNotEmpty();
+      }
+   }
+
+   public void testUpdateStack() throws ParseException {
+      for (String region : api.getConfiguredRegions()) {
+         final StackApi stackApi = api.getStackApi(region);
+         JSONParser parser = new JSONParser();
+         Object obj = parser.parse(stringFromResource("/simple_stack.json"));
+
+         JSONObject jsonObject = (JSONObject) obj;
+
+         String stackName = getName();
+         CreateStack createStack = CreateStack.builder().name(stackName).template(String.valueOf(jsonObject.get("template"))).build();
+         Stack stack = stackApi.create(createStack);
+         assertThat(stack).isNotNull();
+         String stackId = stack.getId();
+         assertThat(stackId).isNotEmpty();
+
+         boolean success = retry(new Predicate<String>() {
+            public boolean apply(String stackId) {
+               return stackApi.get(stackId).getStatus() == StackStatus.CREATE_COMPLETE;
+            }
+
+         }, 60, 1, SECONDS).apply(stackId);
+
+         if (!success) {
+            Assert.fail("Stack didn't get to status CREATE_COMPLETE in 20m.");
+         }
+
+         UpdateStack updateOptions = UpdateStack.builder().templateUrl(TEMPLATE_URL).build();
+         assertThat(stackApi.update(stackName, stack.getId(), updateOptions)).isTrue();
+         Stack stackAfterUpdate = stackApi.get(stackName, stack.getId());
+         assertThat(stackAfterUpdate.getStatus()).isEqualTo(StackStatus.UPDATE_IN_PROGRESS);
+
+      }
+   }
+
+   public void testCreateWithParameters() throws ParseException {
+      for (String region : api.getConfiguredRegions()) {
+         StackApi stackApi = api.getStackApi(region);
+         Map<String, Object> parameters = new HashMap<String, Object>();
+         parameters.put("key_name", "myKey");
+
+         JSONParser parser = new JSONParser();
+         Object obj = parser.parse(stringFromResource("/stack_with_parameters.json"));
+
+         JSONObject jsonObject = (JSONObject) obj;
+
+         CreateStack createStack = CreateStack.builder().name(getName()).template(String.valueOf(jsonObject.get("template")))
+               .parameters(parameters).build();
+         Stack stack = stackApi.create(createStack);
+         assertThat(stack).isNotNull();
+         assertThat(stack.getId()).isNotEmpty();
+
+         Stack stackFromList = stackApi.get(stack.getId());
+         assertThat(stackFromList).isNotNull();
+         assertThat(stackFromList.getId()).isEqualTo(stack.getId());
+         for (String parmName : parameters.keySet()) {
+            assertThat(stackFromList.getParameters().containsKey(parmName)).isTrue();
+         }
+
+      }
+   }
+
+   public void testCreateWithFilesAndEnvironment() throws IOException, ParseException {
+      for (String region : api.getConfiguredRegions()) {
+         StackApi stackApi = api.getStackApi(region);
+         JSONParser parser = new JSONParser();
+         Object obj = parser.parse(stringFromResource("/stack_with_environment_and_files.json"));
+         JSONObject jsonObject = (JSONObject) obj;
+
+         Object objFilesJsone = parser.parse(stringFromResource("/files_for_atck_template.json"));
+         JSONObject filesAsJason = (JSONObject) objFilesJsone;
+         Map<String, String> files = new HashMap<String, String>();
+         files.put("VolumeB.template.yaml", String.valueOf(filesAsJason.get("VolumeB.template.yaml")));
+
+         CreateStack createStack = CreateStack.builder().name(getName())
+               .template(String.valueOf(jsonObject.get("template")))
+               .environment(String.valueOf(jsonObject.get("environment")))
+               .files(files).build();
+
+         Stack stack = stackApi.create(createStack);
+         assertThat(stack).isNotNull();
+         assertThat(stack.getId()).isNotEmpty();
+      }
+   }
+
+   public void testListAndGetStackResources() throws ParseException {
+      for (String region : api.getConfiguredRegions()) {
+         StackApi stackApi = api.getStackApi(region);
+         JSONParser parser = new JSONParser();
+         Object obj = parser.parse(stringFromResource("/stack_with_parameters.json"));
+
+         JSONObject jsonObject = (JSONObject) obj;
+         Map<String, Object> parameters = new HashMap<String, Object>();
+         parameters.put("key_name", "myKey");
+
+         String stackName = getName();
+         CreateStack createStack = CreateStack.builder().name(stackName).template(String.valueOf(jsonObject.get("template"))).parameters(parameters).build();
+         Stack stack = stackApi.create(createStack);
+         assertThat(stack).isNotNull();
+         assertThat(stack.getId()).isNotEmpty();
+         List<StackResource> resources = stackApi.listStackResources(stackName, stack.getId());
+         assertThat(resources).isNotNull();
+         assertThat(resources).isNotEmpty();
+         for (StackResource stackResource : resources) {
+            assertThat(stackResource).isNotNull();
+            String stackResourceName = stackResource.getName();
+            assertThat(stackResourceName).isNotNull();
+            assertThat(stackResourceName).isNotEmpty();
+            assertThat(stackResource.getStatus()).isNotEqualTo(StackResourceStatus.UNRECOGNIZED);
+            StackResource resourceFromGet = stackApi.getStackResource(stackName, stack.getId(), stackResourceName);
+            assertThat(resourceFromGet).isNotNull();
+            assertThat(resourceFromGet.getName()).isEqualTo(stackResourceName);
+         }
+      }
+   }
+
+   public void testGetStackResourceMetadata() throws ParseException {
+      for (String region : api.getConfiguredRegions()) {
+         StackApi stackApi = api.getStackApi(region);
+         JSONParser parser = new JSONParser();
+         Object obj = parser.parse(stringFromResource("/simple_stack.json"));
+
+         JSONObject jsonObject = (JSONObject) obj;
+
+         String stackName = getName();
+         CreateStack createStack = CreateStack.builder().name(stackName).template(String.valueOf(jsonObject.get("template"))).build();
+         Stack stack = stackApi.create(createStack);
+         assertThat(stack).isNotNull();
+         assertThat(stack.getId()).isNotEmpty();
+         List<StackResource> resources = stackApi.listStackResources(stackName, stack.getId());
+         assertThat(resources).isNotNull();
+         assertThat(resources).isNotEmpty();
+         for (StackResource stackResource : resources) {
+            assertThat(stackResource).isNotNull();
+            String stackResourceName = stackResource.getName();
+            assertThat(stackResourceName).isNotNull();
+            assertThat(stackResourceName).isNotEmpty();
+            assertThat(stackResource.getStatus()).isNotEqualTo(StackResourceStatus.UNRECOGNIZED);
+            Map metadata = stackApi.getStackResourceMetadata(stackName, stack.getId(), stackResourceName);
+            assertThat(metadata).isNotNull();
+            assertThat(metadata).isNotEmpty();
+         }
+      }
+   }
+
+ @AfterClass
+ public void cleanup(){
+
+    for (String region : api.getConfiguredRegions()) {
+       StackApi stackApi = api.getStackApi(region);
+       List<Stack> stacks = stackApi.list();
+       for (Stack stack : stacks){
+          if (stack.getName().startsWith(stackName)){
+             stackApi.delete(stack.getName(), stack.getId());
+          }
+       }
+    }
+
+ }
+
+   private String getName() {
+      return stackName + "_" + System.currentTimeMillis();
+   }
+
+   private String stringFromResource(String resourceName) {
+      try {
+         return Strings2.toStringAndClose(getClass().getResourceAsStream(resourceName));
+      } catch (IOException e) {
+         throw Throwables.propagate(e);
+      }
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/ce4d93f8/openstack-heat/src/test/java/org/jclouds/openstack/heat/v1/features/StackApiMockTest.java
----------------------------------------------------------------------
diff --git a/openstack-heat/src/test/java/org/jclouds/openstack/heat/v1/features/StackApiMockTest.java b/openstack-heat/src/test/java/org/jclouds/openstack/heat/v1/features/StackApiMockTest.java
new file mode 100644
index 0000000..54ce022
--- /dev/null
+++ b/openstack-heat/src/test/java/org/jclouds/openstack/heat/v1/features/StackApiMockTest.java
@@ -0,0 +1,542 @@
+/*
+ * 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.jclouds.openstack.heat.v1.features;
+
+import com.squareup.okhttp.mockwebserver.MockResponse;
+import com.squareup.okhttp.mockwebserver.MockWebServer;
+import org.jclouds.openstack.heat.v1.HeatApi;
+import org.jclouds.openstack.heat.v1.domain.Stack;
+import org.jclouds.openstack.heat.v1.domain.StackResource;
+import org.jclouds.openstack.heat.v1.internal.BaseHeatApiMockTest;
+import org.jclouds.openstack.heat.v1.options.CreateStack;
+import org.jclouds.openstack.heat.v1.options.ListStackOptions;
+import org.jclouds.openstack.heat.v1.options.UpdateStack;
+import org.testng.annotations.Test;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+
+/**
+ * Tests annotation parsing of {@code StackApi}
+ */
+@Test(groups = "unit", testName = "StackApiMockTest")
+public class StackApiMockTest extends BaseHeatApiMockTest {
+
+   public static final String TEST_STACK_NAME = "testStack";
+   public static final String TEST_STACK_ID = "testStack";
+   public static final String RESOURCES_TEST_NAME = "testResources";
+   public static final String TEST_STACK_RESOURCE_NAME = "cinder_volume";
+
+
+   public void testGetAutoStack() throws Exception {
+      MockWebServer server = mockOpenStackServer();
+      server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
+      server.enqueue(addCommonHeaders(
+            new MockResponse().setResponseCode(200).setBody(stringFromResource("/stack_get_response.json"))));
+
+      try {
+         HeatApi heatApi = api(server.getUrl("/").toString(), "openstack-heat", overrides);
+         StackApi api = heatApi.getStackApi("RegionOne");
+
+         Stack stack = api.get("simple_stack", "3095aefc-09fb-4bc7-b1f0-f21a304e864c");
+
+         assertThat(server.getRequestCount()).isEqualTo(2);
+         assertAuthentication(server);
+         assertRequest(server.takeRequest(), "GET", BASE_URI + "/stacks/simple_stack/3095aefc-09fb-4bc7-b1f0-f21a304e864c");
+
+
+         assertThat(stack).isNotNull();
+      } finally {
+         server.shutdown();
+      }
+   }
+
+   public void testGetStackFail() throws Exception {
+      MockWebServer server = mockOpenStackServer();
+      server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
+      server.enqueue(addCommonHeaders(new MockResponse().setResponseCode(404)));
+
+      try {
+         HeatApi heatApi = api(server.getUrl("/").toString(), "openstack-heat", overrides);
+         StackApi api = heatApi.getStackApi("RegionOne");
+
+         Stack stack = api.get("Non_Existing_Stack", "Non-Existing-Stack");
+
+         assertThat(server.getRequestCount()).isEqualTo(2);
+         assertAuthentication(server);
+         assertRequest(server.takeRequest(), "GET", BASE_URI + "/stacks/Non_Existing_Stack/Non-Existing-Stack");
+
+         assertNull(stack);
+
+      } finally {
+         server.shutdown();
+      }
+   }
+
+   public void testGeStackWithIDOnly() throws Exception {
+      MockWebServer server = mockOpenStackServer();
+      server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
+      server.enqueue(addCommonHeaders(
+            new MockResponse().setResponseCode(200).setBody(stringFromResource("/stack_get_response.json"))));
+
+      try {
+         HeatApi heatApi = api(server.getUrl("/").toString(), "openstack-heat", overrides);
+         StackApi api = heatApi.getStackApi("RegionOne");
+
+         Stack stack = api.get("3095aefc-09fb-4bc7-b1f0-f21a304e864c");
+
+         assertThat(server.getRequestCount()).isEqualTo(2);
+         assertAuthentication(server);
+         assertRequest(server.takeRequest(), "GET", BASE_URI + "/stacks/3095aefc-09fb-4bc7-b1f0-f21a304e864c");
+
+
+         assertThat(stack).isNotNull();
+      } finally {
+         server.shutdown();
+      }
+   }
+
+   public void testGetStackWithIDOnlyFail() throws Exception {
+      MockWebServer server = mockOpenStackServer();
+      server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
+      server.enqueue(addCommonHeaders(new MockResponse().setResponseCode(404)));
+
+      try {
+         HeatApi heatApi = api(server.getUrl("/").toString(), "openstack-heat", overrides);
+         StackApi api = heatApi.getStackApi("RegionOne");
+
+         Stack stack = api.get("Non-Existing-Stack");
+
+         assertThat(server.getRequestCount()).isEqualTo(2);
+         assertAuthentication(server);
+         assertRequest(server.takeRequest(), "GET", BASE_URI + "/stacks/Non-Existing-Stack");
+
+         assertNull(stack);
+
+      } finally {
+         server.shutdown();
+      }
+   }
+
+   public void testGetStackResourceFail() throws Exception {
+      MockWebServer server = mockOpenStackServer();
+      server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
+      server.enqueue(addCommonHeaders(new MockResponse().setResponseCode(404)));
+
+      try {
+         HeatApi heatApi = api(server.getUrl("/").toString(), "openstack-heat", overrides);
+         StackApi api = heatApi.getStackApi("RegionOne");
+
+         StackResource stackResource = api.getStackResource(TEST_STACK_NAME, TEST_STACK_ID, "Non_Existing_Stack_resource");
+
+         assertThat(server.getRequestCount()).isEqualTo(2);
+         assertAuthentication(server);
+         assertRequest(server.takeRequest(), "GET", BASE_URI + "/stacks/" + TEST_STACK_NAME + "/" + TEST_STACK_ID + "/resources/Non_Existing_Stack_resource");
+
+         assertNull(stackResource);
+
+      } finally {
+         server.shutdown();
+      }
+   }
+
+
+   public void testList() throws Exception {
+      MockWebServer server = mockOpenStackServer();
+      server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
+      server.enqueue(addCommonHeaders(
+            new MockResponse().setResponseCode(200).setBody(stringFromResource("/stack_list_response.json"))));
+
+      try {
+         HeatApi heatApi = api(server.getUrl("/").toString(), "openstack-heat", overrides);
+         StackApi api = heatApi.getStackApi("RegionOne");
+
+         List<Stack> stacks = api.list();
+
+         /*
+          * Check request
+          */
+         assertThat(server.getRequestCount()).isEqualTo(2);
+         assertAuthentication(server);
+         assertRequest(server.takeRequest(), "GET", BASE_URI + "/stacks");
+
+         /*
+          * Check response
+          */
+         assertThat(stacks).isNotEmpty();
+         assertThat(stacks.size()).isEqualTo(1);
+
+      } finally {
+         server.shutdown();
+      }
+   }
+
+   public void testListWithOptions() throws Exception {
+      MockWebServer server = mockOpenStackServer();
+      server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
+      server.enqueue(addCommonHeaders(
+            new MockResponse().setResponseCode(200).setBody(stringFromResource("/stack_list_response.json"))));
+
+      try {
+         HeatApi heatApi = api(server.getUrl("/").toString(), "openstack-heat", overrides);
+         StackApi api = heatApi.getStackApi("RegionOne");
+
+         ListStackOptions options = ListStackOptions.Builder.name("simple_stack");
+
+         List<Stack> stacks = api.list(options);
+
+         /*
+          * Check request
+          */
+         assertThat(server.getRequestCount()).isEqualTo(2);
+         assertAuthentication(server);
+         assertRequest(server.takeRequest(), "GET", BASE_URI + "/stacks?name=simple_stack");
+
+         /*
+          * Check response
+          */
+         assertThat(stacks).isNotEmpty();
+         assertThat(stacks.size()).isEqualTo(1);
+
+      } finally {
+         server.shutdown();
+      }
+   }
+
+   public void testListResource() throws Exception {
+      MockWebServer server = mockOpenStackServer();
+      server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
+      server.enqueue(addCommonHeaders(
+            new MockResponse().setResponseCode(200).setBody(stringFromResource("/stack_resources_list_response.json"))));
+
+      try {
+         HeatApi heatApi = api(server.getUrl("/").toString(), "openstack-heat", overrides);
+         StackApi api = heatApi.getStackApi("RegionOne");
+
+         List<StackResource> stackResources = api.listStackResources(TEST_STACK_NAME, TEST_STACK_ID);
+
+         /*
+          * Check request
+          */
+         assertThat(server.getRequestCount()).isEqualTo(2);
+         assertAuthentication(server);
+         assertRequest(server.takeRequest(), "GET", BASE_URI + "/stacks/" + TEST_STACK_NAME + "/" + TEST_STACK_ID + "/resources");
+
+         /*
+          * Check response
+          */
+         assertThat(stackResources).isNotEmpty();
+         assertThat(stackResources.size()).isEqualTo(1);
+
+      } finally {
+         server.shutdown();
+      }
+   }
+
+   public void testGetResource() throws Exception {
+      MockWebServer server = mockOpenStackServer();
+      server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
+      server.enqueue(addCommonHeaders(
+            new MockResponse().setResponseCode(200).setBody(stringFromResource("/stack_resources_get_response.json"))));
+
+      try {
+         HeatApi heatApi = api(server.getUrl("/").toString(), "openstack-heat", overrides);
+         StackApi api = heatApi.getStackApi("RegionOne");
+
+         StackResource stackResource = api.getStackResource(TEST_STACK_NAME, TEST_STACK_ID, TEST_STACK_RESOURCE_NAME);
+
+         /*
+          * Check request
+          */
+         assertThat(server.getRequestCount()).isEqualTo(2);
+         assertAuthentication(server);
+         assertRequest(server.takeRequest(), "GET", BASE_URI + "/stacks/" + TEST_STACK_NAME + "/" + TEST_STACK_ID + "/resources/" + TEST_STACK_RESOURCE_NAME);
+
+         /*
+          * Check response
+          */
+         assertThat(stackResource).isNotNull();
+         assertThat(stackResource.getName()).isNotNull();
+         assertThat(stackResource.getName()).isNotEmpty();
+         assertThat(stackResource.getLogicalResourceId()).isNotNull();
+         assertThat(stackResource.getLogicalResourceId()).isNotEmpty();
+         assertThat(stackResource.getPhysicalResourceId()).isNotNull();
+         assertThat(stackResource.getPhysicalResourceId()).isNotEmpty();
+
+      } finally {
+         server.shutdown();
+      }
+   }
+
+   public void testListIsEmpty() throws Exception {
+      MockWebServer server = mockOpenStackServer();
+      server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
+      server.enqueue(addCommonHeaders(new MockResponse().setResponseCode(404)));
+
+      try {
+         HeatApi heatApi = api(server.getUrl("/").toString(), "openstack-heat", overrides);
+         StackApi api = heatApi.getStackApi("RegionOne");
+
+         List<Stack> stacks = api.list();
+
+         /*
+          * Check request
+          */
+         assertThat(server.getRequestCount()).isEqualTo(2);
+         assertAuthentication(server);
+         assertRequest(server.takeRequest(), "GET", BASE_URI + "/stacks");
+
+         /*
+          * Check response
+          */
+         assertThat(stacks).isEmpty();
+      } finally {
+         server.shutdown();
+      }
+   }
+
+   public void testListWithOptionsIsEmpty() throws Exception {
+      MockWebServer server = mockOpenStackServer();
+      server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
+      server.enqueue(addCommonHeaders(new MockResponse().setResponseCode(404)));
+
+      try {
+         HeatApi heatApi = api(server.getUrl("/").toString(), "openstack-heat", overrides);
+         StackApi api = heatApi.getStackApi("RegionOne");
+
+         ListStackOptions options = ListStackOptions.Builder.name("Stack_dont_exist");
+         List<Stack> stacks = api.list(options);
+
+         /*
+          * Check request
+          */
+         assertThat(server.getRequestCount()).isEqualTo(2);
+         assertAuthentication(server);
+         assertRequest(server.takeRequest(), "GET", BASE_URI + "/stacks?name=Stack_dont_exist");
+
+         /*
+          * Check response
+          */
+         assertThat(stacks).isEmpty();
+      } finally {
+         server.shutdown();
+      }
+   }
+
+   public void testListStackResourceIsEmpty() throws Exception {
+      MockWebServer server = mockOpenStackServer();
+      server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
+      server.enqueue(addCommonHeaders(new MockResponse().setResponseCode(404)));
+
+      try {
+         HeatApi heatApi = api(server.getUrl("/").toString(), "openstack-heat", overrides);
+         StackApi api = heatApi.getStackApi("RegionOne");
+
+         List<StackResource> stackResources = api.listStackResources("empty_stack", "empty_stack_id");
+
+         /*
+          * Check request
+          */
+         assertThat(server.getRequestCount()).isEqualTo(2);
+         assertAuthentication(server);
+         assertRequest(server.takeRequest(), "GET", BASE_URI + "/stacks/empty_stack/empty_stack_id/resources");
+
+         /*
+          * Check response
+          */
+         assertThat(stackResources).isEmpty();
+      } finally {
+         server.shutdown();
+      }
+   }
+
+   public void testCreateWithTemplateUrl() throws Exception {
+      MockWebServer server = mockOpenStackServer();
+      server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
+      server.enqueue(addCommonHeaders(new MockResponse().setResponseCode(200).setBody(stringFromResource("/create_stack.json"))));
+
+      try {
+         HeatApi heatApi = api(server.getUrl("/").toString(), "openstack-heat", overrides);
+         StackApi api = heatApi.getStackApi("RegionOne");
+
+         Map<String, String> parameters = new HashMap<String, String>();
+         parameters.put("key_name", "myKey");
+         parameters.put("image_id", "3b7be1fa-d381-4067-bb81-e835df630564");
+         parameters.put("instance_type", "SMALL_1");
+         CreateStack createStack = CreateStack.builder().name(TEST_STACK_NAME).templateUrl("http://10.5.5.121/Installs/cPaaS/YAML/simple_stack.yaml").build();
+         Stack stack = api.create(createStack);
+
+         /*
+          * Check request
+          */
+         assertThat(server.getRequestCount()).isEqualTo(2);
+         assertAuthentication(server);
+         assertRequest(server.takeRequest(), "POST", BASE_URI + "/stacks");
+
+         /*
+          * Check response
+          */
+         assertThat(stack).isNotNull();
+         assertThat(stack.getId()).isEqualTo("3095aefc-09fb-4bc7-b1f0-f21a304e864c");
+
+      } finally {
+         server.shutdown();
+      }
+   }
+
+   public void testDeleteStack() throws IOException, InterruptedException {
+      MockWebServer server = mockOpenStackServer();
+      server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
+      server.enqueue(addCommonHeaders(new MockResponse().setResponseCode(200)));
+
+      try {
+         HeatApi heatApi = api(server.getUrl("/").toString(), "openstack-heat", overrides);
+         StackApi api = heatApi.getStackApi("RegionOne");
+
+         boolean result = api.delete(TEST_STACK_NAME, TEST_STACK_ID);
+
+         /*
+          * Check request
+          */
+         assertEquals(server.getRequestCount(), 2);
+         assertAuthentication(server);
+         assertRequest(server.takeRequest(), "DELETE", BASE_URI + "/stacks/" + TEST_STACK_NAME + "/" + TEST_STACK_ID);
+
+         /*
+          * Check response
+          */
+         assertTrue(result);
+      } finally {
+         server.shutdown();
+      }
+   }
+
+   public void testDeleteReturnFalseOn404Stack() throws IOException, InterruptedException {
+      MockWebServer server = mockOpenStackServer();
+      server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
+      server.enqueue(addCommonHeaders(new MockResponse().setResponseCode(404)));
+
+      try {
+         HeatApi heatApi = api(server.getUrl("/").toString(), "openstack-heat", overrides);
+         StackApi api = heatApi.getStackApi("RegionOne");
+
+         boolean result = api.delete("Non-Existing-Stack-Name", "Non-Existing-Stack-ID");
+
+         /*
+          * Check request
+          */
+         assertEquals(server.getRequestCount(), 2);
+         assertAuthentication(server);
+         assertRequest(server.takeRequest(), "DELETE", BASE_URI + "/stacks/Non-Existing-Stack-Name/Non-Existing-Stack-ID");
+
+         /*
+          * Check response
+          */
+         assertFalse(result);
+      } finally {
+         server.shutdown();
+      }
+   }
+
+   public void testUpdateStack() throws IOException, InterruptedException {
+      MockWebServer server = mockOpenStackServer();
+      server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
+      server.enqueue(addCommonHeaders(new MockResponse().setResponseCode(202)));
+
+      try {
+         HeatApi heatApi = api(server.getUrl("/").toString(), "openstack-heat", overrides);
+         StackApi api = heatApi.getStackApi("RegionOne");
+
+         UpdateStack updateOptions = UpdateStack.builder().templateUrl("http://10.5.5.121/Installs/cPaaS/YAML/simple_stack.yaml").build();
+
+         boolean result = api.update(TEST_STACK_NAME, TEST_STACK_ID, updateOptions);
+
+         /*
+          * Check request
+          */
+         assertEquals(server.getRequestCount(), 2);
+         assertAuthentication(server);
+         assertRequest(server.takeRequest(), "PUT", BASE_URI + "/stacks/" + TEST_STACK_NAME + "/" + TEST_STACK_ID);
+
+         /*
+          * Check response
+          */
+         assertTrue(result);
+      } finally {
+         server.shutdown();
+      }
+   }
+
+   public void testResourcesMetadata() throws IOException, InterruptedException {
+      MockWebServer server = mockOpenStackServer();
+      server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
+      server.enqueue(addCommonHeaders(new MockResponse().setResponseCode(200).setBody(stringFromResource("/resources_metadata.json"))));
+
+      try {
+         HeatApi heatApi = api(server.getUrl("/").toString(), "openstack-heat", overrides);
+         StackApi api = heatApi.getStackApi("RegionOne");
+
+         Map<String, Object> metadata = api.getStackResourceMetadata(TEST_STACK_NAME, TEST_STACK_ID, RESOURCES_TEST_NAME);
+
+         /*
+          * Check request
+          */
+         assertEquals(server.getRequestCount(), 2);
+         assertAuthentication(server);
+         assertRequest(server.takeRequest(), "GET", BASE_URI + "/stacks/" + TEST_STACK_NAME + "/" + TEST_STACK_ID + "/resources/" + RESOURCES_TEST_NAME + "/metadata");
+         assertThat(metadata).isNotEmpty();
+
+
+      } finally {
+         server.shutdown();
+      }
+   }
+
+   public void testResourcesMetadataReturnNullOn404() throws IOException, InterruptedException {
+      MockWebServer server = mockOpenStackServer();
+      server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
+      server.enqueue(addCommonHeaders(new MockResponse().setResponseCode(404)));
+
+      try {
+         HeatApi heatApi = api(server.getUrl("/").toString(), "openstack-heat", overrides);
+         StackApi api = heatApi.getStackApi("RegionOne");
+
+         Map<String, Object> metadata = api.getStackResourceMetadata(TEST_STACK_NAME, TEST_STACK_ID, "Stack_Resource_dont_exist");
+
+         /*
+          * Check request
+          */
+         assertEquals(server.getRequestCount(), 2);
+         assertAuthentication(server);
+         assertRequest(server.takeRequest(), "GET", BASE_URI + "/stacks/" + TEST_STACK_NAME + "/" + TEST_STACK_ID + "/resources/Stack_Resource_dont_exist/metadata");
+         assertThat(metadata).isEmpty();
+
+
+      } finally {
+         server.shutdown();
+      }
+   }
+}
+

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/ce4d93f8/openstack-heat/src/test/java/org/jclouds/openstack/heat/v1/options/ListStackOptionsTest.java
----------------------------------------------------------------------
diff --git a/openstack-heat/src/test/java/org/jclouds/openstack/heat/v1/options/ListStackOptionsTest.java b/openstack-heat/src/test/java/org/jclouds/openstack/heat/v1/options/ListStackOptionsTest.java
new file mode 100644
index 0000000..066f95e
--- /dev/null
+++ b/openstack-heat/src/test/java/org/jclouds/openstack/heat/v1/options/ListStackOptionsTest.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.jclouds.openstack.heat.v1.options;
+
+import com.google.common.collect.ImmutableSet;
+import org.jclouds.openstack.heat.v1.domain.StackStatus;
+import org.jclouds.openstack.heat.v1.options.ListStackOptions.SortDirection;
+import org.jclouds.openstack.heat.v1.options.ListStackOptions.SortKey;
+import org.testng.annotations.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.jclouds.openstack.heat.v1.options.ListStackOptions.Builder.globalTenant;
+import static org.jclouds.openstack.heat.v1.options.ListStackOptions.Builder.limit;
+import static org.jclouds.openstack.heat.v1.options.ListStackOptions.Builder.marker;
+import static org.jclouds.openstack.heat.v1.options.ListStackOptions.Builder.name;
+import static org.jclouds.openstack.heat.v1.options.ListStackOptions.Builder.sortDirection;
+import static org.jclouds.openstack.heat.v1.options.ListStackOptions.Builder.sortKey;
+import static org.jclouds.openstack.heat.v1.options.ListStackOptions.Builder.status;
+
+/**
+ * Tests behavior of {@code ListVirtualMachinesOptions}
+ */
+@Test(groups = "unit")
+public class ListStackOptionsTest {
+
+   public void testLimit() {
+      ListStackOptions options = new ListStackOptions().limit(42);
+      assertThat(options.buildQueryParameters().get("limit")).isEqualTo(ImmutableSet.of("42"));
+   }
+
+   public void testLimitStatic() {
+      ListStackOptions options = limit(42);
+      assertThat(options.buildQueryParameters().get("limit")).isEqualTo(ImmutableSet.of("42"));
+   }
+
+   public void testMarker() {
+      ListStackOptions options = new ListStackOptions().marker("deploy");
+      assertThat(options.buildQueryParameters().get("marker")).isEqualTo(ImmutableSet.of("deploy"));
+   }
+
+   public void testMarkerStatic() {
+      ListStackOptions options = marker("deploy");
+      assertThat(options.buildQueryParameters().get("marker")).isEqualTo(ImmutableSet.of("deploy"));
+   }
+
+   public void testStatus() {
+      ListStackOptions options = new ListStackOptions().status(StackStatus.CREATE_COMPLETE);
+      assertThat(options.buildQueryParameters().get("status"))
+            .isEqualTo(ImmutableSet.of(StackStatus.CREATE_COMPLETE.toString()));
+   }
+
+   public void testStatusStatic() {
+      ListStackOptions options = status(StackStatus.CREATE_COMPLETE);
+      assertThat(options.buildQueryParameters().get("status"))
+            .isEqualTo(ImmutableSet.of(StackStatus.CREATE_COMPLETE.toString()));
+   }
+
+   public void testName() {
+      ListStackOptions options = new ListStackOptions().name("deployment");
+      assertThat(options.buildQueryParameters().get("name")).isEqualTo(ImmutableSet.of("deployment"));
+   }
+
+   public void testNameStatic() {
+      ListStackOptions options = name("deployment");
+      assertThat(options.buildQueryParameters().get("name")).isEqualTo(ImmutableSet.of("deployment"));
+   }
+
+   public void testSortKey() {
+      ListStackOptions options = new ListStackOptions().sortKey(SortKey.NAME);
+      assertThat(options.buildQueryParameters().get("sort_keys")).isEqualTo(ImmutableSet.of(SortKey.NAME.toString()));
+   }
+
+   public void testSortKeyStatic() {
+      ListStackOptions options = sortKey(SortKey.NAME);
+      assertThat(options.buildQueryParameters().get("sort_keys")).isEqualTo(ImmutableSet.of(SortKey.NAME.toString()));
+   }
+
+   public void testSortDirection() {
+      ListStackOptions options = new ListStackOptions().sortDirection(SortDirection.ASCENDING);
+      assertThat(options.buildQueryParameters().get("sort_dir"))
+            .isEqualTo(ImmutableSet.of(SortDirection.ASCENDING.toString()));
+   }
+
+   public void testSortDirectionStatic() {
+      ListStackOptions options = sortDirection(SortDirection.DESCENDING);
+      assertThat(options.buildQueryParameters().get("sort_dir"))
+            .isEqualTo(ImmutableSet.of(SortDirection.DESCENDING.toString()));
+   }
+
+   public void testGlobalTenant() {
+      ListStackOptions options = globalTenant(true);
+      assertThat(options.buildQueryParameters().get("global_tenant"))
+            .isEqualTo(ImmutableSet.of("true"));
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/ce4d93f8/openstack-heat/src/test/resources/create_stack.json
----------------------------------------------------------------------
diff --git a/openstack-heat/src/test/resources/create_stack.json b/openstack-heat/src/test/resources/create_stack.json
new file mode 100644
index 0000000..18a03ed
--- /dev/null
+++ b/openstack-heat/src/test/resources/create_stack.json
@@ -0,0 +1,11 @@
+{
+    "stack": {
+        "id": "3095aefc-09fb-4bc7-b1f0-f21a304e864c",
+        "links": [
+            {
+                "href": "http://192.168.123.200:8004/v1/eb1c63a4f77141548385f113a28f0f52/stacks/simple_stack/3095aefc-09fb-4bc7-b1f0-f21a304e864c",
+                "rel": "self"
+            }
+        ]
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/ce4d93f8/openstack-heat/src/test/resources/files_for_atck_template.json
----------------------------------------------------------------------
diff --git a/openstack-heat/src/test/resources/files_for_atck_template.json b/openstack-heat/src/test/resources/files_for_atck_template.json
new file mode 100644
index 0000000..c0b20de
--- /dev/null
+++ b/openstack-heat/src/test/resources/files_for_atck_template.json
@@ -0,0 +1,33 @@
+{
+    "VolumeB.template.yaml": {
+        "resources": {
+            "Block-StorageB": {
+                "properties": {
+                    "description": "Used for VM \\/storage partition.",
+                    "name": "voldB",
+                    "size": {
+                        "get_param": "storage_size"
+                    }
+                },
+                "type": "OS::Cinder::Volume"
+            }
+        },
+        "heat_template_version": "2013-05-23",
+        "description": "Template to set up volumes for the initial deployment",
+        "parameters": {
+            "storage_size": {
+                "default": "4",
+                "description": "GBs for block storage",
+                "type": "number"
+            }
+        },
+        "outputs": {
+            "volumeId": {
+                "description": "VolumeId",
+                "value": {
+                    "get_resource": "Block-StorageB"
+                }
+            }
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/ce4d93f8/openstack-heat/src/test/resources/resources_metadata.json
----------------------------------------------------------------------
diff --git a/openstack-heat/src/test/resources/resources_metadata.json b/openstack-heat/src/test/resources/resources_metadata.json
new file mode 100644
index 0000000..b43b6b6
--- /dev/null
+++ b/openstack-heat/src/test/resources/resources_metadata.json
@@ -0,0 +1,6 @@
+{
+    "metadata": {
+        "some_other_key": "some_other_value",
+        "some_key": "some_value"
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/ce4d93f8/openstack-heat/src/test/resources/simple_stack.json
----------------------------------------------------------------------
diff --git a/openstack-heat/src/test/resources/simple_stack.json b/openstack-heat/src/test/resources/simple_stack.json
new file mode 100644
index 0000000..22fd8bb
--- /dev/null
+++ b/openstack-heat/src/test/resources/simple_stack.json
@@ -0,0 +1,21 @@
+{
+    "template": {
+        "heat_template_version": "2013-05-23",
+
+        "description": "HOT template to deploy volume",
+
+        "resources": {
+            "cinder_volume": {
+                "type": "OS::Cinder::Volume",
+                "properties": {
+                    "size": "1"
+                },
+                "metadata": {
+                    "some_key": "some_value",
+                    "some_other_key": 5,
+                    "more_key": null
+                }
+            }
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/ce4d93f8/openstack-heat/src/test/resources/stack_get_response.json
----------------------------------------------------------------------
diff --git a/openstack-heat/src/test/resources/stack_get_response.json b/openstack-heat/src/test/resources/stack_get_response.json
new file mode 100644
index 0000000..cce3ff3
--- /dev/null
+++ b/openstack-heat/src/test/resources/stack_get_response.json
@@ -0,0 +1,27 @@
+{
+    "stack": {
+        "capabilities": [],
+        "creation_time": "2014-06-03T20:59:46Z",
+        "description": "sample stack",
+        "disable_rollback": true,
+        "id": "3095aefc-09fb-4bc7-b1f0-f21a304e864c",
+        "links": [
+            {
+                "href": "http://192.168.123.200:8004/v1/eb1c63a4f77141548385f113a28f0f52/stacks/simple_stack/3095aefc-09fb-4bc7-b1f0-f21a304e864c",
+                "rel": "self"
+            }
+        ],
+        "notification_topics": [],
+        "outputs": [],
+        "parameters": {
+            "OS::stack_id": "3095aefc-09fb-4bc7-b1f0-f21a304e864c",
+            "OS::stack_name": "simple_stack"
+        },
+        "stack_name": "simple_stack",
+        "stack_status": "CREATE_COMPLETE",
+        "stack_status_reason": "Stack CREATE completed successfully",
+        "template_description": "sample stack",
+        "timeout_mins": 15,
+        "updated_time": "2014-06-12T20:59:46Z"
+    }
+}
\ No newline at end of file