cleaned up joyent and fixed ssh auth related issues

This commit is contained in:
Adrian Cole 2012-07-24 23:01:08 -07:00
parent 5d6e005e8b
commit 01918a02ec
52 changed files with 1059 additions and 575 deletions

View File

@ -63,6 +63,10 @@ public class JoyentCloudApiMetadata extends BaseRestApiMetadata {
public static Properties defaultProperties() {
Properties properties = BaseRestApiMetadata.defaultProperties();
// auth fail sometimes happens, as the rc.local script that injects the
// authorized key executes after ssh has started.
properties.setProperty("jclouds.ssh.max-retries", "7");
properties.setProperty("jclouds.ssh.retry-auth", "true");
properties.setProperty(JoyentCloudProperties.AUTOGENERATE_KEYS, "true");
return properties;
}

View File

@ -1,54 +0,0 @@
/**
* 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.joyent.cloudapi.v6_5.binders;
import java.util.Map;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.jclouds.http.HttpRequest;
import org.jclouds.joyent.cloudapi.v6_5.domain.Key;
import org.jclouds.json.Json;
import org.jclouds.rest.binders.BindToJsonPayload;
/**
*
* @author Adrian Cole
*
*/
@Singleton
public class BindKeyToJsonPayload extends BindToJsonPayload {
@Inject
public BindKeyToJsonPayload(Json jsonBinder) {
super(jsonBinder);
}
@Override
public <R extends HttpRequest> R bindToRequest(R request, Object toBind) {
// don't include created in the http request
return super.bindToRequest(request, Key.class.cast(toBind).toBuilder().created(null).build());
}
@Override
public <R extends HttpRequest> R bindToRequest(R request, Map<String, Object> postParams) {
throw new IllegalStateException("This should be assigned only a single arg");
}
}

View File

@ -94,7 +94,7 @@ public class JoyentCloudComputeService extends BaseComputeService {
CreateNodesInGroupThenAddToSet runNodesAndAddToSetStrategy, RebootNodeStrategy rebootNodeStrategy,
DestroyNodeStrategy destroyNodeStrategy, ResumeNodeStrategy startNodeStrategy,
SuspendNodeStrategy stopNodeStrategy, Provider<TemplateBuilder> templateBuilderProvider,
Provider<TemplateOptions> templateOptionsProvider,
@Named("DEFAULT") Provider<TemplateOptions> templateOptionsProvider,
@Named(TIMEOUT_NODE_RUNNING) Predicate<AtomicReference<NodeMetadata>> nodeRunning,
@Named(TIMEOUT_NODE_TERMINATED) Predicate<AtomicReference<NodeMetadata>> nodeTerminated,
@Named(TIMEOUT_NODE_SUSPENDED) Predicate<AtomicReference<NodeMetadata>> nodeSuspended,

View File

@ -20,7 +20,6 @@ package org.jclouds.joyent.cloudapi.v6_5.compute.config;
import static org.jclouds.joyent.cloudapi.v6_5.config.JoyentCloudProperties.AUTOGENERATE_KEYS;
import java.security.SecureRandom;
import java.util.Map;
import java.util.Set;
@ -119,9 +118,9 @@ public class JoyentCloudComputeServiceContextModule extends
@Override
protected TemplateOptions provideTemplateOptions(Injector injector, TemplateOptions options) {
return options.as(JoyentCloudTemplateOptions.class)
.generateKey(injector.getInstance(
com.google.inject.Key.get(boolean.class, Names.named(AUTOGENERATE_KEYS))));
boolean generateKey = injector.getInstance(com.google.inject.Key.get(boolean.class,
Names.named(AUTOGENERATE_KEYS)));
return options.as(JoyentCloudTemplateOptions.class).generateKey(generateKey);
}
@Provides
@ -130,6 +129,7 @@ public class JoyentCloudComputeServiceContextModule extends
CacheLoader<DatacenterAndName, KeyAndPrivateKey> in) {
return CacheBuilder.newBuilder().build(in);
}
@Provides
@Singleton
protected Supplier<Map<String, Location>> createLocationIndexedById(
@ -150,12 +150,6 @@ public class JoyentCloudComputeServiceContextModule extends
}, locations);
}
@Provides
@Singleton
protected SecureRandom provideSecureRandom() {
return new SecureRandom();
}
@VisibleForTesting
public static final Map<Machine.State, NodeMetadata.Status> toPortableNodeStatus = ImmutableMap

View File

@ -59,10 +59,11 @@ public class DatasetInDatacenterToImage implements Function<DatasetInDatacenter,
Dataset dataset = datasetInDatacenter.get();
return new ImageBuilder()
.id(datasetInDatacenter.slashEncode())
.providerId(dataset.getId())
// note that it is urn that is the expected value!
.providerId(dataset.getUrn())
.name(dataset.getName())
.operatingSystem(imageToOs.apply(dataset))
.description(dataset.getUrn())
.description(dataset.getDescription())
.version(dataset.getVersion())
.location(location)
.status(Image.Status.AVAILABLE).build();

View File

@ -94,7 +94,7 @@ public class MachineInDatacenterToNodeMetadata implements Function<MachineInData
builder.id(machineInDatacenter.slashEncode());
builder.providerId(from.getId());
builder.name(from.getName());
builder.hostname(from.getName());
builder.hostname(from.getId());
builder.location(zone);
addMetadataAndParseTagsFromCommaDelimitedValue(builder, filterKeys(from.getMetadata(), new Predicate<String>() {
@ -109,7 +109,7 @@ public class MachineInDatacenterToNodeMetadata implements Function<MachineInData
}));
builder.group(nodeNamingConvention.groupInUniqueNameOrNull(from.getName()));
builder.imageId(DatacenterAndName.fromDatacenterAndName(machineInDatacenter.getDatacenter(), from.get())
builder.imageId(DatacenterAndName.fromDatacenterAndName(machineInDatacenter.getDatacenter(), from.getDatasetURN())
.slashEncode());
builder.operatingSystem(findOperatingSystemForMachineOrNull(machineInDatacenter));
builder.hardware(findHardwareForMachineOrNull(machineInDatacenter));
@ -132,7 +132,7 @@ public class MachineInDatacenterToNodeMetadata implements Function<MachineInData
protected OperatingSystem findOperatingSystemForMachineOrNull(MachineInDatacenter machineInDatacenter) {
Image image = findObjectOfTypeForMachineOrNull(images.get(), "image", machineInDatacenter.get()
.get(), machineInDatacenter);
.getDatasetURN(), machineInDatacenter);
return (image != null) ? image.getOperatingSystem() : null;
}

View File

