Issue 309: support CRUD on instances, w/incidental security group fns

This commit is contained in:
Adrian Cole 2012-07-25 22:42:05 -07:00
parent 4ef5d60ce6
commit 0bc602a3d2
30 changed files with 2117 additions and 259 deletions

View File

@ -0,0 +1,79 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds 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.rds.binders;
import static com.google.common.base.Preconditions.checkNotNull;
import org.jclouds.http.HttpRequest;
import org.jclouds.rds.domain.InstanceRequest;
import com.google.common.collect.ImmutableMultimap;
/**
*
* @see <a
* href="http://docs.amazonwebservices.com/AmazonRDS/latest/APIReference/API_CreateDBInstance.html"
* >doc</a>
*
* @author Adrian Cole
*/
public class BindInstanceRequestToFormParams implements org.jclouds.rest.Binder {
/**
* {@inheritDoc}
*/
@SuppressWarnings("unchecked")
@Override
public <R extends HttpRequest> R bindToRequest(R request, Object input) {
InstanceRequest instanceRequest = InstanceRequest.class.cast(checkNotNull(input, "instanceRequest must be set!"));
ImmutableMultimap.Builder<String, String> formParameters = ImmutableMultimap.builder();
formParameters.put("AllocatedStorage", instanceRequest.getAllocatedStorageGB() + "");
formParameters.put("AutoMinorVersionUpgrade", instanceRequest.isAutoMinorVersionUpgrade() + "");
formParameters.put("BackupRetentionPeriod", instanceRequest.getBackupRetentionPeriod() + "");
if (instanceRequest.getCharacterSet().isPresent())
formParameters.put("CharacterSetName", instanceRequest.getCharacterSet().get());
formParameters.put("DBInstanceClass", instanceRequest.getInstanceClass());
if (instanceRequest.getName().isPresent())
formParameters.put("DBName", instanceRequest.getName().get());
if (instanceRequest.getParameterGroup().isPresent())
formParameters.put("DBParameterGroupName", instanceRequest.getParameterGroup().get());
int groupIndex = 1;
for (String securityGroup : instanceRequest.getSecurityGroups())
formParameters.put("DBSecurityGroups.member." + groupIndex++, securityGroup);
if (instanceRequest.getSubnetGroup().isPresent())
formParameters.put("DBSubnetGroupName", instanceRequest.getSubnetGroup().get());
formParameters.put("Engine", instanceRequest.getEngine());
if (instanceRequest.getEngineVersion().isPresent())
formParameters.put("EngineVersion", instanceRequest.getEngineVersion().get());
if (instanceRequest.getLicenseModel().isPresent())
formParameters.put("LicenseModel", instanceRequest.getLicenseModel().get());
formParameters.put("MasterUserPassword", instanceRequest.getMasterPassword());
formParameters.put("MasterUsername", instanceRequest.getMasterUsername());
if (instanceRequest.getOptionGroup().isPresent())
formParameters.put("OptionGroupName", instanceRequest.getOptionGroup().get());
if (instanceRequest.getPort().isPresent())
formParameters.put("Port", instanceRequest.getPort().get().toString());
return (R) request.toBuilder().replaceFormParams(formParameters.build()).build();
}
}

View File

@ -0,0 +1,151 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds 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.rds.domain;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.base.CaseFormat;
import com.google.common.base.Objects;
/**
*
* @author Adrian Cole
*/
public class Authorization {
/**
* Status of a source of traffic to the security group
*/
public static enum Status {
AUTHORIZING, AUTHORIZED, REVOKING, UNRECOGNIZED;
public String value() {
return name().toLowerCase();
}
@Override
public String toString() {
return value();
}
public static Status fromValue(String status) {
try {
return valueOf(CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_UNDERSCORE, checkNotNull(status, "status")));
} catch (IllegalArgumentException e) {
return UNRECOGNIZED;
}
}
}
public static Builder<?> builder() {
return new ConcreteBuilder();
}
public Builder<?> toBuilder() {
return new ConcreteBuilder().fromAuthorization(this);
}
public static abstract class Builder<T extends Builder<T>> {
protected abstract T self();
protected String rawStatus;
protected Status status;
/**
* @see Authorization#getRawStatus()
*/
public T rawStatus(String rawStatus) {
this.rawStatus = rawStatus;
return self();
}
/**
* @see Authorization#getStatus()
*/
public T status(Status status) {
this.status = status;
return self();
}
public T fromAuthorization(Authorization in) {
return this.rawStatus(in.getRawStatus()).status(in.getStatus());
}
}
private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
@Override
protected ConcreteBuilder self() {
return this;
}
}
protected final String rawStatus;
protected final Status status;
protected Authorization(String rawStatus, Status status) {
this.rawStatus = checkNotNull(rawStatus, "rawStatus");
this.status = checkNotNull(status, "status");
}
/**
* Specifies the status of the authorization.
*/
public Status getStatus() {
return status;
}
/**
* Specifies the status of the authorization.
*/
public String getRawStatus() {
return rawStatus;
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
return Objects.hashCode(rawStatus);
}
/**
* {@inheritDoc}
*/
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Authorization other = (Authorization) obj;
return Objects.equal(this.rawStatus, other.rawStatus);
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
return Objects.toStringHelper(this).omitNullValues().add("status", rawStatus).toString();
}
}

View File

