You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jclouds.apache.org by na...@apache.org on 2016/06/17 20:58:17 UTC

[1/3] jclouds-labs git commit: JCLOUDS-664 Azurecompute-arm compute service

Repository: jclouds-labs
Updated Branches:
  refs/heads/master e4cbe223b -> 88ef0a221


http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/03f72d5e/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/util/DeploymentTemplateBuilder.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/util/DeploymentTemplateBuilder.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/util/DeploymentTemplateBuilder.java
index ffa533e..ed5ec9e 100644
--- a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/util/DeploymentTemplateBuilder.java
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/util/DeploymentTemplateBuilder.java
@@ -19,6 +19,7 @@ package org.jclouds.azurecompute.arm.util;
 import com.google.common.collect.ImmutableMap;
 import com.google.inject.assistedinject.Assisted;
 
+import org.jclouds.azurecompute.arm.compute.config.AzureComputeServiceContextModule;
 import org.jclouds.azurecompute.arm.domain.DeploymentProperties;
 import org.jclouds.compute.options.TemplateOptions;
 import org.jclouds.azurecompute.arm.domain.DataDisk;
@@ -72,12 +73,11 @@ public class DeploymentTemplateBuilder {
    private TemplateOptions options;
    private List<ResourceDefinition> resources;
    private Map<String, String> variables;
-   private String loginUser;
-   private String loginPassword;
+   private static String loginUser;
+   private static String loginPassword;
    private String location;
+   private AzureComputeServiceContextModule.AzureComputeConstants azureComputeConstants;
 
-   public static final String DEFAULT_LOGIN_USER = "jclouds";
-   public static final String DEFAULT_LOGIN_PASSWORD = "Password1!";
    private static final String DEPLOYMENT_MODE = "Incremental";
    private static final String DEFAULT_DATA_DISK_SIZE = "1023";
 
@@ -85,24 +85,37 @@ public class DeploymentTemplateBuilder {
    private static final String DEFAULT_subnetAddressPrefix = "10.0.0.0/24";
 
    @Inject
-   DeploymentTemplateBuilder(Json json, @Assisted("group") String group, @Assisted("name") String name, @Assisted Template template) {
+   DeploymentTemplateBuilder(Json json, @Assisted("group") String group, @Assisted("name") String name, @Assisted Template template,
+                             final AzureComputeServiceContextModule.AzureComputeConstants azureComputeConstants) {
       this.name = name;
       this.group = group;
       this.template = template;
       this.options = template.getOptions().as(TemplateOptions.class);
       this.variables = new HashMap<String, String>();
       this.resources = new ArrayList<ResourceDefinition>();
-      this.loginUser = options.getLoginUser() == null ? DEFAULT_LOGIN_USER : options.getLoginUser();
-      this.loginPassword = options.getLoginPassword() == null ? DEFAULT_LOGIN_PASSWORD : options.getLoginPassword();
       this.location = template.getLocation().getId();
       this.json = json;
+
+      this.azureComputeConstants = azureComputeConstants;
+
+      String[] defaultLogin = this.azureComputeConstants.azureDefaultImageLogin().split(":");
+      String defaultUser = null;
+      String defaultPassword = null;
+
+      if (defaultLogin.length == 2) {
+         defaultUser = defaultLogin[0].trim();
+         defaultPassword = defaultLogin[1].trim();
+      }
+
+      loginUser = options.getLoginUser() == null ? defaultUser : options.getLoginUser();
+      loginPassword = options.getLoginPassword() == null ? defaultPassword : options.getLoginPassword();
    }
 
-   public String getLoginUserUsername() {
+   public static String getLoginUserUsername() {
       return loginUser;
    }
 
-   public String getLoginPassword() {
+   public static String getLoginPassword() {
       return loginPassword;
    }
 

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/03f72d5e/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/compute/AzureComputeServiceContextLiveTest.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/compute/AzureComputeServiceContextLiveTest.java b/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/compute/AzureComputeServiceContextLiveTest.java
new file mode 100644
index 0000000..940f785
--- /dev/null
+++ b/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/compute/AzureComputeServiceContextLiveTest.java
@@ -0,0 +1,284 @@
+/*
+ * 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.azurecompute.arm.compute;
+
+import com.google.common.base.Preconditions;
+import com.google.common.base.Predicate;
+import com.google.inject.Module;
+import org.jclouds.azurecompute.arm.AzureComputeProviderMetadata;
+import org.jclouds.azurecompute.arm.internal.AzureLiveTestUtils;
+import org.jclouds.compute.RunNodesException;
+import org.jclouds.compute.RunScriptOnNodesException;
+import org.jclouds.compute.domain.ComputeMetadata;
+import org.jclouds.compute.domain.ExecResponse;
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.domain.OsFamily;
+import org.jclouds.compute.domain.Template;
+import org.jclouds.compute.domain.TemplateBuilder;
+import org.jclouds.compute.internal.BaseComputeServiceContextLiveTest;
+import org.jclouds.domain.Credentials;
+import org.jclouds.domain.LoginCredentials;
+import org.jclouds.providers.ProviderMetadata;
+import org.jclouds.sshj.config.SshjSshClientModule;
+import org.testng.annotations.Test;
+
+import java.util.Map;
+import java.util.Properties;
+import java.util.Random;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.RESOURCE_GROUP_NAME;
+import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_RUNNING;
+import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_SCRIPT_COMPLETE;
+
+import static org.jclouds.compute.predicates.NodePredicates.inGroup;
+import static org.jclouds.compute.options.TemplateOptions.Builder.overrideLoginCredentials;
+import static org.jclouds.scriptbuilder.domain.Statements.exec;
+import static org.testng.Assert.assertTrue;
+
+@Test(groups = "live", testName = "AzureComputeServiceContextLiveTest")
+public class AzureComputeServiceContextLiveTest extends BaseComputeServiceContextLiveTest {
+
+   public String azureGroup;
+   protected static final int RAND = new Random().nextInt(999);
+
+   @Override
+   protected Module getSshModule() {
+      return new SshjSshClientModule();
+   }
+
+   @Override protected Properties setupProperties() {
+      azureGroup = "jc" + RAND;
+
+      Properties properties = super.setupProperties();
+      long scriptTimeout = TimeUnit.MILLISECONDS.convert(60, TimeUnit.MINUTES);
+      properties.setProperty(TIMEOUT_SCRIPT_COMPLETE, scriptTimeout + "");
+      properties.setProperty(TIMEOUT_NODE_RUNNING, scriptTimeout + "");
+
+      AzureLiveTestUtils.defaultProperties(properties);
+      checkNotNull(setIfTestSystemPropertyPresent(properties, "oauth.endpoint"), "test.oauth.endpoint");
+
+      properties.put(RESOURCE_GROUP_NAME, azureGroup);
+      return properties;
+   }
+
+   public AzureComputeServiceContextLiveTest() {
+      provider = "azurecompute-arm";
+   }
+
+   @Test
+   public void testDefault() throws RunNodesException {
+
+      final String groupName = this.azureGroup;
+      final TemplateBuilder templateBuilder = view.getComputeService().templateBuilder();
+      templateBuilder.osFamily(OsFamily.UBUNTU);
+      templateBuilder.osVersionMatches("14.04");
+      templateBuilder.hardwareId("Standard_A0");
+      templateBuilder.locationId("westus");
+
+      final Template template = templateBuilder.build();
+
+      try {
+         Set<? extends NodeMetadata> nodes = view.getComputeService().createNodesInGroup(groupName, 1, template);
+         assertThat(nodes).hasSize(1);
+      } finally {
+// Do not destroy         view.getComputeService().destroyNodesMatching(inGroup(groupName));
+      }
+   }
+
+   private LoginCredentials getLogin() {
+      Credentials credentials = new Credentials("jclouds", "Password1!");
+      LoginCredentials login = LoginCredentials.fromCredentials(credentials);
+      return login;
+   }
+
+   @Test(dependsOnMethods = "testDefault")
+   public void testExec() throws RunScriptOnNodesException {
+      final String groupName = this.azureGroup;
+      String command = "echo hello";
+
+      Map<? extends NodeMetadata, ExecResponse> responses = view.getComputeService().runScriptOnNodesMatching(//
+              inGroup(groupName), // predicate used to select nodes
+              exec(command), // what you actually intend to run
+              overrideLoginCredentials(getLogin()) // use my local user &
+                      // ssh key
+                      .runAsRoot(false) // don't attempt to run as root (sudo)
+                      .wrapInInitScript(false)); // run command directly
+
+      assertTrue(responses.size() > 0);
+   }
+
+   public static Predicate<ComputeMetadata> nameStartsWith(final String prefix) {
+      Preconditions.checkNotNull(prefix, "prefix must be defined");
+
+      return new Predicate<ComputeMetadata>() {
+         @Override
+         public boolean apply(ComputeMetadata computeMetadata) {
+            return computeMetadata.getName().startsWith(prefix);
+         }
+
+         @Override
+         public String toString() {
+            return "nameStartsWith(" + prefix + ")";
+         }
+      };
+   }
+
+   @Test(dependsOnMethods = "testExec")
+   public void testStop() throws RunScriptOnNodesException {
+      final String groupName = this.azureGroup;
+      Set<? extends NodeMetadata> nodes = view.getComputeService().suspendNodesMatching(inGroup(groupName));
+      assertTrue(nodes.size() > 0);
+
+      boolean allStopped = false;
+      while (!allStopped) {
+         nodes = view.getComputeService().listNodesDetailsMatching(nameStartsWith(groupName));
+         for (NodeMetadata node : nodes) {
+            if (node.getStatus() != NodeMetadata.Status.SUSPENDED)
+            {
+               // Not stopped yet
+               allStopped = false;
+               try {
+                  Thread.sleep(15 * 1000);
+               } catch (InterruptedException e) {
+               }
+               continue;
+            }
+            else
+            {
+               allStopped = true;
+            }
+         }
+      }
+      assertTrue(allStopped);
+   }
+
+   @Test(dependsOnMethods = "testStop")
+   public void testStart() throws RunScriptOnNodesException {
+      final String groupName = this.azureGroup;
+      Set<? extends NodeMetadata> nodes = view.getComputeService().resumeNodesMatching(inGroup(groupName));
+      assertTrue(nodes.size() > 0);
+
+      boolean allStarted = false;
+      while (!allStarted) {
+         nodes = view.getComputeService().listNodesDetailsMatching(nameStartsWith(groupName));
+         for (NodeMetadata node : nodes) {
+            if (node.getStatus() != NodeMetadata.Status.RUNNING)
+            {
+               // Not started yet
+               allStarted = false;
+               try {
+                  Thread.sleep(15 * 1000);
+               } catch (InterruptedException e) {
+               }
+               continue;
+            }
+            else
+            {
+               allStarted = true;
+            }
+         }
+      }
+      assertTrue(allStarted);
+   }
+
+   @Test(dependsOnMethods = "testStart")
+   public void testRestart() throws RunScriptOnNodesException {
+      final String groupName = this.azureGroup;
+      Set<? extends NodeMetadata> nodes = view.getComputeService().rebootNodesMatching(inGroup(groupName));
+      assertTrue(nodes.size() > 0);
+
+      boolean allRestarted = false;
+      while (!allRestarted) {
+         nodes = view.getComputeService().listNodesDetailsMatching(nameStartsWith(groupName));
+         for (NodeMetadata node : nodes) {
+            if (node.getStatus() != NodeMetadata.Status.RUNNING)
+            {
+               // Not started yet
+               allRestarted = false;
+               try {
+                  Thread.sleep(30 * 1000);
+               } catch (InterruptedException e) {
+               }
+               continue;
+            }
+            else
+            {
+               allRestarted = true;
+            }
+         }
+      }
+      assertTrue(allRestarted);
+
+      view.getComputeService().destroyNodesMatching(inGroup(groupName));
+   }
+
+   @Test(dependsOnMethods = "testRestart")
+   public void testLinuxNode() throws RunNodesException {
+      final String groupName = this.azureGroup;
+      final TemplateBuilder templateBuilder = view.getComputeService().templateBuilder();
+      templateBuilder.osFamily(OsFamily.UBUNTU);
+      templateBuilder.osVersionMatches("14.04");
+      templateBuilder.hardwareId("Standard_A0");
+      templateBuilder.locationId("westus");
+      final Template template = templateBuilder.build();
+
+      try {
+         Set<? extends NodeMetadata> nodes = view.getComputeService().createNodesInGroup(groupName, 1, template);
+         assertThat(nodes).hasSize(1);
+      } finally {
+         view.getComputeService().destroyNodesMatching(inGroup(groupName));
+      }
+   }
+
+   @Test(dependsOnMethods = "testLinuxNode")
+   public void testWindowsNode() throws RunNodesException {
+      final String groupName = this.azureGroup;
+      final TemplateBuilder templateBuilder = view.getComputeService().templateBuilder();
+      templateBuilder.imageId("global/MicrosoftWindowsServer/WindowsServer/Windows-Server-Technical-Preview");
+      templateBuilder.hardwareId("Standard_A0");
+      templateBuilder.locationId("westus");
+      final Template template = templateBuilder.build();
+
+      try {
+         Set<? extends NodeMetadata> nodes = view.getComputeService().createNodesInGroup(groupName, 1, template);
+         assertThat(nodes).hasSize(1);
+      } finally {
+         view.getComputeService().destroyNodesMatching(inGroup(groupName));
+      }
+   }
+
+   @Override
+   protected ProviderMetadata createProviderMetadata() {
+      AzureComputeProviderMetadata pm = AzureComputeProviderMetadata.builder().build();
+      return pm;
+   }
+
+   protected String setIfTestSystemPropertyPresent(Properties overrides, String key) {
+      if (System.getProperties().containsKey("test." + key)) {
+         String val = System.getProperty("test." + key);
+         overrides.setProperty(key, val);
+         return val;
+      } else {
+         return null;
+      }
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/03f72d5e/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/compute/AzureComputeServiceLiveTest.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/compute/AzureComputeServiceLiveTest.java b/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/compute/AzureComputeServiceLiveTest.java
new file mode 100644
index 0000000..ae43511
--- /dev/null
+++ b/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/compute/AzureComputeServiceLiveTest.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.azurecompute.arm.compute;
+
+import org.jclouds.compute.internal.BaseComputeServiceLiveTest;
+import org.jclouds.sshj.config.SshjSshClientModule;
+import org.testng.annotations.Test;
+
+import org.jclouds.providers.ProviderMetadata;
+
+import org.jclouds.azurecompute.arm.AzureComputeProviderMetadata;
+
+import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.RESOURCE_GROUP_NAME;
+import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_RUNNING;
+import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_SCRIPT_COMPLETE;
+import org.jclouds.azurecompute.arm.internal.AzureLiveTestUtils;
+
+import com.google.inject.Module;
+import java.util.Properties;
+import java.util.concurrent.TimeUnit;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Live tests for the {@link org.jclouds.compute.ComputeService} integration.
+ */
+@Test(groups = "live", singleThreaded = true, testName = "AzureComputeServiceLiveTest")
+public class AzureComputeServiceLiveTest extends BaseComputeServiceLiveTest {
+   public String azureGroup;
+
+   public AzureComputeServiceLiveTest() {
+      provider = "azurecompute-arm";
+   }
+
+   @Override
+   protected Module getSshModule() {
+      return new SshjSshClientModule();
+   }
+
+   @Override
+   protected ProviderMetadata createProviderMetadata() {
+      AzureComputeProviderMetadata pm = AzureComputeProviderMetadata.builder().build();
+      return pm;
+   }
+
+   @Override protected Properties setupProperties() {
+      azureGroup = "jc" + System.getProperty("user.name").substring(0, 3);
+      Properties properties = super.setupProperties();
+      long scriptTimeout = TimeUnit.MILLISECONDS.convert(20, TimeUnit.MINUTES);
+      properties.setProperty(TIMEOUT_SCRIPT_COMPLETE, scriptTimeout + "");
+      properties.setProperty(TIMEOUT_NODE_RUNNING, scriptTimeout + "");
+      properties.put(RESOURCE_GROUP_NAME, azureGroup);
+
+      AzureLiveTestUtils.defaultProperties(properties);
+      checkNotNull(setIfTestSystemPropertyPresent(properties, "oauth.endpoint"), "test.oauth.endpoint");
+
+      return properties;
+
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/03f72d5e/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/compute/AzureTemplateBuilderLiveTest.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/compute/AzureTemplateBuilderLiveTest.java b/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/compute/AzureTemplateBuilderLiveTest.java
new file mode 100644
index 0000000..652c12a
--- /dev/null
+++ b/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/compute/AzureTemplateBuilderLiveTest.java
@@ -0,0 +1,77 @@
+/*
+ * 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.azurecompute.arm.compute;
+
+import com.google.inject.Module;
+import org.jclouds.azurecompute.arm.AzureComputeProviderMetadata;
+import org.jclouds.azurecompute.arm.internal.AzureLiveTestUtils;
+import org.jclouds.compute.internal.BaseTemplateBuilderLiveTest;
+import org.jclouds.providers.ProviderMetadata;
+import org.jclouds.sshj.config.SshjSshClientModule;
+import org.testng.annotations.Test;
+
+import java.util.Properties;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+import com.google.common.collect.ImmutableSet;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.RESOURCE_GROUP_NAME;
+import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_RUNNING;
+import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_SCRIPT_COMPLETE;
+
+@Test(groups = "live", testName = "AzureTemplateBuilderLiveTest")
+public class AzureTemplateBuilderLiveTest extends BaseTemplateBuilderLiveTest {
+   public String azureGroup;
+
+   @Override
+   protected Set<String> getIso3166Codes() {
+      return ImmutableSet.of("US-IA", "US-VA", "US-IL", "US-TX", "US-CA", "IE", "NL", "HK", "SG", "JP-11", "JP-27", "BR", "AU-NSW", "AU-VIC");
+   }
+
+   public AzureTemplateBuilderLiveTest() {
+      provider = "azurecompute-arm";
+   }
+
+   @Override
+   protected Module getSshModule() {
+      return new SshjSshClientModule();
+   }
+
+   @Override
+   protected ProviderMetadata createProviderMetadata() {
+      AzureComputeProviderMetadata pm = AzureComputeProviderMetadata.builder().build();
+      return pm;
+   }
+
+   @Override
+   protected Properties setupProperties() {
+      azureGroup = "jc" + System.getProperty("user.name").substring(0, 3);
+      Properties properties = super.setupProperties();
+      long scriptTimeout = TimeUnit.MILLISECONDS.convert(20, TimeUnit.MINUTES);
+      properties.setProperty(TIMEOUT_SCRIPT_COMPLETE, scriptTimeout + "");
+      properties.setProperty(TIMEOUT_NODE_RUNNING, scriptTimeout + "");
+      properties.put(RESOURCE_GROUP_NAME, azureGroup);
+
+      AzureLiveTestUtils.defaultProperties(properties);
+      checkNotNull(setIfTestSystemPropertyPresent(properties, "oauth.endpoint"), "test.oauth.endpoint");
+
+      return properties;
+
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/03f72d5e/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/ResourceProviderAPIMockTest.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/ResourceProviderAPIMockTest.java b/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/ResourceProviderAPIMockTest.java
new file mode 100644
index 0000000..70f2ad6
--- /dev/null
+++ b/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/ResourceProviderAPIMockTest.java
@@ -0,0 +1,67 @@
+/*
+ * 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.azurecompute.arm.features;
+
+import org.jclouds.azurecompute.arm.domain.ResourceProviderMetaData;
+import org.jclouds.azurecompute.arm.internal.BaseAzureComputeApiMockTest;
+import org.testng.annotations.Test;
+
+import java.util.List;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+
+@Test(groups = "unit", testName = "NetworkInterfaceCardApiMockTest", singleThreaded = true)
+public class ResourceProviderAPIMockTest extends BaseAzureComputeApiMockTest {
+
+   final String apiVersion = "2015-01-01";
+   final String resource = "Microsoft.Compute";
+   private final String vm_resource = "virtualMachines";
+
+   public void getPublicIPAddressInfo() throws InterruptedException {
+      server.enqueue(jsonResponse("/getresourceprovidermetadata.json"));
+
+      final ResourceProviderApi resourceProviderApi = api.getResourceProviderApi();
+
+      List<ResourceProviderMetaData> metaDatas = resourceProviderApi.get(resource);
+
+      String path = String.format("/subscriptions/SUBSCRIPTIONID/providers/%s?api-version=%s", resource, apiVersion);
+
+      assertSent(server, "GET", path);
+      assertTrue(metaDatas.size() > 0);
+      ResourceProviderMetaData md = metaDatas.get(0);
+
+      assertEquals(md.resourceType(), "availabilitySets");
+      assertEquals(md.locations().get(0), "East US");
+      assertEquals(md.apiVersions().get(0), "2016-03-30");
+   }
+
+   public void getPublicIPAddressInfoEmpty() throws InterruptedException {
+      server.enqueue(response404());
+
+      final ResourceProviderApi resourceProviderApi = api.getResourceProviderApi();
+
+      List<ResourceProviderMetaData> metaDatas = resourceProviderApi.get(resource);
+
+      String path = String.format("/subscriptions/SUBSCRIPTIONID/providers/%s?api-version=%s", resource, apiVersion);
+
+      assertSent(server, "GET", path);
+      assertNull(metaDatas);
+   }
+}
+

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/03f72d5e/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/ResourceProviderApiLiveTest.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/ResourceProviderApiLiveTest.java b/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/ResourceProviderApiLiveTest.java
new file mode 100644
index 0000000..5ebf996
--- /dev/null
+++ b/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/ResourceProviderApiLiveTest.java
@@ -0,0 +1,55 @@
+/*
+ * 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.azurecompute.arm.features;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+import org.jclouds.azurecompute.arm.domain.ResourceProviderMetaData;
+import org.jclouds.azurecompute.arm.internal.BaseAzureComputeApiLiveTest;
+import org.testng.annotations.Test;
+
+import java.util.List;
+
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+
+
+@Test(groups = "live", testName = "ResourceProviderApiLiveTest")
+public class ResourceProviderApiLiveTest extends BaseAzureComputeApiLiveTest {
+
+   private final String PROVIDER = "Microsoft.Compute";
+   private final String VM_RESOURCE_TYPE = "virtualMachines";
+
+   private ResourceProviderApi api() {
+      return api.getResourceProviderApi();
+   }
+
+   @Test
+   public void testGetComputeProviderMetadata() {
+
+      List<ResourceProviderMetaData> resourceProviderMetaDatas = api().get(PROVIDER);
+
+      assertNotNull(resourceProviderMetaDatas);
+
+      assertTrue(Iterables.any(resourceProviderMetaDatas, new Predicate<ResourceProviderMetaData>() {
+         @Override
+         public boolean apply(final ResourceProviderMetaData providerMetaData) {
+            return providerMetaData.resourceType().equals(VM_RESOURCE_TYPE);
+         }
+      }));
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/03f72d5e/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/internal/AbstractAzureComputeApiLiveTest.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/internal/AbstractAzureComputeApiLiveTest.java b/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/internal/AbstractAzureComputeApiLiveTest.java
index bd55694..337812b 100644
--- a/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/internal/AbstractAzureComputeApiLiveTest.java
+++ b/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/internal/AbstractAzureComputeApiLiveTest.java
@@ -17,20 +17,19 @@
 package org.jclouds.azurecompute.arm.internal;
 
 import static com.google.common.base.Preconditions.checkNotNull;
-import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.OPERATION_POLL_INITIAL_PERIOD;
-import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.OPERATION_POLL_MAX_PERIOD;
-import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.OPERATION_TIMEOUT;
-import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.TCP_RULE_FORMAT;
-import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.TCP_RULE_REGEXP;
 
 import java.util.Properties;
 import java.util.Random;
+import com.google.inject.Module;
+import com.google.inject.Injector;
+
+
 import org.jclouds.apis.BaseApiLiveTest;
 import org.jclouds.azurecompute.arm.AzureComputeApi;
 import org.jclouds.azurecompute.arm.AzureComputeProviderMetadata;
-import org.jclouds.compute.config.ComputeServiceProperties;
 import org.jclouds.providers.ProviderMetadata;
 
+
 public abstract class AbstractAzureComputeApiLiveTest extends BaseApiLiveTest<AzureComputeApi> {
 
    protected static final int RAND = new Random().nextInt(999);
@@ -39,19 +38,16 @@ public abstract class AbstractAzureComputeApiLiveTest extends BaseApiLiveTest<Az
       provider = "azurecompute-arm";
    }
 
+   @Override protected AzureComputeApi create(Properties props, Iterable<Module> modules) {
+      Injector injector = newBuilder().modules(modules).overrides(props).buildInjector();
+      return injector.getInstance(AzureComputeApi.class);
+   }
+
    @Override protected Properties setupProperties() {
       Properties properties = super.setupProperties();
-      properties.put(ComputeServiceProperties.POLL_INITIAL_PERIOD, 1000);
-      properties.put(ComputeServiceProperties.POLL_MAX_PERIOD, 10000);
-      properties.setProperty(OPERATION_TIMEOUT, "60000");
-      properties.setProperty(OPERATION_POLL_INITIAL_PERIOD, "5");
-      properties.setProperty(OPERATION_POLL_MAX_PERIOD, "15");
-      properties.setProperty(TCP_RULE_FORMAT, "tcp_%s-%s");
-      properties.setProperty(TCP_RULE_REGEXP, "tcp_\\d{1,5}-\\d{1,5}");
 
       // for oauth
       AzureLiveTestUtils.defaultProperties(properties);
-      checkNotNull(setIfTestSystemPropertyPresent(properties, "jclouds.oauth.resource"), "test.jclouds.oauth.resource");
       checkNotNull(setIfTestSystemPropertyPresent(properties, "oauth.endpoint"), "test.oauth.endpoint");
       return properties;
    }

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/03f72d5e/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/internal/BaseAzureComputeApiLiveTest.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/internal/BaseAzureComputeApiLiveTest.java b/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/internal/BaseAzureComputeApiLiveTest.java
index 900d3e0..bd9adfc 100644
--- a/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/internal/BaseAzureComputeApiLiveTest.java
+++ b/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/internal/BaseAzureComputeApiLiveTest.java
@@ -65,25 +65,6 @@ public class BaseAzureComputeApiLiveTest extends AbstractAzureComputeApiLiveTest
 
    private String storageServiceName = null;
 
-
-   protected String getCredential() {
-      String credential = null;
-      if (System.getProperty("test.azurecompute-arm.credential") != null) {
-         credential = System.getProperty("test.azurecompute-arm.credential");
-      }
-      assertNotNull(credential);
-      return credential;
-   }
-
-   protected String getIdentity() {
-      String identity = null;
-      if (System.getProperty("test.azurecompute-arm.identity") != null) {
-         identity = System.getProperty("test.azurecompute-arm.identity");
-      }
-      assertNotNull(identity);
-      return identity;
-   }
-
    protected String getStorageServiceName() {
       if (storageServiceName == null) {
          storageServiceName = String.format("%3.24s",

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/03f72d5e/azurecompute-arm/src/test/resources/getresourceprovidermetadata.json
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/test/resources/getresourceprovidermetadata.json b/azurecompute-arm/src/test/resources/getresourceprovidermetadata.json
new file mode 100644
index 0000000..b7693d7
--- /dev/null
+++ b/azurecompute-arm/src/test/resources/getresourceprovidermetadata.json
@@ -0,0 +1,366 @@
+{
+  "id": "/subscriptions/SUBSCRIPTIONID/providers/Microsoft.Compute",
+  "namespace": "Microsoft.Compute",
+  "authorization": {
+    "applicationId": "12312312-1212-1212-1212-121212121212",
+    "roleDefinitionId": "34534534-272e-4238-8723-123423452224"
+  },
+  "resourceTypes": [
+    {
+      "resourceType": "availabilitySets",
+      "locations": [
+        "East US",
+        "East US 2",
+        "West US",
+        "Central US",
+        "North Central US",
+        "South Central US",
+        "North Europe",
+        "West Europe",
+        "East Asia",
+        "Southeast Asia",
+        "Japan East",
+        "Japan West",
+        "Brazil South"
+      ],
+      "apiVersions": [
+        "2016-03-30",
+        "2015-06-15",
+        "2015-05-01-preview"
+      ],
+      "capabilities": "CrossResourceGroupResourceMove, CrossSubscriptionResourceMove"
+    },
+    {
+      "resourceType": "virtualMachines",
+      "locations": [
+        "East US",
+        "East US 2",
+        "West US",
+        "Central US",
+        "North Central US",
+        "South Central US",
+        "North Europe",
+        "West Europe",
+        "East Asia",
+        "Southeast Asia",
+        "Japan East",
+        "Japan West",
+        "Brazil South"
+      ],
+      "apiVersions": [
+        "2016-03-30",
+        "2015-06-15",
+        "2015-05-01-preview"
+      ],
+      "capabilities": "CrossResourceGroupResourceMove, CrossSubscriptionResourceMove"
+    },
+    {
+      "resourceType": "virtualMachines/extensions",
+      "locations": [
+        "East US",
+        "East US 2",
+        "West US",
+        "Central US",
+        "North Central US",
+        "South Central US",
+        "North Europe",
+        "West Europe",
+        "East Asia",
+        "Southeast Asia",
+        "Japan East",
+        "Japan West",
+        "Brazil South"
+      ],
+      "apiVersions": [
+        "2016-03-30",
+        "2015-06-15",
+        "2015-05-01-preview"
+      ],
+      "capabilities": "CrossResourceGroupResourceMove, CrossSubscriptionResourceMove"
+    },
+    {
+      "resourceType": "virtualMachines/diagnosticSettings",
+      "locations": [
+        "East US",
+        "East US 2",
+        "West US",
+        "Central US",
+        "North Central US",
+        "South Central US",
+        "North Europe",
+        "West Europe",
+        "East Asia",
+        "Southeast Asia",
+        "Japan East",
+        "Japan West",
+        "Brazil South"
+      ],
+      "apiVersions": [
+        "2014-04-01"
+      ]
+    },
+    {
+      "resourceType": "virtualMachines/metricDefinitions",
+      "locations": [
+        "East US",
+        "East US 2",
+        "West US",
+        "Central US",
+        "North Central US",
+        "South Central US",
+        "North Europe",
+        "West Europe",
+        "East Asia",
+        "Southeast Asia",
+        "Japan East",
+        "Japan West",
+        "Brazil South"
+      ],
+      "apiVersions": [
+        "2014-04-01"
+      ]
+    },
+    {
+      "resourceType": "virtualMachineScaleSets",
+      "locations": [
+        "East US",
+        "East US 2",
+        "West US",
+        "Central US",
+        "North Central US",
+        "South Central US",
+        "North Europe",
+        "West Europe",
+        "East Asia",
+        "Southeast Asia",
+        "Japan East",
+        "Japan West",
+        "Brazil South"
+      ],
+      "apiVersions": [
+        "2016-03-30",
+        "2015-06-15",
+        "2015-05-01-preview"
+      ],
+      "capabilities": "None"
+    },
+    {
+      "resourceType": "virtualMachineScaleSets/extensions",
+      "locations": [
+        "East US",
+        "East US 2",
+        "West US",
+        "Central US",
+        "North Central US",
+        "South Central US",
+        "North Europe",
+        "West Europe",
+        "East Asia",
+        "Southeast Asia",
+        "Japan East",
+        "Japan West",
+        "Brazil South"
+      ],
+      "apiVersions": [
+        "2016-03-30",
+        "2015-06-15",
+        "2015-05-01-preview"
+      ],
+      "capabilities": "CrossResourceGroupResourceMove, CrossSubscriptionResourceMove"
+    },
+    {
+      "resourceType": "virtualMachineScaleSets/virtualMachines",
+      "locations": [
+        "East US",
+        "East US 2",
+        "West US",
+        "Central US",
+        "North Central US",
+        "South Central US",
+        "North Europe",
+        "West Europe",
+        "East Asia",
+        "Southeast Asia",
+        "Japan East",
+        "Japan West",
+        "Brazil South"
+      ],
+      "apiVersions": [
+        "2016-03-30",
+        "2015-06-15",
+        "2015-05-01-preview"
+      ]
+    },
+    {
+      "resourceType": "virtualMachineScaleSets/networkInterfaces",
+      "locations": [
+        "East US",
+        "East US 2",
+        "West US",
+        "Central US",
+        "North Central US",
+        "South Central US",
+        "North Europe",
+        "West Europe",
+        "East Asia",
+        "Southeast Asia",
+        "Japan East",
+        "Japan West",
+        "Brazil South"
+      ],
+      "apiVersions": [
+        "2016-03-30",
+        "2015-06-15",
+        "2015-05-01-preview"
+      ]
+    },
+    {
+      "resourceType": "virtualMachineScaleSets/virtualMachines/networkInterfaces",
+      "locations": [
+        "East US",
+        "East US 2",
+        "West US",
+        "Central US",
+        "North Central US",
+        "South Central US",
+        "North Europe",
+        "West Europe",
+        "East Asia",
+        "Southeast Asia",
+        "Japan East",
+        "Japan West",
+        "Brazil South"
+      ],
+      "apiVersions": [
+        "2016-03-30",
+        "2015-06-15",
+        "2015-05-01-preview"
+      ]
+    },
+    {
+      "resourceType": "locations",
+      "locations": [],
+      "apiVersions": [
+        "2016-03-30",
+        "2015-06-15",
+        "2015-05-01-preview"
+      ]
+    },
+    {
+      "resourceType": "locations/operations",
+      "locations": [
+        "East US",
+        "East US 2",
+        "West US",
+        "Central US",
+        "North Central US",
+        "South Central US",
+        "North Europe",
+        "West Europe",
+        "East Asia",
+        "Southeast Asia",
+        "Japan East",
+        "Japan West",
+        "Brazil South"
+      ],
+      "apiVersions": [
+        "2016-03-30",
+        "2015-06-15",
+        "2015-05-01-preview"
+      ]
+    },
+    {
+      "resourceType": "locations/vmSizes",
+      "locations": [
+        "East US",
+        "East US 2",
+        "West US",
+        "Central US",
+        "North Central US",
+        "South Central US",
+        "North Europe",
+        "West Europe",
+        "East Asia",
+        "Southeast Asia",
+        "Japan East",
+        "Japan West",
+        "Brazil South"
+      ],
+      "apiVersions": [
+        "2016-03-30",
+        "2015-06-15",
+        "2015-05-01-preview"
+      ]
+    },
+    {
+      "resourceType": "locations/usages",
+      "locations": [
+        "East US",
+        "East US 2",
+        "West US",
+        "Central US",
+        "North Central US",
+        "South Central US",
+        "North Europe",
+        "West Europe",
+        "East Asia",
+        "Southeast Asia",
+        "Japan East",
+        "Japan West",
+        "Brazil South"
+      ],
+      "apiVersions": [
+        "2016-03-30",
+        "2015-06-15",
+        "2015-05-01-preview"
+      ]
+    },
+    {
+      "resourceType": "locations/publishers",
+      "locations": [
+        "East US",
+        "East US 2",
+        "West US",
+        "Central US",
+        "North Central US",
+        "South Central US",
+        "North Europe",
+        "West Europe",
+        "East Asia",
+        "Southeast Asia",
+        "Japan East",
+        "Japan West",
+        "Brazil South"
+      ],
+      "apiVersions": [
+        "2016-03-30",
+        "2015-06-15",
+        "2015-05-01-preview"
+      ]
+    },
+    {
+      "resourceType": "operations",
+      "locations": [
+        "East US",
+        "East US 2",
+        "West US",
+        "Central US",
+        "North Central US",
+        "South Central US",
+        "North Europe",
+        "West Europe",
+        "East Asia",
+        "Southeast Asia",
+        "Japan East",
+        "Japan West",
+        "Brazil South"
+      ],
+      "apiVersions": [
+        "2016-03-30",
+        "2015-06-15",
+        "2015-05-01-preview"
+      ]
+    }
+  ],
+  "registrationState": "Registered"
+}
\ No newline at end of file


[3/3] jclouds-labs git commit: Algin labs providers with the new NodeAndTemplateOptionsToStatement interface

Posted by na...@apache.org.
Algin labs providers with the new NodeAndTemplateOptionsToStatement interface


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

Branch: refs/heads/master
Commit: 88ef0a2210afb5a079b59f03899d18141cdc5acc
Parents: 03f72d5
Author: Ignasi Barrera <na...@apache.org>
Authored: Fri Jun 17 22:22:35 2016 +0200
Committer: Ignasi Barrera <na...@apache.org>
Committed: Fri Jun 17 22:52:44 2016 +0200

----------------------------------------------------------------------
 .../CloudSigma2ComputeServiceContextModule.java | 36 +++++-----
 ...plateOptionsToStatementWithoutPublicKey.java | 59 ---------------
 ...eOptionsToStatementWithoutPublicKeyTest.java | 75 --------------------
 3 files changed, 19 insertions(+), 151 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/88ef0a22/cloudsigma2/src/main/java/org/jclouds/cloudsigma2/compute/config/CloudSigma2ComputeServiceContextModule.java
----------------------------------------------------------------------
diff --git a/cloudsigma2/src/main/java/org/jclouds/cloudsigma2/compute/config/CloudSigma2ComputeServiceContextModule.java b/cloudsigma2/src/main/java/org/jclouds/cloudsigma2/compute/config/CloudSigma2ComputeServiceContextModule.java
index 84497e4..cd9e5d8 100644
--- a/cloudsigma2/src/main/java/org/jclouds/cloudsigma2/compute/config/CloudSigma2ComputeServiceContextModule.java
+++ b/cloudsigma2/src/main/java/org/jclouds/cloudsigma2/compute/config/CloudSigma2ComputeServiceContextModule.java
@@ -16,18 +16,21 @@
  */
 package org.jclouds.cloudsigma2.compute.config;
 
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Function;
-import com.google.common.base.Predicate;
-import com.google.common.collect.ImmutableMap;
-import com.google.inject.Provides;
-import com.google.inject.TypeLiteral;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.jclouds.cloudsigma2.config.CloudSigma2Properties.TIMEOUT_DRIVE_CLONED;
+import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_SUSPENDED;
+import static org.jclouds.util.Predicates2.retry;
+
+import java.util.Map;
+
+import javax.inject.Named;
+import javax.inject.Singleton;
+
 import org.jclouds.cloudsigma2.CloudSigma2Api;
 import org.jclouds.cloudsigma2.compute.functions.LibraryDriveToImage;
 import org.jclouds.cloudsigma2.compute.functions.NICToAddress;
 import org.jclouds.cloudsigma2.compute.functions.ServerDriveToVolume;
 import org.jclouds.cloudsigma2.compute.functions.ServerInfoToNodeMetadata;
-import org.jclouds.cloudsigma2.compute.functions.TemplateOptionsToStatementWithoutPublicKey;
 import org.jclouds.cloudsigma2.compute.options.CloudSigma2TemplateOptions;
 import org.jclouds.cloudsigma2.compute.strategy.CloudSigma2ComputeServiceAdapter;
 import org.jclouds.cloudsigma2.domain.DriveInfo;
@@ -43,21 +46,20 @@ import org.jclouds.compute.domain.Hardware;
 import org.jclouds.compute.domain.Image;
 import org.jclouds.compute.domain.NodeMetadata;
 import org.jclouds.compute.domain.Volume;
-import org.jclouds.compute.functions.TemplateOptionsToStatement;
+import org.jclouds.compute.functions.NodeAndTemplateOptionsToStatement;
+import org.jclouds.compute.functions.NodeAndTemplateOptionsToStatementWithoutPublicKey;
 import org.jclouds.compute.options.TemplateOptions;
 import org.jclouds.compute.reference.ComputeServiceConstants.PollPeriod;
 import org.jclouds.compute.reference.ComputeServiceConstants.Timeouts;
 import org.jclouds.domain.Location;
 import org.jclouds.functions.IdentityFunction;
 
-import javax.inject.Named;
-import javax.inject.Singleton;
-import java.util.Map;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static org.jclouds.cloudsigma2.config.CloudSigma2Properties.TIMEOUT_DRIVE_CLONED;
-import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_SUSPENDED;
-import static org.jclouds.util.Predicates2.retry;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableMap;
+import com.google.inject.Provides;
+import com.google.inject.TypeLiteral;
 
 public class CloudSigma2ComputeServiceContextModule extends
       ComputeServiceAdapterContextModule<ServerInfo, Hardware, LibraryDrive, Location> {
@@ -84,7 +86,7 @@ public class CloudSigma2ComputeServiceContextModule extends
       }).to(NICToAddress.class);
 
       bind(TemplateOptions.class).to(CloudSigma2TemplateOptions.class);
-      bind(TemplateOptionsToStatement.class).to(TemplateOptionsToStatementWithoutPublicKey.class);
+      bind(NodeAndTemplateOptionsToStatement.class).to(NodeAndTemplateOptionsToStatementWithoutPublicKey.class);
    }
 
    @VisibleForTesting

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/88ef0a22/cloudsigma2/src/main/java/org/jclouds/cloudsigma2/compute/functions/TemplateOptionsToStatementWithoutPublicKey.java
----------------------------------------------------------------------
diff --git a/cloudsigma2/src/main/java/org/jclouds/cloudsigma2/compute/functions/TemplateOptionsToStatementWithoutPublicKey.java b/cloudsigma2/src/main/java/org/jclouds/cloudsigma2/compute/functions/TemplateOptionsToStatementWithoutPublicKey.java
deleted file mode 100644
index 077917b..0000000
--- a/cloudsigma2/src/main/java/org/jclouds/cloudsigma2/compute/functions/TemplateOptionsToStatementWithoutPublicKey.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * 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.cloudsigma2.compute.functions;
-
-import com.google.common.collect.ImmutableList;
-import org.jclouds.compute.functions.TemplateOptionsToStatement;
-import org.jclouds.compute.options.TemplateOptions;
-import org.jclouds.scriptbuilder.InitScript;
-import org.jclouds.scriptbuilder.domain.Statement;
-import org.jclouds.scriptbuilder.domain.StatementList;
-import org.jclouds.scriptbuilder.statements.ssh.InstallRSAPrivateKey;
-
-import javax.inject.Singleton;
-
-/**
- * Convert the template options into a statement, but ignoring the public key.
- * <p/>
- * The {@link org.jclouds.cloudsigma2.compute.strategy.CloudSigma2ComputeServiceAdapter} already takes care of
- * installing it using the server metadata.
- */
-@Singleton
-public class TemplateOptionsToStatementWithoutPublicKey extends TemplateOptionsToStatement {
-
-   @Override
-   public Statement apply(TemplateOptions options) {
-      ImmutableList.Builder<Statement> builder = ImmutableList.builder();
-      if (options.getRunScript() != null) {
-         builder.add(options.getRunScript());
-      }
-      if (options.getPrivateKey() != null) {
-         builder.add(new InstallRSAPrivateKey(options.getPrivateKey()));
-      }
-
-      ImmutableList<Statement> bootstrap = builder.build();
-      if (bootstrap.isEmpty()) {
-         return null;
-      }
-
-      if (options.getTaskName() == null && !(options.getRunScript() instanceof InitScript)) {
-         options.nameTask("bootstrap");
-      }
-      return bootstrap.size() == 1 ? bootstrap.get(0) : new StatementList(bootstrap);
-   }
-
-}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/88ef0a22/cloudsigma2/src/test/java/org/jclouds/cloudsigma2/compute/functions/TemplateOptionsToStatementWithoutPublicKeyTest.java
----------------------------------------------------------------------
diff --git a/cloudsigma2/src/test/java/org/jclouds/cloudsigma2/compute/functions/TemplateOptionsToStatementWithoutPublicKeyTest.java b/cloudsigma2/src/test/java/org/jclouds/cloudsigma2/compute/functions/TemplateOptionsToStatementWithoutPublicKeyTest.java
deleted file mode 100644
index 93caac0..0000000
--- a/cloudsigma2/src/test/java/org/jclouds/cloudsigma2/compute/functions/TemplateOptionsToStatementWithoutPublicKeyTest.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * 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.cloudsigma2.compute.functions;
-
-import org.jclouds.compute.options.TemplateOptions;
-import org.jclouds.scriptbuilder.domain.OsFamily;
-import org.jclouds.scriptbuilder.domain.Statement;
-import org.jclouds.scriptbuilder.domain.StatementList;
-import org.jclouds.scriptbuilder.statements.ssh.InstallRSAPrivateKey;
-import org.jclouds.ssh.SshKeys;
-import org.testng.annotations.Test;
-
-import java.util.Map;
-
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertNull;
-import static org.testng.Assert.assertTrue;
-
-/**
- * Unit tests for the {@link TemplateOptionsToStatementWithoutPublicKey} class.
- */
-@Test(groups = "unit", testName = "TemplateOptionsToStatementWithoutPublicKeyTest")
-public class TemplateOptionsToStatementWithoutPublicKeyTest {
-
-   @Test
-   public void testPublicKeyDoesNotGenerateAuthorizePublicKeyStatementIfOnlyPublicKeyOptionsConfigured() {
-      Map<String, String> keys = SshKeys.generate();
-      TemplateOptions options = TemplateOptions.Builder.authorizePublicKey(keys.get("public"));
-
-      TemplateOptionsToStatementWithoutPublicKey function = new TemplateOptionsToStatementWithoutPublicKey();
-      assertNull(function.apply(options));
-   }
-
-   @Test
-   public void testPublicAndRunScriptKeyDoesNotGenerateAuthorizePublicKeyStatementIfRunScriptPresent() {
-      Map<String, String> keys = SshKeys.generate();
-      TemplateOptions options = TemplateOptions.Builder.authorizePublicKey(keys.get("public")).runScript("uptime");
-
-      TemplateOptionsToStatementWithoutPublicKey function = new TemplateOptionsToStatementWithoutPublicKey();
-      Statement statement = function.apply(options);
-
-      assertEquals(statement.render(OsFamily.UNIX), "uptime\n");
-   }
-
-   @Test
-   public void testPublicAndPrivateKeyAndRunScriptDoesNotGenerateAuthorizePublicKeyStatementIfOtherOptionsPresent() {
-      Map<String, String> keys = SshKeys.generate();
-      TemplateOptions options = TemplateOptions.Builder.authorizePublicKey(keys.get("public"))
-            .installPrivateKey(keys.get("private")).runScript("uptime");
-
-      TemplateOptionsToStatementWithoutPublicKey function = new TemplateOptionsToStatementWithoutPublicKey();
-      Statement statement = function.apply(options);
-
-      assertTrue(statement instanceof StatementList);
-      StatementList statements = (StatementList) statement;
-
-      assertEquals(statements.size(), 2);
-      assertEquals(statements.get(0).render(OsFamily.UNIX), "uptime\n");
-      assertTrue(statements.get(1) instanceof InstallRSAPrivateKey);
-   }
-}


[2/3] jclouds-labs git commit: JCLOUDS-664 Azurecompute-arm compute service

Posted by na...@apache.org.
JCLOUDS-664 Azurecompute-arm compute service


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

Branch: refs/heads/master
Commit: 03f72d5e2f225f212d05151c1c15a99af14cd29e
Parents: e4cbe22
Author: Rita Zhang <ri...@gmail.com>
Authored: Mon May 16 18:55:01 2016 -0700
Committer: Ignasi Barrera <na...@apache.org>
Committed: Fri Jun 17 22:24:32 2016 +0200

----------------------------------------------------------------------
 azurecompute-arm/README.md                      |  20 +-
 .../azurecompute/arm/AzureComputeApi.java       |  11 +-
 .../arm/AzureComputeProviderMetadata.java       |  21 +-
 .../arm/AzureManagementApiMetadata.java         |   2 +
 .../arm/compute/AzureComputeService.java        | 104 +++++
 .../arm/compute/AzureComputeServiceAdapter.java | 393 +++++++++++++++++++
 .../AzureComputeServiceContextModule.java       | 191 +++++++++
 .../functions/DeploymentToNodeMetadata.java     | 207 ++++++++++
 .../compute/functions/LocationToLocation.java   |   7 +-
 .../compute/functions/VMHardwareToHardware.java |  79 ++++
 .../arm/compute/functions/VMImageToImage.java   | 124 ++++++
 .../CreateResourceGroupThenCreateNodes.java     |  96 +++++
 .../arm/config/AzureComputeProperties.java      |   8 +
 .../arm/domain/ResourceProviderMetaData.java    |  68 ++++
 .../azurecompute/arm/domain/VMDeployment.java   |   2 +
 .../azurecompute/arm/domain/VMHardware.java     |  68 ++++
 .../azurecompute/arm/domain/VMImage.java        |  53 +++
 .../azurecompute/arm/domain/VirtualMachine.java |   2 +-
 .../arm/features/ResourceProviderApi.java       |  57 +++
 .../arm/functions/CleanupResources.java         | 107 +++++
 .../arm/util/DeploymentTemplateBuilder.java     |  31 +-
 .../AzureComputeServiceContextLiveTest.java     | 284 ++++++++++++++
 .../compute/AzureComputeServiceLiveTest.java    |  73 ++++
 .../compute/AzureTemplateBuilderLiveTest.java   |  77 ++++
 .../features/ResourceProviderAPIMockTest.java   |  67 ++++
 .../features/ResourceProviderApiLiveTest.java   |  55 +++
 .../AbstractAzureComputeApiLiveTest.java        |  24 +-
 .../internal/BaseAzureComputeApiLiveTest.java   |  19 -
 .../resources/getresourceprovidermetadata.json  | 366 +++++++++++++++++
 29 files changed, 2564 insertions(+), 52 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/03f72d5e/azurecompute-arm/README.md
----------------------------------------------------------------------
diff --git a/azurecompute-arm/README.md b/azurecompute-arm/README.md
index fc8bb54..c944a8a 100644
--- a/azurecompute-arm/README.md
+++ b/azurecompute-arm/README.md
@@ -53,13 +53,25 @@ azure login -u <Application-id> -p <password> --service-principal --tenant <Tena
 
 ## Run Live Tests
 
-Use the following to run the live tests
+Use the following to run one live test:
+
+```bash
+mvn -Dtest=<name of the live test> \
+    -Dtest.azurecompute-arm.identity="<Application-id>" \
+    -Dtest.azurecompute-arm.credential="<password>" \
+    -Dtest.azurecompute-arm.endpoint="https://management.azure.com/subscriptions/<Subscription-id>" \
+    -Dtest.oauth.endpoint="https://login.microsoftonline.com/<Tenant-id>/oauth2/token" test
+
+```
+
+Use the following to run all the live tests:
 
 ```bash
 
 mvn clean verify -Plive \
-    -Dtest.azurecompute-arm.identity=<Application-id> \
-    -Dtest.azurecompute-arm.credential=<password> \
-    -Dtest.azurecompute-arm.endpoint=https://management.azure.com/subscriptions/<Subscription-id> \
+    -Dtest.azurecompute-arm.identity="<Application-id>"" \
+    -Dtest.azurecompute-arm.credential="<password>"" \
+    -Dtest.azurecompute-arm.endpoint="https://management.azure.com/subscriptions/<Subscription-id>"" \
     -Dtest.oauth.endpoint=https://login.microsoftonline.com/<Tenant-id>/oauth2/token
+
 ```

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/03f72d5e/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/AzureComputeApi.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/AzureComputeApi.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/AzureComputeApi.java
index c39022e..42749cf 100644
--- a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/AzureComputeApi.java
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/AzureComputeApi.java
@@ -17,18 +17,19 @@
 package org.jclouds.azurecompute.arm;
 
 import org.jclouds.azurecompute.arm.features.DeploymentApi;
+import org.jclouds.azurecompute.arm.util.DeploymentTemplateBuilder;
 import org.jclouds.azurecompute.arm.features.JobApi;
 import org.jclouds.azurecompute.arm.features.LocationApi;
 import org.jclouds.azurecompute.arm.features.NetworkInterfaceCardApi;
 import org.jclouds.azurecompute.arm.features.OSImageApi;
 import org.jclouds.azurecompute.arm.features.PublicIPAddressApi;
 import org.jclouds.azurecompute.arm.features.ResourceGroupApi;
+import org.jclouds.azurecompute.arm.features.ResourceProviderApi;
 import org.jclouds.azurecompute.arm.features.StorageAccountApi;
 import org.jclouds.azurecompute.arm.features.SubnetApi;
 import org.jclouds.azurecompute.arm.features.VirtualMachineApi;
 import org.jclouds.azurecompute.arm.features.VirtualNetworkApi;
 import org.jclouds.azurecompute.arm.features.VMSizeApi;
-import org.jclouds.azurecompute.arm.util.DeploymentTemplateBuilder;
 import org.jclouds.azurecompute.arm.features.NetworkSecurityGroupApi;
 import org.jclouds.azurecompute.arm.features.NetworkSecurityRuleApi;
 import org.jclouds.rest.annotations.Delegate;
@@ -156,6 +157,14 @@ public interface AzureComputeApi extends Closeable {
    NetworkSecurityRuleApi getNetworkSecurityRuleApi(@PathParam("resourcegroup") String resourcegroup,
                                                     @PathParam("networksecuritygroup") String networksecuritygroup);
 
+   /**
+    * The Azure Resource Provider API provides information about a resource provider and its supported resource types.
+    *
+    * @see <a href="https://msdn.microsoft.com/en-us/library/azure/dn790534.aspx">docs</a>
+    */
+   @Delegate
+   ResourceProviderApi getResourceProviderApi();
+
    @Provides
    DeploymentTemplateBuilder.Factory deploymentTemplateFactory();
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/03f72d5e/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/AzureComputeProviderMetadata.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/AzureComputeProviderMetadata.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/AzureComputeProviderMetadata.java
index 39defdc..4bbc508 100644
--- a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/AzureComputeProviderMetadata.java
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/AzureComputeProviderMetadata.java
@@ -16,11 +16,17 @@
  */
 package org.jclouds.azurecompute.arm;
 
+
+import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.IMAGE_PUBLISHERS;
+import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.RESOURCE_GROUP_NAME;
+import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.OPERATION_TIMEOUT;
 import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.OPERATION_POLL_INITIAL_PERIOD;
 import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.OPERATION_POLL_MAX_PERIOD;
-import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.OPERATION_TIMEOUT;
 import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.TCP_RULE_FORMAT;
 import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.TCP_RULE_REGEXP;
+
+import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.DEFAULT_IMAGE_LOGIN;
+
 import static org.jclouds.oauth.v2.config.CredentialType.CLIENT_CREDENTIALS_SECRET;
 import static org.jclouds.oauth.v2.config.OAuthProperties.RESOURCE;
 import static org.jclouds.oauth.v2.config.OAuthProperties.CREDENTIAL_TYPE;
@@ -30,6 +36,9 @@ import java.util.Properties;
 import org.jclouds.azurecompute.arm.domain.Region;
 import org.jclouds.providers.ProviderMetadata;
 import org.jclouds.providers.internal.BaseProviderMetadata;
+import org.jclouds.compute.config.ComputeServiceProperties;
+
+import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_TERMINATED;
 
 import com.google.auto.service.AutoService;
 
@@ -51,13 +60,19 @@ public class AzureComputeProviderMetadata extends BaseProviderMetadata {
 
    public static Properties defaultProperties() {
       final Properties properties = AzureManagementApiMetadata.defaultProperties();
-      properties.setProperty(OPERATION_TIMEOUT, "60000");
+      properties.put(ComputeServiceProperties.POLL_INITIAL_PERIOD, 1000);
+      properties.put(ComputeServiceProperties.POLL_MAX_PERIOD, 10000);
+      properties.setProperty(OPERATION_TIMEOUT, "46000000");
       properties.setProperty(OPERATION_POLL_INITIAL_PERIOD, "5");
       properties.setProperty(OPERATION_POLL_MAX_PERIOD, "15");
       properties.setProperty(TCP_RULE_FORMAT, "tcp_%s-%s");
       properties.setProperty(TCP_RULE_REGEXP, "tcp_\\d{1,5}-\\d{1,5}");
-      properties.put(RESOURCE, "https://management.azure.com");
+      properties.put(RESOURCE, "https://management.azure.com/");
       properties.put(CREDENTIAL_TYPE, CLIENT_CREDENTIALS_SECRET.toString());
+      properties.put(RESOURCE_GROUP_NAME, "jcloudsgroup");
+      properties.put(IMAGE_PUBLISHERS, "Microsoft.WindowsAzure.Compute, MicrosoftWindowsServer, Canonical");
+      properties.put(DEFAULT_IMAGE_LOGIN, "jclouds:Password1!");
+      properties.put(TIMEOUT_NODE_TERMINATED, 60 * 10 * 1000);
       return properties;
    }
 

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/03f72d5e/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/AzureManagementApiMetadata.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/AzureManagementApiMetadata.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/AzureManagementApiMetadata.java
index 9a3292c..989fd84 100644
--- a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/AzureManagementApiMetadata.java
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/AzureManagementApiMetadata.java
@@ -26,6 +26,7 @@ import org.jclouds.azurecompute.arm.config.AzureComputeHttpApiModule;
 import org.jclouds.compute.ComputeServiceContext;
 import org.jclouds.rest.internal.BaseHttpApiMetadata;
 import org.jclouds.oauth.v2.config.OAuthModule;
+import org.jclouds.azurecompute.arm.compute.config.AzureComputeServiceContextModule;
 
 import com.google.common.collect.ImmutableSet;
 import com.google.inject.Module;
@@ -68,6 +69,7 @@ public class AzureManagementApiMetadata extends BaseHttpApiMetadata<AzureCompute
                  .defaultProperties(AzureManagementApiMetadata.defaultProperties())
                  .view(typeToken(ComputeServiceContext.class))
                  .defaultModules(ImmutableSet.<Class<? extends Module>>builder()
+                         .add(AzureComputeServiceContextModule.class)
                          .add(OAuthModule.class)
                          .add(OkHttpCommandExecutorServiceModule.class)
                          .add(AzureComputeHttpApiModule.class).build());

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/03f72d5e/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/AzureComputeService.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/AzureComputeService.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/AzureComputeService.java
new file mode 100644
index 0000000..c215e37
--- /dev/null
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/AzureComputeService.java
@@ -0,0 +1,104 @@
+/*
+ * 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.azurecompute.arm.compute;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_RUNNING;
+import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_SUSPENDED;
+import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_TERMINATED;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicReference;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Provider;
+import javax.inject.Singleton;
+
+import org.jclouds.Constants;
+import org.jclouds.collect.Memoized;
+import org.jclouds.compute.ComputeServiceContext;
+import org.jclouds.compute.callables.RunScriptOnNode;
+import org.jclouds.compute.domain.Hardware;
+import org.jclouds.compute.domain.Image;
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.domain.TemplateBuilder;
+import org.jclouds.compute.extensions.ImageExtension;
+import org.jclouds.compute.extensions.SecurityGroupExtension;
+import org.jclouds.compute.internal.BaseComputeService;
+import org.jclouds.compute.internal.PersistNodeCredentials;
+import org.jclouds.compute.options.TemplateOptions;
+import org.jclouds.compute.reference.ComputeServiceConstants.Timeouts;
+import org.jclouds.compute.strategy.CreateNodesInGroupThenAddToSet;
+import org.jclouds.compute.strategy.DestroyNodeStrategy;
+import org.jclouds.compute.strategy.GetImageStrategy;
+import org.jclouds.compute.strategy.GetNodeMetadataStrategy;
+import org.jclouds.compute.strategy.InitializeRunScriptOnNodeOrPlaceInBadMap;
+import org.jclouds.compute.strategy.ListNodesStrategy;
+import org.jclouds.compute.strategy.RebootNodeStrategy;
+import org.jclouds.compute.strategy.ResumeNodeStrategy;
+import org.jclouds.compute.strategy.SuspendNodeStrategy;
+import org.jclouds.domain.Credentials;
+import org.jclouds.domain.Location;
+import org.jclouds.azurecompute.arm.functions.CleanupResources;
+import org.jclouds.scriptbuilder.functions.InitAdminAccess;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Predicate;
+import com.google.common.base.Supplier;
+import com.google.common.util.concurrent.ListeningExecutorService;
+
+@Singleton
+public class AzureComputeService extends BaseComputeService {
+   protected final CleanupResources cleanupResources;
+
+   @Inject
+   protected AzureComputeService(ComputeServiceContext context, Map<String, Credentials> credentialStore,
+                                @Memoized Supplier<Set<? extends Image>> images, @Memoized Supplier<Set<? extends Hardware>> sizes,
+                                @Memoized Supplier<Set<? extends Location>> locations, ListNodesStrategy listNodesStrategy,
+                                GetImageStrategy getImageStrategy, GetNodeMetadataStrategy getNodeMetadataStrategy,
+                                CreateNodesInGroupThenAddToSet runNodesAndAddToSetStrategy, RebootNodeStrategy rebootNodeStrategy,
+                                DestroyNodeStrategy destroyNodeStrategy, ResumeNodeStrategy startNodeStrategy,
+                                SuspendNodeStrategy stopNodeStrategy, Provider<TemplateBuilder> templateBuilderProvider,
+                                @Named("DEFAULT") Provider<TemplateOptions> templateOptionsProvider,
+                                @Named(TIMEOUT_NODE_RUNNING) Predicate<AtomicReference<NodeMetadata>> nodeRunning,
+                                @Named(TIMEOUT_NODE_TERMINATED) Predicate<AtomicReference<NodeMetadata>> nodeTerminated,
+                                @Named(TIMEOUT_NODE_SUSPENDED) Predicate<AtomicReference<NodeMetadata>> nodeSuspended,
+                                InitializeRunScriptOnNodeOrPlaceInBadMap.Factory initScriptRunnerFactory,
+                                RunScriptOnNode.Factory runScriptOnNodeFactory, InitAdminAccess initAdminAccess,
+                                PersistNodeCredentials persistNodeCredentials, Timeouts timeouts,
+                                @Named(Constants.PROPERTY_USER_THREADS) ListeningExecutorService userExecutor,
+                                 CleanupResources cleanupResources,
+                                Optional<ImageExtension> imageExtension,
+                                Optional<SecurityGroupExtension> securityGroupExtension) {
+      super(context, credentialStore, images, sizes, locations, listNodesStrategy, getImageStrategy,
+              getNodeMetadataStrategy, runNodesAndAddToSetStrategy, rebootNodeStrategy, destroyNodeStrategy,
+              startNodeStrategy, stopNodeStrategy, templateBuilderProvider, templateOptionsProvider, nodeRunning,
+              nodeTerminated, nodeSuspended, initScriptRunnerFactory, initAdminAccess, runScriptOnNodeFactory,
+              persistNodeCredentials, timeouts, userExecutor, imageExtension, securityGroupExtension);
+      this.cleanupResources = checkNotNull(cleanupResources, "cleanupResources");
+
+   }
+
+   @Override
+   protected void cleanUpIncidentalResourcesOfDeadNodes(Set<? extends NodeMetadata> deadNodes) {
+      for (NodeMetadata deadNode : deadNodes) {
+         cleanupResources.apply(deadNode.getId());
+      }
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/03f72d5e/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/AzureComputeServiceAdapter.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/AzureComputeServiceAdapter.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/AzureComputeServiceAdapter.java
new file mode 100644
index 0000000..9a1d221
--- /dev/null
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/AzureComputeServiceAdapter.java
@@ -0,0 +1,393 @@
+/*
+ * 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.azurecompute.arm.compute;
+
+import static java.lang.String.format;
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.jclouds.util.Predicates2.retry;
+import java.util.ArrayList;
+
+import java.util.Collection;
+
+import java.util.List;
+import java.util.Set;
+
+import javax.annotation.Resource;
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.Multimap;
+import com.google.common.net.UrlEscapers;
+import org.jclouds.azurecompute.arm.AzureComputeApi;
+import org.jclouds.azurecompute.arm.compute.config.AzureComputeServiceContextModule.AzureComputeConstants;
+import org.jclouds.azurecompute.arm.compute.functions.VMImageToImage;
+import org.jclouds.azurecompute.arm.domain.Deployment;
+import org.jclouds.azurecompute.arm.domain.DeploymentBody;
+import org.jclouds.azurecompute.arm.domain.DeploymentProperties;
+import org.jclouds.azurecompute.arm.domain.VMImage;
+import org.jclouds.azurecompute.arm.domain.VMHardware;
+import org.jclouds.azurecompute.arm.domain.Location;
+import org.jclouds.azurecompute.arm.domain.Offer;
+import org.jclouds.azurecompute.arm.domain.PublicIPAddress;
+import org.jclouds.azurecompute.arm.domain.ResourceProviderMetaData;
+import org.jclouds.azurecompute.arm.domain.SKU;
+import org.jclouds.azurecompute.arm.domain.VMDeployment;
+import org.jclouds.azurecompute.arm.domain.VMSize;
+import org.jclouds.azurecompute.arm.domain.VirtualMachine;
+import org.jclouds.azurecompute.arm.features.DeploymentApi;
+import org.jclouds.azurecompute.arm.features.OSImageApi;
+import org.jclouds.azurecompute.arm.features.VirtualMachineApi;
+import org.jclouds.azurecompute.arm.util.DeploymentTemplateBuilder;
+import org.jclouds.compute.ComputeServiceAdapter;
+import org.jclouds.compute.domain.Template;
+import org.jclouds.compute.reference.ComputeServiceConstants;
+import org.jclouds.domain.LoginCredentials;
+import org.jclouds.logging.Logger;
+import org.jclouds.azurecompute.arm.functions.CleanupResources;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import com.google.common.base.Splitter;
+import static com.google.common.base.Preconditions.checkState;
+
+/**
+ * Defines the connection between the {@link AzureComputeApi} implementation and the jclouds
+ * {@link org.jclouds.compute.ComputeService}.
+ */
+@Singleton
+public class AzureComputeServiceAdapter implements ComputeServiceAdapter<VMDeployment, VMHardware, VMImage, Location> {
+
+   private String azureGroup;
+   protected final CleanupResources cleanupResources;
+
+   @Resource
+   @Named(ComputeServiceConstants.COMPUTE_LOGGER)
+   private Logger logger = Logger.NULL;
+
+   private final AzureComputeApi api;
+
+   private final AzureComputeConstants azureComputeConstants;
+
+   @Inject
+   AzureComputeServiceAdapter(final AzureComputeApi api, final AzureComputeConstants azureComputeConstants,
+                              CleanupResources cleanupResources) {
+
+      this.api = api;
+      this.azureComputeConstants = azureComputeConstants;
+
+      this.azureGroup = azureComputeConstants.azureResourceGroup();
+
+      logger.debug("AzureComputeServiceAdapter set azuregroup to: " + azureGroup);
+
+      this.cleanupResources = cleanupResources;
+   }
+
+   @Override
+   public NodeAndInitialCredentials<VMDeployment> createNodeWithGroupEncodedIntoName(
+           final String group, final String name, final Template template) {
+
+      DeploymentTemplateBuilder deploymentTemplateBuilder = api.deploymentTemplateFactory().create(group, name, template);
+
+      final String loginUser = DeploymentTemplateBuilder.getLoginUserUsername();
+      final String loginPassword = DeploymentTemplateBuilder.getLoginPassword();
+
+      DeploymentBody deploymentTemplateBody =  deploymentTemplateBuilder.getDeploymentTemplate();
+
+      DeploymentProperties properties = DeploymentProperties.create(deploymentTemplateBody);
+
+      final String deploymentTemplate = UrlEscapers.urlFormParameterEscaper().escape(deploymentTemplateBuilder.getDeploymentTemplateJson(properties));
+
+      logger.debug("Deployment created with name: %s group: %s", name, group);
+
+
+
+      final Set<VMDeployment> deployments = Sets.newHashSet();
+
+      final DeploymentApi deploymentApi = api.getDeploymentApi(group);
+
+      if (!retry(new Predicate<String>() {
+         @Override
+         public boolean apply(final String name) {
+
+            Deployment deployment = deploymentApi.create(name, deploymentTemplate);
+
+            if (deployment != null) {
+               VMDeployment vmDeployment = new VMDeployment();
+               vmDeployment.deployment = deployment;
+               deployments.add(vmDeployment);
+            } else {
+               logger.debug("Failed to create deployment!");
+            }
+            return !deployments.isEmpty();
+         }
+      }, azureComputeConstants.operationTimeout(), 1, SECONDS).apply(name)) {
+         final String illegalStateExceptionMessage = format("Deployment %s was not created within %sms so it will be destroyed.",
+                 name, azureComputeConstants.operationTimeout());
+         logger.warn(illegalStateExceptionMessage);
+         destroyNode(name);
+         throw new IllegalStateException(illegalStateExceptionMessage);
+      }
+
+      final VMDeployment deployment = deployments.iterator().next();
+
+
+      return new NodeAndInitialCredentials<VMDeployment>(deployment, name,
+              LoginCredentials.builder().user(loginUser).identity(loginUser).password(loginPassword).authenticateSudo(true).build());
+   }
+
+   @Override
+   public Iterable<VMHardware> listHardwareProfiles() {
+
+      final List<VMHardware> hwProfiles = Lists.newArrayList();
+      final List<String> locationIds = Lists.newArrayList();
+
+      Iterable<Location> locations = listLocations();
+      for (Location location : locations){
+         locationIds.add(location.name());
+
+         Iterable<VMSize> vmSizes = api.getVMSizeApi(location.name()).list();
+
+         for (VMSize vmSize : vmSizes){
+            VMHardware hwProfile = new VMHardware();
+            hwProfile.name = vmSize.name();
+            hwProfile.numberOfCores = vmSize.numberOfCores();
+            hwProfile.osDiskSizeInMB = vmSize.osDiskSizeInMB();
+            hwProfile.resourceDiskSizeInMB = vmSize.resourceDiskSizeInMB();
+            hwProfile.memoryInMB = vmSize.memoryInMB();
+            hwProfile.maxDataDiskCount = vmSize.maxDataDiskCount();
+            hwProfile.location = location.name();
+            hwProfiles.add(hwProfile);
+         }
+
+      }
+
+      checkAndSetHwAvailability(hwProfiles, Sets.newHashSet(locationIds));
+
+      return hwProfiles;
+   }
+   private void checkAndSetHwAvailability(List<VMHardware> hwProfiles, Collection<String> locations) {
+      Multimap<String, String> hwMap = ArrayListMultimap.create();
+      for (VMHardware hw : hwProfiles) {
+         hwMap.put(hw.name, hw.location);
+      }
+
+      for (VMHardware hw : hwProfiles) {
+         hw.globallyAvailable = hwMap.get(hw.name).containsAll(locations);
+      }
+   }
+
+   private void getImagesFromPublisher(String publisherName, List<VMImage> osImagesRef, String location) {
+
+      OSImageApi osImageApi = api.getOSImageApi(location);
+
+      Iterable<Offer> offerList = osImageApi.listOffers(publisherName);
+
+      for (Offer offer : offerList) {
+         Iterable<SKU> skuList = osImageApi.listSKUs(publisherName, offer.name());
+
+         for (SKU sku : skuList) {
+            VMImage vmImage = new VMImage();
+            vmImage.publisher = publisherName;
+            vmImage.offer = offer.name();
+            vmImage.sku = sku.name();
+            vmImage.location = location;
+            osImagesRef.add(vmImage);
+         }
+      }
+   }
+
+   private List<VMImage> listImagesByLocation(String location) {
+      final List<VMImage> osImages = Lists.newArrayList();
+      Iterable<String> publishers = Splitter.on(',').trimResults().omitEmptyStrings().split(this.azureComputeConstants.azureImagePublishers());
+      for (String publisher : publishers) {
+         getImagesFromPublisher(publisher, osImages, location);
+      }
+      return osImages;
+   }
+
+   @Override
+   public Iterable<VMImage> listImages() {
+
+      final List<VMImage> osImages = Lists.newArrayList();
+      final List<String> locationIds = Lists.newArrayList();
+
+      for (Location location : listLocations()){
+         locationIds.add(location.name());
+         osImages.addAll(listImagesByLocation(location.name()));
+      }
+      checkAndSetImageAvailability(osImages, Sets.newHashSet(locationIds));
+      return osImages;
+   }
+
+   private void checkAndSetImageAvailability(List<VMImage> images, Collection<String> locations) {
+      Multimap<String, String> map = ArrayListMultimap.create();
+
+      for (VMImage image : images) {
+         map.put( image.offer + "/" + image.sku, image.location);
+      }
+
+      for (VMImage image : images) {
+         image.globallyAvailable = map.get(image.offer + "/" + image.sku).containsAll(locations);
+      }
+   }
+
+   @Override
+   public VMImage getImage(final String id) {
+      String[] fields = VMImageToImage.decodeFieldsFromUniqueId(id);
+
+      Iterable<VMImage> images = listImages();
+
+      for (VMImage image : images) {
+         String imageId = VMImageToImage.encodeFieldsToUniqueId(image);
+         if (id.equals(imageId)){
+            return image;
+         }
+      }
+      return null;
+   }
+
+   @Override
+   public Iterable<Location> listLocations() {
+      List<Location> locations = api.getLocationApi().list();
+
+      List<ResourceProviderMetaData> resources = api.getResourceProviderApi().get("Microsoft.Compute");
+
+      final List<String> vmLocations = new ArrayList<String>();
+
+      for (ResourceProviderMetaData m : resources){
+         if (m.resourceType().equals("virtualMachines")){
+            vmLocations.addAll(m.locations());
+            break;
+         }
+      }
+
+      Iterable<Location> result = Iterables.filter(locations, new Predicate<Location>() {
+         @Override
+         public boolean apply(Location input) {
+            return vmLocations.contains(input.displayName());
+         }
+      });
+
+      return  result;
+   }
+
+   private String getResourceGroupFromId(String id) {
+      String searchStr = "/resourceGroups/";
+      int indexStart = id.lastIndexOf(searchStr) + searchStr.length();
+      searchStr = "/providers/";
+      int indexEnd = id.lastIndexOf(searchStr);
+
+      String resourceGroup = id.substring(indexStart, indexEnd);
+      return resourceGroup;
+   }
+
+   @Override
+   public VMDeployment getNode(final String id) {
+      Deployment deployment = api.getDeploymentApi(azureGroup).get(id);
+      if (deployment == null)
+         return null;
+      String resourceGroup = getResourceGroupFromId(deployment.id());
+      VMDeployment vmDeployment = new VMDeployment();
+      vmDeployment.deployment = deployment;
+      List<PublicIPAddress> list = getIPAddresses(deployment);
+      vmDeployment.ipAddressList = list;
+
+      VirtualMachine vm = api.getVirtualMachineApi(azureGroup).get(id);
+      vmDeployment.virtualMachine = vm;
+      return vmDeployment;
+   }
+
+   @Override
+   public void destroyNode(final String id) {
+      checkState(cleanupResources.apply(id), "server(%s) and its resources still there after deleting!?", id);
+   }
+
+   @Override
+   public void rebootNode(final String id) {
+      api.getVirtualMachineApi(azureGroup).restart(id);
+   }
+
+   @Override
+   public void resumeNode(final String id) {
+      api.getVirtualMachineApi(azureGroup).start(id);
+   }
+
+   @Override
+   public void suspendNode(final String id) {
+      api.getVirtualMachineApi(azureGroup).stop(id);
+   }
+
+   private List<PublicIPAddress> getIPAddresses(Deployment deployment) {
+      List<PublicIPAddress> list = new ArrayList<PublicIPAddress>();
+      String resourceGroup = getResourceGroupFromId(deployment.id());
+
+      if (deployment.properties() != null && deployment.properties().dependencies() != null) {
+         List<Deployment.Dependency> dependencies = deployment.properties().dependencies();
+         for (int d = 0; d < dependencies.size(); d++) {
+            if (dependencies.get(d).resourceType().equals("Microsoft.Network/networkInterfaces")) {
+               List<Deployment.Dependency> dependsOn = dependencies.get(d).dependsOn();
+               for (int e = 0; e < dependsOn.size(); e++) {
+                  if (dependsOn.get(e).resourceType().equals("Microsoft.Network/publicIPAddresses")) {
+                     String resourceName = dependsOn.get(e).resourceName();
+                     PublicIPAddress ip = api.getPublicIPAddressApi(resourceGroup).get(resourceName);
+                     list.add(ip);
+                     break;
+                  }
+               }
+            }
+         }
+      }
+      return list;
+   }
+
+   @Override
+   public Iterable<VMDeployment> listNodes() {
+      List<Deployment> deployments = api.getDeploymentApi(azureGroup).list();
+
+      List<VMDeployment> vmDeployments = new ArrayList<VMDeployment>();
+
+      for (Deployment d : deployments){
+         VMDeployment vmDeployment = new VMDeployment();
+         vmDeployment.deployment = d;
+         VirtualMachineApi vmApi = api.getVirtualMachineApi(azureGroup);
+         vmDeployment.vm = vmApi.getInstanceDetails(d.name());
+         List<PublicIPAddress> list = getIPAddresses(d);
+         vmDeployment.ipAddressList = list;
+
+         VirtualMachine virtualMachine = vmApi.get(d.name());
+         vmDeployment.virtualMachine = virtualMachine;
+
+         vmDeployments.add(vmDeployment);
+      }
+      return vmDeployments;
+   }
+
+   @Override
+   public Iterable<VMDeployment> listNodesByIds(final Iterable<String> ids) {
+      return Iterables.filter(listNodes(), new Predicate<VMDeployment>() {
+         @Override
+         public boolean apply(final VMDeployment input) {
+            return Iterables.contains(ids, input.deployment.name());
+         }
+      });
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/03f72d5e/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/config/AzureComputeServiceContextModule.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/config/AzureComputeServiceContextModule.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/config/AzureComputeServiceContextModule.java
new file mode 100644
index 0000000..9844be4
--- /dev/null
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/config/AzureComputeServiceContextModule.java
@@ -0,0 +1,191 @@
+/*
+ * 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.azurecompute.arm.compute.config;
+
+import javax.inject.Named;
+import javax.inject.Singleton;
+import com.google.inject.Provides;
+
+import org.jclouds.azurecompute.arm.compute.AzureComputeServiceAdapter;
+import org.jclouds.azurecompute.arm.compute.functions.VMImageToImage;
+import org.jclouds.azurecompute.arm.compute.functions.DeploymentToNodeMetadata;
+import org.jclouds.azurecompute.arm.compute.functions.VMHardwareToHardware;
+import org.jclouds.azurecompute.arm.compute.functions.LocationToLocation;
+import org.jclouds.azurecompute.arm.domain.VMDeployment;
+import org.jclouds.azurecompute.arm.domain.VMHardware;
+import org.jclouds.azurecompute.arm.domain.VMImage;
+import org.jclouds.azurecompute.arm.domain.Location;
+import org.jclouds.azurecompute.arm.compute.strategy.CreateResourceGroupThenCreateNodes;
+import org.jclouds.azurecompute.arm.AzureComputeApi;
+import org.jclouds.azurecompute.arm.functions.ParseJobStatus;
+import org.jclouds.azurecompute.arm.compute.AzureComputeService;
+
+import org.jclouds.compute.ComputeServiceAdapter;
+import org.jclouds.compute.config.ComputeServiceAdapterContextModule;
+import org.jclouds.compute.domain.Hardware;
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.strategy.CreateNodesInGroupThenAddToSet;
+import org.jclouds.compute.reference.ComputeServiceConstants.Timeouts;
+import org.jclouds.compute.reference.ComputeServiceConstants.PollPeriod;
+import org.jclouds.compute.ComputeService;
+import static org.jclouds.util.Predicates2.retry;
+import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_TERMINATED;
+
+import com.google.common.base.Function;
+import com.google.inject.Inject;
+import com.google.inject.TypeLiteral;
+import com.google.common.base.Predicate;
+import static com.google.common.base.Preconditions.checkNotNull;
+import com.google.common.annotations.VisibleForTesting;
+import java.net.URI;
+
+
+import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.IMAGE_PUBLISHERS;
+import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.RESOURCE_GROUP_NAME;
+import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.OPERATION_TIMEOUT;
+import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.OPERATION_POLL_INITIAL_PERIOD;
+import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.OPERATION_POLL_MAX_PERIOD;
+import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.TCP_RULE_FORMAT;
+import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.TCP_RULE_REGEXP;
+import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.DEFAULT_IMAGE_LOGIN;
+import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.TIMEOUT_RESOURCE_DELETED;
+
+public class AzureComputeServiceContextModule
+        extends ComputeServiceAdapterContextModule<VMDeployment, VMHardware, VMImage, Location> {
+
+   @Override
+   protected void configure() {
+      super.configure();
+      bind(new TypeLiteral<ComputeServiceAdapter<VMDeployment, VMHardware, VMImage, Location>>() {
+      }).to(AzureComputeServiceAdapter.class);
+      bind(new TypeLiteral<Function<VMImage, org.jclouds.compute.domain.Image>>() {
+      }).to(VMImageToImage.class);
+      bind(new TypeLiteral<Function<VMHardware, Hardware>>() {
+      }).to(VMHardwareToHardware.class);
+      bind(new TypeLiteral<Function<VMDeployment, NodeMetadata>>() {
+      }).to(DeploymentToNodeMetadata.class);
+      bind(new TypeLiteral<Function<Location, org.jclouds.domain.Location>>() {
+      }).to(LocationToLocation.class);
+      bind(ComputeService.class).to(AzureComputeService.class);
+      install(new LocationsFromComputeServiceAdapterModule<VMDeployment, VMHardware, VMImage, Location>() {
+      });
+
+      bind(CreateNodesInGroupThenAddToSet.class).to(CreateResourceGroupThenCreateNodes.class);
+   }
+
+   @Singleton
+   public static class AzureComputeConstants {
+
+      @Named(OPERATION_TIMEOUT)
+      @Inject
+      private String operationTimeoutProperty;
+
+      @Named(OPERATION_POLL_INITIAL_PERIOD)
+      @Inject
+      private String operationPollInitialPeriodProperty;
+
+      @Named(OPERATION_POLL_MAX_PERIOD)
+      @Inject
+      private String operationPollMaxPeriodProperty;
+
+      @Named(TCP_RULE_FORMAT)
+      @Inject
+      private String tcpRuleFormatProperty;
+
+      @Named(TCP_RULE_REGEXP)
+      @Inject
+      private String tcpRuleRegexpProperty;
+
+      @Named(RESOURCE_GROUP_NAME)
+      @Inject
+      private String azureResourceGroupProperty;
+
+      @Named(IMAGE_PUBLISHERS)
+      @Inject
+      private String azureImagePublishersProperty;
+
+      @Named(DEFAULT_IMAGE_LOGIN)
+      @Inject
+      private String azureDefaultImageLoginProperty;
+
+      public Long operationTimeout() {
+         return Long.parseLong(operationTimeoutProperty);
+      }
+
+      public String azureResourceGroup() {
+         return azureResourceGroupProperty;
+      }
+
+      public String azureImagePublishers() {
+         return azureImagePublishersProperty;
+      }
+
+      public String azureDefaultImageLogin() {
+         return azureDefaultImageLoginProperty;
+      }
+
+      public Integer operationPollInitialPeriod() {
+         return Integer.parseInt(operationPollInitialPeriodProperty);
+      }
+
+      public Integer operationPollMaxPeriod() {
+         return Integer.parseInt(operationPollMaxPeriodProperty);
+      }
+
+      public String tcpRuleFormat() {
+         return tcpRuleFormatProperty;
+      }
+
+      public String tcpRuleRegexp() {
+         return tcpRuleRegexpProperty;
+      }
+   }
+
+   @Provides
+   @Named(TIMEOUT_NODE_TERMINATED)
+   protected Predicate<URI> provideNodeTerminatedPredicate(final AzureComputeApi api, Timeouts timeouts,
+                                                                  PollPeriod pollPeriod) {
+      return retry(new ActionDonePredicate(api), timeouts.nodeTerminated, pollPeriod.pollInitialPeriod,
+              pollPeriod.pollMaxPeriod);
+   }
+
+   @Provides
+   @Named(TIMEOUT_RESOURCE_DELETED)
+   protected Predicate<URI> provideResourceDeletedPredicate(final AzureComputeApi api, Timeouts timeouts,
+                                                           PollPeriod pollPeriod) {
+      return retry(new ActionDonePredicate(api), timeouts.nodeTerminated, pollPeriod.pollInitialPeriod,
+              pollPeriod.pollMaxPeriod);
+   }
+
+   @VisibleForTesting
+   static class ActionDonePredicate implements Predicate<URI> {
+
+      private final AzureComputeApi api;
+
+      public ActionDonePredicate(AzureComputeApi api) {
+         this.api = checkNotNull(api, "api must not be null");
+      }
+
+      @Override
+      public boolean apply(URI uri) {
+         checkNotNull(uri, "uri cannot be null");
+         return ParseJobStatus.JobStatus.DONE == api.getJobApi().jobStatus(uri);
+      }
+
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/03f72d5e/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/DeploymentToNodeMetadata.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/DeploymentToNodeMetadata.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/DeploymentToNodeMetadata.java
new file mode 100644
index 0000000..bccc63c
--- /dev/null
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/DeploymentToNodeMetadata.java
@@ -0,0 +1,207 @@
+/*
+ * 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.azurecompute.arm.compute.functions;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.inject.Inject;
+
+import com.google.common.collect.Sets;
+import org.jclouds.azurecompute.arm.AzureComputeApi;
+import org.jclouds.azurecompute.arm.domain.ComputeNode;
+import org.jclouds.azurecompute.arm.domain.Deployment;
+import org.jclouds.azurecompute.arm.domain.ImageReference;
+import org.jclouds.azurecompute.arm.domain.PublicIPAddress;
+import org.jclouds.azurecompute.arm.domain.VMDeployment;
+import org.jclouds.azurecompute.arm.domain.VMHardware;
+import org.jclouds.azurecompute.arm.domain.VMImage;
+import org.jclouds.azurecompute.arm.domain.VMSize;
+import org.jclouds.azurecompute.arm.domain.VirtualMachineInstance;
+import org.jclouds.azurecompute.arm.util.DeploymentTemplateBuilder;
+import org.jclouds.azurecompute.arm.util.GetEnumValue;
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.domain.NodeMetadataBuilder;
+import org.jclouds.compute.functions.GroupNamingConvention;
+import org.jclouds.domain.Credentials;
+import org.jclouds.domain.Location;
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableMap;
+import org.jclouds.domain.LoginCredentials;
+import org.jclouds.compute.domain.Image;
+import org.jclouds.compute.domain.Hardware;
+
+public class DeploymentToNodeMetadata implements Function<VMDeployment, NodeMetadata> {
+
+   public static final String AZURE_LOGIN_USERNAME = DeploymentTemplateBuilder.getLoginUserUsername();
+   public static final String AZURE_LOGIN_PASSWORD = DeploymentTemplateBuilder.getLoginPassword();
+
+   private static final Map<ComputeNode.Status, NodeMetadata.Status> INSTANCESTATUS_TO_NODESTATUS =
+           ImmutableMap.<ComputeNode.Status, NodeMetadata.Status>builder().
+                   put(ComputeNode.Status.GOOD, NodeMetadata.Status.RUNNING).
+                   put(ComputeNode.Status.BAD, NodeMetadata.Status.ERROR).
+                   put(ComputeNode.Status.UNRECOGNIZED, NodeMetadata.Status.UNRECOGNIZED).
+                   build();
+
+   // When using the Deployment API to deploy an ARM template, the deployment goes through
+   // stages.  Accepted -> Running -> Succeeded.  Only when the deployment has SUCCEEDED is
+   // the resource deployed using the template actually ready.
+   //
+   // To get details about the resource(s) deployed via template, one needs to query the
+   // various resources after the deployment has "SUCCEEDED".
+   private static final Map<Deployment.ProvisioningState, NodeMetadata.Status> STATUS_TO_NODESTATUS =
+           ImmutableMap.<Deployment.ProvisioningState, NodeMetadata.Status>builder().
+                   put(Deployment.ProvisioningState.ACCEPTED, NodeMetadata.Status.PENDING).
+                   put(Deployment.ProvisioningState.READY, NodeMetadata.Status.PENDING).
+                   put(Deployment.ProvisioningState.RUNNING, NodeMetadata.Status.PENDING).
+                   put(Deployment.ProvisioningState.CANCELED, NodeMetadata.Status.TERMINATED).
+                   put(Deployment.ProvisioningState.FAILED, NodeMetadata.Status.ERROR).
+                   put(Deployment.ProvisioningState.DELETED, NodeMetadata.Status.TERMINATED).
+                   put(Deployment.ProvisioningState.SUCCEEDED, NodeMetadata.Status.RUNNING).
+                   put(Deployment.ProvisioningState.UNRECOGNIZED, NodeMetadata.Status.UNRECOGNIZED).
+                   build();
+
+   public static Deployment.ProvisioningState provisioningStateFromString(final String text) {
+      return (Deployment.ProvisioningState) GetEnumValue.fromValueOrDefault(text, Deployment.ProvisioningState.UNRECOGNIZED);
+   }
+
+   private final AzureComputeApi api;
+
+   private final LocationToLocation locationToLocation;
+
+   private final GroupNamingConvention nodeNamingConvention;
+
+   private final VMImageToImage vmImageToImage;
+
+   private final VMHardwareToHardware vmHardwareToHardware;
+
+   private final Map<String, Credentials> credentialStore;
+
+   @Inject
+   DeploymentToNodeMetadata(
+           AzureComputeApi api,
+           LocationToLocation locationToLocation,
+           GroupNamingConvention.Factory namingConvention, VMImageToImage vmImageToImage,
+           VMHardwareToHardware vmHardwareToHardware, Map<String, Credentials> credentialStore) {
+
+      this.nodeNamingConvention = namingConvention.createWithoutPrefix();
+      this.locationToLocation = locationToLocation;
+      this.vmImageToImage = vmImageToImage;
+      this.vmHardwareToHardware = vmHardwareToHardware;
+      this.credentialStore = credentialStore;
+      this.api = api;
+   }
+
+   @Override
+   public NodeMetadata apply(final VMDeployment from) {
+      final NodeMetadataBuilder builder = new NodeMetadataBuilder();
+      final Deployment deployment = from.deployment;
+      builder.id(deployment.name());
+      builder.providerId(deployment.name());
+      builder.name(deployment.name());
+      String group =  this.nodeNamingConvention.extractGroup(deployment.name());
+      builder.group(group);
+
+      NodeMetadata.Status status = STATUS_TO_NODESTATUS.get(provisioningStateFromString(deployment.properties().provisioningState()));
+      if (status == NodeMetadata.Status.RUNNING && from.vm != null && from.vm.statuses() != null) {
+         List<VirtualMachineInstance.VirtualMachineStatus> statuses = from.vm.statuses();
+         for (int c = 0; c < statuses.size(); c++) {
+            if (statuses.get(c).code().substring(0, 10).equals("PowerState")) {
+               if (statuses.get(c).displayStatus().equals("VM running")) {
+                  status = NodeMetadata.Status.RUNNING;
+               } else if (statuses.get(c).displayStatus().equals("VM stopped")) {
+                  status = NodeMetadata.Status.SUSPENDED;
+               }
+               break;
+            }
+         }
+      }
+
+      builder.status(status);
+
+      Credentials credentials = credentialStore.get("node#" + from.deployment.name());
+      if (credentials == null) {
+         credentials = new Credentials(AZURE_LOGIN_USERNAME, AZURE_LOGIN_PASSWORD);
+      }
+      builder.credentials(LoginCredentials.fromCredentials(credentials));
+
+      final Set<String> publicIpAddresses = Sets.newLinkedHashSet();
+      if (from.ipAddressList != null) {
+         for (int c = 0; c < from.ipAddressList.size(); c++) {
+            PublicIPAddress ip = from.ipAddressList.get(c);
+            if (ip != null && ip.properties() != null && ip.properties().ipAddress() != null)
+            {
+               publicIpAddresses.add(ip.properties().ipAddress());
+               break;
+            }
+
+         }
+         if (publicIpAddresses.size() > 0)
+            builder.publicAddresses(publicIpAddresses);
+      }
+
+      org.jclouds.azurecompute.arm.domain.Location myLocation = null;
+      if (from.virtualMachine != null) {
+         String locationName = from.virtualMachine.location();
+         List<org.jclouds.azurecompute.arm.domain.Location> locations = api.getLocationApi().list();
+
+         for (org.jclouds.azurecompute.arm.domain.Location location : locations) {
+            if (location.name().equals(locationName)) {
+               myLocation = location;
+               break;
+            }
+         }
+         Location jLocation = this.locationToLocation.apply(myLocation);
+         builder.location(jLocation);
+
+         ImageReference imageReference = from.virtualMachine.properties().storageProfile().imageReference();
+
+         VMImage vmImage = new VMImage();
+         vmImage.publisher = imageReference.publisher();
+         vmImage.offer = imageReference.offer();
+         vmImage.sku = imageReference.sku();
+         vmImage.location = locationName;
+         Image image = vmImageToImage.apply(vmImage);
+         builder.imageId(image.getId());
+
+         VMSize myVMSize = null;
+         String vmSizeName = from.virtualMachine.properties().hardwareProfile().vmSize();
+         List<VMSize> vmSizes = api.getVMSizeApi(locationName).list();
+         for (VMSize vmSize : vmSizes) {
+            if (vmSize.name().equals(vmSizeName)) {
+               myVMSize = vmSize;
+               break;
+            }
+         }
+
+         VMHardware hwProfile = new VMHardware();
+         hwProfile.name = myVMSize.name();
+         hwProfile.numberOfCores = myVMSize.numberOfCores();
+         hwProfile.osDiskSizeInMB = myVMSize.osDiskSizeInMB();
+         hwProfile.resourceDiskSizeInMB = myVMSize.resourceDiskSizeInMB();
+         hwProfile.memoryInMB = myVMSize.memoryInMB();
+         hwProfile.maxDataDiskCount = myVMSize.maxDataDiskCount();
+         hwProfile.location = locationName;
+
+         Hardware hardware = vmHardwareToHardware.apply(hwProfile);
+         builder.hardware(hardware);
+      }
+
+      return builder.build();
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/03f72d5e/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/LocationToLocation.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/LocationToLocation.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/LocationToLocation.java
index a4d4b1e..0ca1458 100644
--- a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/LocationToLocation.java
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/LocationToLocation.java
@@ -38,6 +38,7 @@ public class LocationToLocation implements Function<Location, org.jclouds.domain
 
    private final JustProvider justProvider;
 
+   // allow us to lazy discover the provider of a resource
    @Inject
    LocationToLocation(JustProvider justProvider) {
       this.justProvider = justProvider;
@@ -46,7 +47,11 @@ public class LocationToLocation implements Function<Location, org.jclouds.domain
    @Override
    public org.jclouds.domain.Location apply(final Location location) {
       final LocationBuilder builder = new LocationBuilder();
-      builder.id(location.id());
+      String id = location.id();
+      int index = id.lastIndexOf('/');
+      if (index > 0 && (index + 1) < id.length())
+         id = id.substring(index + 1);
+      builder.id(id);
       builder.description(location.displayName());
       builder.parent(getOnlyElement(justProvider.get()));
 

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/03f72d5e/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/VMHardwareToHardware.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/VMHardwareToHardware.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/VMHardwareToHardware.java
new file mode 100644
index 0000000..51a6e5e
--- /dev/null
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/VMHardwareToHardware.java
@@ -0,0 +1,79 @@
+/*
+ * 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.azurecompute.arm.compute.functions;
+
+import com.google.common.base.Supplier;
+import com.google.common.collect.FluentIterable;
+import com.google.inject.Inject;
+import org.jclouds.azurecompute.arm.domain.VMHardware;
+import org.jclouds.collect.Memoized;
+import org.jclouds.compute.domain.Hardware;
+import org.jclouds.compute.domain.HardwareBuilder;
+import org.jclouds.compute.domain.Processor;
+import org.jclouds.compute.domain.Volume;
+import org.jclouds.compute.domain.VolumeBuilder;
+
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import org.jclouds.domain.Location;
+import org.jclouds.location.predicates.LocationPredicates;
+
+import java.util.Set;
+
+public class VMHardwareToHardware implements Function<VMHardware, Hardware> {
+
+   private final Supplier<Set<? extends Location>> locations;
+
+   @Inject
+   VMHardwareToHardware(@Memoized final Supplier<Set<? extends Location>> locations) {
+      this.locations = locations;
+   }
+
+   @Override
+   public Hardware apply(VMHardware from) {
+      final HardwareBuilder builder = new HardwareBuilder()
+              .name(from.name)
+              .id(from.name)
+              .processors(ImmutableList.of(new Processor(from.numberOfCores, 2)))
+              .ram(from.memoryInMB)
+              .location(from.globallyAvailable ? null : FluentIterable.from(locations.get())
+                      .firstMatch(LocationPredicates.idEquals(from.location))
+                      .get());
+
+      // No id or providerId from Azure
+      if (from.resourceDiskSizeInMB != null) {
+         builder.volume(new VolumeBuilder()
+                 .size(Float.valueOf(from.resourceDiskSizeInMB))
+                 .type(Volume.Type.LOCAL)
+                 .build());
+      }
+      if (from.osDiskSizeInMB != null) {
+         builder.volume(new VolumeBuilder()
+                 .size(Float.valueOf(from.osDiskSizeInMB))
+                 .type(Volume.Type.LOCAL)
+                 .build());
+      }
+
+      ImmutableMap.Builder<String, String> metadata = ImmutableMap.builder();
+      metadata.put("maxDataDiskCount", String.valueOf(from.maxDataDiskCount));
+      builder.userMetadata(metadata.build());
+
+      return builder.build();
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/03f72d5e/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/VMImageToImage.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/VMImageToImage.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/VMImageToImage.java
new file mode 100644
index 0000000..65a3d4b
--- /dev/null
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/VMImageToImage.java
@@ -0,0 +1,124 @@
+/*
+ * 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.azurecompute.arm.compute.functions;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.base.Supplier;
+import com.google.common.collect.FluentIterable;
+import org.jclouds.azurecompute.arm.domain.VMImage;
+import org.jclouds.collect.Memoized;
+import org.jclouds.compute.domain.Image;
+import org.jclouds.compute.domain.ImageBuilder;
+import org.jclouds.compute.domain.OperatingSystem;
+import org.jclouds.compute.domain.OsFamily;
+
+import com.google.common.base.Function;
+import com.google.inject.Inject;
+import org.jclouds.domain.Location;
+import org.jclouds.location.predicates.LocationPredicates;
+
+import java.util.Set;
+
+public class VMImageToImage implements Function<VMImage, Image> {
+
+   private static final String UNRECOGNIZED = "UNRECOGNIZED";
+
+   private static final String UBUNTU = "Ubuntu";
+
+   private static final String WINDOWS = "Windows";
+
+   private static final String OPENLOGIC = "openLogic";
+
+   private static final String CENTOS = "CentOS";
+
+   private static final String COREOS = "CoreOS";
+
+   private static final String OPENSUSE = "openSUSE";
+
+   private static final String SUSE = "SUSE";
+
+   private static final String SQL_SERVER = "SQL Server";
+
+   private static final String ORACLE_lINUX = "Oracle Linux";
+
+   private final Supplier<Set<? extends org.jclouds.domain.Location>> locations;
+
+   public static String encodeFieldsToUniqueId(VMImage imageReference){
+      return (imageReference.globallyAvailable ? "global" : imageReference.location) + "/" + imageReference.publisher + "/" + imageReference.offer + "/" + imageReference.sku;
+   }
+
+   public static String[] decodeFieldsFromUniqueId(final String id) {
+      return checkNotNull(id, "id").split("/");
+   }
+
+   @Inject
+   VMImageToImage(@Memoized final Supplier<Set<? extends Location>> locations) {
+      this.locations = locations;
+   }
+
+   @Override
+   public Image apply(final VMImage image) {
+
+      final ImageBuilder builder = new ImageBuilder()
+              .name(image.offer)
+              .description(image.sku)
+              .status(Image.Status.AVAILABLE)
+              .version(image.sku)
+              .id(encodeFieldsToUniqueId(image))
+              .providerId(image.publisher)
+              .location(image.globallyAvailable ? null : FluentIterable.from(locations.get())
+                      .firstMatch(LocationPredicates.idEquals(image.location))
+                      .get());
+
+
+      final OperatingSystem.Builder osBuilder = osFamily().apply(image);
+      return builder.operatingSystem(osBuilder.build()).build();
+   }
+
+   public static Function<VMImage, OperatingSystem.Builder> osFamily() {
+      return new Function<VMImage, OperatingSystem.Builder>() {
+         @Override
+         public OperatingSystem.Builder apply(final VMImage image) {
+            checkNotNull(image.offer, "offer");
+            final String label = image.offer;
+
+            OsFamily family = OsFamily.UNRECOGNIZED;
+            if (label.contains(CENTOS)) {
+               family = OsFamily.CENTOS;
+            } else if (label.contains(OPENLOGIC)) {
+               family = OsFamily.CENTOS;
+            } else if (label.contains(SUSE)) {
+               family = OsFamily.SUSE;
+            } else if (label.contains(UBUNTU)) {
+               family = OsFamily.UBUNTU;
+            } else if (label.contains(WINDOWS)) {
+               family = OsFamily.WINDOWS;
+            } else if (label.contains(ORACLE_lINUX)) {
+               family = OsFamily.OEL;
+            }
+
+            // only 64bit OS images are supported by Azure ARM
+            return OperatingSystem.builder().
+                    family(family).
+                    is64Bit(true).
+                    description(image.sku).
+                    version(image.sku);
+         }
+      };
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/03f72d5e/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/strategy/CreateResourceGroupThenCreateNodes.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/strategy/CreateResourceGroupThenCreateNodes.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/strategy/CreateResourceGroupThenCreateNodes.java
new file mode 100644
index 0000000..6900f17
--- /dev/null
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/strategy/CreateResourceGroupThenCreateNodes.java
@@ -0,0 +1,96 @@
+/*
+ * 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.azurecompute.arm.compute.strategy;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Map;
+import java.util.Set;
+
+import javax.annotation.Resource;
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import com.google.common.collect.ImmutableMap;
+import org.jclouds.Constants;
+import org.jclouds.azurecompute.arm.domain.ResourceGroup;
+import org.jclouds.azurecompute.arm.features.ResourceGroupApi;
+import org.jclouds.compute.config.CustomizationResponse;
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.domain.Template;
+import org.jclouds.compute.functions.GroupNamingConvention;
+import org.jclouds.compute.reference.ComputeServiceConstants;
+import org.jclouds.compute.strategy.CreateNodeWithGroupEncodedIntoName;
+import org.jclouds.compute.strategy.CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap;
+import org.jclouds.compute.strategy.ListNodesStrategy;
+import org.jclouds.compute.strategy.impl.CreateNodesWithGroupEncodedIntoNameThenAddToSet;
+import org.jclouds.azurecompute.arm.AzureComputeApi;
+
+import org.jclouds.logging.Logger;
+import com.google.common.collect.Multimap;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+
+@Singleton
+public class CreateResourceGroupThenCreateNodes extends CreateNodesWithGroupEncodedIntoNameThenAddToSet {
+
+   @Resource
+   @Named(ComputeServiceConstants.COMPUTE_LOGGER)
+   protected Logger logger = Logger.NULL;
+
+   private final AzureComputeApi api;
+
+   @Inject
+   protected CreateResourceGroupThenCreateNodes(
+           CreateNodeWithGroupEncodedIntoName addNodeWithGroupStrategy,
+           ListNodesStrategy listNodesStrategy,
+           GroupNamingConvention.Factory namingConvention,
+           @Named(Constants.PROPERTY_USER_THREADS) ListeningExecutorService userExecutor,
+           CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap.Factory customizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapFactory,
+           AzureComputeApi api) {
+      super(addNodeWithGroupStrategy, listNodesStrategy, namingConvention, userExecutor,
+              customizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapFactory);
+      this.api = checkNotNull(api, "api cannot be null");
+      checkNotNull(userExecutor, "userExecutor cannot be null");
+   }
+
+   @Override
+   public Map<?, ListenableFuture<Void>> execute(String group, int count, Template template,
+                                                 Set<NodeMetadata> goodNodes, Map<NodeMetadata, Exception> badNodes,
+                                                 Multimap<NodeMetadata, CustomizationResponse> customizationResponses) {
+
+      ResourceGroupApi resourceGroupApi = api.getResourceGroupApi();
+      ResourceGroup resourceGroup = resourceGroupApi.get(group);
+      final String location = template.getLocation().getId();
+      final String resourceGroupName;
+
+      if (resourceGroup == null){
+
+         final Map<String, String> tags = ImmutableMap.of("description", "jClouds managed VMs");
+         resourceGroupName = resourceGroupApi.create(group, location, tags).name();
+      } else {
+         resourceGroupName = resourceGroup.name();
+      }
+
+      Map<?, ListenableFuture<Void>> responses = super.execute(resourceGroupName, count, template, goodNodes, badNodes,
+              customizationResponses);
+
+      return responses;
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/03f72d5e/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/config/AzureComputeProperties.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/config/AzureComputeProperties.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/config/AzureComputeProperties.java
index 48188c4..48d6287 100644
--- a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/config/AzureComputeProperties.java
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/config/AzureComputeProperties.java
@@ -33,4 +33,12 @@ public class AzureComputeProperties {
 
    public static final String STORAGE_API_VERSION = "2015-06-15";
 
+   public static final String RESOURCE_GROUP_NAME = "jclouds.azurecompute.arm.operation.resourcegroup";
+
+   public static final String IMAGE_PUBLISHERS = "jclouds.azurecompute.arm.publishers";
+
+   public static final String DEFAULT_IMAGE_LOGIN = "jclouds.azurecompute.arm.defaultimagelogin";
+
+   public static final String TIMEOUT_RESOURCE_DELETED = "jclouds.azurecompute.arm.timeout.resourcedeleted";
+
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/03f72d5e/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/ResourceProviderMetaData.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/ResourceProviderMetaData.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/ResourceProviderMetaData.java
new file mode 100644
index 0000000..84526b9
--- /dev/null
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/ResourceProviderMetaData.java
@@ -0,0 +1,68 @@
+/*
+ * 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.azurecompute.arm.domain;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableList;
+import org.jclouds.json.SerializedNames;
+
+import java.util.List;
+
+@AutoValue
+public abstract class ResourceProviderMetaData {
+
+   public abstract String resourceType();
+
+   public abstract List<String> locations();
+
+   public abstract List<String> apiVersions();
+
+   @SerializedNames({"resourceType", "locations", "apiVersions"})
+   public static ResourceProviderMetaData create(final String resourceType, final List<String> locations, final List<String> apiVersions) {
+      ResourceProviderMetaData.Builder builder = ResourceProviderMetaData.builder()
+              .resourceType(resourceType)
+              .locations(locations == null ? ImmutableList.<String>of() : ImmutableList.copyOf(locations))
+              .apiVersions(apiVersions == null ? ImmutableList.<String>of() : ImmutableList.copyOf(apiVersions));
+
+      return builder.build();
+   }
+
+   public static Builder builder() {
+      return new AutoValue_ResourceProviderMetaData.Builder();
+   }
+
+   @AutoValue.Builder
+   public abstract static class Builder {
+      public abstract Builder resourceType(String resourceType);
+
+      public abstract Builder locations(List<String> locations);
+
+      public abstract Builder apiVersions(List<String> apiVersions);
+
+      abstract List<String> locations();
+
+      abstract List<String> apiVersions();
+
+      abstract ResourceProviderMetaData autoBuild();
+
+      public ResourceProviderMetaData build() {
+         locations(locations() != null ? ImmutableList.copyOf(locations()) : ImmutableList.<String>of());
+         apiVersions(apiVersions() != null ? ImmutableList.copyOf(apiVersions()) : ImmutableList.<String>of());
+         return autoBuild();
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/03f72d5e/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/VMDeployment.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/VMDeployment.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/VMDeployment.java
index 2504409..6909a7b 100644
--- a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/VMDeployment.java
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/VMDeployment.java
@@ -26,4 +26,6 @@ public class VMDeployment {
    public List<PublicIPAddress> ipAddressList;
 
    public VirtualMachineInstance vm;
+
+   public VirtualMachine virtualMachine;
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/03f72d5e/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/VMHardware.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/VMHardware.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/VMHardware.java
new file mode 100644
index 0000000..d338327
--- /dev/null
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/VMHardware.java
@@ -0,0 +1,68 @@
+/*
+ * 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.azurecompute.arm.domain;
+
+import com.google.auto.value.AutoValue;
+
+/**
+ * A VM Size that is available in a region for a given subscription.
+ *
+ * @see <a href="https://msdn.microsoft.com/en-us/library/azure/mt269440.aspx" >api</a>
+ */
+@AutoValue
+public class VMHardware {
+
+   /**
+    * The name of the VM size.
+    */
+   public String name;
+
+   /**
+    * The number of cores that are available in the VM size.
+    */
+   public Integer numberOfCores;
+
+   /**
+    * Specifies the size in MB of the OS Disk.
+    */
+   public Integer osDiskSizeInMB;
+
+   /**
+    * The size of the resource disk.
+    */
+   public Integer resourceDiskSizeInMB;
+
+   /**
+    * Specifies the available RAM in MB.
+    */
+   public Integer memoryInMB;
+
+   /**
+    * Specifies the maximum number of data disks that can be attached to the VM size.
+    */
+   public Integer maxDataDiskCount;
+
+   /**
+    * Specifies the location of the HW resource
+    */
+   public String location;
+
+   /**
+    * Specifies if this HW is globally available
+    */
+   public boolean globallyAvailable;
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/03f72d5e/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/VMImage.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/VMImage.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/VMImage.java
new file mode 100644
index 0000000..ccfb05a
--- /dev/null
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/VMImage.java
@@ -0,0 +1,53 @@
+/*
+ * 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.azurecompute.arm.domain;
+
+import com.google.auto.value.AutoValue;
+
+@AutoValue
+public class VMImage {
+
+   /**
+    * The publisher of the image reference.
+    */
+   public String publisher;
+
+   /**
+    * The offer of the image reference.
+    */
+   public String offer;
+
+   /**
+    * The sku of the image reference.
+    */
+   public String sku;
+
+   /**
+    * The version of the image reference.
+    */
+   public String version;
+
+   /**
+    * The location from where Image was fetched
+    */
+   public String location;
+
+   /**
+    * Specifies if this image is globally available
+    */
+   public boolean globallyAvailable;
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/03f72d5e/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/VirtualMachine.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/VirtualMachine.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/VirtualMachine.java
index 71387e6..3013543 100644
--- a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/VirtualMachine.java
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/VirtualMachine.java
@@ -64,6 +64,6 @@ public abstract class VirtualMachine {
    public static VirtualMachine create(final String id, final String name, final String type, final String location,
                                        @Nullable final Map<String, String> tags, VirtualMachineProperties properties) {
 
-      return new AutoValue_VirtualMachine(id, name, location, type, tags == null ? null : ImmutableMap.copyOf(tags), properties);
+      return new AutoValue_VirtualMachine(id, name, type, location, tags == null ? null : ImmutableMap.copyOf(tags), properties);
    }
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/03f72d5e/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/features/ResourceProviderApi.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/features/ResourceProviderApi.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/features/ResourceProviderApi.java
new file mode 100644
index 0000000..e3d38b8
--- /dev/null
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/features/ResourceProviderApi.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.azurecompute.arm.features;
+
+
+import org.jclouds.Fallbacks.NullOnNotFoundOr404;
+import org.jclouds.azurecompute.arm.domain.ResourceProviderMetaData;
+import org.jclouds.javax.annotation.Nullable;
+import org.jclouds.oauth.v2.filters.OAuthFilter;
+import org.jclouds.rest.annotations.Fallback;
+import org.jclouds.rest.annotations.QueryParams;
+import org.jclouds.rest.annotations.RequestFilters;
+import org.jclouds.rest.annotations.SelectJson;
+
+import javax.inject.Named;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.core.MediaType;
+import java.io.Closeable;
+import java.util.List;
+
+/**
+ * The Azure Resource Provider API provides information about a resource provider and its supported resource types.
+ *
+ * @see <a href="https://msdn.microsoft.com/en-us/library/azure/dn790534.aspx">docs</a>
+ */
+@Path("/providers")
+
+@QueryParams(keys = "api-version", values = "2015-01-01")
+@RequestFilters(OAuthFilter.class)
+@Consumes(MediaType.APPLICATION_JSON)
+public interface ResourceProviderApi extends Closeable {
+
+   @Named("providers:get")
+   @GET
+   @Path("/{namespace}")
+   @SelectJson("resourceTypes")
+   @Fallback(NullOnNotFoundOr404.class)
+   @Nullable
+   List<ResourceProviderMetaData> get(@PathParam("namespace") String namespace);
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/03f72d5e/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/functions/CleanupResources.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/functions/CleanupResources.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/functions/CleanupResources.java
new file mode 100644
index 0000000..2b6a18e
--- /dev/null
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/functions/CleanupResources.java
@@ -0,0 +1,107 @@
+/*
+ * 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.azurecompute.arm.functions;
+
+import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.TIMEOUT_RESOURCE_DELETED;
+import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_TERMINATED;
+
+import javax.annotation.Resource;
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import com.google.common.base.Predicate;
+import org.jclouds.azurecompute.arm.AzureComputeApi;
+import org.jclouds.compute.reference.ComputeServiceConstants;
+import org.jclouds.logging.Logger;
+
+import com.google.common.base.Function;
+
+import java.net.URI;
+
+@Singleton
+public class CleanupResources implements Function<String, Boolean> {
+
+   @Resource
+   @Named(ComputeServiceConstants.COMPUTE_LOGGER)
+   protected Logger logger = Logger.NULL;
+
+   protected final AzureComputeApi api;
+   private Predicate<URI> nodeTerminated;
+   private Predicate<URI> resourceDeleted;
+
+   @Inject
+   public CleanupResources(AzureComputeApi azureComputeApi,
+                           @Named(TIMEOUT_NODE_TERMINATED) Predicate<URI> nodeTerminated,
+                           @Named(TIMEOUT_RESOURCE_DELETED) Predicate<URI> resourceDeleted) {
+
+      this.api = azureComputeApi;
+      this.nodeTerminated = nodeTerminated;
+      this.resourceDeleted = resourceDeleted;
+   }
+
+   @Override
+   public Boolean apply(String id) {
+
+      logger.debug("Destroying %s ...", id);
+      String storageAccountName = id.replaceAll("[^A-Za-z0-9 ]", "") + "storage";
+      int index = id.lastIndexOf("-");
+      String group = id.substring(0, index);
+
+      // Delete VM
+      URI uri = api.getVirtualMachineApi(group).delete(id);
+      if (uri != null){
+         boolean jobDone = nodeTerminated.apply(uri);
+
+         if (jobDone) {
+            // Delete storage account
+            api.getStorageAccountApi(group).delete(storageAccountName);
+
+            // Delete NIC
+            uri = api.getNetworkInterfaceCardApi(group).delete(id + "nic");
+            if (uri != null){
+               jobDone = resourceDeleted.apply(uri);
+               if (jobDone) {
+
+                  // Delete deployment
+                  uri = api.getDeploymentApi(group).delete(id);
+                  jobDone = resourceDeleted.apply(uri);
+                  if (jobDone) {
+                     // Delete public ip
+                     boolean ipDeleteStatus = api.getPublicIPAddressApi(group).delete(id + "publicip");
+
+                     // Delete Virtual network
+                     boolean vnetDeleteStatus = api.getVirtualNetworkApi(group).delete(group + "virtualnetwork");
+                     return ipDeleteStatus && vnetDeleteStatus;
+
+                  } else {
+                     return false;
+                  }
+               } else {
+                  return false;
+               }
+            } else {
+               return false;
+            }
+         } else {
+            return false;
+         }
+      } else {
+         return false;
+      }
+   }
+}