@ -20,18 +20,15 @@ package org.jclouds.joyent.cloudapi.v6_5.compute.loaders;
import static com.google.common.base.Preconditions.checkNotNull;
import java.security.SecureRandom;
import java.util.Map;
import javax.annotation.Resource;
import javax.inject.Named;
import javax.inject.Provider;
import javax.inject.Singleton;
import org.jclouds.compute.functions.GroupNamingConvention;
import org.jclouds.compute.reference.ComputeServiceConstants;
import org.jclouds.crypto.Crypto;
import org.jclouds.crypto.SshKeys;
import org.jclouds.crypto.SshKeyPairGenerator;
import org.jclouds.joyent.cloudapi.v6_5.JoyentCloudApi;
import org.jclouds.joyent.cloudapi.v6_5.compute.internal.KeyAndPrivateKey;
import org.jclouds.joyent.cloudapi.v6_5.domain.Key;
@ -51,23 +48,22 @@ public class CreateUniqueKey extends CacheLoader<DatacenterAndName, KeyAndPrivat
protected Logger logger = Logger.NULL;
protected final JoyentCloudApi cloudApiApi;
protected final GroupNamingConvention.Factory namingConvention;
protected final Crypto crypto;
protected final Provider<SecureRandom> secureRandom;
protected final SshKeyPairGenerator sshKeyPairGenerator;
@Inject
public CreateUniqueKey(JoyentCloudApi cloudApiApi, GroupNamingConvention.Factory namingConvention, Crypto crypto, Provider<SecureRandom> secureRandom) {
public CreateUniqueKey(JoyentCloudApi cloudApiApi, GroupNamingConvention.Factory namingConvention,
SshKeyPairGenerator sshKeyPairGenerator) {
this.cloudApiApi = checkNotNull(cloudApiApi, "cloudApiApi");
this.namingConvention = checkNotNull(namingConvention, "namingConvention");
this.crypto = checkNotNull(crypto, "crypto");
this.secureRandom = checkNotNull(secureRandom, "secureRandom");
this.sshKeyPairGenerator = checkNotNull(sshKeyPairGenerator, "sshKeyPairGenerator");
}
@Override
public KeyAndPrivateKey load(DatacenterAndName datacenterAndName) {
String datacenterId = checkNotNull(datacenterAndName, "datacenterAndName").getDatacenter();
String prefix = datacenterAndName.getName();
Map<String, String> keyPair = SshKeys.generate(crypto.rsaKeyPairGenerator(), secureRandom.get());
Map<String, String> keyPair = sshKeyPairGenerator.get();
String publicKey = keyPair.get("public");
String privateKey = keyPair.get("private");

View File

@ -28,6 +28,7 @@ import org.jclouds.scriptbuilder.domain.Statement;
import com.google.common.base.Objects;
import com.google.common.base.Objects.ToStringHelper;
import com.google.common.base.Optional;
/**
* Contains options supported in the {@code ComputeService#createNodesInGroup} operation on the
@ -48,6 +49,7 @@ import com.google.common.base.Objects.ToStringHelper;
* @author Adrian Cole
*/
public class JoyentCloudTemplateOptions extends TemplateOptions implements Cloneable {
@Override
public JoyentCloudTemplateOptions clone() {
JoyentCloudTemplateOptions options = new JoyentCloudTemplateOptions();
@ -60,11 +62,11 @@ public class JoyentCloudTemplateOptions extends TemplateOptions implements Clone
super.copyTo(to);
if (to instanceof JoyentCloudTemplateOptions) {
JoyentCloudTemplateOptions eTo = JoyentCloudTemplateOptions.class.cast(to);
eTo.generateKey(shouldGenerateKey());
eTo.generateKey = generateKey;
}
}
protected boolean generateKey = false;
protected Optional<Boolean> generateKey = Optional.absent();
@Override
public boolean equals(Object o) {
@ -83,17 +85,14 @@ public class JoyentCloudTemplateOptions extends TemplateOptions implements Clone
@Override
public ToStringHelper string() {
ToStringHelper toString = super.string();
if (generateKey)
toString.add("generateKey", generateKey);
return toString;
return super.string().add("generateKey", generateKey.orNull());
}
/**
* @see #shouldGenerateKey()
*/
public JoyentCloudTemplateOptions generateKey(boolean enable) {
this.generateKey = enable;
this.generateKey = Optional.of(enable);
return this;
}
@ -101,7 +100,7 @@ public class JoyentCloudTemplateOptions extends TemplateOptions implements Clone
*
* @return true if auto generation of keys is enabled
*/
public boolean shouldGenerateKey() {
public Optional<Boolean> shouldGenerateKey() {
return generateKey;
}

View File

@ -20,6 +20,7 @@ package org.jclouds.joyent.cloudapi.v6_5.compute.strategy;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.jclouds.joyent.cloudapi.v6_5.config.JoyentCloudProperties.AUTOGENERATE_KEYS;
import java.util.Map;
import java.util.Set;
@ -55,6 +56,7 @@ public class ApplyJoyentCloudTemplateOptionsCreateNodesWithGroupEncodedIntoNameT
CreateNodesWithGroupEncodedIntoNameThenAddToSet {
private final LoadingCache<DatacenterAndName, KeyAndPrivateKey> keyCache;
private final boolean defaultToAutogenerateKeys;
@Inject
protected ApplyJoyentCloudTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddToSet(
@ -63,10 +65,12 @@ public class ApplyJoyentCloudTemplateOptionsCreateNodesWithGroupEncodedIntoNameT
GroupNamingConvention.Factory namingConvention,
CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap.Factory customizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapFactory,
@Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor,
LoadingCache<DatacenterAndName, KeyAndPrivateKey> keyCache) {
LoadingCache<DatacenterAndName, KeyAndPrivateKey> keyCache,
@Named(AUTOGENERATE_KEYS) boolean defaultToAutogenerateKeys) {
super(addNodeWithTagStrategy, listNodesStrategy, namingConvention, executor,
customizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapFactory);
this.keyCache = checkNotNull(keyCache, "keyCache");
this.defaultToAutogenerateKeys = defaultToAutogenerateKeys;
}
@Override
@ -80,8 +84,11 @@ public class ApplyJoyentCloudTemplateOptionsCreateNodesWithGroupEncodedIntoNameT
assert template.getOptions().equals(templateOptions) : "options didn't clone properly";
String datacenter = mutableTemplate.getLocation().getId();
if (!templateOptions.shouldGenerateKey().isPresent())
templateOptions.generateKey(defaultToAutogenerateKeys);
if (templateOptions.shouldGenerateKey()) {
if (templateOptions.shouldGenerateKey().get()) {
KeyAndPrivateKey keyPair = keyCache.getUnchecked(DatacenterAndName.fromDatacenterAndName(datacenter, namingConvention.create()
.sharedNameForGroup(group)));
// in order to delete the key later

View File

@ -65,7 +65,6 @@ public class JoyentCloudRestClientModule extends RestClientModule<JoyentCloudApi
@Override
protected void configure() {
bind(DateAdapter.class).to(Iso8601DateAdapter.class);
install(new JoyentCloudParserModule());
super.configure();
}

View File

@ -1,156 +1,346 @@
/**
* 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.joyent.cloudapi.v6_5.domain;
import java.util.Date;
import static com.google.common.base.Preconditions.checkNotNull;
import java.beans.ConstructorProperties;
import java.util.Date;
import java.util.Map;
import javax.inject.Named;
import org.jclouds.domain.JsonBall;
import org.jclouds.joyent.cloudapi.v6_5.domain.Machine.Type;
import org.jclouds.json.Json;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
import com.google.common.base.Functions;
import com.google.common.base.Objects;
import com.google.gson.annotations.SerializedName;
import com.google.common.collect.ComparisonChain;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
/**
* Listing of a dataset.
* A dataset is the image of the software on your machine. It contains the software packages that
* will be available on newly provisioned machines. In the case of virtual machines, the dataset
* also includes the operating system.
*
* @author Gerald Pereira
* @see <a href= "http://apidocs.joyent.com/cloudApiapidoc/cloudapi/#datasets" />
* @see <a href= "http://apidocs.joyent.com/sdcapidoc/cloudapi/index.html#ListDatasets" >docs</a>
*/
public class Dataset implements Comparable<Dataset> {
public static Builder builder() {
return new Builder();
}
public Builder toBuilder() {
return new Builder().fromDataset(this);
}
public static class Builder {
private String id;
private String name;
private Type type;
private String version;
private String urn;
private String name;
private String os;
private Type type;
private String description;
private boolean isDefault;
private ImmutableMap.Builder<String, JsonBall> requirements = ImmutableMap.<String, JsonBall>builder();
private String version;
private Date created;
/**
* @see Dataset#getId()
*/
public Builder id(String id) {
this.id = id;
return this;
}
public Builder name(String name) {
this.name = name;
return this;
}
public Builder type(Type type) {
this.type = type;
return this;
}
public Builder version(String version) {
this.version = version;
return this;
}
/**
* @see Dataset#getUrn()
*/
public Builder urn(String urn) {
this.urn = urn;
return this;
}
/**
* @see Dataset#getName()
*/
public Builder name(String name) {
this.name = name;
return this;
}
/**
* @see Dataset#getOs()
*/
public Builder os(String os) {
this.os = os;
return this;
}
/**
* @see Dataset#getType()
*/
public Builder type(Type type) {
this.type = type;
return this;
}
/**
* @see Dataset#getDescription()
*/
public Builder description(String description) {
this.description = description;
return this;
}
/**
* @see Dataset#isDefault()
*/
public Builder isDefault(boolean isDefault) {
this.isDefault = isDefault;
return this;
}
/**
* @see Dataset#getRequirements()
*/
public Builder requirements(Map<String, JsonBall> requirements) {
this.requirements = ImmutableMap.<String, JsonBall> builder();
this.requirements.putAll(checkNotNull(requirements, "requirements"));
return this;
}
/**
* @see Dataset#getRequirements()
*/
public Builder addRequirement(String name, JsonBall values) {
this.requirements.put(checkNotNull(name, "name"), checkNotNull(values, "value of %s", name));
return this;
}
/**
* @see Dataset#getVersion()
*/
public Builder version(String version) {
this.version = version;
return this;
}
/**
* @see Dataset#getCreated()
*/
public Builder created(Date created) {
this.created = created;
return this;
}
public Dataset build() {
return new Dataset(id, name, type, version, urn, isDefault, created);
return new Dataset(id, urn, name, os, type, description, isDefault, requirements.build(), version,
created);
}
public Builder fromDataset(Dataset in) {
return id(in.getId()).name(in.getName()).type(in.getType()).version(in.getVersion()).urn(in.getUrn())
.isDefault(in.isDefault()).created(in.getCreated());
return id(in.getId()).urn(in.getUrn()).name(in.getName()).os(in.getOs()).type(in.getType()).description(in.getDescription())
.isDefault(in.isDefault()).requirements(in.requirements).version(in.getVersion()).created(in.getCreated());
}
}
// The globally unique id for this dataset
protected final String id;
// The "friendly" name for this dataset
protected final String name;
// Whether this is a smartmachine or virtualmachine
protected final Type type;
// The version for this dataset
protected final String version;
// The full URN for this dataset
protected final String urn;
// Whether this is the default dataset in this datacenter
@SerializedName("default")
protected final boolean isDefault;
// Date (ISO8601) When this dataset was created
protected final Date created;
public Dataset(String id, String name, Type type, String version, String urn, boolean isDefault, Date created) {
super();
this.id = id;
this.name = name;
this.type = type;
this.version = version;
this.urn = urn;
private final String id;
private final String name;
private final String os;
private final String urn;
private final Type type;
private final String description;
@Named("default")
private final boolean isDefault;
private final Map<String, JsonBall> requirements;
private final String version;
private final Date created;
@ConstructorProperties({ "id", "urn", "name", "os", "type", "description", "default", "requirements", "version",
"created" })
public Dataset(String id, String urn, String name, String os, Type type, String description, boolean isDefault,
Map<String, JsonBall> requirements, String version, Date created) {
this.id = checkNotNull(id, "id");
this.urn = checkNotNull(urn, "urn of dataset(%s)", id);
this.name = checkNotNull(name, "name of dataset(%s)", id);
this.os = checkNotNull(os, "os of dataset(%s)", id);
this.type = checkNotNull(type, "type of dataset(%s)", id);
this.description = checkNotNull(description, "description of dataset(%s)", id);
this.isDefault = isDefault;
this.created = created;
this.requirements = ImmutableMap.copyOf(checkNotNull(requirements, "requirements of dataset(%s)", id));
this.version = checkNotNull(version, "version of dataset(%s)", id);
this.created = checkNotNull(created, "created of dataset(%s)", id);
}
/**
* The globally unique id for this dataset
*/
public String getId() {
return id;
}
/**
* The full URN for this dataset
*/
public String getUrn() {
return urn;
}
/**
* The friendly name for this dataset
*/
public String getName() {
return name;
}
/**
* The underlying operating system for this dataset
*/
public String getOs() {
return os;
}
/**
* Whether this is a smartmachine or virtualmachine dataset
*/
public Type getType() {
return type;
}
public String getVersion() {
return version;
/**
* The description of this dataset
*/
public String getDescription() {
return description;
}
public String getUrn() {
return urn;
}
/**
* Whether this is the default dataset in this datacenter
*/
public boolean isDefault() {
return isDefault;
}
/**
* If the value is a string, it will be quoted, as that's how json strings are represented.
*
* @return key to a json literal of the value
* @see #getRequirements
* @see Json#fromJson
*/
public Map<String, String> getRequirementsAsJsonLiterals() {
return Maps.transformValues(requirements, Functions.toStringFunction());
}
/**
* Contains a grouping of various minimum requirements for provisioning a machine with this
* dataset. For example 'password' indicates that a password must be provided.
*
* <h4>Note</h4>
*
* requirements can contain arbitrarily complex values. If the value has structure, you should
* use {@link #getRequirementsAsJsonLiterals}
*/
public Map<String, String> getRequirements() {
return Maps.transformValues(requirements, Functions.compose(Functions.toStringFunction(), unquoteString));
}
/**
* The version for this dataset
*/
public String getVersion() {
return version;
}
/**
* When the dataset was created
*/
public Date getCreated() {
return created;
}
@Override
public int compareTo(Dataset other) {
return id.compareTo(other.getId());
}
@VisibleForTesting
static final Function<JsonBall, String> unquoteString = new Function<JsonBall, String>() {
@Override
public String apply(JsonBall input) {
String value = input.toString();
if (value.length() >= 2 && value.charAt(0) == '"' && value.charAt(input.length() - 1) == '"')
return value.substring(1, input.length() - 1);
return value;
}
};
@Override
public boolean equals(Object object) {
if (this == object) {
return true;
}
if (object instanceof Machine) {
return Objects.equal(id, ((Machine) object).id);
if (object instanceof Dataset) {
Dataset that = Dataset.class.cast(object);
return Objects.equal(id, that.id);
} else {
return false;
}
}
@Override
public int hashCode() {
return Objects.hashCode(id);
}
@Override
public String toString() {
return String.format("[id=%s, name=%s, type=%s, version=%s, urn=%s, default=%s, created=%s]", id, name,
type.name(), type.name(), version, urn, isDefault, created);
return Objects.toStringHelper("").omitNullValues()
.add("id", id)
.add("urn", urn)
.add("name", name)
.add("os", os)
.add("type", type)
.add("description", description)
.add("default", isDefault)
.add("requirements", requirements)
.add("version", version)
.add("created", created).toString();
}
@Override
public int compareTo(Dataset that) {
return ComparisonChain.start()
.compare(this.urn, that.urn)
.compare(this.name, that.name)
.compare(this.os, that.os)
.compare(this.type, that.type)
.compare(this.description, that.description)
.compare(this.version, that.version)
.compare(this.created, that.created)
.compare(this.id, that.id).result();
}
}

View File

@ -1,17 +1,37 @@
/**
* 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.joyent.cloudapi.v6_5.domain;
import static com.google.common.base.Preconditions.checkNotNull;
import java.beans.ConstructorProperties;
import java.util.Date;
import com.google.common.base.Objects;
import com.google.common.collect.ComparisonChain;
/**
* Keys are the means by which you operate on your SSH/signing keys. Currently
* CloudAPI supports uploads of public keys in the OpenSSH format.
*
* @author Adrian Cole
* @see <a href="http://apidocs.joyent.com/cloudApiapidoc/cloudapi/#keys" >docs</a>
* @see <a href="http://apidocs.joyent.com/sdcapidoc/cloudapi/index.html#ListKeys" >docs</a>
*/
public class Key implements Comparable<Key> {
@ -26,18 +46,27 @@ public class Key implements Comparable<Key> {
public static class Builder {
private String name;
private String key;
private Date created;
private Date created = new Date();
/**
* @see Key#getName()
*/
public Builder name(String name) {
this.name = name;
return this;
}
/**
* @see Key#get()
*/
public Builder key(String key) {
this.key = key;
return this;
}
/**
* @see Key#getCreated()
*/
public Builder created(Date created) {
this.created = created;
return this;
@ -54,12 +83,14 @@ public class Key implements Comparable<Key> {
protected final String name;
protected final String key;
protected final Date created;
// don't include created in the http request
transient protected final Date created;
@ConstructorProperties({ "name", "key", "created" })
public Key(String name, String key, Date created) {
this.name = checkNotNull(name, "name");
this.key = checkNotNull(key, "key: OpenSSH formatted public key");
this.created = created;
this.key = checkNotNull(key, "key: OpenSSH formatted public key of key(%s)", name);
this.created = checkNotNull(created, "created date of key(%s)", name);
}
/**
@ -75,23 +106,22 @@ public class Key implements Comparable<Key> {
public String get() {
return key;
}
/**
* Date the key was created
*/
public Date getCreated() {
return created;
}
@Override
public int compareTo(Key other) {
return name.compareTo(other.getName());
}
@Override
public boolean equals(Object object) {
if (this == object) {
return true;
}
if (object instanceof Key) {
return Objects.equal(name, ((Key) object).name);
Key that = Key.class.cast(object);
return Objects.equal(name, that.name);
} else {
return false;
}
@ -104,6 +134,16 @@ public class Key implements Comparable<Key> {
@Override
public String toString() {
return String.format("[name=%s, key=%s, created=%s]", name, key, created);
return Objects.toStringHelper("").omitNullValues()
.add("name", name)
.add("key", key)
.add("created", created).toString();
}
@Override
public int compareTo(Key that) {
return ComparisonChain.start()
.compare(this.name, that.name)
.compare(this.created, that.created).result();
}
}

View File

@ -20,10 +20,13 @@ package org.jclouds.joyent.cloudapi.v6_5.domain;
import static com.google.common.base.Preconditions.checkNotNull;
import java.beans.ConstructorProperties;
import java.util.Date;
import java.util.Map;
import java.util.Set;
import javax.inject.Named;
import org.jclouds.domain.JsonBall;
import org.jclouds.joyent.cloudapi.v6_5.reference.Metadata;
import org.jclouds.json.Json;
@ -33,19 +36,40 @@ import com.google.common.base.CaseFormat;
import com.google.common.base.Function;
import com.google.common.base.Functions;
import com.google.common.base.Objects;
import com.google.common.collect.ComparisonChain;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import com.google.gson.annotations.SerializedName;
/**
* Listing of a machine.
* a SmartMachine or traditional Virtual Machine
*
* @author Gerald Pereira
* @see <a href= "http://apidocs.joyent.com/cloudApiapidoc/cloudapi/#machines" />
* @see <a href= "http://apidocs.joyent.com/sdcapidoc/cloudapi/index.html#machines" />
*/
public class Machine implements Comparable<Machine> {
public static enum Type {
VIRTUALMACHINE, SMARTMACHINE, UNRECOGNIZED;
public static Type fromValue(String type) {
try {
return valueOf(CaseFormat.UPPER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, checkNotNull(type, "type")));
} catch (IllegalArgumentException e) {
return UNRECOGNIZED;
}
}
public String value() {
return (CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, name()));
}
@Override
public String toString() {
return value();
}
}
public static enum State {
PROVISIONING, RUNNING, STOPPING, STOPPED, OFFLINE, DELETED, UNRECOGNIZED;
@ -83,56 +107,95 @@ public class Machine implements Comparable<Machine> {
private String dataset;
private int memorySizeMb;
private int diskSizeGb;
private Set<String> ips;
private ImmutableSet.Builder<String> ips = ImmutableSet.<String> builder();
private Date created;
private Date updated;
private Map<String, JsonBall> metadata = ImmutableMap.of();
private ImmutableMap.Builder<String, JsonBall> metadata = ImmutableMap.<String, JsonBall>builder();
/**
* @see Machine#getId()
*/
public Builder id(String id) {
this.id = id;
return this;
}
/**
* @see Machine#getName()
*/
public Builder name(String name) {
this.name = name;
return this;
}
/**
* @see Machine#getType()
*/
public Builder type(Type type) {
this.type = type;
return this;
}
/**
* @see Machine#getState()
*/
public Builder state(State state) {
this.state = state;
return this;
}
/**
* @see Machine#getDatasetURN()
*/
public Builder dataset(String dataset) {
this.dataset = dataset;
return this;
}
/**
* @see Machine#getMemorySizeMb()
*/
public Builder memorySizeMb(int memorySizeMb) {
this.memorySizeMb = memorySizeMb;
return this;
}
/**
* @see Machine#getDiskSizeGb()
*/
public Builder diskSizeGb(int diskSizeGb) {
this.diskSizeGb = diskSizeGb;
return this;
}
/**
* @see Machine#getIps()
*/
public Builder ips(Set<String> ips) {
this.ips = ips;
this.ips = ImmutableSet.<String> builder();
this.ips.addAll(checkNotNull(ips, "ips"));
return this;
}
/**
* @see Machine#getIps()
*/
public Builder addIp(String ip) {
this.ips.add(checkNotNull(ip, "ip"));
return this;
}
/**
* @see Machine#getCreated()
*/
public Builder created(Date created) {
this.created = created;
return this;
}
/**
* @see Machine#getUpdated()
*/
public Builder updated(Date updated) {
this.updated = updated;
return this;
@ -142,110 +205,137 @@ public class Machine implements Comparable<Machine> {
* @see Machine#getMetadata()
*/
public Builder metadata(Map<String, JsonBall> metadata) {
this.metadata = metadata;
this.metadata = ImmutableMap.<String, JsonBall> builder();
this.metadata.putAll(checkNotNull(metadata, "metadata"));
return this;
}
/**
* @see Machine#getMetadata()
*/
public Builder addMetadata(String name, JsonBall values) {
this.metadata.put(checkNotNull(name, "name"), checkNotNull(values, "value of %s", name));
return this;
}
public Machine build() {
return new Machine(id, name, type, state, dataset, memorySizeMb, diskSizeGb, ips, created, updated, metadata);
return new Machine(id, name, type, state, dataset, memorySizeMb, diskSizeGb, ips.build(), created, updated, metadata.build());
}
public Builder fromMachine(Machine in) {
return id(in.getId()).name(in.getName()).type(in.getType()).state(in.getState()).dataset(in.get())
return id(in.getId()).name(in.getName()).type(in.getType()).state(in.getState()).dataset(in.getDatasetURN())
.memorySizeMb(in.getMemorySizeMb()).diskSizeGb(in.getDiskSizeGb()).ips(in.getIps())
.metadata(in.metadata).created(in.getCreated()).updated(in.getUpdated());
}
}
// The globally unique id for this machine
protected final String id;
// The "friendly" name for this machine
protected final String name;
// Whether this is a smartmachine or virtualmachine
protected final Type type;
// The current state of this machine
protected final State state;
// The dataset urn this machine was provisioned with
protected final String dataset;
// The amount of memory this machine has (Mb)
@SerializedName("memory")
@Named("memory")
protected final int memorySizeMb;
// The amount of disk this machine has (Gb)
@SerializedName("disk")
@Named("disk")
protected final int diskSizeGb;
// The IP addresses this machine has
protected final Set<String> ips;
// Date (ISO8601) When this machine was created
protected final Date created;
// Date (ISO8601) When this machine was updated
protected final Date updated;
// metadata Object[String => String] Any "extra" metadata this machine has
private final Map<String, JsonBall> metadata;
@Override
public int compareTo(Machine other) {
return id.compareTo(other.getId());
}
@ConstructorProperties({ "id", "name", "type", "state", "dataset", "memory", "disk", "ips", "created", "updated", "metadata" })
public Machine(String id, String name, Type type, State state, String dataset, int memorySizeMb, int diskSizeGb,
Set<String> ips, Date created, Date updated, final Map<String, JsonBall> metadata) {
super();
this.id = id;
this.name = name;
this.type = type;
this.state = state;
this.dataset = dataset;
this.id = checkNotNull(id, "id");
this.name = checkNotNull(name, "name of machine(%s)", id);
this.type = checkNotNull(type, "type of machine(%s)", id);
this.state = checkNotNull(state, "state of machine(%s)", id);
this.dataset = checkNotNull(dataset, "dataset of machine(%s)", id);
this.memorySizeMb = memorySizeMb;
this.diskSizeGb = diskSizeGb;
this.ips = ImmutableSet.<String> copyOf(ips);
this.created = created;
this.updated = updated;
this.metadata = metadata;
this.ips = ImmutableSet.<String> copyOf(checkNotNull(ips, "ips of machine(%s)", id));
this.created = checkNotNull(created, "created date of machine(%s)", id);
this.updated = checkNotNull(created, "updated date of machine(%s)", id);
this.metadata = ImmutableMap.<String, JsonBall> copyOf(checkNotNull(metadata, "metadata of machine(%s)", id));
}
/**
* The globally unique id for this machine
*/
public String getId() {
return id;
}
/**
* The "friendly" name for this machine
*/
public String getName() {
return name;
}
/**
* Whether this is a smartmachine or virtualmachine
*/
public Type getType() {
return type;
}
/**
* The current state of this machine
*/
public State getState() {
return state;
}
public String get() {
/**
* The dataset urn this machine was provisioned with
*/
public String getDatasetURN() {
return dataset;
}
/**
* The amount of memory this machine has (Mb)
*/
public int getMemorySizeMb() {
return memorySizeMb;
}
/**
* The amount of disk this machine has (Gb)
*/
public int getDiskSizeGb() {
return diskSizeGb;
}
/**
* The IP addresses this machine has
*/
public Set<String> getIps() {
return ips;
}
/**
* When this machine was created
*/
public Date getCreated() {
return created;
}
/**
* When this machine was updated
*/
public Date getUpdated() {
return updated;
}
/**
* If the value is a string, it will be quoted, as that's how json strings are represented.
*
* <h4>note</h4>
*
* If the value is a string, it will be quoted, as that's how json strings are represented.
*
* @return key to a json literal of the value
* @see Metadata#valueType
@ -256,9 +346,13 @@ public class Machine implements Comparable<Machine> {
}
/**
* Note!! metadata can contain arbitrarily complex values. If the value has structure, you should use {@link #getMetadataAsJsonLiterals}
* Any "extra" metadata this machine has
*
* <h4>note</h4>
*
* metadata can contain arbitrarily complex values. If the value has structure, you should use
* {@link #getMetadataAsJsonLiterals}
*
* @return metadata
*/
public Map<String, String> getMetadata() {
return Maps.transformValues(metadata, Functions.compose(Functions.toStringFunction(), unquoteString));
@ -276,14 +370,15 @@ public class Machine implements Comparable<Machine> {
}
};
@Override
public boolean equals(Object object) {
if (this == object) {
return true;
}
if (object instanceof Machine) {
return Objects.equal(id, ((Machine) object).id);
Machine that = Machine.class.cast(object);
return Objects.equal(id, that.id);
} else {
return false;
}
@ -296,7 +391,23 @@ public class Machine implements Comparable<Machine> {
@Override
public String toString() {
return String.format("[id=%s, name=%s, type=%s, state=%s, memory=%s, disk=%s, ips=%s, created=%s, updated=%s]",
id, name, type.name(), state.name(), memorySizeMb, diskSizeGb, ips, created, updated);
return Objects.toStringHelper("").omitNullValues()
.add("id", id)
.add("name", name)
.add("type", type)
.add("state", state)
.add("memorySizeMb", memorySizeMb)
.add("diskSizeGb", diskSizeGb)
.add("ips", ips)
.add("created", created)
.add("updated", updated).toString();
}
@Override
public int compareTo(Machine that) {
return ComparisonChain.start()
.compare(this.name, that.name)
.compare(this.created, that.created)
.compare(this.id, that.id).result();
}
}

View File

@ -18,20 +18,32 @@
*/
package org.jclouds.joyent.cloudapi.v6_5.domain;
import static com.google.common.base.Preconditions.checkNotNull;
import java.beans.ConstructorProperties;
import javax.inject.Named;
import com.google.common.base.Objects;
import com.google.gson.annotations.SerializedName;
import com.google.common.collect.ComparisonChain;
/**
* Listing of a package.
* Packages are named collections of resources that are used to describe the sizes of either a
* smart machine or a virtual machine. These resources include (but are not limited to) RAM, CPUs,
* CPU Caps, Lightweight Threads, Disk Space, Swap size, and Logical Networks.
*
* @author Gerald Pereira
* @see <a href= "http://apidocs.joyent.com/cloudApiapidoc/cloudapi/#machines" />
* @see <a href= "http://apidocs.joyent.com/sdcapidoc/cloudapi/index.html#packages" >docs</a>
*/
public class Package implements Comparable<Package> {
public static Builder builder() {
return new Builder();
}
public Builder toBuilder() {
return new Builder().fromPackage(this);
}
public static class Builder {
private String name;
@ -40,26 +52,41 @@ public class Package implements Comparable<Package> {
private int swapSizeMb;
private boolean isDefault;
/**
* @see Package#getName()
*/
public Builder name(String name) {
this.name = name;
return this;
}
/**
* @see Package#getMemorySizeMb()
*/
public Builder memorySizeMb(int memorySizeMb) {
this.memorySizeMb = memorySizeMb;
return this;
}
/**
* @see Package#getDiskSizeGb()
*/
public Builder diskSizeGb(int diskSizeGb) {
this.diskSizeGb = diskSizeGb;
return this;
}
/**
* @see Package#getSwapSizeMb()
*/
public Builder swapSizeMb(int swapSizeMb) {
this.swapSizeMb = swapSizeMb;
return this;
}
/**
* @see Package#isDefault()
*/
public Builder isDefault(boolean isDefault) {
this.isDefault = isDefault;
return this;
@ -75,51 +102,56 @@ public class Package implements Comparable<Package> {
}
}
// The "friendly" name for this machine
protected final String name;
// The amount of memory this package has (Mb)
@SerializedName("memory")
@Named("memory")
protected final int memorySizeMb;
// The amount of disk this package has (Gb)
@SerializedName("disk")
@Named("disk")
protected final int diskSizeGb;
// The amount of swap this package has (Gb)
@SerializedName("swap")
@Named("swap")
protected final int swapSizeMb;
// Whether this is the default package in this datacenter
@SerializedName("default")
@Named("default")
protected final boolean isDefault;
@Override
public int compareTo(Package other) {
return name.compareTo(other.getName());
}
@ConstructorProperties({ "name", "memory", "disk", "swap", "default" })
public Package(String name, int memorySizeMb, int diskSizeGb, int swapSizeMb, boolean isDefault) {
super();
this.name = name;
this.name = checkNotNull(name, "name");
this.memorySizeMb = memorySizeMb;
this.diskSizeGb = diskSizeGb;
this.swapSizeMb = swapSizeMb;
this.isDefault = isDefault;
}
/**
* The "friendly name for this package
*/
public String getName() {
return name;
}
/**
* How much memory will by available (in Mb)
*/
public int getMemorySizeMb() {
return memorySizeMb;
}
/**
* How much disk space will be available (in Gb)
*/
public int getDiskSizeGb() {
return diskSizeGb;
}
/**
* How much swap memory will be available (in Mb)
*/
public int getSwapSizeMb() {
return swapSizeMb;
}
/**
* Whether this is the default package in this datacenter
*/
public boolean isDefault() {
return isDefault;
}
@ -130,7 +162,8 @@ public class Package implements Comparable<Package> {
return true;
}
if (object instanceof Package) {
return Objects.equal(name, ((Package) object).name);
Package that = Package.class.cast(object);
return Objects.equal(name, that.name);
} else {
return false;
}
@ -143,7 +176,20 @@ public class Package implements Comparable<Package> {
@Override
public String toString() {
return String.format("[name=%s, memory=%s, disk=%s, swap=%s, default=%s]", name, memorySizeMb, diskSizeGb,
swapSizeMb, isDefault);
return Objects.toStringHelper("").omitNullValues()
.add("name", name)
.add("memorySizeMb", memorySizeMb)
.add("diskSizeGb", diskSizeGb)
.add("swapSizeMb", swapSizeMb)
.add("isDefault", isDefault).toString();
}
@Override
public int compareTo(Package that) {
return ComparisonChain.start()
.compare(this.name, that.name)
.compare(this.memorySizeMb, that.memorySizeMb)
.compare(this.diskSizeGb, that.diskSizeGb)
.compare(this.swapSizeMb, that.swapSizeMb).result();
}
}

View File

@ -1,26 +0,0 @@
package org.jclouds.joyent.cloudapi.v6_5.domain;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.base.CaseFormat;
public enum Type {
VIRTUALMACHINE, SMARTMACHINE, UNRECOGNIZED;
public static Type fromValue(String type) {
try {
return valueOf(CaseFormat.UPPER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, checkNotNull(type, "type")));
} catch (IllegalArgumentException e) {
return UNRECOGNIZED;
}
}
public String value() {
return (CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, name()));
}
@Override
public String toString() {
return value();
}
}

View File

@ -29,7 +29,7 @@ public class DatasetInDatacenter extends DatacenterAndId {
protected final Dataset dataset;
public DatasetInDatacenter(Dataset dataset, String datacenterId) {
super(datacenterId, checkNotNull(dataset, "dataset").getId());
super(datacenterId, checkNotNull(dataset, "dataset").getUrn());
this.dataset = dataset;
}

View File

@ -29,7 +29,7 @@ import org.jclouds.concurrent.Timeout;
*
* @see DatacenterAsyncApi
* @author Adrian Cole
* @see <a href="http://cloudApi.joyent.org/cloudApiapi.html">api doc</a>
* @see <a href="http://apidocs.joyent.com/sdcapidoc/cloudapi/index.html#datacenters">api doc</a>
*/
@Timeout(duration = 30, timeUnit = TimeUnit.SECONDS)
public interface DatacenterApi {

View File

@ -40,7 +40,7 @@ import com.google.common.util.concurrent.ListenableFuture;
*
* @see DatacenterApi
* @author Adrian Cole
* @see <a href="http://cloudApi.joyent.org/cloudApiapi.html">api doc</a>
* @see <a href="http://apidocs.joyent.com/sdcapidoc/cloudapi/index.html#datacenters">api doc</a>
*/
@SkipEncoding({ '/', '=' })
@Headers(keys = "X-Api-Version", values = "{jclouds.api-version}")

View File

@ -12,7 +12,7 @@ import org.jclouds.joyent.cloudapi.v6_5.domain.Dataset;
*
* @author Gerald Pereira
* @see DatasetAsyncApi
* @see <a href="http://apidocs.joyent.com/cloudApiapidoc/cloudapi">api doc</a>
* @see <a href="http://apidocs.joyent.com/sdcapidoc/cloudapi/index.html#datasets">api doc</a>
*/
@Timeout(duration = 30, timeUnit = TimeUnit.SECONDS)
public interface DatasetApi {

View File

@ -25,7 +25,7 @@ import com.google.common.util.concurrent.ListenableFuture;
*
* @author Gerald Pereira
* @see DatasetApi
* @see <a href="http://apidocs.joyent.com/cloudApiapidoc/cloudapi">api doc</a>
* @see <a href="http://apidocs.joyent.com/sdcapidoc/cloudapi/index.html#datasets">api doc</a>
*/
@SkipEncoding({ '/', '=' })
@Headers(keys = "X-Api-Version", values = "{jclouds.api-version}")

View File

@ -12,7 +12,7 @@ import org.jclouds.joyent.cloudapi.v6_5.domain.Key;
*
* @author Adrian Cole
* @see KeyAsyncApi
* @see <a href="http://apidocs.joyent.com/cloudApiapidoc/cloudapi/#keys">api doc</a>
* @see <a href="http://apidocs.joyent.com/sdcapidoc/cloudapi/index.html#keys">api doc</a>
*/
@Timeout(duration = 30, timeUnit = TimeUnit.SECONDS)
public interface KeyApi {

View File

@ -11,22 +11,23 @@ import javax.ws.rs.PathParam;
import javax.ws.rs.core.MediaType;
import org.jclouds.http.filters.BasicAuthentication;
import org.jclouds.joyent.cloudapi.v6_5.binders.BindKeyToJsonPayload;
import org.jclouds.joyent.cloudapi.v6_5.domain.Key;
import org.jclouds.rest.annotations.BinderParam;
import org.jclouds.rest.annotations.ExceptionParser;
import org.jclouds.rest.annotations.Headers;
import org.jclouds.rest.annotations.RequestFilters;
import org.jclouds.rest.annotations.SkipEncoding;
import org.jclouds.rest.binders.BindToJsonPayload;
import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404;
import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404;
import org.jclouds.rest.functions.ReturnVoidOnNotFoundOr404;
import com.google.common.util.concurrent.ListenableFuture;
/**
* @author Adrian Cole
* @see KeyApi
* @see <a href="http://apidocs.joyent.com/cloudApiapidoc/cloudapi/#keys">api doc</a>
* @see <a href="http://apidocs.joyent.com/sdcapidoc/cloudapi/index.html#keys">api doc</a>
*/
@SkipEncoding({ '/', '=' })
@Headers(keys = "X-Api-Version", values = "{jclouds.api-version}")
@ -56,7 +57,7 @@ public interface KeyAsyncApi {
@POST
@Path("/my/keys")
@Consumes(MediaType.APPLICATION_JSON)
ListenableFuture<Key> create(@BinderParam(BindKeyToJsonPayload.class) Key key);
ListenableFuture<Key> create(@BinderParam(BindToJsonPayload.class) Key key);
/**
* @see KeyApi#delete
@ -64,6 +65,7 @@ public interface KeyAsyncApi {
@DELETE
@Consumes(MediaType.APPLICATION_JSON)
@Path("/my/keys/{name}")
@ExceptionParser(ReturnVoidOnNotFoundOr404.class)
ListenableFuture<Void> delete(@PathParam("name") String name);
}

View File

@ -31,7 +31,7 @@ import org.jclouds.joyent.cloudapi.v6_5.options.CreateMachineOptions;
*
* @author Gerald Pereira
* @see MachineAsyncApi
* @see <a href="http://apidocs.joyent.com/cloudApiapidoc/cloudapi">api doc</a>
* @see <a href="http://apidocs.joyent.com/sdcapidoc/cloudapi/index.html#machines">api doc</a>
*/
@Timeout(duration = 30, timeUnit = TimeUnit.SECONDS)
public interface MachineApi {

View File

@ -41,6 +41,7 @@ import org.jclouds.rest.annotations.RequestFilters;
import org.jclouds.rest.annotations.SkipEncoding;
import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404;
import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404;
import org.jclouds.rest.functions.ReturnVoidOnNotFoundOr404;
import com.google.common.util.concurrent.ListenableFuture;
@ -50,7 +51,7 @@ import com.google.common.util.concurrent.ListenableFuture;
*
* @author Gerald Pereira
* @see MachineApi
* @see <a href="http://apidocs.joyent.com/cloudApiapidoc/cloudapi">api doc</a>
* @see <a href="http://apidocs.joyent.com/sdcapidoc/cloudapi/index.html#machines">api doc</a>
*/
@SkipEncoding({ '/', '=' })
@Headers(keys = "X-Api-Version", values = "{jclouds.api-version}")
@ -74,7 +75,7 @@ public interface MachineAsyncApi {
@Consumes(MediaType.APPLICATION_JSON)
@ExceptionParser(ReturnNullOnNotFoundOr404.class)
ListenableFuture<Machine> get(@PathParam("id") String id);
/**
* @see MachineApi#createWithDataset(String)
*/
@ -82,63 +83,62 @@ public interface MachineAsyncApi {
@Path("/my/machines")
@Consumes(MediaType.APPLICATION_JSON)
ListenableFuture<Machine> createWithDataset(@QueryParam("dataset") String datasetURN);
/**
* @see MachineApi#createWithDataset(String, CreateMachineOptions)
*/
@POST
/**
* @see MachineApi#createWithDataset(String, CreateMachineOptions)
*/
@POST
@Path("/my/machines")
@Consumes(MediaType.APPLICATION_JSON)
ListenableFuture<Machine> createWithDataset(@QueryParam("dataset") String datasetURN, CreateMachineOptions options);
/**
* @see MachineApi#stop
*/
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_FORM_URLENCODED)
@Path("/my/machines/{id}")
@Payload("action=stop")
ListenableFuture<Void> stop(@PathParam("id") String id);
/**
* @see MachineApi#start
*/
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_FORM_URLENCODED)
@Path("/my/machines/{id}")
@Payload("action=start")
ListenableFuture<Void> start(@PathParam("id") String id);
/**
* @see MachineApi#reboot
*/
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_FORM_URLENCODED)
@Path("/my/machines/{id}")
@Payload("action=reboot")
ListenableFuture<Void> reboot(@PathParam("id") String id);
/**
* @see MachineApi#resize
*/
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_FORM_URLENCODED)
@Path("/my/machines/{id}")
@Payload("action=resize&package={package}")
ListenableFuture<Void> resize(@PathParam("id") String id,@PayloadParam("package") String packageJoyentCloud);
/**
* @see MachineApi#delete
*/
@DELETE
@Consumes(MediaType.APPLICATION_JSON)
@Path("/my/machines/{id}")
ListenableFuture<Void> delete(@PathParam("id") String id);
/**
* @see MachineApi#stop
*/
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_FORM_URLENCODED)
@Path("/my/machines/{id}")
@Payload("action=stop")
ListenableFuture<Void> stop(@PathParam("id") String id);
/**
* @see MachineApi#start
*/
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_FORM_URLENCODED)
@Path("/my/machines/{id}")
@Payload("action=start")
ListenableFuture<Void> start(@PathParam("id") String id);
/**
* @see MachineApi#reboot
*/
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_FORM_URLENCODED)
@Path("/my/machines/{id}")
@Payload("action=reboot")
ListenableFuture<Void> reboot(@PathParam("id") String id);
/**
* @see MachineApi#resize
*/
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_FORM_URLENCODED)
@Path("/my/machines/{id}")
@Payload("action=resize&package={package}")
ListenableFuture<Void> resize(@PathParam("id") String id, @PayloadParam("package") String packageJoyentCloud);
/**
* @see MachineApi#delete
*/
@DELETE
@Consumes(MediaType.APPLICATION_JSON)
@Path("/my/machines/{id}")
@ExceptionParser(ReturnVoidOnNotFoundOr404.class)
ListenableFuture<Void> delete(@PathParam("id") String id);
}

View File

@ -11,7 +11,7 @@ import org.jclouds.concurrent.Timeout;
*
* @author Gerald Pereira
* @see PackageAsyncApi
* @see <a href="http://apidocs.joyent.com/cloudApiapidoc/cloudapi">api doc</a>
* @see <a href="http://apidocs.joyent.com/sdcapidoc/cloudapi/index.html#packages">api doc</a>
*/
@Timeout(duration = 30, timeUnit = TimeUnit.SECONDS)
public interface PackageApi {

View File

@ -24,7 +24,7 @@ import com.google.common.util.concurrent.ListenableFuture;
*
* @author Gerald Pereira
* @see PackageApi
* @see <a href="http://apidocs.joyent.com/cloudApiapidoc/cloudapi">api doc</a>
* @see <a href="http://apidocs.joyent.com/sdcapidoc/cloudapi/index.html#packages">api doc</a>
*/
@SkipEncoding({ '/', '=' })
@Headers(keys = "X-Api-Version", values = "{jclouds.api-version}")

View File

@ -1,59 +0,0 @@
/**
* 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.joyent.cloudapi.v6_5.functions.internal;
import java.io.IOException;
import org.jclouds.joyent.cloudapi.v6_5.domain.Machine;
import org.jclouds.joyent.cloudapi.v6_5.domain.Type;
import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
/**
* @author Adrian Cole
*/
public class JoyentCloudTypeAdapters {
public static class MachineStateAdapter extends TypeAdapter<Machine.State> {
@Override
public void write(JsonWriter writer, Machine.State value) throws IOException {
writer.value(value.value());
}
@Override
public Machine.State read(JsonReader reader) throws IOException {
return Machine.State.fromValue(reader.nextString());
}
}
public static class JoyentCloudTypeAdapter extends TypeAdapter<Type> {
@Override
public void write(JsonWriter writer, Type value) throws IOException {
writer.value(value.value());
}
@Override
public Type read(JsonReader reader) throws IOException {
return Type.fromValue(reader.nextString());
}
}
}

View File

@ -1,5 +1,26 @@
/**
* 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
*
* Unles 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 expres or implied. See the License for the
* specific language governing permisions and limitations
* under the License.
*/
package org.jclouds.joyent.cloudapi.v6_5.suppliers;
import static com.google.common.collect.Maps.filterKeys;
import static com.google.common.collect.Maps.transformValues;
import java.net.URI;
import java.util.Map;
@ -7,25 +28,27 @@ import javax.inject.Inject;
import javax.inject.Singleton;
import org.jclouds.joyent.cloudapi.v6_5.features.DatacenterApi;
import org.jclouds.location.predicates.fromconfig.AnyOrConfiguredZoneId;
import org.jclouds.location.suppliers.ZoneIdToURISupplier;
import org.jclouds.util.Suppliers2;
import com.google.common.base.Supplier;
import com.google.common.collect.Maps;
@Singleton
public class ZoneIdToURIFromDatacentersApi implements ZoneIdToURISupplier {
private final DatacenterApi api;
private final AnyOrConfiguredZoneId filter;
@Inject
public ZoneIdToURIFromDatacentersApi(DatacenterApi api) {
public ZoneIdToURIFromDatacentersApi(DatacenterApi api, AnyOrConfiguredZoneId filter) {
this.api = api;
this.filter = filter;
}
@Override
public Map<String, Supplier<URI>> get() {
return Maps.transformValues(api.getDatacenters(), Suppliers2.<URI> ofInstanceFunction());
return filterKeys(transformValues(api.getDatacenters(), Suppliers2.<URI> ofInstanceFunction()), filter);
}
@Override

View File

@ -0,0 +1,141 @@
/**
* 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.joyent.cloudapi.v6_5.compute;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicInteger;
import org.jclouds.compute.ComputeService;
import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.compute.options.TemplateOptions;
import org.jclouds.crypto.SshKeyPairGenerator;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse;
import org.jclouds.joyent.cloudapi.v6_5.compute.internal.BaseJoyentCloudComputeServiceExpectTest;
import org.jclouds.joyent.cloudapi.v6_5.compute.options.JoyentCloudTemplateOptions;
import org.jclouds.joyent.cloudapi.v6_5.features.DatasetApiExpectTest;
import org.jclouds.joyent.cloudapi.v6_5.features.MachineApiExpectTest;
import org.jclouds.joyent.cloudapi.v6_5.features.PackageApiExpectTest;
import org.jclouds.location.reference.LocationConstants;
import org.testng.annotations.Test;
import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMap.Builder;
import com.google.common.collect.Iterables;
import com.google.inject.AbstractModule;
import com.google.inject.TypeLiteral;
/**
*
* @author Adrian Cole
*/
@Test(groups = "unit", testName = "JoyentCloudComputeServiceExpectTest")
public class JoyentCloudComputeServiceExpectTest extends BaseJoyentCloudComputeServiceExpectTest {
Properties onlySW = new Properties();
public JoyentCloudComputeServiceExpectTest(){
onlySW.setProperty(LocationConstants.PROPERTY_ZONES, "us-sw-1");
}
private ImmutableMap<String, String> keyPair = ImmutableMap.of("public", "ssh-rsa AAAAB3NzaC...", "private",
"-----BEGIN RSA PRIVATE KEY-----\n");
DatasetApiExpectTest datasets = new DatasetApiExpectTest();
PackageApiExpectTest packages = new PackageApiExpectTest();
MachineApiExpectTest machines = new MachineApiExpectTest();
@Test
public void testCreateNodeWithGeneratedKeyPairInWestRegion() throws Exception {
Builder<HttpRequest, HttpResponse> requestResponseMap = ImmutableMap.<HttpRequest, HttpResponse> builder();
requestResponseMap.put(getDatacenters, getDatacentersResponse);
requestResponseMap.put(datasets.list, datasets.listResponse);
requestResponseMap.put(packages.list, packages.listResponse);
HttpRequest createKey = HttpRequest.builder().method("POST")
.endpoint("https://api.joyentcloud.com/my/keys")
.addHeader("X-Api-Version", "~6.5")
.addHeader("Accept", "application/json")
.addHeader("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==")
.payload(
payloadFromStringWithContentType(
"{\"name\":\"jclouds-test-0\",\"key\":\"" + keyPair.get("public") + "\"}",
"application/json")).build();
HttpResponse createKeyResponse = HttpResponse.builder().statusCode(202).message("HTTP/1.1 202 Accepted")
.payload(payloadFromResourceWithContentType("/key.json", "application/json; charset=UTF-8"))
.build();
requestResponseMap.put(createKey, createKeyResponse);
// look for number to start count at
requestResponseMap.put(machines.list, machines.listResponse);
HttpRequest createMachine = HttpRequest.builder().method("POST")
.endpoint("https://us-sw-1.api.joyentcloud.com/my/machines?dataset=sdc%3Asdc%3Aubuntu-10.04%3A1.0.1&name=test-1&package=Small%201GB")
.addHeader("X-Api-Version", "~6.5")
.addHeader("Accept", "application/json")
.addHeader("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build();
HttpResponse createMachineResponse = HttpResponse.builder().statusCode(202).message("HTTP/1.1 202 Accepted")
.payload(payloadFromResourceWithContentType("/new_machine.json", "application/json; charset=UTF-8"))
.build();
requestResponseMap.put(createMachine, createMachineResponse);
ComputeService apiThatCreatesNode = requestsSendResponses(requestResponseMap.build(), new AbstractModule() {
@Override
protected void configure() {
// predicatable node names
final AtomicInteger suffix = new AtomicInteger();
bind(new TypeLiteral<Supplier<String>>() {
}).toInstance(new Supplier<String>() {
@Override
public String get() {
return suffix.getAndIncrement() + "";
}
});
bind(SshKeyPairGenerator.class).toInstance(new SshKeyPairGenerator() {
@Override
public Map<String, String> get() {
return keyPair;
}
});
}
}, onlySW);
TemplateOptions options = apiThatCreatesNode.templateOptions().blockUntilRunning(false);
assertTrue(options.as(JoyentCloudTemplateOptions.class).shouldGenerateKey().get());
NodeMetadata node = Iterables.getOnlyElement(apiThatCreatesNode.createNodesInGroup("test", 1, options));
assertEquals(node.getCredentials().getPrivateKey(), keyPair.get("private"));
}
}

View File

@ -21,6 +21,5 @@ public class JoyentCloudComputeServiceLiveTest extends BaseComputeServiceLiveTes
protected Module getSshModule() {
return new SshjSshClientModule();
}
}

View File

@ -44,7 +44,7 @@ import com.google.common.collect.ImmutableMap;
*
* @author Adrian Cole
*/
@Test(testName = "DatasetInDatacenterToHardwareTest")
@Test(testName = "DatasetInDatacenterToImageTest")
public class DatasetInDatacenterToImageTest {
Location provider = new LocationBuilder().scope(LocationScope.PROVIDER).id("joyent-cloudapi")
@ -66,14 +66,14 @@ public class DatasetInDatacenterToImageTest {
org.jclouds.compute.domain.Image convertedImage = converter.apply(datasetInZoneToConvert);
assertEquals(convertedImage.getId(), datasetInZoneToConvert.slashEncode());
assertEquals(convertedImage.getProviderId(), datasetToConvert.getId());
assertEquals(convertedImage.getId(), "us-sw-1/" + datasetToConvert.getUrn());
assertEquals(convertedImage.getProviderId(), datasetToConvert.getUrn());
assertEquals(convertedImage.getLocation(), locationIndex.get().get("us-sw-1"));
assertEquals(convertedImage.getName(), datasetToConvert.getName());
assertEquals(convertedImage.getStatus(), org.jclouds.compute.domain.Image.Status.AVAILABLE);
assertEquals(convertedImage.getOperatingSystem(), operatingSystem);
assertEquals(convertedImage.getDescription(), datasetToConvert.getUrn());
assertEquals(convertedImage.getDescription(), datasetToConvert.getDescription());
assertEquals(convertedImage.getVersion(), datasetToConvert.getVersion());
}

View File

@ -0,0 +1,57 @@
/**
* 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.joyent.cloudapi.v6_5.compute.internal;
import java.util.Properties;
import org.jclouds.apis.ApiMetadata;
import org.jclouds.compute.ComputeServiceContext;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse;
import org.jclouds.joyent.cloudapi.v6_5.JoyentCloudApiMetadata;
import org.jclouds.joyent.cloudapi.v6_5.internal.BaseJoyentCloudExpectTest;
import com.google.common.base.Function;
import com.google.inject.Module;
/**
* Base class for writing Expect tests with the ComputeService abstraction
*
* @author Adrian Cole
*/
public abstract class BaseJoyentCloudComputeServiceContextExpectTest<T> extends BaseJoyentCloudExpectTest<T> implements
Function<ComputeServiceContext, T> {
@Override
public T createClient(Function<HttpRequest, HttpResponse> fn, Module module, Properties props) {
return apply(createComputeServiceContext(fn, module, props));
}
private ComputeServiceContext createComputeServiceContext(Function<HttpRequest, HttpResponse> fn, Module module,
Properties props) {
return createInjector(fn, module, props).getInstance(ComputeServiceContext.class);
}
@Override
protected ApiMetadata createApiMetadata() {
return new JoyentCloudApiMetadata();
}
}

View File

@ -16,34 +16,20 @@
* specific language governing permissions and limitations
* under the License.
*/
package org.jclouds.joyent.cloudapi.v6_5.config;
package org.jclouds.joyent.cloudapi.v6_5.compute.internal;
import java.lang.reflect.Type;
import java.util.Map;
import javax.inject.Singleton;
import org.jclouds.joyent.cloudapi.v6_5.domain.Machine;
import org.jclouds.joyent.cloudapi.v6_5.functions.internal.JoyentCloudTypeAdapters;
import com.google.common.collect.ImmutableMap;
import com.google.inject.AbstractModule;
import com.google.inject.Provides;
import org.jclouds.compute.ComputeService;
import org.jclouds.compute.ComputeServiceContext;
/**
*
* @author Adrian Cole
*/
public class JoyentCloudParserModule extends AbstractModule {
@Provides
@Singleton
public Map<Type, Object> provideCustomAdapterBindings() {
return ImmutableMap.<Type, Object> of(Machine.State.class, new JoyentCloudTypeAdapters.MachineStateAdapter(), Type.class,
new JoyentCloudTypeAdapters.JoyentCloudTypeAdapter());
}
public class BaseJoyentCloudComputeServiceExpectTest extends BaseJoyentCloudComputeServiceContextExpectTest<ComputeService> {
@Override
protected void configure() {
public ComputeService apply(ComputeServiceContext input) {
return input.getComputeService();
}
}

View File

@ -22,30 +22,18 @@ import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.verify;
import static org.jclouds.crypto.PemsTest.PRIVATE_KEY;
import static org.jclouds.crypto.PemsTest.PUBLIC_KEY;
import static org.testng.Assert.assertEquals;
import java.io.IOException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.util.Map;
import org.jclouds.compute.functions.GroupNamingConvention;
import org.jclouds.compute.functions.GroupNamingConvention.Factory;
import org.jclouds.crypto.Crypto;
import org.jclouds.crypto.Pems;
import org.jclouds.crypto.SshKeys;
import org.jclouds.io.Payloads;
import org.jclouds.crypto.SshKeyPairGenerator;
import org.jclouds.joyent.cloudapi.v6_5.JoyentCloudApi;
import org.jclouds.joyent.cloudapi.v6_5.compute.internal.KeyAndPrivateKey;
import org.jclouds.joyent.cloudapi.v6_5.compute.loaders.CreateUniqueKey;
import org.jclouds.joyent.cloudapi.v6_5.domain.Key;
import org.jclouds.joyent.cloudapi.v6_5.domain.datacenterscoped.DatacenterAndName;
import org.jclouds.joyent.cloudapi.v6_5.features.KeyApi;
@ -54,21 +42,20 @@ import org.testng.annotations.Test;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableMap;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.TypeLiteral;
import com.google.inject.util.Providers;
/**
* @author Adrian Cole
*/
@Test(groups = "unit", testName = "CreateUniqueKeyTest")
public class CreateUniqueKeyTest {
private static final String lineSeparator = System.getProperty("line.separator");
private ImmutableMap<String, String> keyPair = ImmutableMap.of("public", "ssh-rsa AAAAB3NzaC...", "private",
"-----BEGIN RSA PRIVATE KEY-----\n");
private Factory namingConvention;
private KeyPair keyPair;
private String openSshKey;
@BeforeClass
public void setup() throws InvalidKeySpecException, NoSuchAlgorithmException, IOException {
@ -80,42 +67,35 @@ public class CreateUniqueKeyTest {
}).toInstance(Suppliers.ofInstance("foo"));
}
}).getInstance(GroupNamingConvention.Factory.class);
KeyFactory keyfactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyfactory.generatePrivate(Pems.privateKeySpec(Payloads.newStringPayload(PRIVATE_KEY)));
PublicKey publicKey = keyfactory
.generatePublic(Pems.publicKeySpec(Payloads.newStringPayload(PUBLIC_KEY)));
keyPair = new KeyPair(publicKey, privateKey);
openSshKey = SshKeys.encodeAsOpenSSH(RSAPublicKey.class.cast(publicKey));
}
@Test
public void testApply() {
JoyentCloudApi cloudApiApi = createMock(JoyentCloudApi.class);
SshKeyPairGenerator sshKeyPairGenerator = new SshKeyPairGenerator() {
@Override
public Map<String, String> get() {
return keyPair;
}
};
KeyApi keyApi = createMock(KeyApi.class);
Crypto crypto = createMock(Crypto.class);
KeyPairGenerator rsaKeyPairGenerator = createMock(KeyPairGenerator.class);
SecureRandom secureRandom = createMock(SecureRandom.class);
Key key = Key.builder().name("group-foo").key(openSshKey).build();
expect(crypto.rsaKeyPairGenerator()).andReturn(rsaKeyPairGenerator);
rsaKeyPairGenerator.initialize(2048, secureRandom);
expect(rsaKeyPairGenerator.genKeyPair()).andReturn(keyPair);
Key key = Key.builder().name("group-foo").key(keyPair.get("public")).build();
expect(cloudApiApi.getKeyApi()).andReturn(keyApi);
expect(keyApi.create(key)).andReturn(key);
replay(cloudApiApi, keyApi, crypto, rsaKeyPairGenerator, secureRandom);
replay(cloudApiApi, keyApi);
CreateUniqueKey parser = new CreateUniqueKey(cloudApiApi, namingConvention, crypto, Providers.of(secureRandom));
CreateUniqueKey parser = new CreateUniqueKey(cloudApiApi, namingConvention, sshKeyPairGenerator);
assertEquals(parser.load(DatacenterAndName.fromDatacenterAndName("datacenter", "group")),
KeyAndPrivateKey.fromKeyAndPrivateKey(key, PRIVATE_KEY.replaceAll("\n", lineSeparator)));
KeyAndPrivateKey.fromKeyAndPrivateKey(key, keyPair.get("private")));
verify(cloudApiApi, keyApi, crypto, rsaKeyPairGenerator, secureRandom);
verify(cloudApiApi, keyApi);
}
}

View File

@ -47,19 +47,19 @@ public class JoyentCloudTemplateOptionsTest {
@Test
public void testGenerateKeyDefault() {
JoyentCloudTemplateOptions options = new JoyentCloudTemplateOptions();
assert !options.shouldGenerateKey();
assert !options.shouldGenerateKey().isPresent();
}
@Test
public void testGenerateKey() {
JoyentCloudTemplateOptions options = new JoyentCloudTemplateOptions().generateKey(true);
assert options.shouldGenerateKey();
assert options.shouldGenerateKey().get();
}
@Test
public void testGenerateKeyStatic() {
JoyentCloudTemplateOptions options = generateKey(true);
assert options.shouldGenerateKey();
assert options.shouldGenerateKey().get();
}
// superclass tests

View File

@ -20,8 +20,6 @@ package org.jclouds.joyent.cloudapi.v6_5.features;
import static org.testng.Assert.assertEquals;
import java.net.URI;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse;
import org.jclouds.joyent.cloudapi.v6_5.JoyentCloudApi;
@ -29,7 +27,6 @@ import org.jclouds.joyent.cloudapi.v6_5.internal.BaseJoyentCloudApiExpectTest;
import org.jclouds.joyent.cloudapi.v6_5.parse.ParseDatasetListTest;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableSet;
/**
@ -37,14 +34,16 @@ import com.google.common.collect.ImmutableSet;
*/
@Test(groups = "unit", testName = "DatasetApiExpectTest")
public class DatasetApiExpectTest extends BaseJoyentCloudApiExpectTest {
HttpRequest list = HttpRequest.builder().method("GET").endpoint(
URI.create("https://us-sw-1.api.joyentcloud.com/my/datasets")).headers(
ImmutableMultimap.<String, String> builder().put("X-Api-Version", "~6.5").put("Accept", "application/json")
.put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build()).build();
public HttpRequest list = HttpRequest.builder().method("GET")
.endpoint("https://us-sw-1.api.joyentcloud.com/my/datasets")
.addHeader("X-Api-Version", "~6.5")
.addHeader("Accept", "application/json")
.addHeader("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build();
public HttpResponse listResponse = HttpResponse.builder().statusCode(200)
.payload(payloadFromResource("/dataset_list.json")).build();
public void testListDatasetsWhenResponseIs2xx() {
HttpResponse listResponse = HttpResponse.builder().statusCode(200).payload(
payloadFromResource("/dataset_list.json")).build();
JoyentCloudApi apiWhenDatasetsExists = requestsSendResponses(getDatacenters, getDatacentersResponse, list, listResponse);
@ -59,15 +58,4 @@ public class DatasetApiExpectTest extends BaseJoyentCloudApiExpectTest {
assertEquals(listWhenNone.getDatasetApiForDatacenter("us-sw-1").list(), ImmutableSet.of());
}
// [id=e4cd7b9e-4330-11e1-81cf-3bb50a972bda, name=centos-6, type=VIRTUALMACHINE, version=1.0.1,
// urn=sdc:sdc:centos-6:1.0.1, default=false, created=Mon Feb 13 07:30:33 CET 2012],
// [id=e4cd7b9e-4330-11e1-81cf-3bb50a972bda, name=centos-6, type=VIRTUALMACHINE, version=1.0.1,
// urn=sdc:sdc:centos-6:1.0.1, default=false, created=Mon Feb 13 07:30:33 CET 2012],
//
// [id=e62c30b4-cdda-11e0-9dd4-af4d032032e3, name=nodejs, type=SMARTMACHINE, version=1.2.3,
// urn=sdc:sdc:nodejs:1.2.3, default=false, created=Thu Sep 15 10:15:29 CEST 2011]]
//
// [id=e62c30b4-cdda-11e0-9dd4-af4d032032e3, name=nodejs, type=SMARTMACHINE, version=1.2.3,
// urn=sdc:sdc:nodejs:1.2.3, default=false, created=Thu Sep 15 10:15:29 CEST 2011]] but got
}

View File

@ -35,15 +35,16 @@ import com.google.common.collect.ImmutableSet;
*/
@Test(groups = "unit", testName = "KeyApiExpectTest")
public class KeyApiExpectTest extends BaseJoyentCloudApiExpectTest {
HttpRequest list = HttpRequest.builder().method("GET")
.endpoint("https://api.joyentcloud.com/my/keys")
.addHeader("X-Api-Version", "~6.5")
.addHeader("Accept", "application/json")
.addHeader("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build();
public HttpRequest list = HttpRequest.builder().method("GET")
.endpoint("https://api.joyentcloud.com/my/keys")
.addHeader("X-Api-Version", "~6.5")
.addHeader("Accept", "application/json")
.addHeader("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build();
public HttpResponse listResponse = HttpResponse.builder().statusCode(200).payload(
payloadFromResource("/key_list.json")).build();
public void testListKeysWhenResponseIs2xx() {
HttpResponse listResponse = HttpResponse.builder().statusCode(200).payload(
payloadFromResource("/key_list.json")).build();
JoyentCloudApi apiWhenKeysExists = requestsSendResponses(getDatacenters, getDatacentersResponse, list, listResponse);

View File

@ -36,15 +36,16 @@ import com.google.common.collect.ImmutableSet;
*/
@Test(groups = "unit", testName = "MachineApiExpectTest")
public class MachineApiExpectTest extends BaseJoyentCloudApiExpectTest {
HttpRequest list = HttpRequest.builder().method("GET")
public HttpRequest list = HttpRequest.builder().method("GET")
.endpoint("https://us-sw-1.api.joyentcloud.com/my/machines")
.addHeader("X-Api-Version", "~6.5")
.addHeader("Accept", "application/json")
.addHeader("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build();
public HttpResponse listResponse = HttpResponse.builder().statusCode(200).payload(
payloadFromResource("/machine_list.json")).build();
public void testListMachinesWhenResponseIs2xx() {
HttpResponse listResponse = HttpResponse.builder().statusCode(200).payload(
payloadFromResource("/machine_list.json")).build();
JoyentCloudApi apiWhenMachinesExists = requestsSendResponses(getDatacenters, getDatacentersResponse, list, listResponse);

View File

@ -68,7 +68,7 @@ public class MachineApiLiveTest extends BaseJoyentCloudApiLiveTest {
assertEquals(newDetails.getName(), machine.getName());
assertEquals(newDetails.getType(), machine.getType());
assertEquals(newDetails.getState(), machine.getState());
assertEquals(newDetails.get(), machine.get());
assertEquals(newDetails.getDatasetURN(), machine.getDatasetURN());
assertEquals(newDetails.getMemorySizeMb(), machine.getMemorySizeMb());
assertEquals(newDetails.getDiskSizeGb(), machine.getDiskSizeGb());
assertEquals(newDetails.getIps(), machine.getIps());

View File

@ -34,15 +34,16 @@ import com.google.common.collect.ImmutableSet;
*/
@Test(groups = "unit", testName = "PackageApiExpectTest")
public class PackageApiExpectTest extends BaseJoyentCloudApiExpectTest {
HttpRequest list = HttpRequest.builder().method("GET")
.endpoint("https://us-sw-1.api.joyentcloud.com/my/packages")
.addHeader("X-Api-Version", "~6.5")
.addHeader("Accept", "application/json")
.addHeader("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build();
public void testListPackagesWhenResponseIs2xx() {
HttpResponse listResponse = HttpResponse.builder().statusCode(200)
public HttpRequest list = HttpRequest.builder().method("GET")
.endpoint("https://us-sw-1.api.joyentcloud.com/my/packages")
.addHeader("X-Api-Version", "~6.5")
.addHeader("Accept", "application/json")
.addHeader("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build();
public HttpResponse listResponse = HttpResponse.builder().statusCode(200)
.payload(payloadFromResource("/package_list.json")).build();
public void testListPackagesWhenResponseIs2xx() {
JoyentCloudApi apiWhenPackagesExists = requestsSendResponses(getDatacenters, getDatacentersResponse, list, listResponse);

View File

@ -18,8 +18,6 @@
*/
package org.jclouds.joyent.cloudapi.v6_5.internal;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse;
import org.jclouds.joyent.cloudapi.v6_5.JoyentCloudApi;
/**
@ -28,13 +26,5 @@ import org.jclouds.joyent.cloudapi.v6_5.JoyentCloudApi;
* @author Adrian Cole
*/
public class BaseJoyentCloudApiExpectTest extends BaseJoyentCloudExpectTest<JoyentCloudApi> {
protected HttpRequest getDatacenters = HttpRequest.builder()
.method("GET")
.endpoint("https://api.joyentcloud.com/my/datacenters")
.addHeader("X-Api-Version", "~6.5")
.addHeader("Accept", "application/json")
.addHeader("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build();
protected HttpResponse getDatacentersResponse = HttpResponse.builder().statusCode(200)
.payload(payloadFromResource("/datacenters.json")).build();
}

View File

@ -18,6 +18,8 @@
*/
package org.jclouds.joyent.cloudapi.v6_5.internal;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse;
import org.jclouds.rest.internal.BaseRestApiExpectTest;
/**
@ -26,7 +28,16 @@ import org.jclouds.rest.internal.BaseRestApiExpectTest;
* @author Adrian Cole
*/
public class BaseJoyentCloudExpectTest<T> extends BaseRestApiExpectTest<T> {
protected HttpRequest getDatacenters = HttpRequest.builder()
.method("GET")
.endpoint("https://api.joyentcloud.com/my/datacenters")
.addHeader("X-Api-Version", "~6.5")
.addHeader("Accept", "application/json")
.addHeader("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build();
protected HttpResponse getDatacentersResponse = HttpResponse.builder().statusCode(200)
.payload(payloadFromResource("/datacenters.json")).build();
public BaseJoyentCloudExpectTest() {
provider = "joyent-cloudapi";
}

View File

@ -23,9 +23,8 @@ import javax.ws.rs.core.MediaType;
import org.jclouds.date.internal.SimpleDateFormatDateService;
import org.jclouds.domain.JsonBall;
import org.jclouds.joyent.cloudapi.v6_5.config.JoyentCloudParserModule;
import org.jclouds.joyent.cloudapi.v6_5.domain.Machine;
import org.jclouds.joyent.cloudapi.v6_5.domain.Type;
import org.jclouds.joyent.cloudapi.v6_5.domain.Machine.Type;
import org.jclouds.json.BaseItemParserTest;
import org.jclouds.json.config.GsonModule;
import org.testng.annotations.Test;
@ -68,7 +67,7 @@ public class ParseCreatedMachineTest extends BaseItemParserTest<Machine> {
}
protected Injector injector() {
return Guice.createInjector(new JoyentCloudParserModule(), new GsonModule() {
return Guice.createInjector(new GsonModule() {
@Override
protected void configure() {

View File

@ -24,9 +24,8 @@ import javax.ws.rs.Consumes;
import javax.ws.rs.core.MediaType;
import org.jclouds.date.internal.SimpleDateFormatDateService;
import org.jclouds.joyent.cloudapi.v6_5.config.JoyentCloudParserModule;
import org.jclouds.joyent.cloudapi.v6_5.domain.Dataset;
import org.jclouds.joyent.cloudapi.v6_5.domain.Type;
import org.jclouds.joyent.cloudapi.v6_5.domain.Machine.Type;
import org.jclouds.json.BaseSetParserTest;
import org.jclouds.json.config.GsonModule;
import org.testng.annotations.Test;
@ -50,21 +49,43 @@ public class ParseDatasetListTest extends BaseSetParserTest<Dataset> {
@Consumes(MediaType.APPLICATION_JSON)
public Set<Dataset> expected() {
return ImmutableSet.of(
Dataset.builder().id("e4cd7b9e-4330-11e1-81cf-3bb50a972bda").name("centos-6").urn("sdc:sdc:centos-6:1.0.1")
.type(Type.VIRTUALMACHINE).version("1.0.1").isDefault(false)
.created(new SimpleDateFormatDateService().iso8601SecondsDateParse("2012-02-13T06:30:33+00:00"))
.build(),
Dataset.builder().id("e62c30b4-cdda-11e0-9dd4-af4d032032e3").name("nodejs").urn("sdc:sdc:nodejs:1.2.3")
.type(Type.SMARTMACHINE).version("1.2.3").isDefault(false)
.created(new SimpleDateFormatDateService().iso8601SecondsDateParse("2011-09-15T08:15:29+00:00"))
.build()
);
Dataset.builder()
.id("71101322-43a5-11e1-8f01-cf2a3031a7f4")
.urn("sdc:sdc:ubuntu-10.04:1.0.1")
.name("ubuntu-10.04")
.os("linux")
.type(Type.VIRTUALMACHINE)
.description("Ubuntu 10.04 VM 1.0.1")
.version("1.0.1")
.created(new SimpleDateFormatDateService().iso8601SecondsDateParse("2012-02-22T18:27:32+00:00"))
.build(),
Dataset.builder()
.id("e4cd7b9e-4330-11e1-81cf-3bb50a972bda")
.urn("sdc:sdc:centos-6:1.0.1")
.name("centos-6")
.os("linux")
.type(Type.VIRTUALMACHINE)
.description("Centos 6 VM 1.0.1")
.version("1.0.1")
.created(new SimpleDateFormatDateService().iso8601SecondsDateParse("2012-02-15T20:04:18+00:00"))
.build(),
Dataset.builder()
.id("9551fdbc-cc9a-11e1-a9e7-eb1e788a8690")
.urn("sdc:sdc:standard64:1.0.1")
.name("standard64")
.os("smartos")
.type(Type.SMARTMACHINE)
.description("64-bit machine image optimized for web development")
.version("1.0.1")
.created(new SimpleDateFormatDateService().iso8601SecondsDateParse("2012-07-13T03:30:22+00:00"))
.build()
);
}
protected Injector injector() {
return Guice.createInjector(new JoyentCloudParserModule(), new GsonModule() {
return Guice.createInjector(new GsonModule() {
@Override
protected void configure() {

View File

@ -22,9 +22,8 @@ import javax.ws.rs.Consumes;
import javax.ws.rs.core.MediaType;
import org.jclouds.date.internal.SimpleDateFormatDateService;
import org.jclouds.joyent.cloudapi.v6_5.config.JoyentCloudParserModule;
import org.jclouds.joyent.cloudapi.v6_5.domain.Dataset;
import org.jclouds.joyent.cloudapi.v6_5.domain.Type;
import org.jclouds.joyent.cloudapi.v6_5.domain.Machine.Type;
import org.jclouds.json.BaseItemParserTest;
import org.jclouds.json.config.GsonModule;
import org.testng.annotations.Test;
@ -46,13 +45,20 @@ public class ParseDatasetTest extends BaseItemParserTest<Dataset> {
@Override
@Consumes(MediaType.APPLICATION_JSON)
public Dataset expected() {
return Dataset.builder().id("e4cd7b9e-4330-11e1-81cf-3bb50a972bda").name("centos-6")
.urn("sdc:sdc:centos-6:1.0.1").type(Type.VIRTUALMACHINE).version("1.0.1").isDefault(false)
.created(new SimpleDateFormatDateService().iso8601SecondsDateParse("2012-02-13T06:30:33+00:00")).build();
return Dataset.builder()
.id("e4cd7b9e-4330-11e1-81cf-3bb50a972bda")
.urn("sdc:sdc:centos-6:1.0.1")
.name("centos-6")
.os("linux")
.type(Type.VIRTUALMACHINE)
.description("Centos 6 VM 1.0.1")
.isDefault(false)
.version("1.0.1")
.created(new SimpleDateFormatDateService().iso8601SecondsDateParse("2012-02-13T06:30:33+00:00")).build();
}
protected Injector injector() {
return Guice.createInjector(new JoyentCloudParserModule(), new GsonModule() {
return Guice.createInjector(new GsonModule() {
@Override
protected void configure() {

View File

@ -24,7 +24,6 @@ import javax.ws.rs.Consumes;
import javax.ws.rs.core.MediaType;
import org.jclouds.date.internal.SimpleDateFormatDateService;
import org.jclouds.joyent.cloudapi.v6_5.config.JoyentCloudParserModule;
import org.jclouds.joyent.cloudapi.v6_5.domain.Key;
import org.jclouds.json.BaseSetParserTest;
import org.jclouds.json.config.GsonModule;
@ -58,7 +57,7 @@ public class ParseKeyListTest extends BaseSetParserTest<Key> {
}
protected Injector injector() {
return Guice.createInjector(new JoyentCloudParserModule(), new GsonModule() {
return Guice.createInjector(new GsonModule() {
@Override
protected void configure() {

View File

@ -22,7 +22,6 @@ import javax.ws.rs.Consumes;
import javax.ws.rs.core.MediaType;
import org.jclouds.date.internal.SimpleDateFormatDateService;
import org.jclouds.joyent.cloudapi.v6_5.config.JoyentCloudParserModule;
import org.jclouds.joyent.cloudapi.v6_5.domain.Key;
import org.jclouds.json.BaseItemParserTest;
import org.jclouds.json.config.GsonModule;
@ -53,7 +52,7 @@ public class ParseKeyTest extends BaseItemParserTest<Key> {
}
protected Injector injector() {
return Guice.createInjector(new JoyentCloudParserModule(), new GsonModule() {
return Guice.createInjector(new GsonModule() {
@Override
protected void configure() {

View File

@ -25,9 +25,8 @@ import javax.ws.rs.core.MediaType;
import org.jclouds.date.internal.SimpleDateFormatDateService;
import org.jclouds.domain.JsonBall;
import org.jclouds.joyent.cloudapi.v6_5.config.JoyentCloudParserModule;
import org.jclouds.joyent.cloudapi.v6_5.domain.Machine;
import org.jclouds.joyent.cloudapi.v6_5.domain.Type;
import org.jclouds.joyent.cloudapi.v6_5.domain.Machine.Type;
import org.jclouds.json.BaseSetParserTest;
import org.jclouds.json.config.GsonModule;
import org.testng.annotations.Test;
@ -52,8 +51,16 @@ public class ParseMachineListTest extends BaseSetParserTest<Machine> {
@Consumes(MediaType.APPLICATION_JSON)
public Set<Machine> expected() {
return ImmutableSet.of(
Machine
.builder()
Machine.builder().id("d73cb0b0-7d1f-44ef-8c40-e040eef0f726").name("sample-e922").type(Type.SMARTMACHINE)
.state(Machine.State.RUNNING).dataset("sdc:sdc:smartosplus:3.1.0")
.ips(ImmutableSet.<String> builder().add("37.153.96.56").add("10.224.0.57").build())
.memorySizeMb(1024).diskSizeGb(61440).metadata(ImmutableMap.<String, JsonBall> of())
.created(new SimpleDateFormatDateService().iso8601SecondsDateParse("2012-05-09T13:39:43+00:00"))
.updated(new SimpleDateFormatDateService().iso8601SecondsDateParse("2012-05-09T13:43:45+00:00"))
.build(),
Machine.builder()
.id("94eba336-ecb7-49f5-8a27-52f5e4dd57a1")
.name("sample-e92")
.type(Type.VIRTUALMACHINE)
@ -67,21 +74,12 @@ public class ParseMachineListTest extends BaseSetParserTest<Machine> {
.put("root_authorized_keys", new JsonBall("ssh-rsa XXXXXX== test@xxxx.ovh.net\n")).build())
.created(new SimpleDateFormatDateService().iso8601SecondsDateParse("2012-05-09T13:32:46+00:00"))
.updated(new SimpleDateFormatDateService().iso8601SecondsDateParse("2012-05-11T09:00:33+00:00"))
.build(),
Machine.builder().id("d73cb0b0-7d1f-44ef-8c40-e040eef0f726").name("sample-e922").type(Type.SMARTMACHINE)
.state(Machine.State.RUNNING).dataset("sdc:sdc:smartosplus:3.1.0")
.ips(ImmutableSet.<String> builder().add("37.153.96.56").add("10.224.0.57").build())
.memorySizeMb(1024).diskSizeGb(61440).metadata(ImmutableMap.<String, JsonBall> of())
.created(new SimpleDateFormatDateService().iso8601SecondsDateParse("2012-05-09T13:39:43+00:00"))
.updated(new SimpleDateFormatDateService().iso8601SecondsDateParse("2012-05-09T13:43:45+00:00"))
.build()
);
}
protected Injector injector() {
return Guice.createInjector(new JoyentCloudParserModule(), new GsonModule() {
return Guice.createInjector(new GsonModule() {
@Override
protected void configure() {

View File

@ -23,9 +23,8 @@ import javax.ws.rs.core.MediaType;
import org.jclouds.date.internal.SimpleDateFormatDateService;
import org.jclouds.domain.JsonBall;
import org.jclouds.joyent.cloudapi.v6_5.config.JoyentCloudParserModule;
import org.jclouds.joyent.cloudapi.v6_5.domain.Machine;
import org.jclouds.joyent.cloudapi.v6_5.domain.Type;
import org.jclouds.joyent.cloudapi.v6_5.domain.Machine.Type;
import org.jclouds.json.BaseItemParserTest;
import org.jclouds.json.config.GsonModule;
import org.testng.annotations.Test;
@ -67,7 +66,7 @@ public class ParseMachineTest extends BaseItemParserTest<Machine> {
}
protected Injector injector() {
return Guice.createInjector(new JoyentCloudParserModule(), new GsonModule() {
return Guice.createInjector(new GsonModule() {
@Override
protected void configure() {

View File

@ -23,7 +23,6 @@ import java.util.Set;
import javax.ws.rs.Consumes;
import javax.ws.rs.core.MediaType;
import org.jclouds.joyent.cloudapi.v6_5.config.JoyentCloudParserModule;
import org.jclouds.joyent.cloudapi.v6_5.domain.Package;
import org.jclouds.json.BaseSetParserTest;
import org.jclouds.json.config.GsonModule;
@ -57,7 +56,7 @@ public class ParsePackageListTest extends BaseSetParserTest<Package> {
}
protected Injector injector() {
return Guice.createInjector(new JoyentCloudParserModule(), new GsonModule() {
return Guice.createInjector(new GsonModule() {
@Override
protected void configure() {

View File

@ -21,7 +21,6 @@ package org.jclouds.joyent.cloudapi.v6_5.parse;
import javax.ws.rs.Consumes;
import javax.ws.rs.core.MediaType;
import org.jclouds.joyent.cloudapi.v6_5.config.JoyentCloudParserModule;
import org.jclouds.json.BaseItemParserTest;
import org.jclouds.json.config.GsonModule;
import org.testng.annotations.Test;
@ -43,12 +42,16 @@ public class ParsePackageTest extends BaseItemParserTest<org.jclouds.joyent.clou
@Override
@Consumes(MediaType.APPLICATION_JSON)
public org.jclouds.joyent.cloudapi.v6_5.domain.Package expected() {
return org.jclouds.joyent.cloudapi.v6_5.domain.Package.builder().name("Small 1GB").memorySizeMb(1024)
.diskSizeGb(30720).swapSizeMb(2048).isDefault(true).build();
return org.jclouds.joyent.cloudapi.v6_5.domain.Package.builder()
.name("Small 1GB")
.memorySizeMb(1024)
.diskSizeGb(30720)
.swapSizeMb(2048)
.isDefault(true).build();
}
protected Injector injector() {
return Guice.createInjector(new JoyentCloudParserModule(), new GsonModule() {
return Guice.createInjector(new GsonModule() {
@Override
protected void configure() {

View File

@ -1 +1,34 @@
[{"id":"e4cd7b9e-4330-11e1-81cf-3bb50a972bda","urn":"sdc:sdc:centos-6:1.0.1","name":"centos-6","os":"linux","type":"virtualmachine","description":"Centos 6 VM 1.0.1","default":false,"requirements":{},"version":"1.0.1","created":"2012-02-13T06:30:33+00:00"},{"id":"e62c30b4-cdda-11e0-9dd4-af4d032032e3","urn":"sdc:sdc:nodejs:1.2.3","name":"nodejs","os":"smartos","type":"smartmachine","description":"Node.js git-deploy PaaS dataset","default":false,"requirements":{},"version":"1.2.3","created":"2011-09-15T08:15:29+00:00"}]
[{
"id": "71101322-43a5-11e1-8f01-cf2a3031a7f4",
"urn": "sdc:sdc:ubuntu-10.04:1.0.1",
"name": "ubuntu-10.04",
"os": "linux",
"type": "virtualmachine",
"description": "Ubuntu 10.04 VM 1.0.1",
"default": false,
"requirements": {},
"version": "1.0.1",
"created": "2012-02-22T18:27:32+00:00"
}, {
"id": "e4cd7b9e-4330-11e1-81cf-3bb50a972bda",
"urn": "sdc:sdc:centos-6:1.0.1",
"name": "centos-6",
"os": "linux",
"type": "virtualmachine",
"description": "Centos 6 VM 1.0.1",
"default": false,
"requirements": {},
"version": "1.0.1",
"created": "2012-02-15T20:04:18+00:00"
}, {
"id": "9551fdbc-cc9a-11e1-a9e7-eb1e788a8690",
"urn": "sdc:sdc:standard64:1.0.1",
"name": "standard64",
"os": "smartos",
"type": "smartmachine",
"description": "64-bit machine image optimized for web development",
"default": false,
"requirements": {},
"version": "1.0.1",
"created": "2012-07-13T03:30:22+00:00"
}]