@ -31,7 +31,7 @@ import com.google.common.base.Optional;
* *
* @author Adrian Cole * @author Adrian Cole
*/ */
public class EC2SecurityGroup { public class EC2SecurityGroup extends Authorization {
public static Builder builder() { public static Builder builder() {
return new Builder(); return new Builder();
@ -41,12 +41,11 @@ public class EC2SecurityGroup {
return new Builder().fromEC2SecurityGroup(this); return new Builder().fromEC2SecurityGroup(this);
} }
public static class Builder { public static class Builder extends Authorization.Builder<Builder> {
protected Optional<String> id = Optional.absent(); protected Optional<String> id = Optional.absent();
protected String name; protected String name;
protected String ownerId; protected String ownerId;
protected String status;
/** /**
* @see EC2SecurityGroup#getId() * @see EC2SecurityGroup#getId()
@ -72,33 +71,29 @@ public class EC2SecurityGroup {
return this; return this;
} }
/**
* @see EC2SecurityGroup#getStatus()
*/
public Builder status(String status) {
this.status = status;
return this;
}
public EC2SecurityGroup build() { public EC2SecurityGroup build() {
return new EC2SecurityGroup(id, name, ownerId, status); return new EC2SecurityGroup(id, name, ownerId, rawStatus, status);
} }
public Builder fromEC2SecurityGroup(EC2SecurityGroup in) { public Builder fromEC2SecurityGroup(EC2SecurityGroup in) {
return this.id(in.getId().orNull()).name(in.getName()).ownerId(in.getOwnerId()).status(in.getStatus()); return fromAuthorization(in).id(in.getId().orNull()).name(in.getName()).ownerId(in.getOwnerId());
}
@Override
protected Builder self() {
return this;
} }
} }
protected final Optional<String> id; protected final Optional<String> id;
protected final String name; protected final String name;
protected final String ownerId; protected final String ownerId;
protected final String status;
protected EC2SecurityGroup(Optional<String> id, String name, String ownerId, String status) { protected EC2SecurityGroup(Optional<String> id, String name, String ownerId, String rawStatus, Status status) {
super(rawStatus, status);
this.id = checkNotNull(id, "id"); this.id = checkNotNull(id, "id");
this.name = checkNotNull(name, "name"); this.name = checkNotNull(name, "name");
this.ownerId = checkNotNull(ownerId, "ownerId"); this.ownerId = checkNotNull(ownerId, "ownerId");
this.status = checkNotNull(status, "status");
} }
/** /**
@ -122,13 +117,6 @@ public class EC2SecurityGroup {
return ownerId; return ownerId;
} }
/**
* Provides the status of the EC2 security group.
*/
public String getStatus() {
return status;
}
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@ -159,7 +147,7 @@ public class EC2SecurityGroup {
@Override @Override
public String toString() { public String toString() {
return Objects.toStringHelper(this).omitNullValues().add("id", id.orNull()).add("name", name) return Objects.toStringHelper(this).omitNullValues().add("id", id.orNull()).add("name", name)
.add("ownerId", ownerId).add("status", status).toString(); .add("ownerId", ownerId).add("status", rawStatus).toString();
} }
} }

View File

@ -29,7 +29,7 @@ import com.google.common.base.Objects;
* *
* @author Adrian Cole * @author Adrian Cole
*/ */
public class IPRange { public class IPRange extends Authorization {
public static Builder builder() { public static Builder builder() {
return new Builder(); return new Builder();
@ -39,42 +39,37 @@ public class IPRange {
return new Builder().fromIPRange(this); return new Builder().fromIPRange(this);
} }
public static class Builder { public static class Builder extends Authorization.Builder<Builder> {
protected String cidrIp; protected String cidrIp;
protected String status;
/** /**
* @see IPRange#getAvailabilityZone() * @see IPRange#getCidrIp()
*/ */
public Builder cidrIp(String cidrIp) { public Builder cidrIp(String cidrIp) {
this.cidrIp = cidrIp; this.cidrIp = cidrIp;
return this; return this;
} }
/**
* @see IPRange#getStatus()
*/
public Builder status(String status) {
this.status = status;
return this;
}
public IPRange build() { public IPRange build() {
return new IPRange(cidrIp, status); return new IPRange(cidrIp, rawStatus, status);
} }
public Builder fromIPRange(IPRange in) { public Builder fromIPRange(IPRange in) {
return this.cidrIp(in.getCIDRIP()).status(in.getStatus()); return fromAuthorization(in).cidrIp(in.getCIDRIP());
}
@Override
protected Builder self() {
return this;
} }
} }
protected final String cidrIp; protected final String cidrIp;
protected final String status;
protected IPRange(String cidrIp, String status) { protected IPRange(String cidrIp, String rawStatus, Status status) {
super(rawStatus, status);
this.cidrIp = checkNotNull(cidrIp, "cidrIp"); this.cidrIp = checkNotNull(cidrIp, "cidrIp");
this.status = checkNotNull(status, "status");
} }
/** /**
@ -84,19 +79,12 @@ public class IPRange {
return cidrIp; return cidrIp;
} }
/**
* Specifies the status of the IP range.
*/
public String getStatus() {
return status;
}
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@Override @Override
public int hashCode() { public int hashCode() {
return Objects.hashCode(cidrIp, status); return Objects.hashCode(cidrIp, rawStatus);
} }
/** /**
@ -111,7 +99,7 @@ public class IPRange {
if (getClass() != obj.getClass()) if (getClass() != obj.getClass())
return false; return false;
IPRange other = IPRange.class.cast(obj); IPRange other = IPRange.class.cast(obj);
return Objects.equal(this.cidrIp, other.cidrIp) && Objects.equal(this.status, other.status); return Objects.equal(this.cidrIp, other.cidrIp) && Objects.equal(this.rawStatus, other.rawStatus);
} }
/** /**
@ -119,8 +107,7 @@ public class IPRange {
*/ */
@Override @Override
public String toString() { public String toString() {
return Objects.toStringHelper(this).omitNullValues().add("cidrIp", cidrIp) return Objects.toStringHelper(this).omitNullValues().add("cidrIp", cidrIp).add("status", rawStatus).toString();
.add("status", status).toString();
} }
} }

View File

@ -23,6 +23,9 @@ import static com.google.common.base.Preconditions.checkNotNull;
import java.util.Date; import java.util.Date;
import java.util.Map; import java.util.Map;
import org.jclouds.rds.domain.internal.BaseInstance;
import com.google.common.base.CaseFormat;
import com.google.common.base.Objects; import com.google.common.base.Objects;
import com.google.common.base.Optional; import com.google.common.base.Optional;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
@ -57,7 +60,41 @@ import com.google.common.net.HostAndPort;
* *
* @author Adrian Cole * @author Adrian Cole
*/ */
public class Instance { public class Instance extends BaseInstance {
public static enum Status {
/**
* the instance is in the process of being created
*/
CREATING,
/**
* the instance is available
*/
AVAILABLE, STORAGE_FULL, INCOMPATIBLE_OPTION_GROUP, INCOMPATIBLE_PARAMETERS, INCOMPATIBLE_RESTORE, FAILED,
/**
* the instance is deleting
*/
DELETING, UNRECOGNIZED;
public String value() {
return name().toLowerCase();
}
@Override
public String toString() {
return value();
}
public static Status fromValue(String status) {
try {
return valueOf(CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_UNDERSCORE, checkNotNull(status, "status")));
} catch (IllegalArgumentException e) {
return UNRECOGNIZED;
}
}
}
public static Builder<?> builder() { public static Builder<?> builder() {
return new ConcreteBuilder(); return new ConcreteBuilder();
} }
@ -66,22 +103,17 @@ public class Instance {
return new ConcreteBuilder().fromInstance(this); return new ConcreteBuilder().fromInstance(this);
} }
public static abstract class Builder<T extends Builder<T>> { public static abstract class Builder<T extends Builder<T>> extends BaseInstance.Builder<T> {
protected abstract T self();
protected String id; protected String id;
protected Optional<String> name = Optional.absent(); protected Optional<HostAndPort> endpoint = Optional.absent();
protected String instanceClass;
protected HostAndPort endpoint;
protected String status;
protected String availabilityZone;
protected boolean multiAZ;
protected String engine;
protected String engineVersion; protected String engineVersion;
protected String rawStatus;
protected Status status;
protected Optional<Date> createdTime = Optional.absent();
protected String licenseModel; protected String licenseModel;
protected String masterUsername; protected Optional<String> availabilityZone = Optional.absent();
protected int allocatedStorageGB; protected boolean multiAZ;
protected Date createdTime;
protected Optional<SubnetGroup> subnetGroup = Optional.absent(); protected Optional<SubnetGroup> subnetGroup = Optional.absent();
protected ImmutableMap.Builder<String, String> securityGroupNameToStatus = ImmutableMap protected ImmutableMap.Builder<String, String> securityGroupNameToStatus = ImmutableMap
.<String, String> builder(); .<String, String> builder();
@ -94,62 +126,30 @@ public class Instance {
return self(); return self();
} }
/**
* @see Instance#getName()
*/
public T name(String name) {
this.name = Optional.fromNullable(name);
return self();
}
/**
* @see Instance#getInstanceClass()
*/
public T instanceClass(String instanceClass) {
this.instanceClass = instanceClass;
return self();
}
/** /**
* @see Instance#getEndpoint() * @see Instance#getEndpoint()
*/ */
public T endpoint(HostAndPort endpoint) { public T endpoint(HostAndPort endpoint) {
this.endpoint = endpoint; this.endpoint = Optional.fromNullable(endpoint);
return self();
}
/**
* @see Instance#getRawStatus()
*/
public T rawStatus(String rawStatus) {
this.rawStatus = rawStatus;
return self(); return self();
} }
/** /**
* @see Instance#getStatus() * @see Instance#getStatus()
*/ */
public T status(String status) { public T status(Status status) {
this.status = status; this.status = status;
return self(); return self();
} }
/**
* @see Instance#getAvailabilityZone()
*/
public T availabilityZone(String availabilityZone) {
this.availabilityZone = availabilityZone;
return self();
}
/**
* @see Instance#isMultiAZ()
*/
public T multiAZ(boolean multiAZ) {
this.multiAZ = multiAZ;
return self();
}
/**
* @see Instance#getEngine()
*/
public T engine(String engine) {
this.engine = engine;
return self();
}
/** /**
* @see Instance#getEngineVersion() * @see Instance#getEngineVersion()
*/ */
@ -166,27 +166,27 @@ public class Instance {
return self(); return self();
} }
/**
* @see Instance#getMasterUsername()
*/
public T masterUsername(String masterUsername) {
this.masterUsername = masterUsername;
return self();
}
/**
* @see Instance#getAllocatedStorageGB()
*/
public T allocatedStorageGB(int allocatedStorageGB) {
this.allocatedStorageGB = allocatedStorageGB;
return self();
}
/** /**
* @see Instance#getCreatedTime() * @see Instance#getCreatedTime()
*/ */
public T createdTime(Date createdTime) { public T createdTime(Date createdTime) {
this.createdTime = createdTime; this.createdTime = Optional.fromNullable(createdTime);
return self();
}
/**
* @see Instance#getAvailabilityZone()
*/
public T availabilityZone(String availabilityZone) {
this.availabilityZone = Optional.fromNullable(availabilityZone);
return self();
}
/**
* @see Instance#isMultiAZ()
*/
public T multiAZ(boolean multiAZ) {
this.multiAZ = multiAZ;
return self(); return self();
} }
@ -216,18 +216,16 @@ public class Instance {
} }
public Instance build() { public Instance build() {
return new Instance(id, name, instanceClass, endpoint, status, availabilityZone, multiAZ, engine, return new Instance(id, name, instanceClass, endpoint, rawStatus, status, availabilityZone, multiAZ, engine,
engineVersion, licenseModel, masterUsername, allocatedStorageGB, createdTime, subnetGroup, engineVersion, licenseModel, masterUsername, allocatedStorageGB, createdTime, subnetGroup,
securityGroupNameToStatus.build()); securityGroupNameToStatus.build());
} }
public T fromInstance(Instance in) { public T fromInstance(Instance in) {
return this.id(in.getId()).name(in.getName().orNull()).instanceClass(in.getInstanceClass()) return fromBaseInstance(in).id(in.getId()).endpoint(in.getEndpoint().orNull()).status(in.getStatus())
.endpoint(in.getEndpoint()).status(in.getStatus()).availabilityZone(in.getAvailabilityZone()) .createdTime(in.getCreatedTime().orNull()).engineVersion(in.getEngineVersion())
.multiAZ(in.isMultiAZ()).engine(in.getEngine()).engineVersion(in.getEngineVersion()) .licenseModel(in.getLicenseModel()).availabilityZone(in.getAvailabilityZone().orNull())
.licenseModel(in.getLicenseModel()).masterUsername(in.getMasterUsername()) .multiAZ(in.isMultiAZ()).subnetGroup(in.getSubnetGroup().orNull())
.allocatedStorageGB(in.getAllocatedStorageGB()).createdTime(in.getCreatedTime())
.subnetGroup(in.getSubnetGroup().orNull())
.securityGroupNameToStatus(in.getSecurityGroupNameToStatus()); .securityGroupNameToStatus(in.getSecurityGroupNameToStatus());
} }
} }
@ -240,37 +238,30 @@ public class Instance {
} }
protected final String id; protected final String id;
protected final Optional<String> name; protected final Optional<HostAndPort> endpoint;
protected final String instanceClass; protected final String rawStatus;
protected final HostAndPort endpoint; protected final Status status;
protected final String status; protected final Optional<Date> createdTime;
protected final String availabilityZone;
protected final boolean multiAZ;
protected final String engine;
protected final String engineVersion; protected final String engineVersion;
protected final String licenseModel; protected final String licenseModel;
protected final String masterUsername; protected final Optional<String> availabilityZone;
protected int allocatedStorageGB; protected final boolean multiAZ;
protected final Date createdTime;
protected final Optional<SubnetGroup> subnetGroup; protected final Optional<SubnetGroup> subnetGroup;
protected final Map<String, String> securityGroupNameToStatus; protected final Map<String, String> securityGroupNameToStatus;
protected Instance(String id, Optional<String> name, String instanceClass, HostAndPort endpoint, String status, protected Instance(String id, Optional<String> name, String instanceClass, Optional<HostAndPort> endpoint,
String availabilityZone, boolean multiAZ, String engine, String engineVersion, String licenseModel, String rawStatus, Status status, Optional<String> availabilityZone, boolean multiAZ, String engine,
String masterUsername, int allocatedStorageGB, Date createdTime, Optional<SubnetGroup> subnetGroup, String engineVersion, String licenseModel, String masterUsername, int allocatedStorageGB,
Map<String, String> securityGroupNameToStatus) { Optional<Date> createdTime, Optional<SubnetGroup> subnetGroup, Map<String, String> securityGroupNameToStatus) {
super(name, instanceClass, engine, masterUsername, allocatedStorageGB);
this.id = checkNotNull(id, "id"); this.id = checkNotNull(id, "id");
this.name = checkNotNull(name, "name of %s", id);
this.endpoint = checkNotNull(endpoint, "endpoint of %s", id);
this.status = checkNotNull(status, "status of %s", id);
this.instanceClass = checkNotNull(instanceClass, "instanceClass of %s", id);
this.availabilityZone = checkNotNull(availabilityZone, "availabilityZone of %s", id); this.availabilityZone = checkNotNull(availabilityZone, "availabilityZone of %s", id);
this.multiAZ = multiAZ; this.multiAZ = multiAZ;
this.engine = checkNotNull(engine, "engine of %s", id); this.endpoint = checkNotNull(endpoint, "endpoint of %s", id);
this.rawStatus = checkNotNull(rawStatus, "rawStatus of %s", id);
this.status = checkNotNull(status, "status of %s", id);
this.engineVersion = checkNotNull(engineVersion, "engineVersion of %s", id); this.engineVersion = checkNotNull(engineVersion, "engineVersion of %s", id);
this.licenseModel = checkNotNull(licenseModel, "licenseModel of %s", id); this.licenseModel = checkNotNull(licenseModel, "licenseModel of %s", id);
this.masterUsername = checkNotNull(masterUsername, "masterUsername of %s", id);
this.allocatedStorageGB = allocatedStorageGB;
this.createdTime = checkNotNull(createdTime, "createdTime of %s", id); this.createdTime = checkNotNull(createdTime, "createdTime of %s", id);
this.subnetGroup = checkNotNull(subnetGroup, "subnetGroup of %s", id); this.subnetGroup = checkNotNull(subnetGroup, "subnetGroup of %s", id);
this.securityGroupNameToStatus = ImmutableMap.copyOf(checkNotNull(securityGroupNameToStatus, this.securityGroupNameToStatus = ImmutableMap.copyOf(checkNotNull(securityGroupNameToStatus,
@ -285,63 +276,33 @@ public class Instance {
return id; return id;
} }
/**
* The meaning of this parameter differs according to the database engine you use.
*
* <h4>MySQL</h4>
*
* Contains the name of the initial database of this instance that was provided at create time,
* if one was specified when the DB Instance was created. This same name is returned for the life
* of the DB Instance.
*
* <h4>Oracle</h4>
*
* Contains the Oracle System ID (SID) of the created DB Instance.
*/
public Optional<String> getName() {
return name;
}
/** /**
* Specifies the current state of this database. * Specifies the current state of this database.
*/ */
public String getStatus() { public Status getStatus() {
return status; return status;
} }
/** /**
* Contains the name of the compute and memory capacity class of the DB Instance. * Specifies the current state of this database unparsed.
*/ */
public String getInstanceClass() { public String getRawStatus() {
return instanceClass; return rawStatus;
} }
/** /**
* Specifies the connection endpoint. * Specifies the connection endpoint, or absent if the database is in {@link Status#CREATING} or {@link Status#DELETING} states
*/ */
public HostAndPort getEndpoint() { public Optional<HostAndPort> getEndpoint() {
return endpoint; return endpoint;
} }
/** /**
* Specifies the name of the Availability Zone the DB Instance is located in. * Provides the date and time the DB Instance was created, or absent if the database is in
* {@code creating} state
*/ */
public String getAvailabilityZone() { public Optional<Date> getCreatedTime() {
return availabilityZone; return createdTime;
}
/**
* Specifies the name of the Availability Zone the DB Instance is located in.
*/
public boolean isMultiAZ() {
return multiAZ;
}
/**
* Provides the name of the database engine to be used for this DB Instance.
*/
public String getEngine() {
return engine;
} }
/** /**
@ -359,24 +320,18 @@ public class Instance {
} }
/** /**
* Contains the master username for the DB Instance. * Specifies the name of the Availability Zone the DB Instance is located in, or absent if the
* database is in {@code creating} state
*/ */
public String getMasterUsername() { public Optional<String> getAvailabilityZone() {
return masterUsername; return availabilityZone;
} }
/** /**
* Specifies the allocated storage size specified in gigabytes. * Specifies the name of the Availability Zone the DB Instance is located in.
*/ */
public int getAllocatedStorageGB() { public boolean isMultiAZ() {
return allocatedStorageGB; return multiAZ;
}
/**
* Provides the date and time the DB Instance was created.
*/
public Date getCreatedTime() {
return createdTime;
} }
/** /**
@ -394,27 +349,19 @@ public class Instance {
return securityGroupNameToStatus; return securityGroupNameToStatus;
} }
/**
* {@inheritDoc}
*/
@Override @Override
public int hashCode() { public int hashCode() {
return Objects.hashCode(id, createdTime); return Objects.hashCode(id);
} }
/**
* {@inheritDoc}
*/
@Override @Override
public boolean equals(Object obj) { public boolean equals(Object obj) {
if (this == obj) if (this == obj)
return true; return true;
if (obj == null) if (obj == null || getClass() != obj.getClass())
return false; return false;
if (getClass() != obj.getClass()) Instance that = Instance.class.cast(obj);
return false; return Objects.equal(this.id, that.id);
Instance other = (Instance) obj;
return Objects.equal(this.id, other.id) && Objects.equal(this.createdTime, other.createdTime);
} }
/** /**
@ -423,11 +370,11 @@ public class Instance {
@Override @Override
public String toString() { public String toString() {
return Objects.toStringHelper(this).omitNullValues().add("id", id).add("name", name.orNull()) return Objects.toStringHelper(this).omitNullValues().add("id", id).add("name", name.orNull())
.add("instanceClass", instanceClass).add("endpoint", endpoint).add("status", status) .add("instanceClass", instanceClass).add("endpoint", endpoint.orNull()).add("status", rawStatus)
.add("availabilityZone", availabilityZone).add("multiAZ", multiAZ).add("engine", engine) .add("availabilityZone", availabilityZone.orNull()).add("multiAZ", multiAZ).add("engine", engine)
.add("engineVersion", engineVersion).add("licenseModel", licenseModel) .add("engineVersion", engineVersion).add("licenseModel", licenseModel)
.add("masterUsername", masterUsername).add("allocatedStorageGB", allocatedStorageGB) .add("masterUsername", masterUsername).add("allocatedStorageGB", allocatedStorageGB)
.add("createdTime", createdTime).add("subnetGroup", subnetGroup.orNull()) .add("createdTime", createdTime.orNull()).add("subnetGroup", subnetGroup.orNull())
.add("securityGroupNameToStatus", securityGroupNameToStatus).toString(); .add("securityGroupNameToStatus", securityGroupNameToStatus).toString();
} }

View File

@ -0,0 +1,374 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds 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.rds.domain;
import static com.google.common.base.Preconditions.checkNotNull;
import java.util.Set;
import org.jclouds.rds.domain.internal.BaseInstance;
import com.google.common.base.Objects.ToStringHelper;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableSet;
/**
* Parameters used to create a new {@link Instance}
*
* @see <a
* href="http://docs.amazonwebservices.com/AmazonRDS/latest/APIReference/API_CreateDBInstance.html"
* >doc</a>
*
* @author Adrian Cole
*/
public class InstanceRequest extends BaseInstance {
public static Builder<?> builder() {
return new ConcreteBuilder();
}
public Builder<?> toBuilder() {
return new ConcreteBuilder().fromInstance(this);
}
public static abstract class Builder<T extends Builder<T>> extends BaseInstance.Builder<T> {
protected Optional<String> engineVersion = Optional.absent();
protected Optional<String> licenseModel = Optional.absent();
protected Optional<Integer> port = Optional.absent();
protected Optional<String> characterSet = Optional.absent();
protected int backupRetentionPeriod = 1;
protected Optional<String> optionGroup = Optional.absent();
protected Optional<String> parameterGroup = Optional.absent();
protected boolean autoMinorVersionUpgrade = true;
protected Optional<String> subnetGroup = Optional.absent();
protected ImmutableSet.Builder<String> securityGroups = ImmutableSet.<String> builder();
protected String masterPassword;
/**
* @see InstanceRequest#getEngineVersion()
*/
public T engineVersion(String engineVersion) {
this.engineVersion = Optional.fromNullable(engineVersion);
return self();
}
/**
* @see InstanceRequest#getLicenseModel()
*/
public T licenseModel(String licenseModel) {
this.licenseModel = Optional.fromNullable(licenseModel);
return self();
}
/**
* @see InstanceRequest#getPort()
*/
public T port(Integer port) {
this.port = Optional.fromNullable(port);
return self();
}
/**
* @see InstanceRequest#getCharacterSet()
*/
public T characterSet(String characterSet) {
this.characterSet = Optional.fromNullable(characterSet);
return self();
}
/**
* @see InstanceRequest#getBackupRetentionPeriod()
*/
public T backupRetentionPeriod(int backupRetentionPeriod) {
this.backupRetentionPeriod = backupRetentionPeriod;
return self();
}
/**
* @see InstanceRequest#getOptionGroup()
*/
public T optionGroup(String optionGroup) {
this.optionGroup = Optional.fromNullable(optionGroup);
return self();
}
/**
* @see InstanceRequest#getParameterGroup()
*/
public T parameterGroup(String parameterGroup) {
this.parameterGroup = Optional.fromNullable(parameterGroup);
return self();
}
/**
* @see InstanceRequest#isAutoMinorVersionUpgrade()
*/
public T autoMinorVersionUpgrade(boolean autoMinorVersionUpgrade) {
this.autoMinorVersionUpgrade = autoMinorVersionUpgrade;
return self();
}
/**
* @see InstanceRequest#getSubnetGroup()
*/
public T subnetGroup(String subnetGroup) {
this.subnetGroup = Optional.fromNullable(subnetGroup);
return self();
}
/**
* @see InstanceRequest#getSecurityGroups()
*/
public T securityGroups(Iterable<String> securityGroups) {
this.securityGroups.addAll(checkNotNull(securityGroups, "securityGroups"));
return self();
}
/**
* @see InstanceRequest#getSecurityGroups()
*/
public T securityGroups(String securityGroupName) {
this.securityGroups.add(checkNotNull(securityGroupName, "securityGroupName"));
return self();
}
/**
* @see InstanceRequest#getMasterPassword()
*/
public T masterPassword(String masterPassword) {
this.masterPassword = checkNotNull(masterPassword, "masterPassword");
return self();
}
public InstanceRequest build() {
return new InstanceRequest(name, instanceClass, port, characterSet, optionGroup, parameterGroup,
autoMinorVersionUpgrade, engine, engineVersion, licenseModel, masterUsername, allocatedStorageGB,
backupRetentionPeriod, subnetGroup, securityGroups.build(), masterPassword);
}
public T fromInstance(InstanceRequest in) {
return fromBaseInstance(in).engineVersion(in.getEngineVersion().orNull())
.licenseModel(in.getLicenseModel().orNull()).port(in.getPort().orNull())
.characterSet(in.getCharacterSet().orNull()).backupRetentionPeriod(in.getBackupRetentionPeriod())
.optionGroup(in.getOptionGroup().orNull()).parameterGroup(in.getParameterGroup().orNull())
.autoMinorVersionUpgrade(in.isAutoMinorVersionUpgrade()).subnetGroup(in.getSubnetGroup().orNull())
.securityGroups(in.getSecurityGroups());
}
}
private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
@Override
protected ConcreteBuilder self() {
return this;
}
}
protected final Optional<String> engineVersion;
protected final Optional<String> licenseModel;
protected final Optional<Integer> port;
protected final Optional<String> characterSet;
protected final int backupRetentionPeriod;
protected final Optional<String> optionGroup;
protected final Optional<String> parameterGroup;
protected final boolean autoMinorVersionUpgrade;
protected final Optional<String> subnetGroup;
protected final Set<String> securityGroups;
protected final String masterPassword;
protected InstanceRequest(Optional<String> name, String instanceClass, Optional<Integer> port,
Optional<String> characterSet, Optional<String> optionGroup, Optional<String> parameterGroup,
boolean autoMinorVersionUpgrade, String engine, Optional<String> engineVersion,
Optional<String> licenseModel, String masterUsername, int allocatedStorageGB, int backupRetentionPeriod,
Optional<String> subnetGroup, Iterable<String> securityGroups, String masterPassword) {
super(name, instanceClass, engine, masterUsername, allocatedStorageGB);
this.engineVersion = checkNotNull(engineVersion, "engineVersion");
this.licenseModel = checkNotNull(licenseModel, "licenseModel");
this.optionGroup = checkNotNull(optionGroup, "optionGroup");
this.parameterGroup = checkNotNull(parameterGroup, "parameterGroup");
this.autoMinorVersionUpgrade = autoMinorVersionUpgrade;
this.port = checkNotNull(port, "port");
this.characterSet = checkNotNull(characterSet, "characterSet");
this.backupRetentionPeriod = checkNotNull(backupRetentionPeriod, "backupRetentionPeriod");
this.subnetGroup = checkNotNull(subnetGroup, "subnetGroup");
this.securityGroups = ImmutableSet.copyOf(checkNotNull(securityGroups, "securityGroups"));
this.masterPassword = checkNotNull(masterPassword, "masterPassword");
}
/**
* The version number of the database engine to use.
*
* MySQL
*
* Example: 5.1.42
*
* Oracle
*
* Example: 11.2.0.2.v2
*
* SQL Server
*
* Example: 10.50.2789.0.v1
*
*/
public Optional<String> getEngineVersion() {
return engineVersion;
}
/**
* License model information for this DB Instance.
*
* Valid values: license-included | bring-your-own-license | general-public-license
*/
public Optional<String> getLicenseModel() {
return licenseModel;
}
/**
* For supported engines, indicates that the DB Instance should be associated with the specified
* CharacterSet.
*/
public Optional<String> getCharacterSet() {
return characterSet;
}
/**
* The port number on which the database accepts connections.
* <table>
* <tr>
* <td>Engine</td>
* <td>Default</td>
* <td>Range</td>
* </tr>
* <tr>
* <td>MySQL</td>
* <td>3306</td>
* <td>1150-65535</td>
* </tr>
* <tr>
* <td>Oracle</td>
* <td>1521</td>
* <td>1150-65535</td>
* </tr>
* <tr>
* <td>SQL Server</td>
* <td>1433</td>
* <td>1150-65535 except for 1434 and 3389</td>
* </tr>
* </table>
*/
public Optional<Integer> getPort() {
return port;
}
/**
* The number of days for which automated backups are retained. Setting this parameter to a
* positive number enables backups. Setting this parameter to 0 disables automated backups.
*
*
* <h4>Constraints</h4>
*
* Must be a value from 0 to 8 Cannot be set to 0 if the DB Instance is a master instance with
* read replicas
*
* @return value which defaults to {@code 1}
*/
public int getBackupRetentionPeriod() {
return backupRetentionPeriod;
}
/**
* Indicates that the DB Instance should be associated with the specified option group.
*/
public Optional<String> getOptionGroup() {
return optionGroup;
}
/**
* The name of the DB Parameter Group to associate with this DB instance. If this argument is
* omitted, the default DBParameterGroup for the specified engine will be used.
*
* <h4>Constraints</h4>
*
* Must be 1 to 255 alphanumeric characters First character must be a letter Cannot end with a
* hyphen or contain two consecutive hyphens
*/
public Optional<String> getParameterGroup() {
return parameterGroup;
}
/**
* Indicates that minor engine upgrades will be applied automatically to the DB Instance during
* the maintenance window.
*
* @return value defaulting to {@code true}
*/
public boolean isAutoMinorVersionUpgrade() {
return autoMinorVersionUpgrade;
}
/**
* A DB Subnet Group to associate with this DB Instance.
*
* If there is no DB Subnet Group, then it is a non-VPC DB instance.
*/
public Optional<String> getSubnetGroup() {
return subnetGroup;
}
/**
* A list of DB Security Groups to associate with this DB Instance.
*
* Default: The default DB Security Group for the database engine.
*/
public Set<String> getSecurityGroups() {
return securityGroups;
}
/**
* The password for the master database user.
*
* MySQL
*
* Constraints: Must contain from 8 to 41 alphanumeric characters.
*
* Oracle
*
* Constraints: Must contain from 8 to 30 alphanumeric characters.
*
* SQL Server
*
* Constraints: Must contain from 8 to 128 alphanumeric characters.
*/
public String getMasterPassword() {
return masterPassword;
}
/**
* {@inheritDoc}
*/
@Override
protected ToStringHelper string() {
return super.string().add("engineVersion", engineVersion.orNull()).add("licenseModel", licenseModel.orNull())
.add("port", port.orNull()).add("characterSet", characterSet.orNull()).add("optionGroup", optionGroup)
.add("parameterGroup", parameterGroup).add("autoMinorVersionUpgrade", autoMinorVersionUpgrade)
.add("backupRetentionPeriod", backupRetentionPeriod).add("subnetGroup", subnetGroup.orNull())
.add("securityGroups", securityGroups);
}
}

View File

@ -23,8 +23,8 @@ import static com.google.common.base.Preconditions.checkNotNull;
import java.util.Set; import java.util.Set;
import com.google.common.base.Objects; import com.google.common.base.Objects;
import com.google.common.base.Optional;
import com.google.common.base.Objects.ToStringHelper; import com.google.common.base.Objects.ToStringHelper;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
/** /**
@ -52,7 +52,6 @@ import com.google.common.collect.ImmutableSet;
* @author Adrian Cole * @author Adrian Cole
*/ */
public class SecurityGroup { public class SecurityGroup {
public static Builder<?> builder() { public static Builder<?> builder() {
return new ConcreteBuilder(); return new ConcreteBuilder();
} }

View File

@ -0,0 +1,205 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds 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.rds.domain.internal;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.base.Objects;
import com.google.common.base.Objects.ToStringHelper;
import com.google.common.base.Optional;
/**
*
* @author Adrian Cole
*/
public class BaseInstance {
public static Builder<?> builder() {
return new ConcreteBuilder();
}
public Builder<?> toBuilder() {
return new ConcreteBuilder().fromBaseInstance(this);
}
public static abstract class Builder<T extends Builder<T>> {
protected abstract T self();
protected Optional<String> name = Optional.absent();
protected String instanceClass;
protected String engine;
protected String masterUsername;
protected int allocatedStorageGB;
/**
* @see BaseInstance#getName()
*/
public T name(String name) {
this.name = Optional.fromNullable(name);
return self();
}
/**
* @see BaseInstance#getInstanceClass()
*/
public T instanceClass(String instanceClass) {
this.instanceClass = instanceClass;
return self();
}
/**
* @see BaseInstance#getEngine()
*/
public T engine(String engine) {
this.engine = engine;
return self();
}
/**
* @see BaseInstance#getMasterUsername()
*/
public T masterUsername(String masterUsername) {
this.masterUsername = masterUsername;
return self();
}
/**
* @see BaseInstance#getAllocatedStorageGB()
*/
public T allocatedStorageGB(int allocatedStorageGB) {
this.allocatedStorageGB = allocatedStorageGB;
return self();
}
public BaseInstance build() {
return new BaseInstance(name, instanceClass, engine, masterUsername, allocatedStorageGB);
}
public T fromBaseInstance(BaseInstance in) {
return this.name(in.getName().orNull()).instanceClass(in.getInstanceClass()).engine(in.getEngine())
.masterUsername(in.getMasterUsername()).allocatedStorageGB(in.getAllocatedStorageGB());
}
}
private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
@Override
protected ConcreteBuilder self() {
return this;
}
}
protected final Optional<String> name;
protected final String instanceClass;
protected final String engine;
protected final String masterUsername;
protected int allocatedStorageGB;
protected BaseInstance(Optional<String> name, String instanceClass, String engine, String masterUsername,
int allocatedStorageGB) {
this.name = checkNotNull(name, "name");
this.instanceClass = checkNotNull(instanceClass, "instanceClass");
this.engine = checkNotNull(engine, "engine");
this.masterUsername = checkNotNull(masterUsername, "masterUsername");
this.allocatedStorageGB = allocatedStorageGB;
}
/**
* The meaning of this parameter differs according to the database engine you use.
*
* <h4>MySQL</h4>
*
* Contains the name of the initial database of this instance that was provided at create time,
* if one was specified when the DB BaseInstance was created. This same name is returned for the
* life of the DB BaseInstance.
*
* <h4>Oracle</h4>
*
* Contains the Oracle System ID (SID) of the created DB BaseInstance.
*/
public Optional<String> getName() {
return name;
}
/**
* Contains the name of the compute and memory capacity class of the DB BaseInstance.
*/
public String getInstanceClass() {
return instanceClass;
}
/**
* Provides the name of the database engine to be used for this DB BaseInstance.
*/
public String getEngine() {
return engine;
}
/**
* Contains the master username for the DB BaseInstance.
*/
public String getMasterUsername() {
return masterUsername;
}
/**
* Specifies the allocated storage size specified in gigabytes.
*/
public int getAllocatedStorageGB() {
return allocatedStorageGB;
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
return Objects.hashCode(name.orNull(), instanceClass, engine, masterUsername, allocatedStorageGB);
}
/**
* {@inheritDoc}
*/
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
BaseInstance other = (BaseInstance) obj;
return Objects.equal(this.name, other.name) && Objects.equal(this.instanceClass, other.instanceClass)
&& Objects.equal(this.engine, other.engine) && Objects.equal(this.masterUsername, other.masterUsername)
&& Objects.equal(this.allocatedStorageGB, other.allocatedStorageGB);
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
return string().toString();
}
protected ToStringHelper string() {
return Objects.toStringHelper(this).omitNullValues().add("name", name.orNull()).add("instanceClass", instanceClass)
.add("engine", engine).add("masterUsername", masterUsername)
.add("allocatedStorageGB", allocatedStorageGB);
}
}

View File

@ -25,6 +25,7 @@ import org.jclouds.collect.PagedIterable;
import org.jclouds.concurrent.Timeout; import org.jclouds.concurrent.Timeout;
import org.jclouds.javax.annotation.Nullable; import org.jclouds.javax.annotation.Nullable;
import org.jclouds.rds.domain.Instance; import org.jclouds.rds.domain.Instance;
import org.jclouds.rds.domain.InstanceRequest;
import org.jclouds.rds.options.ListInstancesOptions; import org.jclouds.rds.options.ListInstancesOptions;
/** /**
@ -37,6 +38,41 @@ import org.jclouds.rds.options.ListInstancesOptions;
*/ */
@Timeout(duration = 30, timeUnit = TimeUnit.SECONDS) @Timeout(duration = 30, timeUnit = TimeUnit.SECONDS)
public interface InstanceApi { public interface InstanceApi {
/**
* Creates a new DB instance in a random, system-chosen Availability Zone in the endpoint's
* region.
*
* @param id
* unique id of the new instance
* @param instanceRequest
* parameters to create the instance with
* @return new instance being created
*/
Instance create(String id, InstanceRequest instanceRequest);
/**
* Creates a new DB instance in the specified {@code availabilityZone}
*
* @param id
* unique id of the new instance
* @param instanceRequest
* parameters to create the instance with
* @param availabilityZone
* The EC2 Availability Zone that the database instance will be created in
* @return new instance being created
*/
Instance createInAvailabilityZone(String id, InstanceRequest instanceRequest, String availabilityZone);
/**
* Creates a Multi-AZ deployment. This is not compatible with Microsoft SQL Server.
*
* @param id
* unique id of the new instance
* @param instanceRequest
* parameters to create the instance with
* @return new instance being created
*/
Instance createMultiAZ(String id, InstanceRequest instanceRequest);
/** /**
* Retrieves information about the specified instance. * Retrieves information about the specified instance.
@ -72,20 +108,38 @@ public interface InstanceApi {
PagedIterable<Instance> list(); PagedIterable<Instance> list();
/** /**
* Deletes the specified Instance. * Deletes the specified Instance, skipping final snapshot.
* *
* <p/> * <p/>
* The DeleteDBInstance API deletes a previously provisioned RDS instance. A successful response * The DeleteDBInstance API deletes a previously provisioned RDS instance. A successful response
* from the web service indicates the request was received correctly. If a final DBSnapshot is * from the web service indicates the request was received correctly. This cannot be canceled or
* requested the status of the RDS instance will be "deleting" until the DBSnapshot is created.
* DescribeDBInstance is used to monitor the status of this operation. This cannot be canceled or
* reverted once submitted. * reverted once submitted.
* *
* *
* @param id * @param id
* The DB Instance identifier for the DB Instance to be deleted. This parameter isn't * The DB Instance identifier for the DB Instance to be deleted. This parameter isn't
* case sensitive. * case sensitive.
* @return final state of instance or null if not found
*/ */
void delete(String id); Instance delete(String id);
/**
* Deletes the specified Instance.
*
* <p/>
* The DeleteDBInstance API deletes a previously provisioned RDS instance. A successful response
* from the web service indicates the request was received correctly. The status of the RDS
* instance will be "deleting" until the DBSnapshot is created. DescribeDBInstance is used to
* monitor the status of this operation. This cannot be canceled or reverted once submitted.
*
*
* @param id
* The DB Instance identifier for the DB Instance to be deleted. This parameter isn't
* case sensitive.
* @param snapshotId
* The DBSnapshotIdentifier of the new DBSnapshot created when SkipFinalSnapshot is set
* to false.
* @return final state of instance or null if not found
*/
Instance deleteAndSaveSnapshot(String id, String snapshotId);
} }

View File

@ -27,11 +27,15 @@ import javax.ws.rs.Path;
import org.jclouds.aws.filters.FormSigner; import org.jclouds.aws.filters.FormSigner;
import org.jclouds.collect.IterableWithMarker; import org.jclouds.collect.IterableWithMarker;
import org.jclouds.collect.PagedIterable; import org.jclouds.collect.PagedIterable;
import org.jclouds.rds.binders.BindInstanceRequestToFormParams;
import org.jclouds.rds.domain.Instance; import org.jclouds.rds.domain.Instance;
import org.jclouds.rds.domain.InstanceRequest;
import org.jclouds.rds.functions.InstancesToPagedIterable; import org.jclouds.rds.functions.InstancesToPagedIterable;
import org.jclouds.rds.functions.ReturnNullOnStateDeletingNotFoundOr404;
import org.jclouds.rds.options.ListInstancesOptions; import org.jclouds.rds.options.ListInstancesOptions;
import org.jclouds.rds.xml.DescribeDBInstancesResultHandler; import org.jclouds.rds.xml.DescribeDBInstancesResultHandler;
import org.jclouds.rds.xml.InstanceHandler; import org.jclouds.rds.xml.InstanceHandler;
import org.jclouds.rest.annotations.BinderParam;
import org.jclouds.rest.annotations.ExceptionParser; import org.jclouds.rest.annotations.ExceptionParser;
import org.jclouds.rest.annotations.FormParams; import org.jclouds.rest.annotations.FormParams;
import org.jclouds.rest.annotations.RequestFilters; import org.jclouds.rest.annotations.RequestFilters;
@ -39,7 +43,6 @@ import org.jclouds.rest.annotations.Transform;
import org.jclouds.rest.annotations.VirtualHost; import org.jclouds.rest.annotations.VirtualHost;
import org.jclouds.rest.annotations.XMLResponseParser; import org.jclouds.rest.annotations.XMLResponseParser;
import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404; import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404;
import org.jclouds.rest.functions.ReturnVoidOnNotFoundOr404;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
@ -47,14 +50,43 @@ import com.google.common.util.concurrent.ListenableFuture;
* Provides access to Amazon RDS via the Query API * Provides access to Amazon RDS via the Query API
* <p/> * <p/>
* *
* @see <a href="http://docs.amazonwebservices.com/AmazonRDS/latest/APIReference" * @see <a href="http://docs.amazonwebservices.com/AmazonRDS/latest/APIReference" >doc</a>
* >doc</a>
* @see InstanceApi * @see InstanceApi
* @author Adrian Cole * @author Adrian Cole
*/ */
@RequestFilters(FormSigner.class) @RequestFilters(FormSigner.class)
@VirtualHost @VirtualHost
public interface InstanceAsyncApi { public interface InstanceAsyncApi {
/**
* @see InstanceApi#create
*/
@POST
@Path("/")
@XMLResponseParser(InstanceHandler.class)
@FormParams(keys = ACTION, values = "CreateDBInstance")
ListenableFuture<Instance> create(@FormParam("DBInstanceIdentifier") String id,
@BinderParam(BindInstanceRequestToFormParams.class) InstanceRequest instanceRequest);
/**
* @see InstanceApi#createInAvailabilityZone
*/
@POST
@Path("/")
@XMLResponseParser(InstanceHandler.class)
@FormParams(keys = ACTION, values = "CreateDBInstance")
ListenableFuture<Instance> createInAvailabilityZone(@FormParam("DBInstanceIdentifier") String id,
@BinderParam(BindInstanceRequestToFormParams.class) InstanceRequest instanceRequest,
@FormParam("AvailabilityZone") String availabilityZone);
/**
* @see InstanceApi#createMultiAZ
*/
@POST
@Path("/")
@XMLResponseParser(InstanceHandler.class)
@FormParams(keys = { ACTION, "MultiAZ" }, values = { "CreateDBInstance", "true" })
ListenableFuture<Instance> createMultiAZ(@FormParam("DBInstanceIdentifier") String id,
@BinderParam(BindInstanceRequestToFormParams.class) InstanceRequest instanceRequest);
/** /**
* @see InstanceApi#get() * @see InstanceApi#get()
@ -90,7 +122,19 @@ public interface InstanceAsyncApi {
*/ */
@POST @POST
@Path("/") @Path("/")
@ExceptionParser(ReturnVoidOnNotFoundOr404.class) @XMLResponseParser(InstanceHandler.class)
@ExceptionParser(ReturnNullOnStateDeletingNotFoundOr404.class)
@FormParams(keys = { ACTION, "SkipFinalSnapshot" }, values = { "DeleteDBInstance", "true" })
ListenableFuture<Instance> delete(@FormParam("DBInstanceIdentifier") String id);
/**
* @see InstanceApi#deleteAndSaveSnapshot
*/
@POST
@Path("/")
@XMLResponseParser(InstanceHandler.class)
@ExceptionParser(ReturnNullOnStateDeletingNotFoundOr404.class)
@FormParams(keys = ACTION, values = "DeleteDBInstance") @FormParams(keys = ACTION, values = "DeleteDBInstance")
ListenableFuture<Void> delete(@FormParam("DBInstanceIdentifier") String id); ListenableFuture<Instance> deleteAndSaveSnapshot(@FormParam("DBInstanceIdentifier") String id,
@FormParam("FinalDBSnapshotIdentifier") String snapshotId);
} }

View File

@ -37,6 +37,38 @@ import org.jclouds.rds.options.ListSecurityGroupsOptions;
*/ */
@Timeout(duration = 30, timeUnit = TimeUnit.SECONDS) @Timeout(duration = 30, timeUnit = TimeUnit.SECONDS)
public interface SecurityGroupApi { public interface SecurityGroupApi {
/**
* Creates a new DB Security Group. DB Security Groups control access to a DB Instance.
*
* @param name
* The name for the DB Security Group. This value is stored as a lowercase string.
*
* Constraints: Must contain no more than 255 alphanumeric characters or hyphens. Must
* not be "Default".
* @param description
* The description for the DB Security Group.
*
* @return the new security group
*/
SecurityGroup createWithNameAndDescription(String name, String description);
/**
* Creates a new DB Security Group. DB Security Groups control access to a DB Instance.
*
* @param vpcId
* The Id of VPC. Indicates which VPC this DB Security Group should belong to. Must be
* specified to create a DB Security Group for a VPC; may not be specified otherwise.
*
* @param name
* The name for the DB Security Group. This value is stored as a lowercase string.
*
* Constraints: Must contain no more than 255 alphanumeric characters or hyphens. Must
* not be "Default".
* @param description
* The description for the DB Security Group.
* @return the new security group
*/
SecurityGroup createInVPCWithNameAndDescription(String vpcId, String name, String description);
/** /**
* Retrieves information about the specified {@link SecurityGroup}. * Retrieves information about the specified {@link SecurityGroup}.
@ -68,6 +100,93 @@ public interface SecurityGroupApi {
*/ */
PagedIterable<SecurityGroup> list(); PagedIterable<SecurityGroup> list();
/**
* Enables ingress to a DBSecurityGroup to an IP range, if the application accessing your
* database is running on the Internet.
*
* @param name
* The name of the DB Security Group to add authorization to.
* @param CIDR
* The IP range to authorize.
* @return updated security group, noting the authorization status may not be complete
*/
SecurityGroup authorizeIngressToIPRange(String name, String CIDR);
/**
* Enables ingress to a DBSecurityGroup if the application using the database is running on EC2
* instances.
*
* <h4>Note</h4>
*
* You cannot authorize ingress from an EC2 security group in one Region to an Amazon RDS DB
* Instance in another.
*
* @param name
* The name of the DB Security Group to add authorization to.
* @param ec2SecurityGroupName
* Name of the EC2 Security Group to authorize.
* @param ec2SecurityGroupOwnerId
* AWS Account Number of the owner of the EC2 Security Group specified in the
* EC2SecurityGroupName parameter. The AWS Access Key ID is not an acceptable value.
* @return updated security group, noting the authorization status may not be complete
*/
SecurityGroup authorizeIngressToEC2SecurityGroupOfOwner(String name, String ec2SecurityGroupName,
String ec2SecurityGroupOwnerId);
/**
* Enables ingress to a DBSecurityGroup if the application using the database is running on VPC
* instances.
*
* <h4>Note</h4>
*
* You cannot authorize ingress from a VPC security group in one VPC to an Amazon RDS DB Instance
* in another.
*
* @param name
* The name of the DB Security Group to add authorization to.
* @param vpcSecurityGroupId
* Id of the EC2 Security Group to authorize.
* @return updated security group, noting the authorization status may not be complete
*/
SecurityGroup authorizeIngressToVPCSecurityGroup(String name, String vpcSecurityGroupId);
/**
* Revokes ingress from a DBSecurityGroup for previously authorized IP range.
*
* @param name
* The name of the DB Security Group to revoke ingress from.
* @param CIDR
* The IP range to revoke.
* @return updated security group, noting the authorization status may not be complete
*/
SecurityGroup revokeIngressFromIPRange(String name, String CIDR);
/**
* Revokes ingress from a DBSecurityGroup for previously authorized EC2 Security Group.
*
* @param name
* The name of the DB Security Group to revoke ingress from.
* @param ec2SecurityGroupName
* Name of the EC2 Security Group to revoke.
* @param ec2SecurityGroupOwnerId
* AWS Account Number of the owner of the EC2 Security Group specified in the
* EC2SecurityGroupName parameter. The AWS Access Key ID is not an acceptable value.
* @return updated security group, noting the authorization status may not be complete
*/
SecurityGroup revokeIngressFromEC2SecurityGroupOfOwner(String name, String ec2SecurityGroupName,
String ec2SecurityGroupOwnerId);
/**
* Revokes ingress from a DBSecurityGroup for previously authorized VPC Security Group.
*
* @param name
* The name of the DB Security Group to revoke ingress from.
* @param vpcSecurityGroupId
* Id of the EC2 Security Group to revoke.
* @return updated security group, noting the authorization status may not be complete
*/
SecurityGroup revokeIngressFromVPCSecurityGroup(String name, String vpcSecurityGroupId);
/** /**
* Deletes a DB security group. * Deletes a DB security group.
* *

View File

@ -47,14 +47,32 @@ import com.google.common.util.concurrent.ListenableFuture;
* Provides access to Amazon RDS via the Query API * Provides access to Amazon RDS via the Query API
* <p/> * <p/>
* *
* @see <a href="http://docs.amazonwebservices.com/AmazonRDS/latest/APIReference" * @see <a href="http://docs.amazonwebservices.com/AmazonRDS/latest/APIReference" >doc</a>
* >doc</a>
* @see SecurityGroupApi * @see SecurityGroupApi
* @author Adrian Cole * @author Adrian Cole
*/ */
@RequestFilters(FormSigner.class) @RequestFilters(FormSigner.class)
@VirtualHost @VirtualHost
public interface SecurityGroupAsyncApi { public interface SecurityGroupAsyncApi {
/**
* @see SecurityGroupApi#createWithNameAndDescription
*/
@POST
@Path("/")
@XMLResponseParser(SecurityGroupHandler.class)
@FormParams(keys = ACTION, values = "CreateDBSecurityGroup")
ListenableFuture<SecurityGroup> createWithNameAndDescription(@FormParam("DBSecurityGroupName") String name,
@FormParam("DBSecurityGroupDescription") String description);
/**
* @see SecurityGroupApi#createInVPCWithNameAndDescription
*/
@POST
@Path("/")
@XMLResponseParser(SecurityGroupHandler.class)
@FormParams(keys = ACTION, values = "CreateDBSecurityGroup")
ListenableFuture<SecurityGroup> createInVPCWithNameAndDescription(@FormParam("EC2VpcId") String vpcId,
@FormParam("DBSecurityGroupName") String name, @FormParam("DBSecurityGroupDescription") String description);
/** /**
* @see SecurityGroupApi#get() * @see SecurityGroupApi#get()
@ -85,6 +103,71 @@ public interface SecurityGroupAsyncApi {
@FormParams(keys = "Action", values = "DescribeDBSecurityGroups") @FormParams(keys = "Action", values = "DescribeDBSecurityGroups")
ListenableFuture<IterableWithMarker<SecurityGroup>> list(ListSecurityGroupsOptions options); ListenableFuture<IterableWithMarker<SecurityGroup>> list(ListSecurityGroupsOptions options);
/**
* @see SecurityGroupApi#authorizeIngressToIPRange
*/
@POST
@Path("/")
@XMLResponseParser(SecurityGroupHandler.class)
@FormParams(keys = ACTION, values = "AuthorizeDBSecurityGroupIngress")
ListenableFuture<SecurityGroup> authorizeIngressToIPRange(@FormParam("DBSecurityGroupName") String name,
@FormParam("CIDRIP") String CIDR);
/**
* @see SecurityGroupApi#authorizeIngressToEC2SecurityGroupOfOwner
*/
@POST
@Path("/")
@XMLResponseParser(SecurityGroupHandler.class)
@FormParams(keys = ACTION, values = "AuthorizeDBSecurityGroupIngress")
ListenableFuture<SecurityGroup> authorizeIngressToEC2SecurityGroupOfOwner(
@FormParam("DBSecurityGroupName") String name,
@FormParam("EC2SecurityGroupName") String ec2SecurityGroupName,
@FormParam("EC2SecurityGroupOwnerId") String ec2SecurityGroupOwnerId);
/**
* @see SecurityGroupApi#authorizeIngressToVPCSecurityGroup
*/
@POST
@Path("/")
@XMLResponseParser(SecurityGroupHandler.class)
@FormParams(keys = ACTION, values = "AuthorizeDBSecurityGroupIngress")
ListenableFuture<SecurityGroup> authorizeIngressToVPCSecurityGroup(@FormParam("DBSecurityGroupName") String name,
@FormParam("EC2SecurityGroupId") String vpcSecurityGroupId);
/**
* @see SecurityGroupApi#revokeIngressFromIPRange
*/
@POST
@Path("/")
@XMLResponseParser(SecurityGroupHandler.class)
@FormParams(keys = ACTION, values = "RevokeDBSecurityGroupIngress")
ListenableFuture<SecurityGroup> revokeIngressFromIPRange(@FormParam("DBSecurityGroupName") String name,
@FormParam("CIDRIP") String CIDR);
/**
* @see SecurityGroupApi#revokeIngressFromEC2SecurityGroupOfOwner
*/
@POST
@Path("/")
@XMLResponseParser(SecurityGroupHandler.class)
@FormParams(keys = ACTION, values = "RevokeDBSecurityGroupIngress")
ListenableFuture<SecurityGroup> revokeIngressFromEC2SecurityGroupOfOwner(
@FormParam("DBSecurityGroupName") String name,
@FormParam("EC2SecurityGroupName") String ec2SecurityGroupName,
@FormParam("EC2SecurityGroupOwnerId") String ec2SecurityGroupOwnerId);
/**
* @see SecurityGroupApi#revokeIngressFromVPCSecurityGroup
*/
@POST
@Path("/")
@XMLResponseParser(SecurityGroupHandler.class)
@FormParams(keys = ACTION, values = "RevokeDBSecurityGroupIngress")
ListenableFuture<SecurityGroup> revokeIngressFromVPCSecurityGroup(@FormParam("DBSecurityGroupName") String name,
@FormParam("EC2SecurityGroupId") String vpcSecurityGroupId);
/** /**
* @see SecurityGroupApi#delete() * @see SecurityGroupApi#delete()
*/ */

View File

@ -0,0 +1,51 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds 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.rds.functions;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.jclouds.aws.AWSResponseException;
import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404;
import com.google.common.base.Function;
/**
* it is ok to have a db instance already in the process of deleting
*/
@Singleton
public class ReturnNullOnStateDeletingNotFoundOr404 implements Function<Exception, Object> {
private ReturnNullOnNotFoundOr404 rto404;
@Inject
private ReturnNullOnStateDeletingNotFoundOr404(ReturnNullOnNotFoundOr404 rto404) {
this.rto404 = rto404;
}
public Object apply(Exception from) {
if (from instanceof AWSResponseException) {
AWSResponseException e = AWSResponseException.class.cast(from);
if (e.getError().getCode().equals("InvalidDBInstanceState")
&& e.getError().getMessage().contains("has state: deleting"))
return null;
}
return rto404.apply(from);
}
}

View File

@ -21,8 +21,9 @@ package org.jclouds.rds.xml;
import static org.jclouds.util.SaxUtils.currentOrNull; import static org.jclouds.util.SaxUtils.currentOrNull;
import static org.jclouds.util.SaxUtils.equalsOrSuffix; import static org.jclouds.util.SaxUtils.equalsOrSuffix;
import org.jclouds.rds.domain.EC2SecurityGroup;
import org.jclouds.http.functions.ParseSax; import org.jclouds.http.functions.ParseSax;
import org.jclouds.rds.domain.Authorization.Status;
import org.jclouds.rds.domain.EC2SecurityGroup;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
/** /**
@ -61,7 +62,9 @@ public class EC2SecurityGroupHandler extends ParseSax.HandlerForGeneratedRequest
} else if (equalsOrSuffix(qName, "EC2SecurityGroupOwnerId")) { } else if (equalsOrSuffix(qName, "EC2SecurityGroupOwnerId")) {
builder.ownerId(currentOrNull(currentText)); builder.ownerId(currentOrNull(currentText));
} else if (equalsOrSuffix(qName, "Status")) { } else if (equalsOrSuffix(qName, "Status")) {
builder.status(currentOrNull(currentText)); String rawStatus = currentOrNull(currentText);
builder.rawStatus(rawStatus);
builder.status(Status.fromValue(rawStatus));
} }
currentText = new StringBuilder(); currentText = new StringBuilder();
} }

View File

@ -21,8 +21,9 @@ package org.jclouds.rds.xml;
import static org.jclouds.util.SaxUtils.currentOrNull; import static org.jclouds.util.SaxUtils.currentOrNull;
import static org.jclouds.util.SaxUtils.equalsOrSuffix; import static org.jclouds.util.SaxUtils.equalsOrSuffix;
import org.jclouds.rds.domain.IPRange;
import org.jclouds.http.functions.ParseSax; import org.jclouds.http.functions.ParseSax;
import org.jclouds.rds.domain.Authorization.Status;
import org.jclouds.rds.domain.IPRange;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
/** /**
@ -57,7 +58,9 @@ public class IPRangeHandler extends ParseSax.HandlerForGeneratedRequestWithResul
if (equalsOrSuffix(qName, "CIDRIP")) { if (equalsOrSuffix(qName, "CIDRIP")) {
builder.cidrIp(currentOrNull(currentText)); builder.cidrIp(currentOrNull(currentText));
} else if (equalsOrSuffix(qName, "Status")) { } else if (equalsOrSuffix(qName, "Status")) {
builder.status(currentOrNull(currentText)); String rawStatus = currentOrNull(currentText);
builder.rawStatus(rawStatus);
builder.status(Status.fromValue(rawStatus));
} }
currentText = new StringBuilder(); currentText = new StringBuilder();
} }

View File

@ -94,9 +94,11 @@ public class InstanceHandler extends ParseSax.HandlerForGeneratedRequestWithResu
@Override @Override
public void endElement(String uri, String name, String qName) throws SAXException { public void endElement(String uri, String name, String qName) throws SAXException {
if (equalsOrSuffix(qName, "SubnetGroup")) { if (equalsOrSuffix(qName, "DBSubnetGroup")) {
builder.subnetGroup(subnetGroupHandler.getResult()); builder.subnetGroup(subnetGroupHandler.getResult());
inSubnetGroup = false; inSubnetGroup = false;
} else if (inSubnetGroup) {
subnetGroupHandler.endElement(uri, name, qName);
} else if (equalsOrSuffix(qName, "DBInstanceIdentifier")) { } else if (equalsOrSuffix(qName, "DBInstanceIdentifier")) {
builder.id(currentOrNull(currentText)); builder.id(currentOrNull(currentText));
} else if (equalsOrSuffix(qName, "InstanceCreateTime")) { } else if (equalsOrSuffix(qName, "InstanceCreateTime")) {
@ -106,13 +108,17 @@ public class InstanceHandler extends ParseSax.HandlerForGeneratedRequestWithResu
} else if (equalsOrSuffix(qName, "AllocatedStorage")) { } else if (equalsOrSuffix(qName, "AllocatedStorage")) {
builder.allocatedStorageGB(Integer.parseInt(currentOrNull(currentText))); builder.allocatedStorageGB(Integer.parseInt(currentOrNull(currentText)));
} else if (equalsOrSuffix(qName, "DBInstanceStatus")) { } else if (equalsOrSuffix(qName, "DBInstanceStatus")) {
builder.status(currentOrNull(currentText)); String rawStatus = currentOrNull(currentText);
builder.rawStatus(rawStatus);
builder.status(Instance.Status.fromValue(rawStatus));
} else if (equalsOrSuffix(qName, "Address")) { } else if (equalsOrSuffix(qName, "Address")) {
address = currentOrNull(currentText); address = currentOrNull(currentText);
} else if (equalsOrSuffix(qName, "Port")) { } else if (equalsOrSuffix(qName, "Port")) {
port = Integer.valueOf(currentOrNull(currentText)); port = Integer.valueOf(currentOrNull(currentText));
} else if (equalsOrSuffix(qName, "Endpoint")) { } else if (equalsOrSuffix(qName, "Endpoint")) {
builder.endpoint(HostAndPort.fromParts(address, port)); // sometimes in deleting state, address is null while port isn't
if (address != null && port != null)
builder.endpoint(HostAndPort.fromParts(address, port));
address = null; address = null;
port = null; port = null;
} else if (equalsOrSuffix(qName, "DBSecurityGroupName")) { } else if (equalsOrSuffix(qName, "DBSecurityGroupName")) {
@ -139,8 +145,6 @@ public class InstanceHandler extends ParseSax.HandlerForGeneratedRequestWithResu
builder.licenseModel(currentOrNull(currentText)); builder.licenseModel(currentOrNull(currentText));
} else if (equalsOrSuffix(qName, "MasterUsername")) { } else if (equalsOrSuffix(qName, "MasterUsername")) {
builder.masterUsername(currentOrNull(currentText)); builder.masterUsername(currentOrNull(currentText));
} else if (inSubnetGroup) {
subnetGroupHandler.endElement(uri, name, qName);
} }
currentText = new StringBuilder(); currentText = new StringBuilder();
} }

View File

@ -27,6 +27,7 @@ import java.util.TimeZone;
import org.jclouds.http.HttpRequest; import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse; import org.jclouds.http.HttpResponse;
import org.jclouds.rds.RDSApi; import org.jclouds.rds.RDSApi;
import org.jclouds.rds.domain.InstanceRequest;
import org.jclouds.rds.internal.BaseRDSApiExpectTest; import org.jclouds.rds.internal.BaseRDSApiExpectTest;
import org.jclouds.rds.parse.DescribeDBInstancesResponseTest; import org.jclouds.rds.parse.DescribeDBInstancesResponseTest;
import org.jclouds.rds.parse.GetInstanceResponseTest; import org.jclouds.rds.parse.GetInstanceResponseTest;
@ -193,9 +194,10 @@ public class InstanceApiExpectTest extends BaseRDSApiExpectTest {
payloadFromStringWithContentType( payloadFromStringWithContentType(
"Action=DeleteDBInstance" + "Action=DeleteDBInstance" +
"&DBInstanceIdentifier=id" + "&DBInstanceIdentifier=id" +
"&Signature=3UFxv5zaG%2B3dZRAhinJZ10De0LAxkJtr3Nb6kfspC7w%3D" + "&Signature=Kag9creOPsl%2BslM1J0fNzWIzo1LrF4ycnOI21v%2Bl6VM%3D" +
"&SignatureMethod=HmacSHA256" + "&SignatureMethod=HmacSHA256" +
"&SignatureVersion=2" + "&SignatureVersion=2" +
"&SkipFinalSnapshot=true" +
"&Timestamp=2009-11-08T15%3A54%3A08.897Z" + "&Timestamp=2009-11-08T15%3A54%3A08.897Z" +
"&Version=2012-04-23" + "&Version=2012-04-23" +
"&AWSAccessKeyId=identity", "&AWSAccessKeyId=identity",
@ -204,7 +206,8 @@ public class InstanceApiExpectTest extends BaseRDSApiExpectTest {
public void testDeleteWhenResponseIs2xx() throws Exception { public void testDeleteWhenResponseIs2xx() throws Exception {
HttpResponse deleteResponse = HttpResponse.builder().statusCode(200).build(); HttpResponse deleteResponse = HttpResponse.builder().statusCode(200)
.payload(payloadFromResourceWithContentType("/delete_instance.xml", "text/xml")).build();
RDSApi apiWhenExist = requestSendsResponse(delete, deleteResponse); RDSApi apiWhenExist = requestSendsResponse(delete, deleteResponse);
@ -219,4 +222,116 @@ public class InstanceApiExpectTest extends BaseRDSApiExpectTest {
apiWhenDontExist.getInstanceApi().delete("id"); apiWhenDontExist.getInstanceApi().delete("id");
} }
public void testDeleteWhenInvalidStateDeleting() throws Exception {
HttpResponse deleteResponse = HttpResponse.builder().statusCode(400).message("HTTP/1.1 400 Bad Request")
.payload(payloadFromResourceWithContentType("/invalid_state.xml", "text/xml")).build();
RDSApi apiWhenDeleting = requestSendsResponse(delete, deleteResponse);
apiWhenDeleting.getInstanceApi().delete("id");
}
public void testDeleteAndSaveSnapshotWhenResponseIs2xx() throws Exception {
HttpRequest delete = HttpRequest.builder().method("POST")
.endpoint("https://rds.us-east-1.amazonaws.com/")
.addHeader("Host", "rds.us-east-1.amazonaws.com")
.payload(
payloadFromStringWithContentType(
"Action=DeleteDBInstance" +
"&DBInstanceIdentifier=id" +
"&FinalDBSnapshotIdentifier=snap" +
"&Signature=aKuG1%2FYbZAzUFdAZTjke1LYRfR5JU86UxDt%2BtwdPJwE%3D" +
"&SignatureMethod=HmacSHA256" +
"&SignatureVersion=2" +
"&Timestamp=2009-11-08T15%3A54%3A08.897Z" +
"&Version=2012-04-23" +
"&AWSAccessKeyId=identity",
"application/x-www-form-urlencoded"))
.build();
HttpResponse deleteResponse = HttpResponse.builder().statusCode(200)
.payload(payloadFromResourceWithContentType("/delete_instance.xml", "text/xml")).build();
RDSApi apiWhenExist = requestSendsResponse(delete, deleteResponse);
apiWhenExist.getInstanceApi().deleteAndSaveSnapshot("id", "snap");
}
public void testCreateWithMinumumParamsWhenResponseIs2xx() throws Exception {
HttpRequest create = HttpRequest.builder()
.method("POST")
.endpoint("https://rds.us-east-1.amazonaws.com/")
.addHeader("Host", "rds.us-east-1.amazonaws.com")
.payload(
payloadFromStringWithContentType(
"Action=CreateDBInstance" +
"&AllocatedStorage=5" +
"&AutoMinorVersionUpgrade=true" +
"&BackupRetentionPeriod=1" +
"&DBInstanceClass=db.t1.micro" +
"&DBInstanceIdentifier=SimCoProd01" +
"&Engine=mysql" +
"&MasterUserPassword=Password01" +
"&MasterUsername=master" +
"&Signature=TecIUViW09soXGFT3kAXcW2dhsK6fY2cNykLpzLJtvk%3D" +
"&SignatureMethod=HmacSHA256" +
"&SignatureVersion=2" +
"&Timestamp=2009-11-08T15%3A54%3A08.897Z" +
"&Version=2012-04-23" +
"&AWSAccessKeyId=identity",
"application/x-www-form-urlencoded"))
.build();
HttpResponse createResponse = HttpResponse.builder().statusCode(200)
.payload(payloadFromResourceWithContentType("/create_instance.xml", "text/xml")).build();
RDSApi apiWhenExist = requestSendsResponse(create, createResponse);
apiWhenExist.getInstanceApi().create("SimCoProd01", InstanceRequest.builder()
.engine("mysql")
.masterUsername("master")
.masterPassword("Password01")
.allocatedStorageGB(5)
.instanceClass("db.t1.micro").build());
}
public void testCreateWithOptionalParamsWhenResponseIs2xx() throws Exception {
HttpRequest create = HttpRequest.builder()
.method("POST")
.endpoint("https://rds.us-east-1.amazonaws.com/")
.addHeader("Host", "rds.us-east-1.amazonaws.com")
.payload(
payloadFromStringWithContentType(
"Action=CreateDBInstance" +
"&AllocatedStorage=10" +
"&AutoMinorVersionUpgrade=true" +
"&BackupRetentionPeriod=1" +
"&DBInstanceClass=db.m1.large" +
"&DBInstanceIdentifier=SimCoProd01" +
"&DBSubnetGroupName=dbSubnetgroup01" +
"&Engine=mysql" +
"&MasterUserPassword=Password01" +
"&MasterUsername=master" +
"&Signature=kfDFp50sxBkSlZd%2Bv8G9u6%2BFdZ133BEVcIRGwwoa8%2Fs%3D" +
"&SignatureMethod=HmacSHA256" +
"&SignatureVersion=2" +
"&Timestamp=2009-11-08T15%3A54%3A08.897Z" +
"&Version=2012-04-23" +
"&AWSAccessKeyId=identity",
"application/x-www-form-urlencoded"))
.build();
HttpResponse createResponse = HttpResponse.builder().statusCode(200)
.payload(payloadFromResourceWithContentType("/create_instance.xml", "text/xml")).build();
RDSApi apiWhenExist = requestSendsResponse(create, createResponse);
apiWhenExist.getInstanceApi().create("SimCoProd01", InstanceRequest.builder()
.engine("mysql")
.masterPassword("Password01")
.allocatedStorageGB(10)
.masterUsername("master")
.instanceClass("db.m1.large")
.subnetGroup("dbSubnetgroup01").build());
}
} }

View File

@ -19,27 +19,172 @@
package org.jclouds.rds.features; package org.jclouds.rds.features;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotEquals;
import static org.testng.Assert.assertTrue;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import org.jclouds.collect.IterableWithMarker; import org.jclouds.collect.IterableWithMarker;
import org.jclouds.predicates.InetSocketAddressConnect;
import org.jclouds.predicates.RetryablePredicate;
import org.jclouds.rds.domain.Authorization;
import org.jclouds.rds.domain.Authorization.Status;
import org.jclouds.rds.domain.Instance; import org.jclouds.rds.domain.Instance;
import org.jclouds.rds.domain.InstanceRequest;
import org.jclouds.rds.domain.SecurityGroup;
import org.jclouds.rds.internal.BaseRDSApiLiveTest; import org.jclouds.rds.internal.BaseRDSApiLiveTest;
import org.jclouds.rds.options.ListInstancesOptions; import org.jclouds.rds.options.ListInstancesOptions;
import org.testng.Assert; import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.net.HostAndPort;
/** /**
* @author Adrian Cole * @author Adrian Cole
*/ */
@Test(groups = "live", testName = "InstanceApiLiveTest") @Test(groups = "live", testName = "InstanceApiLiveTest")
public class InstanceApiLiveTest extends BaseRDSApiLiveTest { public class InstanceApiLiveTest extends BaseRDSApiLiveTest {
public static final String INSTANCE = (System.getProperty("user.name") + "-jclouds-instance").toLowerCase();
private RetryablePredicate<HostAndPort> socketTester;
private RetryablePredicate<Instance> instanceAvailable;
private RetryablePredicate<Instance> instanceGone;
private SecurityGroup securityGroup;
@BeforeClass(groups = "live")
@Override
public void setupContext() {
super.setupContext();
securityGroup = createSecurityGroupAndAuthorizeIngressToAll(INSTANCE);
socketTester = new RetryablePredicate<HostAndPort>(new InetSocketAddressConnect(), 180, 1, 1, TimeUnit.SECONDS);
instanceAvailable = new RetryablePredicate<Instance>(new Predicate<Instance>() {
@Override
public boolean apply(Instance input) {
return api().get(input.getId()).getStatus() == Instance.Status.AVAILABLE;
}
}, 600, 5, 5, TimeUnit.SECONDS);
instanceGone = new RetryablePredicate<Instance>(new Predicate<Instance>() {
@Override
public boolean apply(Instance input) {
return api().get(input.getId()) == null;
}
}, 600, 5, 5, TimeUnit.SECONDS);
}
private SecurityGroup createSecurityGroupAndAuthorizeIngressToAll(String name) {
RetryablePredicate<SecurityGroup> ipRangesAuthorized = new RetryablePredicate<SecurityGroup>(
new Predicate<SecurityGroup>() {
@Override
public boolean apply(SecurityGroup input) {
return Iterables.all(sgApi().get(input.getName()).getIPRanges(), new Predicate<Authorization>() {
@Override
public boolean apply(@Nullable Authorization i2) {
return i2.getStatus() == Status.AUTHORIZED;
}
});
}
}, 30000, 100, 500, TimeUnit.MILLISECONDS);
try {
SecurityGroup securityGroup = sgApi().createWithNameAndDescription(name, "jclouds");
Logger.getAnonymousLogger().info("created securityGroup: " + securityGroup);
// we could look up our IP address alternatively
securityGroup = sgApi().authorizeIngressToIPRange(name, "0.0.0.0/0");
assertTrue(ipRangesAuthorized.apply(securityGroup), securityGroup.toString());
securityGroup = sgApi().get(securityGroup.getName());
Logger.getAnonymousLogger().info("ip range authorized: " + securityGroup);
return securityGroup;
} catch (RuntimeException e) {
sgApi().delete(name);
throw e;
}
}
private Instance instance;
public void testCreateInstance() {
Instance newInstance = api().create(
INSTANCE,
InstanceRequest.builder()
.instanceClass("db.t1.micro")
.allocatedStorageGB(5)
.securityGroups(securityGroup.getName())
.name("jclouds")
.engine("mysql")
.masterUsername("master").masterPassword("Password01")
.backupRetentionPeriod(0).build());
instance = newInstance;
Logger.getAnonymousLogger().info("created instance: " + instance);
assertEquals(instance.getId(), INSTANCE);
assertEquals(instance.getName().get(), "jclouds");
checkInstance(newInstance);
assertTrue(instanceAvailable.apply(newInstance), newInstance.toString());
instance = api().get(newInstance.getId());
Logger.getAnonymousLogger().info("instance available: " + instance);
}
@Test(dependsOnMethods = "testCreateInstance")
protected void testPortResponds() {
assertTrue(socketTester.apply(instance.getEndpoint().get()), instance.toString());
Logger.getAnonymousLogger().info("instance reachable on: " + instance.getEndpoint().get());
}
@Test(dependsOnMethods = "testPortResponds")
public void testDeleteInstance() {
instance = api().delete(instance.getId());
assertTrue(instanceGone.apply(instance), instance.toString());
Logger.getAnonymousLogger().info("instance deleted: " + instance);
}
@Override
@AfterClass(groups = "live")
protected void tearDownContext() {
try {
api().delete(INSTANCE);
} finally {
sgApi().delete(INSTANCE);
}
super.tearDownContext();
}
private void checkInstance(Instance instance) { private void checkInstance(Instance instance) {
checkNotNull(instance.getId(), "Id cannot be null for a Instance: %s", instance); checkNotNull(instance.getId(), "Id cannot be null for a Instance: %s", instance);
checkNotNull(instance.getStatus(), "Status cannot be null for a Instance: %s", instance);
assertNotEquals(instance.getStatus(), Instance.Status.UNRECOGNIZED,
"Status cannot be UNRECOGNIZED for a Instance: " + instance);
checkNotNull(instance.getCreatedTime(), "CreatedTime cannot be null for a Instance: %s", instance); checkNotNull(instance.getCreatedTime(), "CreatedTime cannot be null for a Instance: %s", instance);
checkNotNull(instance.getName(), "While Name can be null for a Instance, its Optional wrapper cannot: %s", checkNotNull(instance.getName(), "While Name can be null for a Instance, its Optional wrapper cannot: %s",
instance); instance);
checkNotNull(instance.getSubnetGroup(), checkNotNull(instance.getSubnetGroup(),
"While SubnetGroup can be null for a Instance, its Optional wrapper cannot: %s", instance); "While SubnetGroup can be null for a Instance, its Optional wrapper cannot: %s", instance);
// TODO: other checks // TODO: other checks
@ -70,4 +215,8 @@ public class InstanceApiLiveTest extends BaseRDSApiLiveTest {
protected InstanceApi api() { protected InstanceApi api() {
return context.getApi().getInstanceApi(); return context.getApi().getInstanceApi();
} }
protected SecurityGroupApi sgApi() {
return context.getApi().getSecurityGroupApi();
}
} }

View File

@ -46,6 +46,61 @@ public class SecurityGroupApiExpectTest extends BaseRDSApiExpectTest {
TimeZone.setDefault(TimeZone.getTimeZone("America/Los_Angeles")); TimeZone.setDefault(TimeZone.getTimeZone("America/Los_Angeles"));
} }
public void testCreateWithNameAndDescriptionWhenResponseIs2xx() throws Exception {
HttpRequest create = HttpRequest.builder()
.method("POST")
.endpoint("https://rds.us-east-1.amazonaws.com/")
.addHeader("Host", "rds.us-east-1.amazonaws.com")
.payload(
payloadFromStringWithContentType(
"Action=CreateDBSecurityGroup" +
"&DBSecurityGroupDescription=My%20new%20DBSecurityGroup" +
"&DBSecurityGroupName=mydbsecuritygroup" +
"&Signature=ZJ0F0Y5veTPir5NWc7KhmHp7cYIijAxKQFikPHJzzBI%3D" +
"&SignatureMethod=HmacSHA256" +
"&SignatureVersion=2" +
"&Timestamp=2009-11-08T15%3A54%3A08.897Z" +
"&Version=2012-04-23" +
"&AWSAccessKeyId=identity",
"application/x-www-form-urlencoded"))
.build();
HttpResponse createResponse = HttpResponse.builder().statusCode(200)
.payload(payloadFromResourceWithContentType("/create_securitygroup.xml", "text/xml")).build();
RDSApi apiWhenExist = requestSendsResponse(create, createResponse);
apiWhenExist.getSecurityGroupApi().createWithNameAndDescription("mydbsecuritygroup", "My new DBSecurityGroup");
}
public void testCreateInVPCWithNameAndDescriptionWhenResponseIs2xx() throws Exception {
HttpRequest create = HttpRequest.builder()
.method("POST")
.endpoint("https://rds.us-east-1.amazonaws.com/")
.addHeader("Host", "rds.us-east-1.amazonaws.com")
.payload(
payloadFromStringWithContentType(
"Action=CreateDBSecurityGroup" +
"&DBSecurityGroupDescription=My%20new%20DBSecurityGroup" +
"&DBSecurityGroupName=mydbsecuritygroup" +
"&EC2VpcId=vpc-1a2b3c4d" +
"&Signature=8MXHQRkGSKb0TzCKRIlDN9ymruqzY%2FQKgLMXoxYcqFI%3D" +
"&SignatureMethod=HmacSHA256" +
"&SignatureVersion=2" +
"&Timestamp=2009-11-08T15%3A54%3A08.897Z" +
"&Version=2012-04-23" +
"&AWSAccessKeyId=identity",
"application/x-www-form-urlencoded"))
.build();
HttpResponse createResponse = HttpResponse.builder().statusCode(200)
.payload(payloadFromResourceWithContentType("/create_securitygroup.xml", "text/xml")).build();
RDSApi apiWhenExist = requestSendsResponse(create, createResponse);
apiWhenExist.getSecurityGroupApi().createInVPCWithNameAndDescription("vpc-1a2b3c4d", "mydbsecuritygroup", "My new DBSecurityGroup");
}
HttpRequest get = HttpRequest.builder() HttpRequest get = HttpRequest.builder()
.method("POST") .method("POST")
.endpoint("https://rds.us-east-1.amazonaws.com/") .endpoint("https://rds.us-east-1.amazonaws.com/")
@ -185,6 +240,171 @@ public class SecurityGroupApiExpectTest extends BaseRDSApiExpectTest {
new DescribeDBSecurityGroupsResponseTest().expected().toString()); new DescribeDBSecurityGroupsResponseTest().expected().toString());
} }
public void testAuthorizeIngressToIPRangeWhenResponseIs2xx() throws Exception {
HttpRequest authorize = HttpRequest.builder()
.method("POST")
.endpoint("https://rds.us-east-1.amazonaws.com/")
.addHeader("Host", "rds.us-east-1.amazonaws.com")
.payload(
payloadFromStringWithContentType(
"Action=AuthorizeDBSecurityGroupIngress" +
"&CIDRIP=0.0.0.0%2F0" +
"&DBSecurityGroupName=mydbsecuritygroup" +
"&Signature=Wk06HjnbFH5j%2FyfguUK6p3ZJU9kpYPgOlN9IGctLVSk%3D" +
"&SignatureMethod=HmacSHA256" +
"&SignatureVersion=2" +
"&Timestamp=2009-11-08T15%3A54%3A08.897Z" +
"&Version=2012-04-23" +
"&AWSAccessKeyId=identity",
"application/x-www-form-urlencoded"))
.build();
HttpResponse authorizeResponse = HttpResponse.builder().statusCode(200)
.payload(payloadFromResourceWithContentType("/authorize_securitygroup.xml", "text/xml")).build();
RDSApi apiWhenExist = requestSendsResponse(authorize, authorizeResponse);
apiWhenExist.getSecurityGroupApi().authorizeIngressToIPRange("mydbsecuritygroup", "0.0.0.0/0");
}
public void testAuthorizeIngressToEC2SecurityGroupOfOwnerWhenResponseIs2xx() throws Exception {
HttpRequest authorize = HttpRequest.builder()
.method("POST")
.endpoint("https://rds.us-east-1.amazonaws.com/")
.addHeader("Host", "rds.us-east-1.amazonaws.com")
.payload(
payloadFromStringWithContentType(
"Action=AuthorizeDBSecurityGroupIngress" +
"&DBSecurityGroupName=mydbsecuritygroup" +
"&EC2SecurityGroupName=myec2securitygroup" +
"&EC2SecurityGroupOwnerId=054794666394" +
"&Signature=MM%2B8ccK7Mh%2BWLS4qA1NUyOqtkjC1ICXug8wcEyD4a6c%3D" +
"&SignatureMethod=HmacSHA256" +
"&SignatureVersion=2" +
"&Timestamp=2009-11-08T15%3A54%3A08.897Z" +
"&Version=2012-04-23" +
"&AWSAccessKeyId=identity",
"application/x-www-form-urlencoded"))
.build();
HttpResponse authorizeResponse = HttpResponse.builder().statusCode(200)
.payload(payloadFromResourceWithContentType("/authorize_securitygroup.xml", "text/xml")).build();
RDSApi apiWhenExist = requestSendsResponse(authorize, authorizeResponse);
apiWhenExist.getSecurityGroupApi().authorizeIngressToEC2SecurityGroupOfOwner("mydbsecuritygroup", "myec2securitygroup", "054794666394");
}
public void testAuthorizeIngressToVPCSecurityGroupWhenResponseIs2xx() throws Exception {
HttpRequest authorize = HttpRequest.builder()
.method("POST")
.endpoint("https://rds.us-east-1.amazonaws.com/")
.addHeader("Host", "rds.us-east-1.amazonaws.com")
.payload(
payloadFromStringWithContentType(
"Action=AuthorizeDBSecurityGroupIngress" +
"&DBSecurityGroupName=mydbsecuritygroup" +
"&EC2SecurityGroupId=sg-1312321312" +
"&Signature=o31Wey%2FwliTbHJoxdF7KGqIJwSM6pfqzkjIYio3XNGs%3D" +
"&SignatureMethod=HmacSHA256" +
"&SignatureVersion=2" +
"&Timestamp=2009-11-08T15%3A54%3A08.897Z" +
"&Version=2012-04-23" +
"&AWSAccessKeyId=identity",
"application/x-www-form-urlencoded"))
.build();
HttpResponse authorizeResponse = HttpResponse.builder().statusCode(200)
.payload(payloadFromResourceWithContentType("/authorize_securitygroup.xml", "text/xml")).build();
RDSApi apiWhenExist = requestSendsResponse(authorize, authorizeResponse);
apiWhenExist.getSecurityGroupApi().authorizeIngressToVPCSecurityGroup("mydbsecuritygroup", "sg-1312321312");
}
public void testRevokeIngressFromIPRangeWhenResponseIs2xx() throws Exception {
HttpRequest revoke = HttpRequest.builder()
.method("POST")
.endpoint("https://rds.us-east-1.amazonaws.com/")
.addHeader("Host", "rds.us-east-1.amazonaws.com")
.payload(
payloadFromStringWithContentType(
"Action=RevokeDBSecurityGroupIngress" +
"&CIDRIP=0.0.0.0%2F0" +
"&DBSecurityGroupName=mydbsecuritygroup" +
"&Signature=YD1%2BzKmoWyYCmqWq1X9f%2FVj6UC7UnkwkPf%2BA5urnz%2BE%3D" +
"&SignatureMethod=HmacSHA256" +
"&SignatureVersion=2" +
"&Timestamp=2009-11-08T15%3A54%3A08.897Z" +
"&Version=2012-04-23" +
"&AWSAccessKeyId=identity",
"application/x-www-form-urlencoded"))
.build();
HttpResponse revokeResponse = HttpResponse.builder().statusCode(200)
.payload(payloadFromResourceWithContentType("/revoke_securitygroup.xml", "text/xml")).build();
RDSApi apiWhenExist = requestSendsResponse(revoke, revokeResponse);
apiWhenExist.getSecurityGroupApi().revokeIngressFromIPRange("mydbsecuritygroup", "0.0.0.0/0");
}
public void testRevokeIngressFromEC2SecurityGroupOfOwnerWhenResponseIs2xx() throws Exception {
HttpRequest revoke = HttpRequest.builder()
.method("POST")
.endpoint("https://rds.us-east-1.amazonaws.com/")
.addHeader("Host", "rds.us-east-1.amazonaws.com")
.payload(
payloadFromStringWithContentType(
"Action=RevokeDBSecurityGroupIngress" +
"&DBSecurityGroupName=mydbsecuritygroup" +
"&EC2SecurityGroupName=myec2securitygroup" +
"&EC2SecurityGroupOwnerId=054794666394" +
"&Signature=OknWXceQDAgmZBNzDdhxjaOJI48hYrnFJDOySBc4Qy4%3D" +
"&SignatureMethod=HmacSHA256" +
"&SignatureVersion=2" +
"&Timestamp=2009-11-08T15%3A54%3A08.897Z" +
"&Version=2012-04-23" +
"&AWSAccessKeyId=identity",
"application/x-www-form-urlencoded"))
.build();
HttpResponse revokeResponse = HttpResponse.builder().statusCode(200)
.payload(payloadFromResourceWithContentType("/revoke_securitygroup.xml", "text/xml")).build();
RDSApi apiWhenExist = requestSendsResponse(revoke, revokeResponse);
apiWhenExist.getSecurityGroupApi().revokeIngressFromEC2SecurityGroupOfOwner("mydbsecuritygroup", "myec2securitygroup", "054794666394");
}
public void testRevokeIngressFromVPCSecurityGroupWhenResponseIs2xx() throws Exception {
HttpRequest revoke = HttpRequest.builder()
.method("POST")
.endpoint("https://rds.us-east-1.amazonaws.com/")
.addHeader("Host", "rds.us-east-1.amazonaws.com")
.payload(
payloadFromStringWithContentType(
"Action=RevokeDBSecurityGroupIngress" +
"&DBSecurityGroupName=mydbsecuritygroup" +
"&EC2SecurityGroupId=sg-1312321312" +
"&Signature=YI2oGYI%2BCx4DGYx43WH%2FehW6CWe6X6wEipsp5zPySzw%3D" +
"&SignatureMethod=HmacSHA256" +
"&SignatureVersion=2" +
"&Timestamp=2009-11-08T15%3A54%3A08.897Z" +
"&Version=2012-04-23" +
"&AWSAccessKeyId=identity",
"application/x-www-form-urlencoded"))
.build();
HttpResponse revokeResponse = HttpResponse.builder().statusCode(200)
.payload(payloadFromResourceWithContentType("/revoke_securitygroup.xml", "text/xml")).build();
RDSApi apiWhenExist = requestSendsResponse(revoke, revokeResponse);
apiWhenExist.getSecurityGroupApi().revokeIngressFromVPCSecurityGroup("mydbsecuritygroup", "sg-1312321312");
}
HttpRequest delete = HttpRequest.builder() HttpRequest delete = HttpRequest.builder()
.method("POST") .method("POST")
.endpoint("https://rds.us-east-1.amazonaws.com/") .endpoint("https://rds.us-east-1.amazonaws.com/")

View File

@ -19,15 +19,28 @@
package org.jclouds.rds.features; package org.jclouds.rds.features;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import org.jclouds.collect.IterableWithMarker; import org.jclouds.collect.IterableWithMarker;
import org.jclouds.predicates.RetryablePredicate;
import org.jclouds.rds.domain.Authorization;
import org.jclouds.rds.domain.Authorization.Status;
import org.jclouds.rds.domain.EC2SecurityGroup; import org.jclouds.rds.domain.EC2SecurityGroup;
import org.jclouds.rds.domain.SecurityGroup; import org.jclouds.rds.domain.SecurityGroup;
import org.jclouds.rds.internal.BaseRDSApiLiveTest; import org.jclouds.rds.internal.BaseRDSApiLiveTest;
import org.jclouds.rds.options.ListSecurityGroupsOptions; import org.jclouds.rds.options.ListSecurityGroupsOptions;
import org.testng.Assert; import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
/** /**
@ -35,19 +48,102 @@ import com.google.common.collect.Iterables;
*/ */
@Test(groups = "live", testName = "SecurityGroupApiLiveTest") @Test(groups = "live", testName = "SecurityGroupApiLiveTest")
public class SecurityGroupApiLiveTest extends BaseRDSApiLiveTest { public class SecurityGroupApiLiveTest extends BaseRDSApiLiveTest {
public static final String SECURITYGROUP = (System.getProperty("user.name") + "-jclouds-securityGroup")
.toLowerCase();
private RetryablePredicate<SecurityGroup> ipRangesAuthorized;
private RetryablePredicate<SecurityGroup> ipRangesRevoked;
@BeforeClass(groups = "live")
@Override
public void setupContext() {
super.setupContext();
ipRangesAuthorized = new RetryablePredicate<SecurityGroup>(new Predicate<SecurityGroup>() {
@Override
public boolean apply(SecurityGroup input) {
return Iterables.all(api().get(input.getName()).getIPRanges(), new Predicate<Authorization>() {
@Override
public boolean apply(@Nullable Authorization i2) {
return i2.getStatus() == Status.AUTHORIZED;
}
});
}
}, 30000, 100, 500, TimeUnit.MILLISECONDS);
ipRangesRevoked = new RetryablePredicate<SecurityGroup>(new Predicate<SecurityGroup>() {
@Override
public boolean apply(SecurityGroup input) {
return api().get(input.getName()).getIPRanges().size() == 0;
}
}, 30000, 100, 500, TimeUnit.MILLISECONDS);
}
private SecurityGroup securityGroup;
public void testCreateSecurityGroup() {
SecurityGroup newSecurityGroup = api().createWithNameAndDescription(SECURITYGROUP, "jclouds");
securityGroup = newSecurityGroup;
Logger.getAnonymousLogger().info("created securityGroup: " + securityGroup);
assertEquals(securityGroup.getName(), SECURITYGROUP);
assertEquals(securityGroup.getDescription(), "jclouds");
checkSecurityGroup(newSecurityGroup);
}
@Test(dependsOnMethods = "testCreateSecurityGroup")
protected void testAuthorizeIPRange() {
securityGroup = api().authorizeIngressToIPRange(SECURITYGROUP, "0.0.0.0/0");
assertTrue(ipRangesAuthorized.apply(securityGroup), securityGroup.toString());
securityGroup = api().get(securityGroup.getName());
Logger.getAnonymousLogger().info("ip range authorized: " + securityGroup);
}
@Test(dependsOnMethods = "testAuthorizeIPRange")
protected void testRevokeIPRange() {
securityGroup = api().revokeIngressFromIPRange(SECURITYGROUP, "0.0.0.0/0");
assertTrue(ipRangesRevoked.apply(securityGroup), securityGroup.toString());
securityGroup = api().get(securityGroup.getName());
Logger.getAnonymousLogger().info("ip range revoked: " + securityGroup);
}
@Test(dependsOnMethods = "testRevokeIPRange")
public void testDeleteSecurityGroup() {
api().delete(securityGroup.getName());
// TODO block and determine the state of a deleted securityGroup
Logger.getAnonymousLogger().info("securityGroup deleted: " + securityGroup);
}
@Override
@AfterClass(groups = "live")
protected void tearDownContext() {
api().delete(SECURITYGROUP);
super.tearDownContext();
}
static void checkSecurityGroup(SecurityGroup securityGroup) { static void checkSecurityGroup(SecurityGroup securityGroup) {
checkNotNull(securityGroup.getName(), "Name cannot be null for a SecurityGroup: %s", securityGroup); checkNotNull(securityGroup.getName(), "Name cannot be null for a SecurityGroup: %s", securityGroup);
checkNotNull(securityGroup.getDescription(), "Description cannot be null for a SecurityGroup: %s", securityGroup); checkNotNull(securityGroup.getDescription(), "Description cannot be null for a SecurityGroup: %s", securityGroup);
checkNotNull(securityGroup.getOwnerId(), "OwnerId cannot be null for a SecurityGroup: %s", securityGroup); checkNotNull(securityGroup.getOwnerId(), "OwnerId cannot be null for a SecurityGroup: %s", securityGroup);
checkNotNull(securityGroup.getVpcId(), "VpcId cannot be null for a SecurityGroup: %s", securityGroup); checkNotNull(securityGroup.getVpcId(), "VpcId cannot be null for a SecurityGroup: %s", securityGroup);
for (EC2SecurityGroup security : securityGroup.getEC2SecurityGroups()) { for (EC2SecurityGroup security : securityGroup.getEC2SecurityGroups()) {
checkEC2SecurityGroup(security); checkEC2SecurityGroup(security);
} }
} }
static void checkEC2SecurityGroup(EC2SecurityGroup security) { static void checkEC2SecurityGroup(EC2SecurityGroup security) {
checkNotNull(security.getId(), "Id can be null for a SecurityGroup, but its Optional Wrapper cannot: %s", security); checkNotNull(security.getId(), "Id can be null for a SecurityGroup, but its Optional Wrapper cannot: %s",
security);
checkNotNull(security.getStatus(), "Status cannot be null for a SecurityGroup: %s", security); checkNotNull(security.getStatus(), "Status cannot be null for a SecurityGroup: %s", security);
checkNotNull(security.getName(), "Name cannot be null for a SecurityGroup: %s", security); checkNotNull(security.getName(), "Name cannot be null for a SecurityGroup: %s", security);
checkNotNull(security.getOwnerId(), "Name cannot be null for a SecurityGroup: %s", security); checkNotNull(security.getOwnerId(), "Name cannot be null for a SecurityGroup: %s", security);

View File

@ -60,7 +60,8 @@ public class DescribeDBInstancesResponseTest extends BaseHandlerTest {
.engine("mysql") .engine("mysql")
.multiAZ(false) .multiAZ(false)
.licenseModel("general-public-license") .licenseModel("general-public-license")
.status("available") .rawStatus("available")
.status(Instance.Status.AVAILABLE)
.engineVersion("5.1.50") .engineVersion("5.1.50")
.endpoint(HostAndPort.fromParts("simcoprod01.cu7u2t4uz396.us-east-1.rds.amazonaws.com", 3306)) .endpoint(HostAndPort.fromParts("simcoprod01.cu7u2t4uz396.us-east-1.rds.amazonaws.com", 3306))
.id("simcoprod01") .id("simcoprod01")

View File

@ -25,6 +25,7 @@ import java.io.InputStream;
import org.jclouds.collect.IterableWithMarker; import org.jclouds.collect.IterableWithMarker;
import org.jclouds.collect.IterableWithMarkers; import org.jclouds.collect.IterableWithMarkers;
import org.jclouds.http.functions.BaseHandlerTest; import org.jclouds.http.functions.BaseHandlerTest;
import org.jclouds.rds.domain.Authorization.Status;
import org.jclouds.rds.domain.EC2SecurityGroup; import org.jclouds.rds.domain.EC2SecurityGroup;
import org.jclouds.rds.domain.IPRange; import org.jclouds.rds.domain.IPRange;
import org.jclouds.rds.domain.SecurityGroup; import org.jclouds.rds.domain.SecurityGroup;
@ -56,17 +57,24 @@ public class DescribeDBSecurityGroupsResponseTest extends BaseHandlerTest {
return IterableWithMarkers.from(ImmutableSet.<SecurityGroup>builder() return IterableWithMarkers.from(ImmutableSet.<SecurityGroup>builder()
.add(SecurityGroup.builder() .add(SecurityGroup.builder()
.ec2SecurityGroup(EC2SecurityGroup.builder() .ec2SecurityGroup(EC2SecurityGroup.builder()
.status("authorized") .rawStatus("authorized")
.status(Status.AUTHORIZED)
.name("myec2securitygroup") .name("myec2securitygroup")
.ownerId("054794666394").build()) .ownerId("054794666394").build())
.description("default") .description("default")
.ipRange(IPRange.builder().cidrIp("127.0.0.1/30").status("authorized").build()) .ipRange(IPRange.builder()
.cidrIp("127.0.0.1/30")
.rawStatus("authorized")
.status(Status.AUTHORIZED).build())
.ownerId("621567473609") .ownerId("621567473609")
.name("default") .name("default")
.vpcId("vpc-1ab2c3d4").build()) .vpcId("vpc-1ab2c3d4").build())
.add(SecurityGroup.builder() .add(SecurityGroup.builder()
.description("My new DBSecurityGroup") .description("My new DBSecurityGroup")
.ipRange(IPRange.builder().cidrIp("192.168.1.1/24").status("authorized").build()) .ipRange(IPRange.builder()
.cidrIp("192.168.1.1/24")
.rawStatus("authorized")
.status(Status.AUTHORIZED).build())
.ownerId("621567473609") .ownerId("621567473609")
.name("mydbsecuritygroup") .name("mydbsecuritygroup")
.vpcId("vpc-1ab2c3d5").build()) .vpcId("vpc-1ab2c3d5").build())

View File

@ -55,7 +55,8 @@ public class GetInstanceResponseTest extends BaseHandlerTest {
.engine("mysql") .engine("mysql")
.multiAZ(false) .multiAZ(false)
.licenseModel("general-public-license") .licenseModel("general-public-license")
.status("available") .rawStatus("available")
.status(Instance.Status.AVAILABLE)
.engineVersion("5.1.50") .engineVersion("5.1.50")
.endpoint(HostAndPort.fromParts("simcoprod01.cu7u2t4uz396.us-east-1.rds.amazonaws.com", 3306)) .endpoint(HostAndPort.fromParts("simcoprod01.cu7u2t4uz396.us-east-1.rds.amazonaws.com", 3306))
.id("simcoprod01") .id("simcoprod01")

View File

@ -23,6 +23,7 @@ import static org.testng.Assert.assertEquals;
import java.io.InputStream; import java.io.InputStream;
import org.jclouds.http.functions.BaseHandlerTest; import org.jclouds.http.functions.BaseHandlerTest;
import org.jclouds.rds.domain.Authorization.Status;
import org.jclouds.rds.domain.EC2SecurityGroup; import org.jclouds.rds.domain.EC2SecurityGroup;
import org.jclouds.rds.domain.IPRange; import org.jclouds.rds.domain.IPRange;
import org.jclouds.rds.domain.SecurityGroup; import org.jclouds.rds.domain.SecurityGroup;
@ -50,11 +51,15 @@ public class GetSecurityGroupResponseTest extends BaseHandlerTest {
public SecurityGroup expected() { public SecurityGroup expected() {
return SecurityGroup.builder() return SecurityGroup.builder()
.ec2SecurityGroup(EC2SecurityGroup.builder() .ec2SecurityGroup(EC2SecurityGroup.builder()
.status("authorized") .rawStatus("authorized")
.status(Status.AUTHORIZED)
.name("myec2securitygroup") .name("myec2securitygroup")
.ownerId("054794666394").build()) .ownerId("054794666394").build())
.description("default") .description("default")
.ipRange(IPRange.builder().cidrIp("127.0.0.1/30").status("authorized").build()) .ipRange(IPRange.builder()
.cidrIp("127.0.0.1/30")
.rawStatus("authorized")
.status(Status.AUTHORIZED).build())
.ownerId("621567473609") .ownerId("621567473609")
.name("default") .name("default")
.vpcId("vpc-1ab2c3d4").build(); .vpcId("vpc-1ab2c3d4").build();

View File

@ -0,0 +1,20 @@
<AuthorizeDBSecurityGroupIngressResponse xmlns="http://rds.amazonaws.com/doc/2012-04-23/">
<AuthorizeDBSecurityGroupIngressResult>
<DBSecurityGroup>
<EC2SecurityGroups/>
<DBSecurityGroupDescription>My new DBSecurityGroup</DBSecurityGroupDescription>
<IPRanges>
<IPRange>
<CIDRIP>192.168.1.1/24</CIDRIP>
<Status>authorizing</Status>
</IPRange>
</IPRanges>
<OwnerId>621567473609</OwnerId>
<DBSecurityGroupName>mydbsecuritygroup</DBSecurityGroupName>
<VpcId>vpc-1ab2c3d4</VpcId>
</DBSecurityGroup>
</AuthorizeDBSecurityGroupIngressResult>
<ResponseMetadata>
<RequestId>d9799197-bf2d-11de-b88d-993294bf1c81</RequestId>
</ResponseMetadata>
</AuthorizeDBSecurityGroupIngressResponse>

View File

@ -0,0 +1,67 @@
<CreateDBInstanceResponse xmlns="http://rds.amazonaws.com/doc/2012-04-23/">
<CreateDBInstanceResult>
<DBInstance>
<ReadReplicaDBInstanceIdentifiers/>
<Engine>mysql</Engine>
<PendingModifiedValues>
<MasterUserPassword>****</MasterUserPassword>
</PendingModifiedValues>
<BackupRetentionPeriod>1</BackupRetentionPeriod>
<MultiAZ>false</MultiAZ>
<LicenseModel>general-public-license</LicenseModel>
<DBSubnetGroup>
<VpcId>990524496922</VpcId>
<SubnetGroupStatus>Complete</SubnetGroupStatus>
<DBSubnetGroupDescription>description</DBSubnetGroupDescription>
<DBSubnetGroupName>subnet_grp1</DBSubnetGroupName>
<Subnets>
<Subnet>
<SubnetStatus>Active</SubnetStatus>
<SubnetIdentifier>subnet-7c5b4115</SubnetIdentifier>
<SubnetAvailabilityZone>
<Name>us-east-1c</Name>
</SubnetAvailabilityZone>
</Subnet>
<Subnet>
<SubnetStatus>Active</SubnetStatus>
<SubnetIdentifier>subnet-7b5b4112</SubnetIdentifier>
<SubnetAvailabilityZone>
<Name>us-east-1b</Name>
</SubnetAvailabilityZone>
</Subnet>
<Subnet>
<SubnetStatus>Active</SubnetStatus>
<SubnetIdentifier>subnet-3ea6bd57</SubnetIdentifier>
<SubnetAvailabilityZone>
<Name>us-east-1d</Name>
</SubnetAvailabilityZone>
</Subnet>
</Subnets>
</DBSubnetGroup>
<DBInstanceStatus>creating</DBInstanceStatus>
<EngineVersion>5.1.50</EngineVersion>
<DBInstanceIdentifier>simcoprod01</DBInstanceIdentifier>
<DBParameterGroups>
<DBParameterGroup>
<ParameterApplyStatus>in-sync</ParameterApplyStatus>
<DBParameterGroupName>default.mysql5.1</DBParameterGroupName>
</DBParameterGroup>
</DBParameterGroups>
<DBSecurityGroups>
<DBSecurityGroup>
<Status>active</Status>
<DBSecurityGroupName>default</DBSecurityGroupName>
</DBSecurityGroup>
</DBSecurityGroups>
<PreferredBackupWindow>00:00-00:30</PreferredBackupWindow>
<AutoMinorVersionUpgrade>true</AutoMinorVersionUpgrade>
<PreferredMaintenanceWindow>sat:07:30-sat:08:00</PreferredMaintenanceWindow>
<AllocatedStorage>10</AllocatedStorage>
<DBInstanceClass>db.m1.large</DBInstanceClass>
<MasterUsername>master</MasterUsername>
</DBInstance>
</CreateDBInstanceResult>
<ResponseMetadata>
<RequestId>2e5d4270-8501-11e0-bd9b-a7b1ece36d51</RequestId>
</ResponseMetadata>
</CreateDBInstanceResponse>

View File

@ -0,0 +1,15 @@
<CreateDBSecurityGroupResponse xmlns="http://rds.amazonaws.com/doc/2012-04-23/">
<CreateDBSecurityGroupResult>
<DBSecurityGroup>
<EC2SecurityGroups/>
<DBSecurityGroupDescription>My new DBSecurityGroup</DBSecurityGroupDescription>
<IPRanges/>
<OwnerId>565419523791</OwnerId>
<DBSecurityGroupName>mydbsecuritygroup</DBSecurityGroupName>
<VpcId>vpc-1a2b3c4d</VpcId>
</DBSecurityGroup>
</CreateDBSecurityGroupResult>
<ResponseMetadata>
<RequestId>ed662948-a57b-11df-9e38-7ffab86c801f</RequestId>
</ResponseMetadata>
</CreateDBSecurityGroupResponse>

View File

@ -0,0 +1,42 @@
<DeleteDBInstanceResponse xmlns="http://rds.amazonaws.com/doc/2012-04-23/">
<DeleteDBInstanceResult>
<DBInstance>
<ReadReplicaDBInstanceIdentifiers/>
<LatestRestorableTime>2011-05-23T07:15:00Z</LatestRestorableTime>
<Engine>mysql</Engine>
<PendingModifiedValues/>
<BackupRetentionPeriod>1</BackupRetentionPeriod>
<MultiAZ>false</MultiAZ>
<LicenseModel>general-public-license</LicenseModel>
<DBInstanceStatus>deleting</DBInstanceStatus>
<EngineVersion>5.1.50</EngineVersion>
<Endpoint>
<Port>3306</Port>
</Endpoint>
<DBInstanceIdentifier>myrestoreddbinstance</DBInstanceIdentifier>
<DBParameterGroups>
<DBParameterGroup>
<ParameterApplyStatus>in-sync</ParameterApplyStatus>
<DBParameterGroupName>default.mysql5.1</DBParameterGroupName>
</DBParameterGroup>
</DBParameterGroups>
<DBSecurityGroups>
<DBSecurityGroup>
<Status>active</Status>
<DBSecurityGroupName>default</DBSecurityGroupName>
</DBSecurityGroup>
</DBSecurityGroups>
<PreferredBackupWindow>00:00-00:30</PreferredBackupWindow>
<AutoMinorVersionUpgrade>true</AutoMinorVersionUpgrade>
<PreferredMaintenanceWindow>sat:07:30-sat:08:00</PreferredMaintenanceWindow>
<AvailabilityZone>us-east-1d</AvailabilityZone>
<InstanceCreateTime>2011-05-23T06:52:48.255Z</InstanceCreateTime>
<AllocatedStorage>10</AllocatedStorage>
<DBInstanceClass>db.m1.large</DBInstanceClass>
<MasterUsername>master</MasterUsername>
</DBInstance>
</DeleteDBInstanceResult>
<ResponseMetadata>
<RequestId>03ea4ae8-850d-11e0-90aa-eb648410240d</RequestId>
</ResponseMetadata>
</DeleteDBInstanceResponse>

View File

@ -0,0 +1,8 @@
<ErrorResponse xmlns="http://rds.amazonaws.com/doc/2012-04-23/">
<Error>
<Type>Sender</Type>
<Code>InvalidDBInstanceState</Code>
<Message>Can only delete db instances with state in: available, failed, storage-full, incompatible-option-group, incompatible-parameters, incompatible-restore, incompatible-network. Instance adriancole-jclouds has state: deleting.</Message>
</Error>
<RequestId>77db43a3-d6d2-11e1-aaa2-59d4ca8d1ef6</RequestId>
</ErrorResponse>

View File

@ -0,0 +1,20 @@
<RevokeDBSecurityGroupIngressResponse xmlns="http://rds.amazonaws.com/doc/2012-04-23/">
<RevokeDBSecurityGroupIngressResult>
<DBSecurityGroup>
<EC2SecurityGroups/>
<DBSecurityGroupDescription>My new DBSecurityGroup</DBSecurityGroupDescription>
<IPRanges>
<IPRange>
<CIDRIP>192.168.1.1/24</CIDRIP>
<Status>revoking</Status>
</IPRange>
</IPRanges>
<OwnerId>621567473609</OwnerId>
<DBSecurityGroupName>mydbsecuritygroup</DBSecurityGroupName>
<VpcId>vpc-1ab2c3d4</VpcId>
</DBSecurityGroup>
</RevokeDBSecurityGroupIngressResult>
<ResponseMetadata>
<RequestId>beecb8ac-bf5a-11de-9f9f-53d6aee22de9</RequestId>
</ResponseMetadata>
</RevokeDBSecurityGroupIngressResponse>