You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jclouds.apache.org by za...@apache.org on 2014/01/07 17:55:32 UTC

[2/2] git commit: JCLOUDS-418 Add schedule Scaling Policy type

JCLOUDS-418 Add schedule Scaling Policy type


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

Branch: refs/heads/1.7.x
Commit: 24a37bde7dde45b9ee2f14242352435f2ec3d85c
Parents: bf5d834
Author: Zack Shoylev <za...@rackspace.com>
Authored: Thu Jan 2 18:42:42 2014 -0600
Committer: Zack Shoylev <za...@rackspace.com>
Committed: Tue Jan 7 10:52:34 2014 -0600

----------------------------------------------------------------------
 .../autoscale/v1/domain/ScalingPolicy.java      | 146 +++++++++++++++++--
 .../v1/domain/ScalingPolicyResponse.java        |   7 +-
 .../v1/functions/ParseGroupResponse.java        |   1 +
 .../functions/ParseScalingPoliciesResponse.java |   1 +
 .../functions/ParseScalingPolicyResponse.java   |   1 +
 .../autoscale/v1/internal/ParseHelper.java      |  10 +-
 .../v1/features/ScalingPolicyApiLiveTest.java   |  55 +++++++
 .../v1/features/ScalingPolicyApiMockTest.java   |  95 ++++++++++++
 .../v1/features/WebhookApiLiveTest.java         |  30 ++--
 ...scale_policy_schedule_at_create_request.json |  11 ++
 ...cale_policy_schedule_at_create_response.json |  20 +++
 ...ale_policy_schedule_cron_create_request.json |  11 ++
 ...le_policy_schedule_cron_create_response.json |  20 +++
 13 files changed, 380 insertions(+), 28 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/24a37bde/rackspace-autoscale/src/main/java/org/jclouds/rackspace/autoscale/v1/domain/ScalingPolicy.java
----------------------------------------------------------------------
diff --git a/rackspace-autoscale/src/main/java/org/jclouds/rackspace/autoscale/v1/domain/ScalingPolicy.java b/rackspace-autoscale/src/main/java/org/jclouds/rackspace/autoscale/v1/domain/ScalingPolicy.java
index cfc2163..6420ee3 100644
--- a/rackspace-autoscale/src/main/java/org/jclouds/rackspace/autoscale/v1/domain/ScalingPolicy.java
+++ b/rackspace-autoscale/src/main/java/org/jclouds/rackspace/autoscale/v1/domain/ScalingPolicy.java
@@ -21,14 +21,17 @@ import static com.google.common.base.Preconditions.checkNotNull;
 
 import java.beans.ConstructorProperties;
 import java.util.EnumSet;
+import java.util.Map;
 
 
 import com.google.common.base.Objects;
 import com.google.common.base.Objects.ToStringHelper;
 import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
 
 /**
- * Autoscale ScalingPolicy. This class is used for requests.
+ * Auto Scale ScalingPolicy. This class is used for requests.
  * 
  * @see GroupApi#create(GroupConfiguration, LaunchConfiguration, java.util.List)
  * @see Group#getScalingPolicies()
@@ -41,18 +44,19 @@ public class ScalingPolicy implements Comparable<ScalingPolicy>{
    private final int cooldown;
    private final String target;
    private final ScalingPolicyTargetType targetType;
+   private final Map<String, String> args;
 
    @ConstructorProperties({
-      "name", "type", "cooldown", "target", "targetType"
+      "name", "type", "cooldown", "target", "targetType", "args"
    })
-   protected ScalingPolicy(String name, ScalingPolicyType type, int cooldown, String target, ScalingPolicyTargetType targetType) {
-
+   protected ScalingPolicy(String name, ScalingPolicyType type, int cooldown, String target, ScalingPolicyTargetType targetType, Map<String, String> args) {
       this.name = checkNotNull(name, "name required");
       this.type = type;
       checkArgument(cooldown >= 0, "cooldown should be non-negative");
       this.cooldown = cooldown;
       this.target = target;
       this.targetType = targetType;
+      this.args = args;
    }
 
    /**
@@ -98,9 +102,46 @@ public class ScalingPolicy implements Comparable<ScalingPolicy>{
       return this.targetType;
    }
 
+   /**
+    * @return The scheduling string, if any.
+    * @see ScalingPolicy.Builder#atSchedule(String)
+    * @see ScalingPolicy.Builder#cronSchedule(String)
+    */
+   protected Map<String, String> getSchedulingArgs() {
+      return this.args;
+   }
+
+   /**
+    * @return The scheduling string, if any.
+    * @see ScalingPolicy.Builder#atSchedule(String)
+    * @see ScalingPolicy.Builder#cronSchedule(String)
+    */
+   public String getSchedulingString() {
+      if (this.args != null) {
+         for (Map.Entry<String, String> entry : this.args.entrySet()) {
+            return entry.getValue();
+         }
+      }
+      return null;
+   }
+
+   /**
+    * @return The type of the schedule this policy uses.
+    */
+   public ScalingPolicyScheduleType getSchedulingType() {
+      if(this.args != null) {
+         for (ScalingPolicyScheduleType type : ScalingPolicyScheduleType.values()) {
+            if (this.args.get(type.toString()) != null) {
+               return type;
+            }
+         }
+      }
+      return null;
+   }
+
    @Override
    public int hashCode() {
-      return Objects.hashCode(name, type, cooldown, target, targetType);
+      return Objects.hashCode(name, type, cooldown, target, targetType, args);
    }
 
    @Override
@@ -112,7 +153,8 @@ public class ScalingPolicy implements Comparable<ScalingPolicy>{
             Objects.equal(this.type, that.type) &&
             Objects.equal(this.cooldown, that.cooldown) &&
             Objects.equal(this.target, that.target) &&
-            Objects.equal(this.targetType, that.targetType);
+            Objects.equal(this.targetType, that.targetType) &&
+            Objects.equal(this.args, that.args);
    }
 
    protected ToStringHelper string() {
@@ -121,7 +163,8 @@ public class ScalingPolicy implements Comparable<ScalingPolicy>{
             .add("type", type)
             .add("cooldown", cooldown)
             .add("target", target)
-            .add("targetType", "targetType");
+            .add("targetType", targetType)
+            .add("args", args);
    }
 
    @Override
@@ -143,6 +186,7 @@ public class ScalingPolicy implements Comparable<ScalingPolicy>{
       protected int cooldown;
       protected String target;
       protected ScalingPolicyTargetType targetType;
+      protected Map<String, String> args;
 
       /** 
        * @param name The name of this ScalingPolicy.
@@ -155,7 +199,7 @@ public class ScalingPolicy implements Comparable<ScalingPolicy>{
       }
 
       /** 
-       * @param name The type for this ScalingPolicy.
+       * @param type The type for this ScalingPolicy.
        * @return The builder object.
        * @see ScalingPolicyType
        * @see ScalingPolicy#getType()
@@ -186,7 +230,7 @@ public class ScalingPolicy implements Comparable<ScalingPolicy>{
       }
 
       /** 
-       * @param target type The target type of this ScalingPolicy.
+       * @param targetType The target type of this ScalingPolicy.
        * @return The builder object.
        * @see ScalingPolicyTargetType
        * @see ScalingPolicy#getTargetType()
@@ -196,20 +240,62 @@ public class ScalingPolicy implements Comparable<ScalingPolicy>{
          return this;
       }
 
+      /** 
+       * @param cron This parameter specifies the recurring time when the policy will be executed as a cron entry. 
+       * For example, if this is parameter is set to "1 0 * * *",
+       * the policy will be executed at one minute past midnight (00:01)
+       * every day of the month, and every day of the week.
+       * You can either provide "cron" or "at" for a given policy, but not both.
+       * @return The builder object.
+       * @see ScalingPolicyTargetType
+       * @see ScalingPolicy#getTargetType()
+       * @see <a href="http://en.wikipedia.org/wiki/Cron">Cron</a>
+       */
+      public Builder cronSchedule(String cron) {
+         this.type = ScalingPolicyType.SCHEDULE;
+         this.args = ImmutableMap.of("cron", cron);
+         return this;
+      }
+
+      /** 
+       * @param at This parameter specifies the time at which this policy will be executed.
+       * This property is mutually exclusive with the "cron" parameter.
+       * You can either provide "cron" or "at" for a given policy, but not both.
+       * Example date string: "2013-12-05T03:12:00Z"
+       * @return The builder object.
+       * @see ScalingPolicyTargetType
+       * @see ScalingPolicy#getTargetType()
+       */
+      public Builder atSchedule(String at) {
+         this.type = ScalingPolicyType.SCHEDULE;
+         this.args = ImmutableMap.of("at", at);
+         return this;
+      }
+
+      private Builder scheduleArgs(Map<String, String> args) {
+         this.args = args;
+         return this;
+      }
+
       /**
        * @return A new ScalingPolicy object.
        */
       public ScalingPolicy build() {
-         return new ScalingPolicy(name, type, cooldown, target, targetType);
+         return new ScalingPolicy(name, type, cooldown, target, targetType, args);
       }
 
+      /**
+       * @param in The target scaling policy
+       * @return The scaling policy builder
+       */
       public Builder fromScalingPolicy(ScalingPolicy in) {
          return this
                .name(in.getName())
                .type(in.getType())
                .cooldown(in.getCooldown())
                .target(in.getTarget())
-               .targetType(in.getTargetType());
+               .targetType(in.getTargetType())
+               .scheduleArgs(in.getSchedulingArgs());
       }        
    }
 
@@ -222,7 +308,8 @@ public class ScalingPolicy implements Comparable<ScalingPolicy>{
     * Enumerates different types of scaling policies
     */
    public static enum ScalingPolicyType {
-      WEBHOOK("webhook");
+      WEBHOOK("webhook"),
+      SCHEDULE("schedule");
 
       private final String name;
 
@@ -271,4 +358,39 @@ public class ScalingPolicy implements Comparable<ScalingPolicy>{
          return Optional.absent();
       }
    }
+
+   /**
+    * Enumerates different types of targets a policy might have
+    */
+   public static enum ScalingPolicyScheduleType {
+      /**
+       * Example: "1 0 * * *"
+       * @see ScalingPolicy.Builder#cronSchedule(String)
+       */
+      AT("at"),
+      /**
+       * Example date string: "2013-12-05T03:12:00Z"
+       * @see ScalingPolicy.Builder#atSchedule(String)
+       */
+      CRON("cron");
+
+      private final String name;
+
+      private ScalingPolicyScheduleType(String name) {
+         this.name = name;
+      }
+
+      public String toString() {
+         return name;
+      }
+
+      public static Optional<ScalingPolicyTargetType> getByValue(String value){
+         for (final ScalingPolicyTargetType element : EnumSet.allOf(ScalingPolicyTargetType.class)) {
+            if (element.toString().equals(value)) {
+               return Optional.of(element);
+            }
+         }
+         return Optional.absent();
+      }
+   }
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/24a37bde/rackspace-autoscale/src/main/java/org/jclouds/rackspace/autoscale/v1/domain/ScalingPolicyResponse.java
----------------------------------------------------------------------
diff --git a/rackspace-autoscale/src/main/java/org/jclouds/rackspace/autoscale/v1/domain/ScalingPolicyResponse.java b/rackspace-autoscale/src/main/java/org/jclouds/rackspace/autoscale/v1/domain/ScalingPolicyResponse.java
index eb1249e..0bf0302 100644
--- a/rackspace-autoscale/src/main/java/org/jclouds/rackspace/autoscale/v1/domain/ScalingPolicyResponse.java
+++ b/rackspace-autoscale/src/main/java/org/jclouds/rackspace/autoscale/v1/domain/ScalingPolicyResponse.java
@@ -20,6 +20,7 @@ import static com.google.common.base.Preconditions.checkNotNull;
 
 import java.beans.ConstructorProperties;
 import java.util.List;
+import java.util.Map;
 
 import org.jclouds.openstack.v2_0.domain.Link;
 
@@ -38,10 +39,10 @@ public class ScalingPolicyResponse extends ScalingPolicy{
    private final String id;
 
    @ConstructorProperties({
-      "name", "type", "cooldown", "target", "targetType", "links", "id"
+      "name", "type", "cooldown", "target", "targetType", "args", "links", "id"
    })
-   public ScalingPolicyResponse(String name, ScalingPolicyType type, int cooldown, String target, ScalingPolicyTargetType targetType, List<Link> links, String id) {
-      super(name, type, cooldown, target, targetType);
+   public ScalingPolicyResponse(String name, ScalingPolicyType type, int cooldown, String target, ScalingPolicyTargetType targetType, Map<String, String> args, List<Link> links, String id) {
+      super(name, type, cooldown, target, targetType, args);
       this.id = checkNotNull(id, "id required");
       this.links = ImmutableList.copyOf(checkNotNull(links, "links required"));
    }

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/24a37bde/rackspace-autoscale/src/main/java/org/jclouds/rackspace/autoscale/v1/functions/ParseGroupResponse.java
----------------------------------------------------------------------
diff --git a/rackspace-autoscale/src/main/java/org/jclouds/rackspace/autoscale/v1/functions/ParseGroupResponse.java b/rackspace-autoscale/src/main/java/org/jclouds/rackspace/autoscale/v1/functions/ParseGroupResponse.java
index 79e29a7..27d6309 100644
--- a/rackspace-autoscale/src/main/java/org/jclouds/rackspace/autoscale/v1/functions/ParseGroupResponse.java
+++ b/rackspace-autoscale/src/main/java/org/jclouds/rackspace/autoscale/v1/functions/ParseGroupResponse.java
@@ -132,6 +132,7 @@ public class ParseGroupResponse implements Function<HttpResponse, Group> {
                      ((Double)scalingPolicyMap.get("cooldown")).intValue(),
                      DoubleMath.isMathematicalInteger(d) ? Integer.toString(d.intValue()) : Double.toString(d), 
                            targetType,
+                           (Map<String, String>) scalingPolicyMap.get("args"),
                            ImmutableList.copyOf(links.build()),
                            (String) scalingPolicyMap.get("id")
                      );

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/24a37bde/rackspace-autoscale/src/main/java/org/jclouds/rackspace/autoscale/v1/functions/ParseScalingPoliciesResponse.java
----------------------------------------------------------------------
diff --git a/rackspace-autoscale/src/main/java/org/jclouds/rackspace/autoscale/v1/functions/ParseScalingPoliciesResponse.java b/rackspace-autoscale/src/main/java/org/jclouds/rackspace/autoscale/v1/functions/ParseScalingPoliciesResponse.java
index e02160c..c003575 100644
--- a/rackspace-autoscale/src/main/java/org/jclouds/rackspace/autoscale/v1/functions/ParseScalingPoliciesResponse.java
+++ b/rackspace-autoscale/src/main/java/org/jclouds/rackspace/autoscale/v1/functions/ParseScalingPoliciesResponse.java
@@ -84,6 +84,7 @@ public class ParseScalingPoliciesResponse implements Function<HttpResponse, Flue
                      ((Double)scalingPolicyMap.get("cooldown")).intValue(),
                      DoubleMath.isMathematicalInteger(d) ? Integer.toString(d.intValue()) : Double.toString(d),
                            targetType,
+                           (Map<String, String>) scalingPolicyMap.get("args"),
                            ImmutableList.copyOf(links.build()),
                            (String) scalingPolicyMap.get("id")
                      );

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/24a37bde/rackspace-autoscale/src/main/java/org/jclouds/rackspace/autoscale/v1/functions/ParseScalingPolicyResponse.java
----------------------------------------------------------------------
diff --git a/rackspace-autoscale/src/main/java/org/jclouds/rackspace/autoscale/v1/functions/ParseScalingPolicyResponse.java b/rackspace-autoscale/src/main/java/org/jclouds/rackspace/autoscale/v1/functions/ParseScalingPolicyResponse.java
index 968254d..392761f 100644
--- a/rackspace-autoscale/src/main/java/org/jclouds/rackspace/autoscale/v1/functions/ParseScalingPolicyResponse.java
+++ b/rackspace-autoscale/src/main/java/org/jclouds/rackspace/autoscale/v1/functions/ParseScalingPolicyResponse.java
@@ -81,6 +81,7 @@ public class ParseScalingPolicyResponse implements Function<HttpResponse, Scalin
                   ((Double)scalingPolicyMap.get("cooldown")).intValue(),
                   DoubleMath.isMathematicalInteger(d) ? Integer.toString(d.intValue()) : Double.toString(d),
                         targetType,
+                        (Map<String, String>) scalingPolicyMap.get("args"),
                         ImmutableList.copyOf(links.build()),
                         (String) scalingPolicyMap.get("id")
                   );

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/24a37bde/rackspace-autoscale/src/main/java/org/jclouds/rackspace/autoscale/v1/internal/ParseHelper.java
----------------------------------------------------------------------
diff --git a/rackspace-autoscale/src/main/java/org/jclouds/rackspace/autoscale/v1/internal/ParseHelper.java b/rackspace-autoscale/src/main/java/org/jclouds/rackspace/autoscale/v1/internal/ParseHelper.java
index 9e00579..8dc43f3 100644
--- a/rackspace-autoscale/src/main/java/org/jclouds/rackspace/autoscale/v1/internal/ParseHelper.java
+++ b/rackspace-autoscale/src/main/java/org/jclouds/rackspace/autoscale/v1/internal/ParseHelper.java
@@ -28,6 +28,8 @@ import com.google.common.collect.Maps;
 import com.google.common.primitives.Floats;
 import com.google.common.primitives.Ints;
 
+import static org.jclouds.rackspace.autoscale.v1.domain.ScalingPolicy.ScalingPolicyType;
+
 /**
  * @author Zack Shoylev
  * 
@@ -75,7 +77,7 @@ public class ParseHelper {
       }
       return scalingPoliciesListBuilder.build();
    }
-   
+
    public static ImmutableMap<String, Object> buildScalingPolicyMap(ScalingPolicy scalingPolicy) {
       ImmutableMap.Builder<String, Object> scalingPolicyMapBuilder = ImmutableMap.builder();
       scalingPolicyMapBuilder.put("cooldown", scalingPolicy.getCooldown());
@@ -97,6 +99,12 @@ public class ParseHelper {
          scalingPolicyMapBuilder.put(scalingPolicy.getTargetType().toString(), targetString);
       }
 
+      if (scalingPolicy.getSchedulingType() != null
+            && scalingPolicy.getType().equals(ScalingPolicyType.SCHEDULE)) {
+         // Have to use getters to rebuild map
+         scalingPolicyMapBuilder.put("args", ImmutableMap.of(scalingPolicy.getSchedulingType().toString(),scalingPolicy.getSchedulingString()));
+      }
+
       return scalingPolicyMapBuilder.build();
    }
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/24a37bde/rackspace-autoscale/src/test/java/org/jclouds/rackspace/autoscale/v1/features/ScalingPolicyApiLiveTest.java
----------------------------------------------------------------------
diff --git a/rackspace-autoscale/src/test/java/org/jclouds/rackspace/autoscale/v1/features/ScalingPolicyApiLiveTest.java b/rackspace-autoscale/src/test/java/org/jclouds/rackspace/autoscale/v1/features/ScalingPolicyApiLiveTest.java
index 124b9ba..cd1c664 100644
--- a/rackspace-autoscale/src/test/java/org/jclouds/rackspace/autoscale/v1/features/ScalingPolicyApiLiveTest.java
+++ b/rackspace-autoscale/src/test/java/org/jclouds/rackspace/autoscale/v1/features/ScalingPolicyApiLiveTest.java
@@ -32,6 +32,7 @@ import org.jclouds.rackspace.autoscale.v1.domain.LaunchConfiguration.LaunchConfi
 import org.jclouds.rackspace.autoscale.v1.domain.LoadBalancer;
 import org.jclouds.rackspace.autoscale.v1.domain.Personality;
 import org.jclouds.rackspace.autoscale.v1.domain.ScalingPolicy;
+import org.jclouds.rackspace.autoscale.v1.domain.ScalingPolicy.ScalingPolicyScheduleType;
 import org.jclouds.rackspace.autoscale.v1.domain.ScalingPolicy.ScalingPolicyTargetType;
 import org.jclouds.rackspace.autoscale.v1.domain.ScalingPolicy.ScalingPolicyType;
 import org.jclouds.rackspace.autoscale.v1.domain.ScalingPolicyResponse;
@@ -170,6 +171,60 @@ public class ScalingPolicyApiLiveTest extends BaseAutoscaleApiLiveTest {
    }
 
    @Test
+   public void testCreateScheduleCronPolicy() {
+      for (String zone : api.getConfiguredZones()) {
+
+         PolicyApi policyApi = api.getPolicyApiForZoneAndGroup(zone, created.get(zone).get(0).getId());
+
+         List<ScalingPolicy> scalingPolicies = Lists.newArrayList();
+
+         ScalingPolicy scalingPolicy = ScalingPolicy.builder()
+               .cooldown(3)
+               .type(ScalingPolicyType.SCHEDULE)
+               .name("scale up by one server")
+               .targetType(ScalingPolicyTargetType.INCREMENTAL)
+               .target("1")
+               .cronSchedule("23 * * * *")
+               .build();
+         scalingPolicies.add(scalingPolicy);
+
+         FluentIterable<ScalingPolicyResponse> scalingPolicyResponse = policyApi.create(scalingPolicies);
+         assertNotNull(scalingPolicyResponse.iterator().next().getId());
+         assertEquals(scalingPolicyResponse.iterator().next().getCooldown(), 3);
+         assertEquals(scalingPolicyResponse.iterator().next().getTarget(), "1");
+         assertEquals(scalingPolicyResponse.iterator().next().getSchedulingType(), ScalingPolicyScheduleType.CRON);
+         assertEquals(scalingPolicyResponse.iterator().next().getSchedulingString(), "23 * * * *");
+      }
+   }
+
+   @Test
+   public void testCreateScheduleAtPolicy() {
+      for (String zone : api.getConfiguredZones()) {
+
+         PolicyApi policyApi = api.getPolicyApiForZoneAndGroup(zone, created.get(zone).get(0).getId());
+
+         List<ScalingPolicy> scalingPolicies = Lists.newArrayList();
+
+         ScalingPolicy scalingPolicy = ScalingPolicy.builder()
+               .cooldown(3)
+               .type(ScalingPolicyType.SCHEDULE)
+               .name("scale up by one server")
+               .targetType(ScalingPolicyTargetType.INCREMENTAL)
+               .target("1")
+               .atSchedule("2020-12-05T03:12:00Z")
+               .build();
+         scalingPolicies.add(scalingPolicy);
+
+         FluentIterable<ScalingPolicyResponse> scalingPolicyResponse = policyApi.create(scalingPolicies);
+         assertNotNull(scalingPolicyResponse.iterator().next().getId());
+         assertEquals(scalingPolicyResponse.iterator().next().getCooldown(), 3);
+         assertEquals(scalingPolicyResponse.iterator().next().getTarget(), "1");
+         assertEquals(scalingPolicyResponse.iterator().next().getSchedulingType(), ScalingPolicyScheduleType.AT);
+         assertEquals(scalingPolicyResponse.iterator().next().getSchedulingString(), "2020-12-05T03:12:00Z");
+      }
+   }
+
+   @Test
    public void testListPolicy() {
       for (String zone : api.getConfiguredZones()) {
 

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/24a37bde/rackspace-autoscale/src/test/java/org/jclouds/rackspace/autoscale/v1/features/ScalingPolicyApiMockTest.java
----------------------------------------------------------------------
diff --git a/rackspace-autoscale/src/test/java/org/jclouds/rackspace/autoscale/v1/features/ScalingPolicyApiMockTest.java b/rackspace-autoscale/src/test/java/org/jclouds/rackspace/autoscale/v1/features/ScalingPolicyApiMockTest.java
index 2ec11f1..0a324cc 100644
--- a/rackspace-autoscale/src/test/java/org/jclouds/rackspace/autoscale/v1/features/ScalingPolicyApiMockTest.java
+++ b/rackspace-autoscale/src/test/java/org/jclouds/rackspace/autoscale/v1/features/ScalingPolicyApiMockTest.java
@@ -27,6 +27,7 @@ import java.util.List;
 
 import org.jclouds.rackspace.autoscale.v1.AutoscaleApi;
 import org.jclouds.rackspace.autoscale.v1.domain.ScalingPolicy;
+import org.jclouds.rackspace.autoscale.v1.domain.ScalingPolicy.ScalingPolicyScheduleType;
 import org.jclouds.rackspace.autoscale.v1.domain.ScalingPolicy.ScalingPolicyTargetType;
 import org.jclouds.rackspace.autoscale.v1.domain.ScalingPolicy.ScalingPolicyType;
 import org.jclouds.rackspace.autoscale.v1.domain.ScalingPolicyResponse;
@@ -126,6 +127,100 @@ public class ScalingPolicyApiMockTest extends BaseAutoscaleApiMockTest {
       }
    }
 
+   public void testCreateCronScheduleScalingPolicy() throws IOException, InterruptedException {
+      MockWebServer server = mockOpenStackServer();
+      server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
+      server.enqueue(addCommonHeaders(new MockResponse().setResponseCode(201).setBody(stringFromResource("/autoscale_policy_schedule_cron_create_response.json"))));
+
+      try {
+         AutoscaleApi autoscaleApi = api(server.getUrl("/").toString(), "rackspace-autoscale", overrides);
+         PolicyApi api = autoscaleApi.getPolicyApiForZoneAndGroup("DFW", "groupId1");         
+
+         List<ScalingPolicy> scalingPolicies = Lists.newArrayList();
+
+         ScalingPolicy scalingPolicy = ScalingPolicy.builder()
+               .cooldown(2)
+               .type(ScalingPolicyType.SCHEDULE)
+               .name("scale down by 5.5 percent at 11pm")
+               .targetType(ScalingPolicyTargetType.PERCENT_CHANGE)
+               .target("-5.5")
+               .cronSchedule("23 * * * *")
+               .build();
+         scalingPolicies.add(scalingPolicy);
+
+         FluentIterable<ScalingPolicyResponse> scalingPolicyResponse = api.create(scalingPolicies);
+
+         /*
+          * Check request
+          */
+         assertAuthentication(server);
+         assertRequest(server.takeRequest(), "POST", "/v1.0/888888/groups/groupId1/policies", "/autoscale_policy_schedule_cron_create_request.json");
+
+         /*
+          * Check response
+          */
+         assertNotNull(scalingPolicyResponse);
+         assertEquals(scalingPolicyResponse.size(), 1);
+         assertEquals(scalingPolicyResponse.get(0).getCooldown(), 2);
+         assertEquals(scalingPolicyResponse.get(0).getId(), "30707675-8e7c-4ea5-9358-c21648afcf29");
+         assertEquals(scalingPolicyResponse.get(0).getName(), "scale down by 5.5 percent at 11pm");
+         assertEquals(scalingPolicyResponse.get(0).getTarget(), "-5.5");
+         assertEquals(scalingPolicyResponse.get(0).getType(), ScalingPolicyType.SCHEDULE);
+         assertEquals(scalingPolicyResponse.get(0).getSchedulingType(), ScalingPolicyScheduleType.CRON);
+         assertEquals(scalingPolicyResponse.get(0).getSchedulingString(), "23 * * * *");
+         assertEquals(scalingPolicyResponse.get(0).getLinks().size(), 1);
+      } finally {
+         server.shutdown();
+      }
+   }
+
+   public void testCreateAtScheduleScalingPolicy() throws IOException, InterruptedException {
+      MockWebServer server = mockOpenStackServer();
+      server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
+      server.enqueue(addCommonHeaders(new MockResponse().setResponseCode(201).setBody(stringFromResource("/autoscale_policy_schedule_at_create_response.json"))));
+
+      try {
+         AutoscaleApi autoscaleApi = api(server.getUrl("/").toString(), "rackspace-autoscale", overrides);
+         PolicyApi api = autoscaleApi.getPolicyApiForZoneAndGroup("DFW", "groupId1");         
+
+         List<ScalingPolicy> scalingPolicies = Lists.newArrayList();
+
+         ScalingPolicy scalingPolicy = ScalingPolicy.builder()
+               .cooldown(2)
+               .type(ScalingPolicyType.SCHEDULE)
+               .name("scale down by 5.5 percent on the 5th")
+               .targetType(ScalingPolicyTargetType.PERCENT_CHANGE)
+               .target("-5.5")
+               .atSchedule("2013-06-05T03:12Z")
+               .build();
+         scalingPolicies.add(scalingPolicy);
+
+         FluentIterable<ScalingPolicyResponse> scalingPolicyResponse = api.create(scalingPolicies);
+
+         /*
+          * Check request
+          */
+         assertAuthentication(server);
+         assertRequest(server.takeRequest(), "POST", "/v1.0/888888/groups/groupId1/policies", "/autoscale_policy_schedule_at_create_request.json");
+
+         /*
+          * Check response
+          */
+         assertNotNull(scalingPolicyResponse);
+         assertEquals(scalingPolicyResponse.size(), 1);
+         assertEquals(scalingPolicyResponse.get(0).getCooldown(), 2);
+         assertEquals(scalingPolicyResponse.get(0).getId(), "9f7c5801-6b25-4f5a-af07-4bb752e23d53");
+         assertEquals(scalingPolicyResponse.get(0).getName(), "scale down by 5.5 percent on the 5th");
+         assertEquals(scalingPolicyResponse.get(0).getTarget(), "-5.5");
+         assertEquals(scalingPolicyResponse.get(0).getType(), ScalingPolicyType.SCHEDULE);
+         assertEquals(scalingPolicyResponse.get(0).getSchedulingType(), ScalingPolicyScheduleType.AT);
+         assertEquals(scalingPolicyResponse.get(0).getSchedulingString(), "2013-06-05T03:12Z");
+         assertEquals(scalingPolicyResponse.get(0).getLinks().size(), 1);
+      } finally {
+         server.shutdown();
+      }
+   }
+
    public void testListScalingPolicies() throws IOException, InterruptedException {
       MockWebServer server = mockOpenStackServer();
       server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/24a37bde/rackspace-autoscale/src/test/java/org/jclouds/rackspace/autoscale/v1/features/WebhookApiLiveTest.java
----------------------------------------------------------------------
diff --git a/rackspace-autoscale/src/test/java/org/jclouds/rackspace/autoscale/v1/features/WebhookApiLiveTest.java b/rackspace-autoscale/src/test/java/org/jclouds/rackspace/autoscale/v1/features/WebhookApiLiveTest.java
index 99d1457..58adad6 100644
--- a/rackspace-autoscale/src/test/java/org/jclouds/rackspace/autoscale/v1/features/WebhookApiLiveTest.java
+++ b/rackspace-autoscale/src/test/java/org/jclouds/rackspace/autoscale/v1/features/WebhookApiLiveTest.java
@@ -173,16 +173,16 @@ public class WebhookApiLiveTest extends BaseAutoscaleApiLiveTest {
       for (String zone : api.getConfiguredZones()) {
          Group g = created.get(zone).get(0);
          WebhookApi webhookApi = api.getWebhookApiForZoneAndGroupAndPolicy(zone, g.getId(), g.getScalingPolicies().iterator().next().getId());
-         FluentIterable<WebhookResponse> webhookList = webhookApi.create(
+         FluentIterable<WebhookResponse> webhookResponse = webhookApi.create(
                ImmutableList.of(
                      Webhook.builder().name("test5").metadata(null).build(),
                      Webhook.builder().name("test6").metadata(ImmutableMap.<String, Object>of("notes2", "different test")).build()
                      ));
 
-         assertEquals(webhookList.get(0).getName(), "test5");
-         assertNull(webhookList.get(0).getMetadata().get("notes"));
-         assertEquals(webhookList.get(1).getName(), "test6");
-         assertEquals(webhookList.get(1).getMetadata().get("notes2"), "different test");
+         assertEquals(webhookResponse.get(0).getName(), "test5");
+         assertNull(webhookResponse.get(0).getMetadata().get("notes"));
+         assertEquals(webhookResponse.get(1).getName(), "test6");
+         assertEquals(webhookResponse.get(1).getMetadata().get("notes2"), "different test");
       }
    }
 
@@ -204,12 +204,18 @@ public class WebhookApiLiveTest extends BaseAutoscaleApiLiveTest {
    public void testGetWebhook() {
       for (String zone : api.getConfiguredZones()) {
          Group g = created.get(zone).get(0);
-         WebhookApi webhookApi = api.getWebhookApiForZoneAndGroupAndPolicy(zone, g.getId(), g.getScalingPolicies().iterator().next().getId());
-         WebhookResponse webhookList = webhookApi.list().first().get();
-         WebhookResponse webhookGet = webhookApi.get(webhookList.getId());
-         assertNotNull(webhookList);
-         assertNotNull(webhookGet);
-         assertEquals(webhookList, webhookGet);
+         WebhookApi webhookApi;
+         boolean foundWebhook = false;
+         for (ScalingPolicyResponse sp :  g.getScalingPolicies()) {
+            webhookApi = api.getWebhookApiForZoneAndGroupAndPolicy(zone, g.getId(), sp.getId());
+            WebhookResponse webhookResponse = webhookApi.list().first().get();
+            if (webhookResponse != null) {
+               WebhookResponse webhookGet = webhookApi.get(webhookResponse.getId());
+               assertEquals(webhookResponse, webhookGet);
+               foundWebhook = true;
+            }
+         }
+         assertTrue(foundWebhook, "No webhooks were found, and some were expected");
       }
    }
 
@@ -255,7 +261,7 @@ public class WebhookApiLiveTest extends BaseAutoscaleApiLiveTest {
          WebhookApi webhookApi = api.getWebhookApiForZoneAndGroupAndPolicy(zone, g.getId(), g.getScalingPolicies().iterator().next().getId());
          WebhookResponse webhook = webhookApi.create("test_execute_fail", ImmutableMap.<String, Object>of("notes", "test metadata")).first().get();
          
-         URI uri = new URI(webhook.getAnonymousExecutionURI().toString() + "123");
+         URI uri = new URI(webhook.getAnonymousExecutionURI().get().toString() + "123");
          assertFalse( AutoscaleUtils.execute(uri) );
       }
    }

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/24a37bde/rackspace-autoscale/src/test/resources/autoscale_policy_schedule_at_create_request.json
----------------------------------------------------------------------
diff --git a/rackspace-autoscale/src/test/resources/autoscale_policy_schedule_at_create_request.json b/rackspace-autoscale/src/test/resources/autoscale_policy_schedule_at_create_request.json
new file mode 100644
index 0000000..17efdf9
--- /dev/null
+++ b/rackspace-autoscale/src/test/resources/autoscale_policy_schedule_at_create_request.json
@@ -0,0 +1,11 @@
+[
+    {
+        "name":"scale down by 5.5 percent on the 5th",
+        "changePercent":-5.5,
+        "cooldown":2,
+        "type":"schedule",
+        "args":{
+            "at":"2013-06-05T03:12Z"
+        }
+    }
+]

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/24a37bde/rackspace-autoscale/src/test/resources/autoscale_policy_schedule_at_create_response.json
----------------------------------------------------------------------
diff --git a/rackspace-autoscale/src/test/resources/autoscale_policy_schedule_at_create_response.json b/rackspace-autoscale/src/test/resources/autoscale_policy_schedule_at_create_response.json
new file mode 100644
index 0000000..d3b9b1c
--- /dev/null
+++ b/rackspace-autoscale/src/test/resources/autoscale_policy_schedule_at_create_response.json
@@ -0,0 +1,20 @@
+{
+    "policies":[
+        {
+            "args":{
+                "at":"2013-06-05T03:12Z"
+            },
+            "changePercent":-5.5,
+            "cooldown":2,
+            "id":"9f7c5801-6b25-4f5a-af07-4bb752e23d53",
+            "links":[
+                {
+                    "href":"https://dfw.autoscale.api.rackspacecloud.com/v1.0/676873/groups/605e13f6-1452-4588-b5da-ac6bb468c5bf/policies/9f7c5801-6b25-4f5a-af07-4bb752e23d53/",
+                    "rel":"self"
+                }
+            ],
+            "name":"scale down by 5.5 percent on the 5th",
+            "type":"schedule"
+        }
+    ]
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/24a37bde/rackspace-autoscale/src/test/resources/autoscale_policy_schedule_cron_create_request.json
----------------------------------------------------------------------
diff --git a/rackspace-autoscale/src/test/resources/autoscale_policy_schedule_cron_create_request.json b/rackspace-autoscale/src/test/resources/autoscale_policy_schedule_cron_create_request.json
new file mode 100644
index 0000000..9c12df9
--- /dev/null
+++ b/rackspace-autoscale/src/test/resources/autoscale_policy_schedule_cron_create_request.json
@@ -0,0 +1,11 @@
+[
+    {
+        "args":{
+            "cron":"23 * * * *"
+        },
+        "changePercent":-5.5,
+        "cooldown":2,
+        "name":"scale down by 5.5 percent at 11pm",
+        "type":"schedule"
+    }
+]

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/24a37bde/rackspace-autoscale/src/test/resources/autoscale_policy_schedule_cron_create_response.json
----------------------------------------------------------------------
diff --git a/rackspace-autoscale/src/test/resources/autoscale_policy_schedule_cron_create_response.json b/rackspace-autoscale/src/test/resources/autoscale_policy_schedule_cron_create_response.json
new file mode 100644
index 0000000..616529c
--- /dev/null
+++ b/rackspace-autoscale/src/test/resources/autoscale_policy_schedule_cron_create_response.json
@@ -0,0 +1,20 @@
+{
+    "policies":[
+        {
+            "args":{
+                "cron":"23 * * * *"
+            },
+            "changePercent":-5.5,
+            "cooldown":2,
+            "id":"30707675-8e7c-4ea5-9358-c21648afcf29",
+            "links":[
+                {
+                    "href":"https://dfw.autoscale.api.rackspacecloud.com/v1.0/676873/groups/605e13f6-1452-4588-b5da-ac6bb468c5bf/policies/30707675-8e7c-4ea5-9358-c21648afcf29/",
+                    "rel":"self"
+                }
+            ],
+            "name":"scale down by 5.5 percent at 11pm",
+            "type":"schedule"
+        }
+    ]
+}