diff --git a/labs/google-compute/README.txt b/labs/google-compute/README.txt new file mode 100644 index 0000000000..5499eaea76 --- /dev/null +++ b/labs/google-compute/README.txt @@ -0,0 +1,16 @@ +Status: + +All the private apis are implemented and tested. +Snapshots are disabled because they are also disabled in GCE. +Just missing jcloud.compute glue code (coming soon!) + +How to run the live tests: + +Pre-requisites: +A Google Api account with Google Compute Engine enabled +The access pk (provided by google in PKCS12 format) in pem format. + +running all tests: + +mvn clean install -Plive -Dtest.google-compute.identity=@developer.gserviceaccount.com -Dtest.google-compute.credential= + diff --git a/labs/google-compute/pom.xml b/labs/google-compute/pom.xml new file mode 100644 index 0000000000..a69db9cef8 --- /dev/null +++ b/labs/google-compute/pom.xml @@ -0,0 +1,116 @@ + + + + + 4.0.0 + + org.jclouds + jclouds-project + 1.6.0-SNAPSHOT + ../../project/pom.xml + + org.jclouds.labs + google-compute + jclouds Google Compute Engine provider + jclouds components to access GoogleCompute + + + 1.6.0-SNAPSHOT + Email associated with the Google API client_id + Private key (PKCS12 file) associated with the Google API client_id + v1beta13 + + + + + + org.jclouds + jclouds-core + ${jclouds.version} + + + org.jclouds.labs + oauth + ${project.version} + jar + + + org.jclouds.labs + oauth + ${project.version} + test-jar + test + + + org.jclouds + jclouds-compute + ${jclouds.version} + + + org.jclouds + jclouds-core + ${jclouds.version} + test-jar + test + + + org.jclouds.driver + jclouds-slf4j + ${jclouds.version} + test + + + ch.qos.logback + logback-classic + test + + + + + live + + + + org.apache.maven.plugins + maven-surefire-plugin + + + integration + integration-test + + test + + + + ${test.google-compute.identity} + ${test.google-compute.credential} + ${test.google-compute.api-version} + ${test.google-compute.build-version} + + + + + + + + + + diff --git a/labs/google-compute/src/main/java/org/jclouds/googlecompute/GoogleComputeApi.java b/labs/google-compute/src/main/java/org/jclouds/googlecompute/GoogleComputeApi.java new file mode 100644 index 0000000000..d106c825ee --- /dev/null +++ b/labs/google-compute/src/main/java/org/jclouds/googlecompute/GoogleComputeApi.java @@ -0,0 +1,69 @@ +/* + * 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.googlecompute; + +import com.google.common.annotations.Beta; +import org.jclouds.concurrent.Timeout; +import org.jclouds.googlecompute.features.OperationApi; +import org.jclouds.googlecompute.features.ProjectApi; +import org.jclouds.googlecompute.features.ZoneApi; +import org.jclouds.rest.annotations.Delegate; + +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import java.util.concurrent.TimeUnit; + +/** + * Provides synchronous access to GoogleCompute. + *

+ * + * @author David Alves + * @see GoogleComputeAsyncApi + * @see api doc + */ +@Beta +@Timeout(duration = 60, timeUnit = TimeUnit.SECONDS) +public interface GoogleComputeApi { + + /** + * Provides synchronous access to Project features + */ + @Delegate + ProjectApi getProjectApi(); + + /** + * Provides synchronous access to Operation features + * + * @param projectName the name of the project + */ + @Delegate + @Path("/projects/{project}") + OperationApi getOperationApiForProject(@PathParam("project") String projectName); + + /** + * Provides synchronous access to Zone features + * + * @param projectName the name of the project + */ + @Delegate + @Path("/projects/{project}") + ZoneApi getZoneApiForProject(@PathParam("project") String projectName); + + +} diff --git a/labs/google-compute/src/main/java/org/jclouds/googlecompute/GoogleComputeApiMetadata.java b/labs/google-compute/src/main/java/org/jclouds/googlecompute/GoogleComputeApiMetadata.java new file mode 100644 index 0000000000..2af0309efd --- /dev/null +++ b/labs/google-compute/src/main/java/org/jclouds/googlecompute/GoogleComputeApiMetadata.java @@ -0,0 +1,103 @@ +/** + * 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.googlecompute; + +import com.google.common.collect.ImmutableSet; +import com.google.common.reflect.TypeToken; +import com.google.inject.Module; +import org.jclouds.apis.ApiMetadata; +import org.jclouds.googlecompute.config.GoogleComputeParserModule; +import org.jclouds.googlecompute.config.GoogleComputeRestClientModule; +import org.jclouds.googlecompute.config.OAuthModuleWithoutTypeAdapters; +import org.jclouds.oauth.v2.config.OAuthAuthenticationModule; +import org.jclouds.rest.RestContext; +import org.jclouds.rest.internal.BaseRestApiMetadata; + +import java.net.URI; +import java.util.Properties; + +import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL; +import static org.jclouds.oauth.v2.config.OAuthProperties.AUDIENCE; +import static org.jclouds.oauth.v2.config.OAuthProperties.SIGNATURE_OR_MAC_ALGORITHM; + +/** + * Implementation of {@link ApiMetadata} for GoogleCompute v1beta13 API + * + * @author David Alves + */ +public class GoogleComputeApiMetadata extends BaseRestApiMetadata { + + public static final TypeToken> CONTEXT_TOKEN = new + TypeToken>() {}; + + @Override + public Builder toBuilder() { + return new Builder().fromApiMetadata(this); + } + + public GoogleComputeApiMetadata() { + this(new Builder()); + } + + protected GoogleComputeApiMetadata(Builder builder) { + super(builder); + } + + public static Properties defaultProperties() { + Properties properties = BaseRestApiMetadata.defaultProperties(); + properties.put("oauth.endpoint", "https://accounts.google.com/o/oauth2/token"); + properties.put(AUDIENCE, "https://accounts.google.com/o/oauth2/token"); + properties.put(SIGNATURE_OR_MAC_ALGORITHM, "RS256"); + properties.put(PROPERTY_SESSION_INTERVAL, 3600); + return properties; + } + + public static class Builder extends BaseRestApiMetadata.Builder { + + protected Builder() { + super(GoogleComputeApi.class, GoogleComputeAsyncApi.class); + id("google-compute") + .name("Google Compute Engine Api") + .identityName("Email associated with the Goole API client_id") + .credentialName("Private key literal associated with the Google API client_id") + .documentation(URI.create("https://developers.google.com/compute/docs")) + .version("v1beta13") + .defaultEndpoint("https://www.googleapis.com/compute/v1beta13") + .defaultProperties(GoogleComputeApiMetadata.defaultProperties()) + .defaultModules(ImmutableSet.>builder() + .add(GoogleComputeRestClientModule.class) + .add(GoogleComputeParserModule.class) + .add(OAuthAuthenticationModule.class) + .add(OAuthModuleWithoutTypeAdapters.class).build()); + } + + @Override + public GoogleComputeApiMetadata build() { + return new GoogleComputeApiMetadata(this); + } + + @Override + public Builder fromApiMetadata(ApiMetadata in) { + super.fromApiMetadata(in); + return this; + } + + } + +} diff --git a/labs/google-compute/src/main/java/org/jclouds/googlecompute/GoogleComputeAsyncApi.java b/labs/google-compute/src/main/java/org/jclouds/googlecompute/GoogleComputeAsyncApi.java new file mode 100644 index 0000000000..2cddad8bab --- /dev/null +++ b/labs/google-compute/src/main/java/org/jclouds/googlecompute/GoogleComputeAsyncApi.java @@ -0,0 +1,63 @@ +/* + * 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.googlecompute; + +import com.google.common.annotations.Beta; +import org.jclouds.googlecompute.features.OperationAsyncApi; +import org.jclouds.googlecompute.features.ProjectAsyncApi; +import org.jclouds.googlecompute.features.ZoneAsyncApi; +import org.jclouds.rest.annotations.Delegate; + +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; + +/** + * Provides asynchronous access to GoogleCompute via their REST API. + *

+ * + * @author David Alves + * @see GoogleComputeApi + */ +@Beta +public interface GoogleComputeAsyncApi { + + /** + * Provides asynchronous access to Project features + */ + @Delegate + ProjectAsyncApi getProjectApi(); + + /** + * Provides asynchronous access to Operation features + * + * @param projectName the name of the project + */ + @Delegate + @Path("/projects/{project}") + OperationAsyncApi getOperationApiForProject(@PathParam("project") String projectName); + + /** + * Provides asynchronous access to Zone features + * + * @param projectName the name of the project + */ + @Delegate + @Path("/projects/{project}") + ZoneAsyncApi getZoneApiForProject(@PathParam("project") String projectName); +} diff --git a/labs/google-compute/src/main/java/org/jclouds/googlecompute/GoogleComputeConstants.java b/labs/google-compute/src/main/java/org/jclouds/googlecompute/GoogleComputeConstants.java new file mode 100644 index 0000000000..2334ee9ce6 --- /dev/null +++ b/labs/google-compute/src/main/java/org/jclouds/googlecompute/GoogleComputeConstants.java @@ -0,0 +1,39 @@ +/* + * 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.googlecompute; + +/** + * @author David Alves + */ +public interface GoogleComputeConstants { + + public static final String COMPUTE_SCOPE = "https://www.googleapis.com/auth/compute"; + + public static final String COMPUTE_READONLY_SCOPE = "https://www.googleapis.com/auth/compute.readonly"; + + /** + * TODO storage scopes should be moved to the GCS api + */ + + public static final String STORAGE_WRITEONLY_SCOPE = "https://www.googleapis.com/auth/devstorage.write_only"; + + public static final String STORAGE_READONLY_SCOPE = "https://www.googleapis.com/auth/devstorage.read_only"; + +} diff --git a/labs/google-compute/src/main/java/org/jclouds/googlecompute/config/GoogleComputeParserModule.java b/labs/google-compute/src/main/java/org/jclouds/googlecompute/config/GoogleComputeParserModule.java new file mode 100644 index 0000000000..ff9abc8119 --- /dev/null +++ b/labs/google-compute/src/main/java/org/jclouds/googlecompute/config/GoogleComputeParserModule.java @@ -0,0 +1,191 @@ +/* + * 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.googlecompute.config; + +import com.google.common.collect.ForwardingMap; +import com.google.common.collect.ImmutableMap; +import com.google.gson.JsonArray; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonPrimitive; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; +import com.google.inject.AbstractModule; +import com.google.inject.Provides; +import org.jclouds.googlecompute.domain.Operation; +import org.jclouds.googlecompute.domain.Project; +import org.jclouds.json.config.GsonModule; +import org.jclouds.oauth.v2.domain.ClaimSet; +import org.jclouds.oauth.v2.domain.Header; +import org.jclouds.oauth.v2.json.ClaimSetTypeAdapter; +import org.jclouds.oauth.v2.json.HeaderTypeAdapter; + +import javax.inject.Singleton; +import java.beans.ConstructorProperties; +import java.lang.reflect.Type; +import java.net.URI; +import java.util.Date; +import java.util.Map; +import java.util.Set; + +/** + * @author David Alves + */ +public class GoogleComputeParserModule extends AbstractModule { + + @Override + protected void configure() { + bind(GsonModule.DateAdapter.class).to(GsonModule.Iso8601DateAdapter.class); + } + + @Provides + @Singleton + public Map provideCustomAdapterBindings() { + return ImmutableMap.of( + Metadata.class, new MetadataTypeAdapter(), + Operation.class, new OperationTypeAdapter(), + Header.class, new HeaderTypeAdapter(), + ClaimSet.class, new ClaimSetTypeAdapter(), + Project.class, new ProjectTypeAdapter() + ); + } + + /** + * Parser for operations that unwraps errors avoiding an extra intermediate object. + * + * @see + */ + @Singleton + private static class OperationTypeAdapter implements JsonDeserializer { + + @Override + public Operation deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws + JsonParseException { + Operation.Builder operationBuilder = ((Operation) context.deserialize(json, + OperationInternal.class)).toBuilder(); + JsonObject error = json.getAsJsonObject().getAsJsonObject("error"); + if (error != null) { + JsonArray array = error.getAsJsonArray("errors"); + if (array != null) { + for (JsonElement element : array) { + operationBuilder.addError((Operation.Error) context.deserialize(element, Operation.Error.class)); + } + } + } + return operationBuilder.build(); + } + + private static class OperationInternal extends Operation { + @ConstructorProperties({ + "id", "creationTimestamp", "selfLink", "name", "description", "targetLink", "targetId", + "clientOperationId", "status", "statusMessage", "user", "progress", "insertTime", "startTime", + "endTime", "httpErrorStatusCode", "httpErrorMessage", "operationType" + }) + private OperationInternal(String id, Date creationTimestamp, URI selfLink, String name, + String description, URI targetLink, String targetId, String clientOperationId, + Status status, String statusMessage, String user, int progress, Date insertTime, + Date startTime, Date endTime, int httpErrorStatusCode, String httpErrorMessage, + String operationType) { + super(id, creationTimestamp, selfLink, name, description, targetLink, targetId, clientOperationId, + status, statusMessage, user, progress, insertTime, startTime, endTime, httpErrorStatusCode, + httpErrorMessage, operationType, null); + } + } + } + + /** + * Parser for Metadata. + */ + @Singleton + private static class MetadataTypeAdapter implements JsonDeserializer, JsonSerializer { + + + @Override + public Metadata deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws + JsonParseException { + ImmutableMap.Builder builder = ImmutableMap.builder(); + JsonObject metadata = json.getAsJsonObject(); + JsonArray items = metadata.getAsJsonArray("items"); + if (items != null) { + for (JsonElement element : items) { + JsonObject object = element.getAsJsonObject(); + builder.put(object.get("key").getAsString(), object.get("value").getAsString()); + } + } + return new Metadata(builder.build()); + } + + @Override + public JsonElement serialize(Metadata src, Type typeOfSrc, JsonSerializationContext context) { + JsonObject metadataObject = new JsonObject(); + metadataObject.add("kind", new JsonPrimitive("compute#metadata")); + JsonArray items = new JsonArray(); + for (Map.Entry entry : src.entrySet()) { + JsonObject object = new JsonObject(); + object.addProperty("key", entry.getKey()); + object.addProperty("value", entry.getValue()); + items.add(object); + } + metadataObject.add("items", items); + return metadataObject; + } + } + + public static class Metadata extends ForwardingMap { + + private final Map delegate; + + public Metadata(Map delegate) { + this.delegate = delegate; + } + + @Override + protected Map delegate() { + return delegate; + } + } + + @Singleton + private static class ProjectTypeAdapter implements JsonDeserializer { + + @Override + public Project deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws + JsonParseException { + return Project.builder().fromProject((Project) context.deserialize(json, ProjectInternal.class)).build(); + } + + private static class ProjectInternal extends Project { + + @ConstructorProperties({ + "id", "creationTimestamp", "selfLink", "name", "description", "commonInstanceMetadata", "quotas", + "externalIpAddresses" + }) + private ProjectInternal(String id, Date creationTimestamp, URI selfLink, String name, String description, + Metadata commonInstanceMetadata, Set quotas, Set externalIpAddresses) { + super(id, creationTimestamp, selfLink, name, description, commonInstanceMetadata, quotas, + externalIpAddresses); + } + + } + } +} diff --git a/labs/google-compute/src/main/java/org/jclouds/googlecompute/config/GoogleComputeRestClientModule.java b/labs/google-compute/src/main/java/org/jclouds/googlecompute/config/GoogleComputeRestClientModule.java new file mode 100644 index 0000000000..851cc8627c --- /dev/null +++ b/labs/google-compute/src/main/java/org/jclouds/googlecompute/config/GoogleComputeRestClientModule.java @@ -0,0 +1,91 @@ +/** + * 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.googlecompute.config; + +import com.google.common.base.Predicate; +import com.google.common.base.Splitter; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Iterables; +import com.google.inject.Provides; +import com.google.inject.TypeLiteral; +import org.jclouds.googlecompute.GoogleComputeApi; +import org.jclouds.googlecompute.GoogleComputeAsyncApi; +import org.jclouds.googlecompute.domain.Operation; +import org.jclouds.googlecompute.features.OperationApi; +import org.jclouds.googlecompute.features.OperationAsyncApi; +import org.jclouds.googlecompute.features.ProjectApi; +import org.jclouds.googlecompute.features.ProjectAsyncApi; +import org.jclouds.googlecompute.features.ZoneApi; +import org.jclouds.googlecompute.features.ZoneAsyncApi; +import org.jclouds.googlecompute.handlers.GoogleComputeErrorHandler; +import org.jclouds.googlecompute.predicates.OperationDonePredicate; +import org.jclouds.http.HttpErrorHandler; +import org.jclouds.http.annotation.ClientError; +import org.jclouds.http.annotation.Redirection; +import org.jclouds.http.annotation.ServerError; +import org.jclouds.json.config.GsonModule.DateAdapter; +import org.jclouds.json.config.GsonModule.Iso8601DateAdapter; +import org.jclouds.rest.ConfiguresRestClient; +import org.jclouds.rest.annotations.Identity; +import org.jclouds.rest.config.RestClientModule; + +import java.util.Map; +import java.util.concurrent.atomic.AtomicReference; + +import static com.google.common.base.Preconditions.checkState; + +/** + * Configures the GoogleCompute connection. + * + * @author David Alves + */ +@ConfiguresRestClient +public class GoogleComputeRestClientModule extends RestClientModule { + public static final Map, Class> DELEGATE_MAP = ImmutableMap., Class>builder() + .put(OperationApi.class, OperationAsyncApi.class) + .put(ProjectApi.class, ProjectAsyncApi.class) + .put(ZoneApi.class, ZoneAsyncApi.class) + .build(); + + public GoogleComputeRestClientModule() { + super(DELEGATE_MAP); + } + + @Override + protected void configure() { + bind(DateAdapter.class).to(Iso8601DateAdapter.class); + bind(new TypeLiteral>>() {}).to(OperationDonePredicate.class); + super.configure(); + } + + @Override + protected void bindErrorHandlers() { + bind(HttpErrorHandler.class).annotatedWith(Redirection.class).to(GoogleComputeErrorHandler.class); + bind(HttpErrorHandler.class).annotatedWith(ClientError.class).to(GoogleComputeErrorHandler.class); + bind(HttpErrorHandler.class).annotatedWith(ServerError.class).to(GoogleComputeErrorHandler.class); + } + + @Provides + @UserProject + public String provideProject(@Identity String identity) { + checkState(identity.indexOf("@") != 1, "identity should be in project_id@developer.gserviceaccount.com format"); + return Iterables.get(Splitter.on("@").split(identity), 0); + } + +} diff --git a/labs/google-compute/src/main/java/org/jclouds/googlecompute/config/OAuthModuleWithoutTypeAdapters.java b/labs/google-compute/src/main/java/org/jclouds/googlecompute/config/OAuthModuleWithoutTypeAdapters.java new file mode 100644 index 0000000000..c611e35b18 --- /dev/null +++ b/labs/google-compute/src/main/java/org/jclouds/googlecompute/config/OAuthModuleWithoutTypeAdapters.java @@ -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.googlecompute.config; + +import com.google.common.base.Function; +import com.google.common.base.Supplier; +import com.google.inject.TypeLiteral; +import org.jclouds.oauth.v2.config.OAuthModule; +import org.jclouds.oauth.v2.domain.OAuthCredentials; +import org.jclouds.oauth.v2.domain.Token; +import org.jclouds.oauth.v2.domain.TokenRequest; +import org.jclouds.oauth.v2.functions.BuildTokenRequest; +import org.jclouds.oauth.v2.functions.FetchToken; +import org.jclouds.oauth.v2.functions.OAuthCredentialsSupplier; +import org.jclouds.oauth.v2.functions.SignOrProduceMacForToken; +import org.jclouds.rest.internal.GeneratedHttpRequest; + +/** + * Overrides OAuthModule leaving TypeAdapters bindings out. + *

+ * TODO overcome this by using multibindings on GSonModule? + * + * @author David Alves + */ +public class OAuthModuleWithoutTypeAdapters extends OAuthModule { + + @Override + protected void configure() { + bind(new TypeLiteral>() {}).to(SignOrProduceMacForToken.class); + bind(new TypeLiteral>() {}).to(OAuthCredentialsSupplier.class); + bind(new TypeLiteral>() {}).to(BuildTokenRequest.class); + bind(new TypeLiteral>() {}).to(FetchToken.class); + } +} diff --git a/labs/google-compute/src/main/java/org/jclouds/googlecompute/config/UserProject.java b/labs/google-compute/src/main/java/org/jclouds/googlecompute/config/UserProject.java new file mode 100644 index 0000000000..5852fc827c --- /dev/null +++ b/labs/google-compute/src/main/java/org/jclouds/googlecompute/config/UserProject.java @@ -0,0 +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.googlecompute.config; + +import javax.inject.Qualifier; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Qualifies a property as the user's project id. + * + * @author David Alves + */ +@Retention(value = RetentionPolicy.RUNTIME) +@Target(value = {ElementType.TYPE, ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD}) +@Qualifier +public @interface UserProject { +} diff --git a/labs/google-compute/src/main/java/org/jclouds/googlecompute/domain/ListPage.java b/labs/google-compute/src/main/java/org/jclouds/googlecompute/domain/ListPage.java new file mode 100644 index 0000000000..e286f137b0 --- /dev/null +++ b/labs/google-compute/src/main/java/org/jclouds/googlecompute/domain/ListPage.java @@ -0,0 +1,179 @@ +/* + * 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.googlecompute.domain; + +import com.google.common.base.Objects; +import com.google.common.base.Optional; +import com.google.common.collect.ImmutableSet; +import org.jclouds.collect.IterableWithMarker; + +import java.net.URI; +import java.util.Iterator; + +import static com.google.common.base.Objects.equal; +import static com.google.common.base.Objects.toStringHelper; +import static com.google.common.base.Preconditions.checkNotNull; +import static org.jclouds.googlecompute.domain.Resource.Kind; + +/** + * The collection returned from any listFirstPage() method. + * + * @author David Alves + */ +public class ListPage extends IterableWithMarker { + + private final Kind kind; + private final String id; + private final URI selfLink; + private final String nextPageToken; + private final Iterable items; + + protected ListPage(Kind kind, String id, URI selfLink, String nextPageToken, Iterable items) { + this.kind = checkNotNull(kind, "kind of %id", id); + this.id = checkNotNull(id, "id"); + this.selfLink = checkNotNull(selfLink, "selfLink of %id", id); + this.nextPageToken = nextPageToken; + this.items = items != null ? ImmutableSet.copyOf(items) : ImmutableSet.of(); + } + + public Kind getKind() { + return kind; + } + + public String getId() { + return id; + } + + public URI getSelfLink() { + return selfLink; + } + + @Override + public Optional nextMarker() { + return Optional.fromNullable(nextPageToken); + } + + @Override + public Iterator iterator() { + return items.iterator(); + } + + /** + * {@inheritDoc} + */ + @Override + public int hashCode() { + return Objects.hashCode(kind, id); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null || getClass() != obj.getClass()) return false; + ListPage that = ListPage.class.cast(obj); + return equal(this.kind, that.kind) + && equal(this.id, that.id); + } + + /** + * {@inheritDoc} + */ + protected Objects.ToStringHelper string() { + return toStringHelper(this) + .omitNullValues() + .add("kind", kind) + .add("id", id) + .add("selfLink", selfLink) + .add("nextPageToken", nextPageToken) + .add("items", items); + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return string().toString(); + } + + public static Builder builder() { + return new Builder(); + } + + public Builder toBuilder() { + return new Builder().fromPagedList(this); + } + + public static final class Builder { + + private Kind kind; + private String id; + private URI selfLink; + private String nextPageToken; + private ImmutableSet.Builder items = ImmutableSet.builder(); + + public Builder kind(Kind kind) { + this.kind = kind; + return this; + } + + public Builder id(String id) { + this.id = id; + return this; + } + + public Builder selfLink(URI selfLink) { + this.selfLink = selfLink; + return this; + } + + public Builder addItem(T item) { + this.items.add(item); + return this; + } + + public Builder items(Iterable items) { + this.items.addAll(items); + return this; + } + + public Builder nextPageToken(String nextPageToken) { + this.nextPageToken = nextPageToken; + return this; + } + + public ListPage build() { + return new ListPage(kind, id, selfLink, nextPageToken, items.build()); + } + + public Builder fromPagedList(ListPage in) { + return this + .kind(in.getKind()) + .id(in.getId()) + .selfLink(in.getSelfLink()) + .nextPageToken((String) in.nextMarker().orNull()) + .items(in); + + } + } +} diff --git a/labs/google-compute/src/main/java/org/jclouds/googlecompute/domain/Operation.java b/labs/google-compute/src/main/java/org/jclouds/googlecompute/domain/Operation.java new file mode 100644 index 0000000000..b8879b9bf5 --- /dev/null +++ b/labs/google-compute/src/main/java/org/jclouds/googlecompute/domain/Operation.java @@ -0,0 +1,513 @@ +/* + * 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.googlecompute.domain; + +import com.google.common.annotations.Beta; +import com.google.common.base.Objects; +import com.google.common.base.Optional; +import com.google.common.collect.ImmutableList; +import org.jclouds.http.HttpResponse; + +import java.beans.ConstructorProperties; +import java.net.URI; +import java.util.Date; +import java.util.List; + +import static com.google.common.base.Objects.equal; +import static com.google.common.base.Objects.toStringHelper; +import static com.google.common.base.Optional.fromNullable; +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Describes an operation being executed on some Resource + * + * @author David Alves + * @see + */ +@Beta +public class Operation extends Resource { + + public static enum Status { + PENDING, + RUNNING, + DONE + } + + private final URI targetLink; + private final String targetId; + private final Optional clientOperationId; + private final Status status; + private final Optional statusMessage; + private final String user; + private final Optional progress; + private final Date insertTime; + private final Optional startTime; + private final Optional endTime; + private final Optional httpError; + private final String operationType; + private final List errors; + + protected Operation(String id, Date creationTimestamp, URI selfLink, String name, String description, + URI targetLink, String targetId, String clientOperationId, Status status, + String statusMessage, String user, Integer progress, Date insertTime, Date startTime, + Date endTime, Integer httpErrorStatusCode, String httpErrorMessage, String operationType, + List errors) { + super(Kind.OPERATION, checkNotNull(id, "id of %s", name), fromNullable(creationTimestamp), + checkNotNull(selfLink, "selfLink of %s", name), checkNotNull(name, "name"), fromNullable(description)); + this.targetLink = checkNotNull(targetLink, "targetLink of %s", name); + this.targetId = checkNotNull(targetId, "targetId of %s", name); + this.clientOperationId = fromNullable(clientOperationId); + this.status = checkNotNull(status, "status of %s", name); + this.statusMessage = fromNullable(statusMessage); + this.user = checkNotNull(user, "user of %s", name); + this.progress = fromNullable(progress); + this.insertTime = checkNotNull(insertTime, "insertTime of %s", name); + this.startTime = fromNullable(startTime); + this.endTime = fromNullable(endTime); + this.httpError = httpErrorStatusCode != null ? + Optional.of(HttpResponse.builder() + .statusCode(httpErrorStatusCode) + .message(httpErrorMessage) + .build()) + : Optional.absent(); + this.operationType = checkNotNull(operationType, "insertTime of %s", name); + this.errors = errors == null ? ImmutableList.of() : ImmutableList.copyOf(errors); + } + + /** + * @return URL of the resource the operation is mutating. + */ + public URI getTargetLink() { + return targetLink; + } + + /** + * @return unique target id which identifies a particular incarnation of the target. + */ + public String getTargetId() { + return targetId; + } + + /** + * @return An optional identifier specified by the client when the mutation was initiated. Must be unique for all + * operation resources in the project. + */ + public Optional getClientOperationId() { + return clientOperationId; + } + + /** + * @return Status of the operation. Can be one of the following: PENDING, RUNNING, or DONE. + */ + public Status getStatus() { + return status; + } + + /** + * @return An optional textual description of the current status of the operation. + */ + public Optional getStatusMessage() { + return statusMessage; + } + + /** + * @return User who requested the operation, for example "user@example.com". + */ + public String getUser() { + return user; + } + + /** + * @return an optional progress indicator that ranges from 0 to 100. This should not be used to guess at when the + * operation will be complete. This number should be monotonically increasing as the operation progresses + * (output only). + */ + public Optional getProgress() { + return progress; + } + + /** + * @return the time that this operation was requested. + */ + public Date getInsertTime() { + return insertTime; + } + + /** + * @return the time that this operation was started by the server. + */ + public Optional getStartTime() { + return startTime; + } + + /** + * @return the time that this operation was completed. + */ + public Optional getEndTime() { + return endTime; + } + + /** + * @return if operation fails, the HttpResponse with error status code returned and the message, e.g. NOT_FOUND. + */ + public Optional getHttpError() { + return httpError; + } + + /** + * @return type of the operation. Examples include insert, update, and delete. + */ + public String getOperationType() { + return operationType; + } + + /** + * @return if error occurred during processing of this operation, this field will be populated. + */ + public List getErrors() { + return errors; + } + + /** + * {@inheritDoc} + */ + protected Objects.ToStringHelper string() { + return super.string() + .omitNullValues() + .add("targetLink", targetLink) + .add("targetId", targetId) + .add("clientOperationId", clientOperationId.orNull()) + .add("status", status) + .add("statusMessage", statusMessage.orNull()) + .add("user", user) + .add("progress", progress.orNull()) + .add("insertTime", insertTime) + .add("startTime", startTime.orNull()) + .add("endTime", endTime.orNull()) + .add("httpError", httpError.orNull()) + .add("operationType", operationType) + .add("errors", errors); + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return string().toString(); + } + + public static Builder builder() { + return new Builder(); + } + + public Builder toBuilder() { + return new Builder().fromOperation(this); + } + + public static final class Builder extends Resource.Builder { + + private URI targetLink; + private String targetId; + private String clientOperationId; + private Status status; + private String statusMessage; + private String user; + private Integer progress; + private Date insertTime; + private Date startTime; + private Date endTime; + private Integer httpErrorStatusCode; + private String httpErrorMessage; + private String operationType; + private ImmutableList.Builder errors = ImmutableList.builder(); + + /** + * @see Operation#getTargetLink() + */ + public Builder targetLink(URI targetLink) { + this.targetLink = targetLink; + return self(); + } + + /** + * @see Operation#getTargetId() + */ + public Builder targetId(String targetId) { + this.targetId = targetId; + return self(); + } + + /** + * @see Operation#getClientOperationId() + */ + public Builder clientOperationId(String clientOperationId) { + this.clientOperationId = clientOperationId; + return self(); + } + + /** + * @see Operation#getStatus() + */ + public Builder status(Status status) { + this.status = status; + return self(); + } + + /** + * @see Operation#getStatusMessage() + */ + public Builder statusMessage(String statusMessage) { + this.statusMessage = statusMessage; + return self(); + } + + /** + * @see Operation#getUser() + */ + public Builder user(String user) { + this.user = user; + return self(); + } + + /** + * @see Operation#getProgress() + */ + public Builder progress(Integer progress) { + this.progress = progress; + return self(); + } + + /** + * @see Operation#getInsertTime() + */ + public Builder insertTime(Date insertTime) { + this.insertTime = insertTime; + return self(); + } + + /** + * @see Operation#getStartTime() + */ + public Builder startTime(Date startTime) { + this.startTime = startTime; + return self(); + } + + /** + * @see Operation#getEndTime() + */ + public Builder endTime(Date endTime) { + this.endTime = endTime; + return self(); + } + + /** + * @see Operation#getHttpError() + */ + public Builder httpErrorStatusCode(Integer httpErrorStatusCode) { + this.httpErrorStatusCode = httpErrorStatusCode; + return self(); + } + + /** + * @see Operation#getHttpError() + */ + public Builder httpErrorMessage(String httpErrorMessage) { + this.httpErrorMessage = httpErrorMessage; + return self(); + } + + /** + * @see Operation#getOperationType() + */ + public Builder operationType(String operationType) { + this.operationType = operationType; + return self(); + } + + /** + * @see Operation#getErrors() + */ + public Builder errors(Iterable errors) { + if (errors != null) + this.errors.addAll(errors); + return self(); + } + + /** + * @see Operation#getErrors() + */ + public Builder addError(Error error) { + this.errors.add(error); + return self(); + } + + @Override + protected Builder self() { + return this; + } + + public Operation build() { + return new Operation(super.id, super.creationTimestamp, super.selfLink, super.name, + super.description, targetLink, targetId, clientOperationId, status, statusMessage, user, progress, + insertTime, startTime, endTime, httpErrorStatusCode, httpErrorMessage, operationType, + errors.build()); + } + + public Builder fromOperation(Operation in) { + return super.fromResource(in).targetLink(in.getTargetLink()).targetId(in.getTargetId()).clientOperationId(in + .getClientOperationId().orNull()).status(in.getStatus()).statusMessage(in.getStatusMessage().orNull()) + .user(in.getUser()).progress(in.getProgress().get()).insertTime(in.getInsertTime()) + .startTime(in.getStartTime().orNull()).endTime(in.getEndTime().orNull()) + .httpErrorStatusCode(in.getHttpError().isPresent() ? in.getHttpError().get().getStatusCode() : null) + .httpErrorMessage(in.getHttpError().isPresent() ? in.getHttpError().get().getMessage() : null) + .operationType(in.getOperationType()).errors(in.getErrors()); + } + } + + /** + * A particular error for an operation including the details. + */ + public static final class Error { + + private final String code; + private final Optional location; + private final Optional message; + + @ConstructorProperties({ + "code", "location", "message" + }) + private Error(String code, String location, String message) { + this.code = checkNotNull(code, "code"); + this.location = fromNullable(location); + this.message = fromNullable(message); + } + + /** + * @return the error type identifier for this error. + */ + public String getCode() { + return code; + } + + /** + * @return indicates the field in the request which caused the error.. + */ + public Optional getLocation() { + return location; + } + + /** + * @return an optional, human-readable error message. + */ + public Optional getMessage() { + return message; + } + + /** + * {@inheritDoc} + */ + @Override + public int hashCode() { + return Objects.hashCode(code, location, message); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null || getClass() != obj.getClass()) return false; + Error that = Error.class.cast(obj); + return equal(this.code, that.code) + && equal(this.location, that.location) + && equal(this.message, that.message); + } + + /** + * {@inheritDoc} + */ + protected Objects.ToStringHelper string() { + return toStringHelper(this) + .omitNullValues() + .add("code", code) + .add("location", location.orNull()) + .add("message", message.orNull()); + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return string().toString(); + } + + public static Builder builder() { + return new Builder(); + } + + public Builder toBuilder() { + return builder().fromOperationErrorDetail(this); + } + + public static final class Builder { + + private String code; + private String location; + private String message; + + /** + * @see org.jclouds.googlecompute.domain.Operation.Error#getCode() + */ + public Builder code(String code) { + this.code = code; + return this; + } + + /** + * @see org.jclouds.googlecompute.domain.Operation.Error#getLocation() + */ + public Builder location(String location) { + this.location = location; + return this; + } + + /** + * @see org.jclouds.googlecompute.domain.Operation.Error#getMessage() + */ + public Builder message(String message) { + this.message = message; + return this; + } + + public Error build() { + return new Error(code, location, message); + } + + public Builder fromOperationErrorDetail(Error in) { + return new Builder().code(in.getCode()).location(in.getLocation().orNull()).message + (in.getMessage().orNull()); + } + } + } +} diff --git a/labs/google-compute/src/main/java/org/jclouds/googlecompute/domain/Project.java b/labs/google-compute/src/main/java/org/jclouds/googlecompute/domain/Project.java new file mode 100644 index 0000000000..e69c74d3d4 --- /dev/null +++ b/labs/google-compute/src/main/java/org/jclouds/googlecompute/domain/Project.java @@ -0,0 +1,307 @@ +/* + * 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.googlecompute.domain; + +import com.google.common.annotations.Beta; +import com.google.common.base.Objects; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import org.jclouds.javax.annotation.Nullable; + +import java.beans.ConstructorProperties; +import java.net.URI; +import java.util.Date; +import java.util.Map; +import java.util.Set; + +import static com.google.common.base.Objects.equal; +import static com.google.common.base.Objects.toStringHelper; +import static com.google.common.base.Optional.fromNullable; +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * A Project resource is the root collection and settings resource for all Google Compute Engine resources. + * + * @author David Alves + * @see + */ +@Beta +public class Project extends Resource { + + private final Map commonInstanceMetadata; + private final Set quotas; + private final Set externalIpAddresses; + + protected Project(String id, Date creationTimestamp, URI selfLink, String name, String description, + Map commonInstanceMetadata, Set quotas, Set externalIpAddresses) { + super(Kind.PROJECT, checkNotNull(id, "id of %s", name), fromNullable(creationTimestamp), checkNotNull(selfLink, + "selfLink of %s", name), checkNotNull(name, "name"), fromNullable(description)); + this.commonInstanceMetadata = commonInstanceMetadata == null ? ImmutableMap.of() : ImmutableMap.copyOf(commonInstanceMetadata); + this.quotas = quotas == null ? ImmutableSet.of() : ImmutableSet.copyOf(quotas); + this.externalIpAddresses = externalIpAddresses == null ? ImmutableSet.of() : ImmutableSet.copyOf + (externalIpAddresses); + } + + /** + * @return metadata key/value pairs available to all instances contained in this project. + */ + public Map getCommonInstanceMetadata() { + return commonInstanceMetadata; + } + + /** + * @return quotas assigned to this project. + */ + public Set getQuotas() { + return quotas; + } + + /** + * @return internet available IP addresses available for use in this project. + */ + @Nullable + public Set getExternalIpAddresses() { + return externalIpAddresses; + } + + /** + * {@inheritDoc} + */ + protected Objects.ToStringHelper string() { + return super.string() + .add("commonInstanceMetadata", commonInstanceMetadata) + .add("quotas", quotas) + .add("externalIpAddresses", externalIpAddresses); + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return string().toString(); + } + + public static Builder builder() { + return new Builder(); + } + + public Builder toBuilder() { + return new Builder().fromProject(this); + } + + public static final class Builder extends Resource.Builder { + + private ImmutableMap.Builder commonInstanceMetadata = ImmutableMap.builder(); + private ImmutableSet.Builder quotas = ImmutableSet.builder(); + private ImmutableSet.Builder externalIpAddresses = ImmutableSet.builder(); + + /** + * @see Project#getCommonInstanceMetadata() + */ + public Builder addCommonInstanceMetadata(String key, String value) { + this.commonInstanceMetadata.put(key, value); + return this; + } + + /** + * @see Project#getCommonInstanceMetadata() + */ + public Builder commonInstanceMetadata(Map commonInstanceMetadata) { + this.commonInstanceMetadata.putAll(checkNotNull(commonInstanceMetadata, "commonInstanceMetadata")); + return this; + } + + /** + * @see Project#getQuotas() + */ + public Builder addQuota(String metric, double usage, double limit) { + this.quotas.add(Quota.builder().metric(metric).usage(usage).limit(limit).build()); + return this; + } + + /** + * @see Project#getQuotas() + */ + public Builder quotas(Set quotas) { + this.quotas.addAll(checkNotNull(quotas)); + return this; + } + + /** + * @see Project#getExternalIpAddresses() + */ + public Builder addExternalIpAddress(String externalIpAddress) { + this.externalIpAddresses.add(checkNotNull(externalIpAddress, "externalIpAddress")); + return this; + } + + /** + * @see Project#getExternalIpAddresses() + */ + public Builder externalIpAddresses(Set externalIpAddresses) { + this.externalIpAddresses.addAll(checkNotNull(externalIpAddresses, "externalIpAddresses")); + return this; + } + + @Override + protected Builder self() { + return this; + } + + public Project build() { + return new Project(super.id, super.creationTimestamp, super.selfLink, super.name, + super.description, commonInstanceMetadata.build(), quotas.build(), externalIpAddresses.build()); + } + + public Builder fromProject(Project in) { + return super.fromResource(in).commonInstanceMetadata(in.getCommonInstanceMetadata()).quotas(in.getQuotas()) + .externalIpAddresses(in.getExternalIpAddresses()); + } + } + + /** + * Quotas assigned to a given project + * + * @see + */ + public static final class Quota { + + private String metric; + private double usage; + private double limit; + + @ConstructorProperties({ + "metric", "usage", "limit" + }) + protected Quota(String metric, Double usage, Double limit) { + this.metric = checkNotNull(metric, "metric"); + this.usage = checkNotNull(usage, "usage"); + this.limit = checkNotNull(limit, "limit"); + } + + /** + * @return name of the quota metric. + */ + public String getMetric() { + return metric; + } + + /** + * @return current usage of this metric. + */ + public Double getUsage() { + return usage; + } + + /** + * @return quota limit for this metric. + */ + public Double getLimit() { + return limit; + } + + /** + * {@inheritDoc} + */ + @Override + public int hashCode() { + return Objects.hashCode(metric); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null || getClass() != obj.getClass()) return false; + Quota that = Quota.class.cast(obj); + return equal(this.metric, that.metric); + } + + /** + * {@inheritDoc} + */ + protected Objects.ToStringHelper string() { + return toStringHelper(this) + .omitNullValues() + .add("metric", metric) + .add("usage", usage) + .add("limit", limit); + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return string().toString(); + } + + public static Builder builder() { + return new Builder(); + } + + public Builder toBuilder() { + return builder().fromQuota(this); + } + + public static class Builder { + + private String metric; + private Double usage; + private Double limit; + + /** + * @see org.jclouds.googlecompute.domain.Project.Quota#getMetric() + */ + public Builder metric(String metric) { + this.metric = checkNotNull(metric, "metric"); + return this; + } + + /** + * @see org.jclouds.googlecompute.domain.Project.Quota#getUsage() + */ + public Builder usage(Double usage) { + this.usage = usage; + return this; + } + + /** + * @see org.jclouds.googlecompute.domain.Project.Quota#getLimit() + */ + public Builder limit(Double limit) { + this.limit = limit; + return this; + } + + public Quota build() { + return new Quota(metric, usage, limit); + } + + public Builder fromQuota(Quota quota) { + return new Builder().metric(quota.getMetric()).usage(quota.getUsage()).limit(quota.getLimit()); + } + } + } +} diff --git a/labs/google-compute/src/main/java/org/jclouds/googlecompute/domain/Resource.java b/labs/google-compute/src/main/java/org/jclouds/googlecompute/domain/Resource.java new file mode 100644 index 0000000000..0ed24acf0e --- /dev/null +++ b/labs/google-compute/src/main/java/org/jclouds/googlecompute/domain/Resource.java @@ -0,0 +1,283 @@ +/* + * 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.googlecompute.domain; + +import com.google.common.annotations.Beta; +import com.google.common.base.CaseFormat; +import com.google.common.base.Joiner; +import com.google.common.base.Objects; +import com.google.common.base.Optional; +import com.google.common.base.Splitter; +import com.google.common.collect.Iterables; +import org.jclouds.javax.annotation.Nullable; + +import java.beans.ConstructorProperties; +import java.net.URI; +import java.util.Date; + +import static com.google.common.base.Objects.ToStringHelper; +import static com.google.common.base.Objects.equal; +import static com.google.common.base.Objects.toStringHelper; +import static com.google.common.base.Optional.fromNullable; +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Base class for Google Compute Engine resources. + * + * @author David Alves + */ +@Beta +public class Resource { + + public enum Kind { + DISK, + DISK_LIST, + FIREWALL, + FIREWALL_LIST, + IMAGE, + IMAGE_LIST, + OPERATION, + OPERATION_LIST, + INSTANCE, + INSTANCE_LIST, + KERNEL, + KERNEL_LIST, + MACHINE_TYPE, + MACHINE_TYPE_LIST, + PROJECT, + NETWORK, + NETWORK_LIST, + ZONE, + ZONE_LIST; + + public String value() { + return Joiner.on("#").join("compute", CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, name())); + } + + @Override + public String toString() { + return value(); + } + + public static Kind fromValue(String kind) { + return valueOf(CaseFormat.LOWER_CAMEL.to(CaseFormat + .UPPER_UNDERSCORE, + Iterables.getLast(Splitter.on("#").split(checkNotNull(kind, + "kind"))))); + } + } + + protected final Kind kind; + protected final String id; + protected final Optional creationTimestamp; + protected final URI selfLink; + protected final String name; + protected final Optional description; + + @ConstructorProperties({ + "kind", "id", "creationTimestamp", "selfLink", "name", "description" + }) + protected Resource(Kind kind, String id, Optional creationTimestamp, URI selfLink, String name, + Optional description) { + this.kind = checkNotNull(kind, "kind"); + this.id = id; + this.creationTimestamp = creationTimestamp; + this.selfLink = selfLink; + this.name = checkNotNull(name, "name"); + this.description = description; + } + + /** + * @return the Type of the resource + */ + public Kind getKind() { + return kind; + } + + /** + * @return unique identifier for the resource; defined by the server. + */ + public String getId() { + return id; + } + + /** + * @return creation timestamp in RFC3339 text format. + */ + public Optional getCreationTimestamp() { + return creationTimestamp; + } + + /** + * @return server defined URL for the resource. + */ + public URI getSelfLink() { + return selfLink; + } + + /** + * @return name of the resource. + */ + public String getName() { + return name; + } + + /** + * @return an optional textual description of the resource. + */ + @Nullable + public Optional getDescription() { + return description; + } + + /** + * {@inheritDoc} + */ + @Override + public int hashCode() { + return Objects.hashCode(kind, id, name); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null || getClass() != obj.getClass()) return false; + Resource that = Resource.class.cast(obj); + return equal(this.kind, that.kind) + && equal(this.id, that.id) + && equal(this.name, that.name); + } + + /** + * {@inheritDoc} + */ + protected ToStringHelper string() { + return toStringHelper(this) + .omitNullValues() + .add("kind", kind) + .add("id", id) + .add("name", name) + .add("creationTimestamp", creationTimestamp.orNull()) + .add("selfLink", selfLink) + .add("name", name) + .add("description", description.orNull()); + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return string().toString(); + } + + public static Builder builder() { + return new ConcreteBuilder(); + } + + public Builder toBuilder() { + return new ConcreteBuilder().fromResource(this); + } + + public abstract static class Builder> { + + protected abstract T self(); + + protected Kind kind; + protected String id; + protected Date creationTimestamp; + protected URI selfLink; + protected String name; + protected String description; + + /** + * @see Resource#getKind() + */ + protected T kind(Kind kind) { + this.kind = kind; + return self(); + } + + /** + * @see Resource#getId() + */ + public T id(String id) { + this.id = id; + return self(); + } + + /** + * @see Resource#getCreationTimestamp() + */ + public T creationTimestamp(Date creationTimestamp) { + this.creationTimestamp = creationTimestamp; + return self(); + } + + /** + * @see Resource#getSelfLink() + */ + public T selfLink(URI selfLink) { + this.selfLink = selfLink; + return self(); + } + + /** + * @see Resource#getName() + */ + public T name(String name) { + this.name = name; + return self(); + } + + /** + * @see Resource#getDescription() + */ + public T description(String description) { + this.description = description; + return self(); + } + + public Resource build() { + return new Resource(kind, id, fromNullable(creationTimestamp), selfLink, name, + fromNullable(description)); + } + + public T fromResource(Resource in) { + return this + .kind(in.getKind()) + .id(in.getId()) + .creationTimestamp(in.getCreationTimestamp().orNull()) + .selfLink(in.getSelfLink()) + .name(in.getName()) + .description(in.getDescription().orNull()); + } + } + + private static class ConcreteBuilder extends Builder { + @Override + protected ConcreteBuilder self() { + return this; + } + } +} diff --git a/labs/google-compute/src/main/java/org/jclouds/googlecompute/domain/Zone.java b/labs/google-compute/src/main/java/org/jclouds/googlecompute/domain/Zone.java new file mode 100644 index 0000000000..4e69c82a3b --- /dev/null +++ b/labs/google-compute/src/main/java/org/jclouds/googlecompute/domain/Zone.java @@ -0,0 +1,338 @@ +/* + * 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.googlecompute.domain; + +import com.google.common.annotations.Beta; +import com.google.common.base.Objects; +import com.google.common.base.Optional; +import com.google.common.collect.ImmutableSet; +import org.jclouds.javax.annotation.Nullable; + +import java.beans.ConstructorProperties; +import java.net.URI; +import java.util.Date; +import java.util.Set; + +import static com.google.common.base.Objects.equal; +import static com.google.common.base.Objects.toStringHelper; +import static com.google.common.base.Optional.fromNullable; +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Represents a zone resource. + * + * @author David Alves + * @see + */ +@Beta +public final class Zone extends Resource { + + public enum Status { + UP, + DOWN + } + + private final Status status; + private final Set maintenanceWindows; + private final Set availableMachineTypes; + + @ConstructorProperties({ + "id", "creationTimestamp", "selfLink", "name", "description", "status", "maintenanceWindows", + "availableMachineTypes" + }) + private Zone(String id, Date creationTimestamp, URI selfLink, String name, String description, + Status status, Set maintenanceWindows, Set availableMachineTypes) { + super(Kind.ZONE, checkNotNull(id, "id of %name", name), fromNullable(creationTimestamp), + checkNotNull(selfLink, "selfLink of %name", name), checkNotNull(name, "name"), fromNullable(description)); + this.status = checkNotNull(status, "status of %name", name); + this.maintenanceWindows = maintenanceWindows == null ? ImmutableSet.of() : ImmutableSet + .copyOf(maintenanceWindows); + this.availableMachineTypes = availableMachineTypes == null ? ImmutableSet.of() : ImmutableSet + .copyOf(availableMachineTypes); + } + + /** + * @return Status of the zone. "UP" or "DOWN". + */ + public Status getStatus() { + return status; + } + + /** + * @return scheduled maintenance windows for the zone. When the zone is in a maintenance window, + * all resources which reside in the zone will be unavailable. + */ + public Set getMaintenanceWindows() { + return maintenanceWindows; + } + + /** + * @return the machine types that can be used in this zone. + */ + @Nullable + public Set getAvailableMachineTypes() { + return availableMachineTypes; + } + + /** + * {@inheritDoc} + */ + protected Objects.ToStringHelper string() { + return super.string() + .add("status", status) + .add("maintenanceWindows", maintenanceWindows) + .add("availableMachineTypes", availableMachineTypes); + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return string().toString(); + } + + public static Builder builder() { + return new Builder(); + } + + public Builder toBuilder() { + return new Builder().fromZone(this); + } + + public static final class Builder extends Resource.Builder { + + private Status status; + private ImmutableSet.Builder maintenanceWindows = ImmutableSet.builder(); + private ImmutableSet.Builder availableMachineTypes = ImmutableSet.builder(); + + /** + * @see Zone#getStatus() + */ + public Builder status(Status status) { + this.status = status; + return this; + } + + /** + * @see Zone#getMaintenanceWindows() + */ + public Builder addMaintenanceWindow(MaintenanceWindow maintenanceWindow) { + this.maintenanceWindows.add(checkNotNull(maintenanceWindow, "maintenanceWindow")); + return this; + } + + /** + * @see Zone#getMaintenanceWindows() + */ + public Builder maintenanceWindows(Set maintenanceWindows) { + this.maintenanceWindows.addAll(checkNotNull(maintenanceWindows, "maintenanceWindows")); + return this; + } + + /** + * @see Zone#getAvailableMachineTypes() + */ + public Builder addAvailableMachineType(String availableMachineType) { + this.availableMachineTypes.add(checkNotNull(availableMachineType, "availableMachineType")); + return this; + } + + /** + * @see Zone#getAvailableMachineTypes() + */ + public Builder availableMachineTypes(Set availableMachineTypes) { + this.availableMachineTypes.addAll(checkNotNull(availableMachineTypes, "availableMachineTypes")); + return this; + } + + @Override + protected Builder self() { + return this; + } + + public Zone build() { + return new Zone(super.id, super.creationTimestamp, super.selfLink, super.name, + super.description, status, maintenanceWindows.build(), availableMachineTypes.build()); + } + + public Builder fromZone(Zone in) { + return super.fromResource(in) + .status(in.getStatus()) + .maintenanceWindows(in.getMaintenanceWindows()) + .availableMachineTypes(in.getAvailableMachineTypes()); + } + } + + /** + * Scheduled maintenance windows for the zone. When the zone is in a maintenance window, + * all resources which reside in the zone will be unavailable. + * + * @see + */ + public static final class MaintenanceWindow { + + private final String name; + private final Optional description; + private final Date beginTime; + private final Date endTime; + + @ConstructorProperties({ + "name", "description", "beginTime", "endTime" + }) + private MaintenanceWindow(String name, String description, Date beginTime, Date endTime) { + this.name = checkNotNull(name, "name"); + this.description = fromNullable(description); + this.beginTime = checkNotNull(beginTime, "beginTime of %name", name); + this.endTime = checkNotNull(endTime, "endTime of %name", name); + } + + /** + * @return name of the maintenance window. + */ + public String getName() { + return name; + } + + /** + * @return textual description of the maintenance window. + */ + public Optional getDescription() { + return description; + } + + /** + * @return begin time of the maintenance window. + */ + public Date getBeginTime() { + return beginTime; + } + + /** + * @return end time of the maintenance window. + */ + public Date getEndTime() { + return endTime; + } + + /** + * {@inheritDoc} + */ + @Override + public int hashCode() { + return Objects.hashCode(name, description, beginTime, endTime); + } + + + /** + * {@inheritDoc} + */ + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null || getClass() != obj.getClass()) return false; + MaintenanceWindow that = MaintenanceWindow.class.cast(obj); + return equal(this.name, that.name) + && equal(this.beginTime, that.beginTime) + && equal(this.endTime, that.endTime); + } + + /** + * {@inheritDoc} + */ + protected Objects.ToStringHelper string() { + return toStringHelper(this) + .omitNullValues() + .add("name", name) + .add("description", description.orNull()) + .add("beginTime", beginTime) + .add("endTime", endTime); + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return string().toString(); + } + + public static Builder builder() { + return new Builder(); + } + + public Builder toBuilder() { + return builder().fromZoneMaintenanceWindow(this); + } + + public static final class Builder { + + private String name; + private String description; + private Date beginTime; + private Date endTime; + + /** + * @see org.jclouds.googlecompute.domain.Zone.MaintenanceWindow#getName() + */ + public Builder name(String name) { + this.name = name; + return this; + } + + /** + * @see org.jclouds.googlecompute.domain.Zone.MaintenanceWindow#getDescription() + */ + public Builder description(String description) { + this.description = description; + return this; + } + + /** + * @see org.jclouds.googlecompute.domain.Zone.MaintenanceWindow#getBeginTime() + */ + public Builder beginTime(Date beginTime) { + this.beginTime = beginTime; + return this; + } + + /** + * @see org.jclouds.googlecompute.domain.Zone.MaintenanceWindow#getEndTime() + */ + public Builder endTime(Date endTime) { + this.endTime = endTime; + return this; + } + + + public MaintenanceWindow build() { + return new MaintenanceWindow(name, description, beginTime, endTime); + } + + public Builder fromZoneMaintenanceWindow(MaintenanceWindow in) { + return new Builder() + .name(in.getName()) + .description(in.getDescription().orNull()) + .beginTime(in.getBeginTime()) + .endTime(in.getEndTime()); + } + } + } +} diff --git a/labs/google-compute/src/main/java/org/jclouds/googlecompute/features/OperationApi.java b/labs/google-compute/src/main/java/org/jclouds/googlecompute/features/OperationApi.java new file mode 100644 index 0000000000..9fcf4b5855 --- /dev/null +++ b/labs/google-compute/src/main/java/org/jclouds/googlecompute/features/OperationApi.java @@ -0,0 +1,93 @@ +/* + * 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.googlecompute.features; + +import org.jclouds.collect.PagedIterable; +import org.jclouds.concurrent.Timeout; +import org.jclouds.googlecompute.domain.ListPage; +import org.jclouds.googlecompute.domain.Operation; +import org.jclouds.googlecompute.options.ListOptions; +import org.jclouds.javax.annotation.Nullable; + +import java.util.concurrent.TimeUnit; + +/** + * Provides synchronous access to Operations via their REST API. + *

+ * + * @author David Alves + * @see + */ +@Timeout(duration = 60, timeUnit = TimeUnit.SECONDS) +public interface OperationApi { + + /** + * Retrieves the specified operation resource. + * + * @param operationName name of the operation resource to return. + * @return If successful, this method returns an Operation resource + */ + public Operation get(String operationName); + + /** + * Deletes the specified operation resource. + * + * @param operationName name of the operation resource to delete. + */ + public void delete(String operationName); + + /** + * @see OperationApi#listAtMarker(String, org.jclouds.googlecompute.options.ListOptions) + */ + public ListPage listFirstPage(); + + /** + * @see OperationApi#listAtMarker(String, org.jclouds.googlecompute.options.ListOptions) + */ + public ListPage listAtMarker(@Nullable String marker); + + /** + * Retrieves the listFirstPage of operation resources contained within the specified project. + * By default the listFirstPage as a maximum size of 100, if no options are provided or ListOptions#getMaxResults() + * has not been set. + * + * @param marker marks the beginning of the next list page + * @param listOptions listing options + * @return a page of the list, starting at marker + * @see ListOptions + * @see org.jclouds.googlecompute.domain.ListPage + */ + public ListPage listAtMarker(@Nullable String marker, ListOptions listOptions); + + + /** + * @see OperationApi#list(org.jclouds.googlecompute.options.ListOptions) + */ + public PagedIterable list(); + + /** + * A paged version of OperationApi#listFirstPage() + * + * @return a Paged, Fluent Iterable that is able to fetch additional pages when required + * @see PagedIterable + * @see OperationApi#listAtMarker(String, org.jclouds.googlecompute.options.ListOptions) + */ + public PagedIterable list(ListOptions listOptions); +} diff --git a/labs/google-compute/src/main/java/org/jclouds/googlecompute/features/OperationAsyncApi.java b/labs/google-compute/src/main/java/org/jclouds/googlecompute/features/OperationAsyncApi.java new file mode 100644 index 0000000000..c3efca5617 --- /dev/null +++ b/labs/google-compute/src/main/java/org/jclouds/googlecompute/features/OperationAsyncApi.java @@ -0,0 +1,138 @@ +/* + * 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.googlecompute.features; + +import com.google.common.util.concurrent.ListenableFuture; +import org.jclouds.collect.PagedIterable; +import org.jclouds.googlecompute.domain.ListPage; +import org.jclouds.googlecompute.domain.Operation; +import org.jclouds.googlecompute.functions.internal.ParseOperations; +import org.jclouds.googlecompute.options.ListOptions; +import org.jclouds.javax.annotation.Nullable; +import org.jclouds.oauth.v2.config.OAuthScopes; +import org.jclouds.oauth.v2.filters.OAuthAuthenticator; +import org.jclouds.rest.annotations.Fallback; +import org.jclouds.rest.annotations.RequestFilters; +import org.jclouds.rest.annotations.ResponseParser; +import org.jclouds.rest.annotations.SkipEncoding; +import org.jclouds.rest.annotations.Transform; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.MediaType; + +import static org.jclouds.Fallbacks.EmptyIterableWithMarkerOnNotFoundOr404; +import static org.jclouds.Fallbacks.EmptyPagedIterableOnNotFoundOr404; +import static org.jclouds.Fallbacks.NullOnNotFoundOr404; +import static org.jclouds.googlecompute.GoogleComputeConstants.COMPUTE_READONLY_SCOPE; +import static org.jclouds.googlecompute.GoogleComputeConstants.COMPUTE_SCOPE; + +/** + * Provides asynchronous access to Operations via their REST API. + * + * @author David Alves + * @see OperationApi + */ +@SkipEncoding({'/', '='}) +@RequestFilters(OAuthAuthenticator.class) +public interface OperationAsyncApi { + + /** + * @see OperationApi#get(String) + */ + @GET + @Path("/operations/{operation}") + @OAuthScopes(COMPUTE_READONLY_SCOPE) + @Consumes(MediaType.APPLICATION_JSON) + @Fallback(NullOnNotFoundOr404.class) + ListenableFuture get(@PathParam("operation") String operationName); + + /** + * @see OperationApi#delete(String) + */ + @DELETE + @Path("/operations/{operation}") + @OAuthScopes(COMPUTE_SCOPE) + @Fallback(NullOnNotFoundOr404.class) + ListenableFuture delete(@PathParam("operation") String operationName); + + /** + * @see OperationApi#listFirstPage() + */ + @GET + @Path("/operations") + @OAuthScopes(COMPUTE_READONLY_SCOPE) + @Consumes(MediaType.APPLICATION_JSON) + @ResponseParser(ParseOperations.class) + @Fallback(EmptyIterableWithMarkerOnNotFoundOr404.class) + ListenableFuture> listFirstPage(); + + /** + * @see OperationApi#listAtMarker(String) + */ + @GET + @Path("/operations") + @OAuthScopes(COMPUTE_READONLY_SCOPE) + @Consumes(MediaType.APPLICATION_JSON) + @ResponseParser(ParseOperations.class) + @Fallback(EmptyIterableWithMarkerOnNotFoundOr404.class) + ListenableFuture> listAtMarker(@QueryParam("pageToken") @Nullable String marker); + + /** + * @see OperationApi#listAtMarker(String, org.jclouds.googlecompute.options.ListOptions) + */ + @GET + @Path("/operations") + @OAuthScopes(COMPUTE_READONLY_SCOPE) + @Consumes(MediaType.APPLICATION_JSON) + @ResponseParser(ParseOperations.class) + @Fallback(EmptyIterableWithMarkerOnNotFoundOr404.class) + ListenableFuture> listAtMarker(@QueryParam("pageToken") @Nullable String marker, + ListOptions listOptions); + + /** + * @see OperationApi#list() + */ + @GET + @Path("/operations") + @OAuthScopes(COMPUTE_READONLY_SCOPE) + @Consumes(MediaType.APPLICATION_JSON) + @ResponseParser(ParseOperations.class) + @Transform(ParseOperations.ToPagedIterable.class) + @Fallback(EmptyPagedIterableOnNotFoundOr404.class) + ListenableFuture> list(); + + /** + * @see OperationApi#list(org.jclouds.googlecompute.options.ListOptions) + */ + @GET + @Path("/operations") + @OAuthScopes(COMPUTE_READONLY_SCOPE) + @Consumes(MediaType.APPLICATION_JSON) + @ResponseParser(ParseOperations.class) + @Transform(ParseOperations.ToPagedIterable.class) + @Fallback(EmptyPagedIterableOnNotFoundOr404.class) + ListenableFuture> list(ListOptions listOptions); + +} diff --git a/labs/google-compute/src/main/java/org/jclouds/googlecompute/features/ProjectApi.java b/labs/google-compute/src/main/java/org/jclouds/googlecompute/features/ProjectApi.java new file mode 100644 index 0000000000..f3c5192836 --- /dev/null +++ b/labs/google-compute/src/main/java/org/jclouds/googlecompute/features/ProjectApi.java @@ -0,0 +1,67 @@ +/* + * 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.googlecompute.features; + +import org.jclouds.concurrent.Timeout; +import org.jclouds.googlecompute.domain.Operation; +import org.jclouds.googlecompute.domain.Project; + +import java.util.Map; +import java.util.concurrent.TimeUnit; + +/** + * Provides synchronous access to Projects via their REST API. + *

+ * + * @author David Alves + * @see ProjectAsyncApi + * @see + */ +@Timeout(duration = 60, timeUnit = TimeUnit.SECONDS) +public interface ProjectApi { + + /** + * Returns the specified project resource. + * + * @param projectName name of the project to return + * @return if successful, this method returns a Project resource + */ + Project get(String projectName); + + /** + * Sets metadata common to all instances within the specified project using the data included in the request. + *

+ * NOTE: This *sets* metadata items on the project (vs *adding* items to metadata), + * if there are pre-existing metadata items that must be kept these must be fetched first and then re-set on the + * new Metadata, e.g. + *


+    *    Metadata.Builder current = projectApi.get("myProject").getCommonInstanceMetadata().toBuilder();
+    *    current.addItem("newItem","newItemValue");
+    *    projectApi.setCommonInstanceMetadata(current.build());
+    * 
+ * + * @param projectName name of the project to return + * @param commonInstanceMetadata the metadata to set + * @return an Operations resource. To check on the status of an operation, poll the Operations resource returned + * to you, and look for the status field. + */ + Operation setCommonInstanceMetadata(String projectName, Map commonInstanceMetadata); + +} diff --git a/labs/google-compute/src/main/java/org/jclouds/googlecompute/features/ProjectAsyncApi.java b/labs/google-compute/src/main/java/org/jclouds/googlecompute/features/ProjectAsyncApi.java new file mode 100644 index 0000000000..0194ca1ed7 --- /dev/null +++ b/labs/google-compute/src/main/java/org/jclouds/googlecompute/features/ProjectAsyncApi.java @@ -0,0 +1,78 @@ +/* + * 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.googlecompute.features; + +import com.google.common.util.concurrent.ListenableFuture; +import org.jclouds.googlecompute.domain.Operation; +import org.jclouds.googlecompute.domain.Project; +import org.jclouds.googlecompute.handlers.MetadataBinder; +import org.jclouds.oauth.v2.config.OAuthScopes; +import org.jclouds.oauth.v2.filters.OAuthAuthenticator; +import org.jclouds.rest.annotations.BinderParam; +import org.jclouds.rest.annotations.Fallback; +import org.jclouds.rest.annotations.RequestFilters; +import org.jclouds.rest.annotations.SkipEncoding; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import java.util.Map; + +import static org.jclouds.Fallbacks.NullOnNotFoundOr404; +import static org.jclouds.googlecompute.GoogleComputeConstants.COMPUTE_READONLY_SCOPE; +import static org.jclouds.googlecompute.GoogleComputeConstants.COMPUTE_SCOPE; + +/** + * Provides asynchronous access to Projects via their REST API. + * + * @author David Alves + * @see ProjectApi + */ +@SkipEncoding({'/', '='}) +@RequestFilters(OAuthAuthenticator.class) +public interface ProjectAsyncApi { + + + /** + * @see ProjectApi#get(String) + */ + @GET + @OAuthScopes(COMPUTE_READONLY_SCOPE) + @Consumes(MediaType.APPLICATION_JSON) + @Fallback(NullOnNotFoundOr404.class) + @Path("/projects/{project}") + ListenableFuture get(@PathParam("project") String projectName); + + /** + * @see ProjectApi#setCommonInstanceMetadata(String, java.util.Map) + */ + @POST + @Path("/projects/{project}/setCommonInstanceMetadata") + @OAuthScopes(COMPUTE_SCOPE) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + ListenableFuture setCommonInstanceMetadata(@PathParam("project") String projectName, + @BinderParam(MetadataBinder.class) + Map commonInstanceMetadata); +} diff --git a/labs/google-compute/src/main/java/org/jclouds/googlecompute/features/ZoneApi.java b/labs/google-compute/src/main/java/org/jclouds/googlecompute/features/ZoneApi.java new file mode 100644 index 0000000000..c505ddaf33 --- /dev/null +++ b/labs/google-compute/src/main/java/org/jclouds/googlecompute/features/ZoneApi.java @@ -0,0 +1,86 @@ +/* + * 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.googlecompute.features; + +import org.jclouds.collect.PagedIterable; +import org.jclouds.concurrent.Timeout; +import org.jclouds.googlecompute.domain.ListPage; +import org.jclouds.googlecompute.domain.Zone; +import org.jclouds.googlecompute.options.ListOptions; +import org.jclouds.javax.annotation.Nullable; + +import java.util.concurrent.TimeUnit; + +/** + * Provides synchronous access to Zones via their REST API. + *

+ * + * @author David Alves + * @see + */ +@Timeout(duration = 60, timeUnit = TimeUnit.SECONDS) +public interface ZoneApi { + + /** + * Returns the specified zone resource + * + * @param zoneName name of the zone resource to return. + * @return If successful, this method returns a Zone resource + */ + Zone get(String zoneName); + + /** + * @see ZoneApi#listAtMarker(String, org.jclouds.googlecompute.options.ListOptions) + */ + ListPage listFirstPage(); + + /** + * @see ZoneApi#listAtMarker(String, org.jclouds.googlecompute.options.ListOptions) + */ + ListPage listAtMarker(@Nullable String marker); + + /** + * Retrieves the listFirstPage of zone resources available to the specified project. + * By default the listFirstPage as a maximum size of 100, if no options are provided or ListOptions#getMaxResults() + * has not been set. + * + * @param marker marks the beginning of the next list page + * @param listOptions listing options + * @return a page of the listFirstPage + * @see ListOptions + * @see ListPage + */ + ListPage listAtMarker(@Nullable String marker, ListOptions listOptions); + + /** + * @see ZoneApi#list(org.jclouds.googlecompute.options.ListOptions) + */ + PagedIterable list(); + + /** + * A paged version of ZoneApi#listFirstPage() + * + * @return a Paged, Fluent Iterable that is able to fetch additional pages when required + * @see ZoneApi#listAtMarker(String, org.jclouds.googlecompute.options.ListOptions) + * @see PagedIterable + */ + PagedIterable list(ListOptions listOptions); + +} diff --git a/labs/google-compute/src/main/java/org/jclouds/googlecompute/features/ZoneAsyncApi.java b/labs/google-compute/src/main/java/org/jclouds/googlecompute/features/ZoneAsyncApi.java new file mode 100644 index 0000000000..5b4adeec3c --- /dev/null +++ b/labs/google-compute/src/main/java/org/jclouds/googlecompute/features/ZoneAsyncApi.java @@ -0,0 +1,118 @@ +/* + * 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.googlecompute.features; + +import com.google.common.util.concurrent.ListenableFuture; +import org.jclouds.collect.PagedIterable; +import org.jclouds.googlecompute.domain.ListPage; +import org.jclouds.googlecompute.domain.Zone; +import org.jclouds.googlecompute.functions.internal.ParseZones; +import org.jclouds.googlecompute.options.ListOptions; +import org.jclouds.oauth.v2.config.OAuthScopes; +import org.jclouds.oauth.v2.filters.OAuthAuthenticator; +import org.jclouds.rest.annotations.Fallback; +import org.jclouds.rest.annotations.RequestFilters; +import org.jclouds.rest.annotations.ResponseParser; +import org.jclouds.rest.annotations.SkipEncoding; +import org.jclouds.rest.annotations.Transform; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.core.MediaType; + +import static org.jclouds.Fallbacks.EmptyIterableWithMarkerOnNotFoundOr404; +import static org.jclouds.Fallbacks.EmptyPagedIterableOnNotFoundOr404; +import static org.jclouds.Fallbacks.NullOnNotFoundOr404; +import static org.jclouds.googlecompute.GoogleComputeConstants.COMPUTE_READONLY_SCOPE; + +/** + * Provides asynchronous access to Zones via their REST API. + * + * @author David Alves + * @see ZoneApi + */ +@SkipEncoding({'/', '='}) +@RequestFilters(OAuthAuthenticator.class) +@Consumes(MediaType.APPLICATION_JSON) +public interface ZoneAsyncApi { + + /** + * @see ZoneApi#get(String) + */ + @GET + @Path("/zones/{zone}") + @OAuthScopes(COMPUTE_READONLY_SCOPE) + @Fallback(NullOnNotFoundOr404.class) + ListenableFuture get(@PathParam("zone") String zoneName); + + /** + * @see ZoneApi#listFirstPage() + */ + @GET + @Path("/zones") + @OAuthScopes(COMPUTE_READONLY_SCOPE) + @ResponseParser(ParseZones.class) + @Fallback(EmptyIterableWithMarkerOnNotFoundOr404.class) + ListenableFuture> listFirstPage(); + + /** + * @see ZoneApi#listAtMarker(String) + */ + @GET + @Path("/zones") + @OAuthScopes(COMPUTE_READONLY_SCOPE) + @ResponseParser(ParseZones.class) + @Fallback(EmptyIterableWithMarkerOnNotFoundOr404.class) + ListenableFuture> listAtMarker(String marker); + + /** + * @see ZoneApi#listAtMarker(String, org.jclouds.googlecompute.options.ListOptions) + */ + @GET + @Path("/zones") + @OAuthScopes(COMPUTE_READONLY_SCOPE) + @ResponseParser(ParseZones.class) + @Fallback(EmptyIterableWithMarkerOnNotFoundOr404.class) + ListenableFuture> listAtMarker(String marker, ListOptions listOptions); + + /** + * @see ZoneApi#list() + */ + @GET + @Path("/zones") + @OAuthScopes(COMPUTE_READONLY_SCOPE) + @ResponseParser(ParseZones.class) + @Transform(ParseZones.ToPagedIterable.class) + @Fallback(EmptyPagedIterableOnNotFoundOr404.class) + ListenableFuture> list(); + + /** + * @see ZoneApi#list(org.jclouds.googlecompute.options.ListOptions) + */ + @GET + @Path("/zones") + @OAuthScopes(COMPUTE_READONLY_SCOPE) + @ResponseParser(ParseZones.class) + @Transform(ParseZones.ToPagedIterable.class) + @Fallback(EmptyPagedIterableOnNotFoundOr404.class) + ListenableFuture> list(ListOptions listOptions); +} diff --git a/labs/google-compute/src/main/java/org/jclouds/googlecompute/functions/internal/BaseToPagedIterable.java b/labs/google-compute/src/main/java/org/jclouds/googlecompute/functions/internal/BaseToPagedIterable.java new file mode 100644 index 0000000000..84fd30fbb7 --- /dev/null +++ b/labs/google-compute/src/main/java/org/jclouds/googlecompute/functions/internal/BaseToPagedIterable.java @@ -0,0 +1,75 @@ +/* + * 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.googlecompute.functions.internal; + +import com.google.common.annotations.Beta; +import com.google.common.base.Function; +import com.google.common.base.Optional; +import com.google.common.base.Predicates; +import com.google.common.collect.Iterables; +import org.jclouds.collect.IterableWithMarker; +import org.jclouds.collect.PagedIterable; +import org.jclouds.collect.PagedIterables; +import org.jclouds.googlecompute.domain.ListPage; +import org.jclouds.googlecompute.options.ListOptions; +import org.jclouds.http.HttpRequest; +import org.jclouds.rest.InvocationContext; +import org.jclouds.rest.internal.GeneratedHttpRequest; + +import java.util.Arrays; + +/** + * @author Adrian Cole + */ +@Beta +public abstract class BaseToPagedIterable> implements + Function, PagedIterable>, InvocationContext { + + private GeneratedHttpRequest request; + + @Override + public PagedIterable apply(ListPage input) { + if (input.nextMarker() == null) + return PagedIterables.of(input); + + Optional project = Iterables.tryFind(Arrays.asList(request.getCaller().get().getArgs()), + Predicates.instanceOf(String.class)); + + Optional listOptions = Iterables.tryFind(request.getArgs(), + Predicates.instanceOf(ListOptions.class)); + + assert project.isPresent() : String.format("programming error, method %s should have a string param for the " + + "project", request.getCaller().get().getMethod()); + + return PagedIterables.advance(input, fetchNextPage(project.get().toString(), (String) input.nextMarker().orNull(), + (ListOptions) listOptions.orNull())); + } + + + protected abstract Function> fetchNextPage(String projectName, String marker, + ListOptions listOptions); + + @SuppressWarnings("unchecked") + @Override + public I setContext(HttpRequest request) { + this.request = GeneratedHttpRequest.class.cast(request); + return (I) this; + } +} diff --git a/labs/google-compute/src/main/java/org/jclouds/googlecompute/functions/internal/ParseOperations.java b/labs/google-compute/src/main/java/org/jclouds/googlecompute/functions/internal/ParseOperations.java new file mode 100644 index 0000000000..6f9c67258a --- /dev/null +++ b/labs/google-compute/src/main/java/org/jclouds/googlecompute/functions/internal/ParseOperations.java @@ -0,0 +1,68 @@ +/* + * 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.googlecompute.functions.internal; + +import com.google.common.base.Function; +import com.google.inject.TypeLiteral; +import org.jclouds.collect.IterableWithMarker; +import org.jclouds.googlecompute.GoogleComputeApi; +import org.jclouds.googlecompute.domain.ListPage; +import org.jclouds.googlecompute.domain.Operation; +import org.jclouds.googlecompute.options.ListOptions; +import org.jclouds.http.functions.ParseJson; +import org.jclouds.json.Json; + +import javax.inject.Inject; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * @author David Alves + */ +public class ParseOperations extends ParseJson> { + + @Inject + public ParseOperations(Json json) { + super(json, new TypeLiteral>() {}); + } + + public static class ToPagedIterable extends BaseToPagedIterable { + + private final GoogleComputeApi api; + + @Inject + protected ToPagedIterable(GoogleComputeApi api) { + this.api = checkNotNull(api, "api"); + } + + @Override + protected Function> fetchNextPage(final String projectName, + final String marker, + final ListOptions options) { + return new Function>() { + + @Override + public IterableWithMarker apply(Object input) { + return api.getOperationApiForProject(projectName).listAtMarker(marker, options); + } + }; + } + } +} diff --git a/labs/google-compute/src/main/java/org/jclouds/googlecompute/functions/internal/ParseZones.java b/labs/google-compute/src/main/java/org/jclouds/googlecompute/functions/internal/ParseZones.java new file mode 100644 index 0000000000..06f5d309dd --- /dev/null +++ b/labs/google-compute/src/main/java/org/jclouds/googlecompute/functions/internal/ParseZones.java @@ -0,0 +1,67 @@ +/* + * 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.googlecompute.functions.internal; + +import com.google.common.base.Function; +import com.google.inject.TypeLiteral; +import org.jclouds.collect.IterableWithMarker; +import org.jclouds.googlecompute.GoogleComputeApi; +import org.jclouds.googlecompute.domain.ListPage; +import org.jclouds.googlecompute.domain.Zone; +import org.jclouds.googlecompute.options.ListOptions; +import org.jclouds.http.functions.ParseJson; +import org.jclouds.json.Json; + +import javax.inject.Inject; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * @author David Alves + */ +public class ParseZones extends ParseJson> { + + @Inject + public ParseZones(Json json) { + super(json, new TypeLiteral>() {}); + } + + public static class ToPagedIterable extends BaseToPagedIterable { + + private final GoogleComputeApi api; + + @Inject + protected ToPagedIterable(GoogleComputeApi api) { + this.api = checkNotNull(api, "api"); + } + + @Override + protected Function> fetchNextPage(final String projectName, final String marker, + final ListOptions options) { + return new Function>() { + + @Override + public IterableWithMarker apply(Object input) { + return api.getZoneApiForProject(projectName).listAtMarker(marker, options); + } + }; + } + } +} diff --git a/labs/google-compute/src/main/java/org/jclouds/googlecompute/handlers/GoogleComputeErrorHandler.java b/labs/google-compute/src/main/java/org/jclouds/googlecompute/handlers/GoogleComputeErrorHandler.java new file mode 100644 index 0000000000..108751d11d --- /dev/null +++ b/labs/google-compute/src/main/java/org/jclouds/googlecompute/handlers/GoogleComputeErrorHandler.java @@ -0,0 +1,66 @@ +/** + * 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.googlecompute.handlers; + +import org.jclouds.http.HttpCommand; +import org.jclouds.http.HttpErrorHandler; +import org.jclouds.http.HttpResponse; +import org.jclouds.http.HttpResponseException; +import org.jclouds.rest.AuthorizationException; +import org.jclouds.rest.ResourceNotFoundException; + +import javax.inject.Singleton; + +import static org.jclouds.http.HttpUtils.closeClientButKeepContentStream; + +/** + * This will parse and set an appropriate exception on the command object. + * + * @author Adrian Cole + */ +@Singleton +public class GoogleComputeErrorHandler implements HttpErrorHandler { + public void handleError(HttpCommand command, HttpResponse response) { + // it is important to always read fully and close streams + byte[] data = closeClientButKeepContentStream(response); + String message = data != null ? new String(data) : null; + + Exception exception = message != null ? new HttpResponseException(command, response, message) + : new HttpResponseException(command, response); + message = message != null ? message : String.format("%s -> %s", command.getCurrentRequest().getRequestLine(), + response.getStatusLine()); + switch (response.getStatusCode()) { + case 400: + break; + case 401: + case 403: + exception = new AuthorizationException(message, exception); + break; + case 404: + if (!command.getCurrentRequest().getMethod().equals("DELETE")) { + exception = new ResourceNotFoundException(message, exception); + } + break; + case 409: + exception = new IllegalStateException(message, exception); + break; + } + command.setException(exception); + } +} diff --git a/labs/google-compute/src/main/java/org/jclouds/googlecompute/handlers/MetadataBinder.java b/labs/google-compute/src/main/java/org/jclouds/googlecompute/handlers/MetadataBinder.java new file mode 100644 index 0000000000..79a7e96e79 --- /dev/null +++ b/labs/google-compute/src/main/java/org/jclouds/googlecompute/handlers/MetadataBinder.java @@ -0,0 +1,46 @@ +/* + * 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.googlecompute.handlers; + +import org.jclouds.googlecompute.config.GoogleComputeParserModule; +import org.jclouds.http.HttpRequest; +import org.jclouds.rest.Binder; +import org.jclouds.rest.binders.BindToJsonPayload; + +import javax.inject.Inject; +import java.util.Map; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * @author David Alves + */ +public class MetadataBinder implements Binder { + + @Inject + private BindToJsonPayload jsonBinder; + + @Override + @SuppressWarnings("unchecked") + public R bindToRequest(R request, Object input) { + Map metadataEntries = (Map) checkNotNull(input, "input metadata"); + return jsonBinder.bindToRequest(request, new GoogleComputeParserModule.Metadata(metadataEntries)); + } +} diff --git a/labs/google-compute/src/main/java/org/jclouds/googlecompute/options/ListOptions.java b/labs/google-compute/src/main/java/org/jclouds/googlecompute/options/ListOptions.java new file mode 100644 index 0000000000..80e7b6d0f0 --- /dev/null +++ b/labs/google-compute/src/main/java/org/jclouds/googlecompute/options/ListOptions.java @@ -0,0 +1,95 @@ +/* + * 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.googlecompute.options; + +import org.jclouds.http.options.BaseHttpRequestOptions; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Allows to optionally specify a filter, max results and a page token for listFirstPage() REST methods. + * + * @author David Alves + * @see + */ +public class ListOptions extends BaseHttpRequestOptions { + + /** + * Optional. Filter expression for filtering listed resources, in the form filter={expression}. Your {expression} + * must contain the following: + *

+ * {@code } + *

    + *
  • {@code }: The name of the field you want to compare. The field name must be valid for the + * type of resource being filtered. Only atomic field types are supported (string, number, + * boolean). Array and object fields are not currently supported.
  • + *
  • {@code }: The comparison string, either eq (equals) or ne (not equals).
  • + *
  • {@code }: The literal string value to filter to. The literal value must be valid + * for the type of field (string, number, boolean). For string fields, the literal value is interpreted as a + * regular expression using RE2 syntax. The literal value must match the entire field. For example, + * when filtering instances, name eq my_instance won't work, but name eq .*my_instance will work.
  • + *
+ *

+ * For example: + *

+ * {@code filter=status ne RUNNING} + *

+ * The above filter returns only results whose status field does not equal RUNNING. You can also enclose your + * literal string in single, double, or no quotes. For example, all three of the following would be valid + * expressions: + *

+ * {@code filter=status ne "RUNNING"}
+ * {@code filter=status ne 'RUNNING'}
+ * {@code filter=status ne RUNNING}
+ *

+ * Complex regular expressions can also be used, like the following: + * {@code name eq '."my_instance_[0-9]+'} + */ + public ListOptions filter(String filter) { + this.queryParameters.put("filter", checkNotNull(filter, "filter")); + return this; + } + + /** + * Sets Maximum count of results to be returned. Maximum and default value is 100. Acceptable values are 0 to + * 100, inclusive. (Default: 100) + */ + public ListOptions maxResults(Integer maxResults) { + this.queryParameters.put("maxResults", checkNotNull(maxResults, "maxResults") + ""); + return this; + } + + public static class Builder { + + /** + * @see ListOptions#filter(String) + */ + public ListOptions filter(String filter) { + return new ListOptions().filter(filter); + } + + /** + * @see ListOptions#maxResults(Integer) + */ + public ListOptions maxResults(Integer maxResults) { + return new ListOptions().maxResults(maxResults); + } + } +} diff --git a/labs/google-compute/src/main/java/org/jclouds/googlecompute/predicates/OperationDonePredicate.java b/labs/google-compute/src/main/java/org/jclouds/googlecompute/predicates/OperationDonePredicate.java new file mode 100644 index 0000000000..3836ae5990 --- /dev/null +++ b/labs/google-compute/src/main/java/org/jclouds/googlecompute/predicates/OperationDonePredicate.java @@ -0,0 +1,61 @@ +/* + * 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.googlecompute.predicates; + +import com.google.common.base.Predicate; +import com.google.inject.Inject; +import org.jclouds.googlecompute.GoogleComputeApi; +import org.jclouds.googlecompute.config.UserProject; +import org.jclouds.googlecompute.domain.Operation; +import org.jclouds.googlecompute.features.OperationApi; + +import java.util.concurrent.atomic.AtomicReference; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Tests that an Operation is done, returning the completed Operation when it is. + * + * @author David Alves + */ +public class OperationDonePredicate implements Predicate> { + + private OperationApi api; + + @Inject + OperationDonePredicate(GoogleComputeApi api, @UserProject String project) { + this.api = api.getOperationApiForProject(project); + } + + @Override + public boolean apply(AtomicReference input) { + checkNotNull(input, "input"); + Operation current = api.get(input.get().getName()); + switch (current.getStatus()) { + case DONE: + input.set(current); + return true; + case PENDING: + case RUNNING: + default: + return false; + } + } +} diff --git a/labs/google-compute/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata b/labs/google-compute/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata new file mode 100644 index 0000000000..27ee158885 --- /dev/null +++ b/labs/google-compute/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata @@ -0,0 +1 @@ +org.jclouds.googlecompute.GoogleComputeApiMetadata diff --git a/labs/google-compute/src/test/java/org/jclouds/googlecompute/GoogleComputeApiMetadataTest.java b/labs/google-compute/src/test/java/org/jclouds/googlecompute/GoogleComputeApiMetadataTest.java new file mode 100644 index 0000000000..f27cfb6690 --- /dev/null +++ b/labs/google-compute/src/test/java/org/jclouds/googlecompute/GoogleComputeApiMetadataTest.java @@ -0,0 +1,41 @@ +/** + * 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.googlecompute; + +import com.google.common.collect.ImmutableSet; +import com.google.common.reflect.TypeToken; +import org.jclouds.View; +import org.jclouds.apis.internal.BaseApiMetadataTest; +import org.testng.annotations.Test; + +/** + * Tests that GoogleComputeApiMetadata is properly registered in ServiceLoader + *

+ *

+ * META-INF/services/org.jclouds.apis.ApiMetadata
+ * 
+ * + * @author Adrian Cole + */ +@Test(groups = "unit", testName = "GoogleComputeApiMetadataTest") +public class GoogleComputeApiMetadataTest extends BaseApiMetadataTest { + public GoogleComputeApiMetadataTest() { + super(new GoogleComputeApiMetadata(), ImmutableSet.>of()); + } +} diff --git a/labs/google-compute/src/test/java/org/jclouds/googlecompute/GoogleComputeAuthenticatedRestContextLiveTest.java b/labs/google-compute/src/test/java/org/jclouds/googlecompute/GoogleComputeAuthenticatedRestContextLiveTest.java new file mode 100644 index 0000000000..09e5668dbb --- /dev/null +++ b/labs/google-compute/src/test/java/org/jclouds/googlecompute/GoogleComputeAuthenticatedRestContextLiveTest.java @@ -0,0 +1,48 @@ +/* + * 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.googlecompute; + +import com.google.common.reflect.TypeToken; +import org.jclouds.oauth.v2.BaseOauthAuthenticatedRestContextLiveTest; +import org.jclouds.rest.RestContext; +import org.testng.annotations.Test; + +/** + * @author David Alves + */ +@Test(groups = "live") +public class GoogleComputeAuthenticatedRestContextLiveTest extends BaseOauthAuthenticatedRestContextLiveTest { + + + public GoogleComputeAuthenticatedRestContextLiveTest() { + provider = "google-compute"; + } + + @Override + public String getScopes() { + return GoogleComputeConstants.COMPUTE_SCOPE; + } + + + @Override + protected TypeToken> contextType() { + return GoogleComputeApiMetadata.CONTEXT_TOKEN; + } +} diff --git a/labs/google-compute/src/test/java/org/jclouds/googlecompute/features/OperationApiExpectTest.java b/labs/google-compute/src/test/java/org/jclouds/googlecompute/features/OperationApiExpectTest.java new file mode 100644 index 0000000000..bb79d29062 --- /dev/null +++ b/labs/google-compute/src/test/java/org/jclouds/googlecompute/features/OperationApiExpectTest.java @@ -0,0 +1,170 @@ +/* + * 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.googlecompute.features; + +import org.jclouds.googlecompute.GoogleComputeApi; +import org.jclouds.googlecompute.internal.BaseGoogleComputeExpectTest; +import org.jclouds.googlecompute.options.ListOptions; +import org.jclouds.googlecompute.parse.ParseOperationListTest; +import org.jclouds.googlecompute.parse.ParseOperationTest; +import org.jclouds.http.HttpRequest; +import org.jclouds.http.HttpResponse; +import org.testng.annotations.Test; + +import static org.jclouds.googlecompute.GoogleComputeConstants.COMPUTE_READONLY_SCOPE; +import static org.jclouds.googlecompute.GoogleComputeConstants.COMPUTE_SCOPE; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNull; +import static org.testng.Assert.assertTrue; + +/** + * @author David Alves + */ +@Test(groups = "unit") +public class OperationApiExpectTest extends BaseGoogleComputeExpectTest { + + private static final String OPERATIONS_URL_PREFIX = "https://www.googleapis" + + ".com/compute/v1beta13/projects/myproject/operations"; + + public void testGetOperationResponseIs2xx() throws Exception { + HttpRequest get = HttpRequest + .builder() + .method("GET") + .endpoint(OPERATIONS_URL_PREFIX + "/operation-1352178598164-4cdcc9d031510-4aa46279") + .addHeader("Accept", "application/json") + .addHeader("Authorization", "Bearer " + TOKEN).build(); + + HttpResponse operationResponse = HttpResponse.builder().statusCode(200) + .payload(payloadFromResource("/operation.json")).build(); + + OperationApi operationApi = requestsSendResponses(requestForScopes(COMPUTE_READONLY_SCOPE), + TOKEN_RESPONSE, get, operationResponse).getOperationApiForProject("myproject"); + + assertEquals(operationApi.get("operation-1352178598164-4cdcc9d031510-4aa46279"), + new ParseOperationTest().expected()); + } + + public void testGetOperationResponseIs4xx() throws Exception { + HttpRequest get = HttpRequest + .builder() + .method("GET") + .endpoint(OPERATIONS_URL_PREFIX + "/operation-1352178598164-4cdcc9d031510-4aa46279") + .addHeader("Accept", "application/json") + .addHeader("Authorization", "Bearer " + TOKEN).build(); + + HttpResponse operationResponse = HttpResponse.builder().statusCode(404).build(); + + OperationApi operationApi = requestsSendResponses(requestForScopes(COMPUTE_READONLY_SCOPE), + TOKEN_RESPONSE, get, operationResponse).getOperationApiForProject("myproject"); + + assertNull(operationApi.get("operation-1352178598164-4cdcc9d031510-4aa46279")); + } + + public void testDeleteOperationResponseIs2xx() throws Exception { + HttpRequest delete = HttpRequest + .builder() + .method("DELETE") + .endpoint(OPERATIONS_URL_PREFIX + "/operation-1352178598164-4cdcc9d031510-4aa46279") + .addHeader("Authorization", "Bearer " + TOKEN).build(); + + HttpResponse operationResponse = HttpResponse.builder().statusCode(204).build(); + + OperationApi operationApi = requestsSendResponses(requestForScopes(COMPUTE_SCOPE), + TOKEN_RESPONSE, delete, operationResponse).getOperationApiForProject("myproject"); + + operationApi.delete("operation-1352178598164-4cdcc9d031510-4aa46279"); + } + + public void testDeleteOperationResponseIs4xx() throws Exception { + HttpRequest delete = HttpRequest + .builder() + .method("DELETE") + .endpoint(OPERATIONS_URL_PREFIX + "/operation-1352178598164-4cdcc9d031510-4aa46279") + .addHeader("Authorization", "Bearer " + TOKEN).build(); + + HttpResponse operationResponse = HttpResponse.builder().statusCode(404).build(); + + OperationApi operationApi = requestsSendResponses(requestForScopes(COMPUTE_SCOPE), + TOKEN_RESPONSE, delete, operationResponse).getOperationApiForProject("myproject"); + + operationApi.delete("operation-1352178598164-4cdcc9d031510-4aa46279"); + } + + public void testLisOperationWithNoOptionsResponseIs2xx() { + HttpRequest get = HttpRequest + .builder() + .method("GET") + .endpoint(OPERATIONS_URL_PREFIX) + .addHeader("Accept", "application/json") + .addHeader("Authorization", "Bearer " + TOKEN).build(); + + HttpResponse operationResponse = HttpResponse.builder().statusCode(200) + .payload(payloadFromResource("/operation_list.json")).build(); + + OperationApi operationApi = requestsSendResponses(requestForScopes(COMPUTE_READONLY_SCOPE), + TOKEN_RESPONSE, get, operationResponse).getOperationApiForProject("myproject"); + + assertEquals(operationApi.listFirstPage().toString(), + new ParseOperationListTest().expected().toString()); + } + + public void testListOperationWithPaginationOptionsResponseIs2xx() { + HttpRequest get = HttpRequest + .builder() + .method("GET") + .endpoint(OPERATIONS_URL_PREFIX + + "?pageToken=CglPUEVSQVRJT04SOzU5MDQyMTQ4Nzg1Mi5vcG" + + "VyYXRpb24tMTM1MjI0NDI1ODAzMC00Y2RkYmU2YTJkNmIwLWVkMzIyMzQz&" + + "filter=" + + "status%20eq%20done&" + + "maxResults=3") + .addHeader("Accept", "application/json") + .addHeader("Authorization", "Bearer " + TOKEN).build(); + + HttpResponse operationResponse = HttpResponse.builder().statusCode(200) + .payload(payloadFromResource("/operation_list.json")).build(); + + OperationApi operationApi = requestsSendResponses(requestForScopes(COMPUTE_READONLY_SCOPE), + TOKEN_RESPONSE, get, operationResponse).getOperationApiForProject("myproject"); + + assertEquals(operationApi.listAtMarker("CglPUEVSQVRJT04SOzU5MDQyMTQ4Nzg1Mi5vcGVyYXRpb24tMTM1Mj" + + "I0NDI1ODAzMC00Y2RkYmU2YTJkNmIwLWVkMzIyMzQz", + new ListOptions.Builder().filter("status eq done").maxResults(3)).toString(), + new ParseOperationListTest().expected().toString()); + } + + public void testListOperationWithPaginationOptionsResponseIs4xx() { + HttpRequest get = HttpRequest + .builder() + .method("GET") + .endpoint(OPERATIONS_URL_PREFIX) + .addHeader("Accept", "application/json") + .addHeader("Authorization", "Bearer " + TOKEN).build(); + + HttpResponse operationResponse = HttpResponse.builder().statusCode(404).build(); + + OperationApi operationApi = requestsSendResponses(requestForScopes(COMPUTE_READONLY_SCOPE), + TOKEN_RESPONSE, get, operationResponse).getOperationApiForProject("myproject"); + + assertTrue(operationApi.list().concat().isEmpty()); + } + + +} diff --git a/labs/google-compute/src/test/java/org/jclouds/googlecompute/features/OperationApiLiveTest.java b/labs/google-compute/src/test/java/org/jclouds/googlecompute/features/OperationApiLiveTest.java new file mode 100644 index 0000000000..a1e6c81196 --- /dev/null +++ b/labs/google-compute/src/test/java/org/jclouds/googlecompute/features/OperationApiLiveTest.java @@ -0,0 +1,96 @@ +/* + * 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.googlecompute.features; + +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; +import org.jclouds.collect.IterableWithMarker; +import org.jclouds.collect.PagedIterable; +import org.jclouds.googlecompute.domain.Operation; +import org.jclouds.googlecompute.internal.BaseGoogleComputeApiLiveTest; +import org.jclouds.googlecompute.options.ListOptions; +import org.testng.annotations.Test; + +import java.util.concurrent.atomic.AtomicInteger; + +import static org.jclouds.googlecompute.features.ProjectApiLiveTest.addItemToMetadata; +import static org.jclouds.googlecompute.features.ProjectApiLiveTest.deleteItemFromMetadata; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; + +/** + * @author David Alves + */ +public class OperationApiLiveTest extends BaseGoogleComputeApiLiveTest { + + private static final String METADATA_ITEM_KEY = "operationLiveTestTestProp"; + private static final String METADATA_ITEM_VALUE = "operationLiveTestTestValue"; + private Operation addOperation; + private Operation deleteOperation; + + private OperationApi api() { + return context.getApi().getOperationApiForProject(getUserProject()); + } + + + @Test(groups = "live") + public void testCreateOperations() { + //create some operations by adding and deleting metadata items + // this will make sure there is stuff to listFirstPage + addOperation = assertOperationDoneSucessfully(addItemToMetadata(context.getApi().getProjectApi(), + getUserProject(), METADATA_ITEM_KEY, METADATA_ITEM_VALUE), 20); + deleteOperation = assertOperationDoneSucessfully(deleteItemFromMetadata(context.getApi() + .getProjectApi(), getUserProject(), METADATA_ITEM_KEY), 20); + + assertNotNull(addOperation); + assertNotNull(deleteOperation); + } + + @Test(groups = "live", dependsOnMethods = "testCreateOperations") + public void testGetOperation() { + Operation operation = api().get(addOperation.getName()); + assertNotNull(operation); + assertOperationEquals(operation, this.addOperation); + } + + @Test(groups = "live", dependsOnMethods = "testCreateOperations") + public void testListOperationsWithFiltersAndPagination() { + PagedIterable operations = api().list(new ListOptions.Builder() + .filter("operationType eq setMetadata") + .maxResults(1)); + + // make sure that in spite of having only one result per page we get at least two results + final AtomicInteger counter = new AtomicInteger(); + operations.firstMatch(new Predicate>() { + + @Override + public boolean apply(IterableWithMarker input) { + counter.addAndGet(Iterables.size(input)); + return counter.get() == 2; + } + }); + } + + private void assertOperationEquals(Operation result, Operation expected) { + assertEquals(result.getName(), expected.getName()); + } + + +} diff --git a/labs/google-compute/src/test/java/org/jclouds/googlecompute/features/ProjectApiExpectTest.java b/labs/google-compute/src/test/java/org/jclouds/googlecompute/features/ProjectApiExpectTest.java new file mode 100644 index 0000000000..43db7fcaf0 --- /dev/null +++ b/labs/google-compute/src/test/java/org/jclouds/googlecompute/features/ProjectApiExpectTest.java @@ -0,0 +1,102 @@ +/* + * 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.googlecompute.features; + +import org.jclouds.googlecompute.GoogleComputeApi; +import org.jclouds.googlecompute.GoogleComputeConstants; +import org.jclouds.googlecompute.internal.BaseGoogleComputeExpectTest; +import org.jclouds.googlecompute.parse.ParseMetadataTest; +import org.jclouds.googlecompute.parse.ParseOperationTest; +import org.jclouds.googlecompute.parse.ParseProjectTest; +import org.jclouds.http.HttpRequest; +import org.jclouds.http.HttpResponse; +import org.testng.annotations.Test; + +import javax.ws.rs.core.MediaType; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNull; + + +/** + * @author David Alves + */ +@Test(groups = "unit") +public class ProjectApiExpectTest extends BaseGoogleComputeExpectTest { + + public static final String PROJECTS_URL_PREFIX = "https://www.googleapis.com/compute/v1beta13/projects"; + + public void testGetProjectResponseIs2xx() throws Exception { + HttpRequest getProjectRequest = HttpRequest + .builder() + .method("GET") + .endpoint(PROJECTS_URL_PREFIX + "/myproject") + .addHeader("Accept", "application/json") + .addHeader("Authorization", "Bearer " + TOKEN).build(); + + HttpResponse getProjectResponse = HttpResponse.builder().statusCode(200) + .payload(payloadFromResource("/project.json")).build(); + + ProjectApi api = requestsSendResponses(requestForScopes(GoogleComputeConstants.COMPUTE_READONLY_SCOPE), + TOKEN_RESPONSE, getProjectRequest, + getProjectResponse).getProjectApi(); + + assertEquals(api.get("myproject"), new ParseProjectTest().expected()); + } + + public void testGetProjectResponseIs4xx() throws Exception { + HttpRequest getProjectRequest = HttpRequest + .builder() + .method("GET") + .endpoint(PROJECTS_URL_PREFIX + "/myproject") + .addHeader("Accept", "application/json") + .addHeader("Authorization", "Bearer " + TOKEN).build(); + + HttpResponse getProjectResponse = HttpResponse.builder().statusCode(404).build(); + + ProjectApi api = requestsSendResponses(requestForScopes(GoogleComputeConstants.COMPUTE_READONLY_SCOPE), + TOKEN_RESPONSE, getProjectRequest, + getProjectResponse).getProjectApi(); + + assertNull(api.get("myproject")); + } + + public void testSetCommonInstanceMetadata() { + HttpRequest setMetadata = HttpRequest + .builder() + .method("POST") + .endpoint(PROJECTS_URL_PREFIX + "/myproject/setCommonInstanceMetadata") + .addHeader("Accept", "application/json") + .addHeader("Authorization", "Bearer " + TOKEN) + .payload(payloadFromResourceWithContentType("/metadata.json", MediaType.APPLICATION_JSON)) + .build(); + + HttpResponse setMetadataResponse = HttpResponse.builder().statusCode(200) + .payload(payloadFromResource("/operation.json")).build(); + + ProjectApi api = requestsSendResponses(requestForScopes(GoogleComputeConstants.COMPUTE_SCOPE), + TOKEN_RESPONSE, setMetadata, + setMetadataResponse).getProjectApi(); + + assertEquals(api.setCommonInstanceMetadata("myproject", new ParseMetadataTest().expected()), + new ParseOperationTest().expected()); + } + +} diff --git a/labs/google-compute/src/test/java/org/jclouds/googlecompute/features/ProjectApiLiveTest.java b/labs/google-compute/src/test/java/org/jclouds/googlecompute/features/ProjectApiLiveTest.java new file mode 100644 index 0000000000..99bec90c67 --- /dev/null +++ b/labs/google-compute/src/test/java/org/jclouds/googlecompute/features/ProjectApiLiveTest.java @@ -0,0 +1,123 @@ +/* + * 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.googlecompute.features; + +/** + * @author David Alves + */ + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Maps; +import org.jclouds.googlecompute.domain.Operation; +import org.jclouds.googlecompute.domain.Project; +import org.jclouds.googlecompute.internal.BaseGoogleComputeApiLiveTest; +import org.testng.annotations.Test; + +import static com.google.common.base.Predicates.equalTo; +import static com.google.common.base.Predicates.not; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertNull; +import static org.testng.Assert.assertSame; +import static org.testng.Assert.assertTrue; + +/** + * @author David Alves + */ +public class ProjectApiLiveTest extends BaseGoogleComputeApiLiveTest { + + private static final String METADATA_ITEM_KEY = "projectLiveTestTestProp"; + private static final String METADATA_ITEM_VALUE = "projectLiveTestTestValue"; + + private ProjectApi projectApi() { + return context.getApi().getProjectApi(); + } + + private Project project; + private int initialMetadataSize; + + @Test(groups = "live") + public void testGetProjectWhenExists() { + this.project = projectApi().get(getUserProject()); + assertNotNull(project); + assertNotNull(project.getId()); + assertNotNull(project.getName()); + } + + @Test(groups = "live") + public void testGetProjectWhenNotExists() { + Project project = projectApi().get("momma"); + assertNull(project); + } + + @Test(groups = "live", dependsOnMethods = "testGetProjectWhenExists") + public void addItemToMetadata() { + this.initialMetadataSize = project.getCommonInstanceMetadata().size(); + assertOperationDoneSucessfully(addItemToMetadata(projectApi(), getUserProject(), METADATA_ITEM_KEY, + METADATA_ITEM_VALUE), 20); + this.project = projectApi().get(getUserProject()); + assertNotNull(project); + assertTrue(this.project.getCommonInstanceMetadata().containsKey(METADATA_ITEM_KEY), + this.project.toString()); + assertEquals(this.project.getCommonInstanceMetadata().get(METADATA_ITEM_KEY), + METADATA_ITEM_VALUE); + } + + @Test(groups = "live", dependsOnMethods = "addItemToMetadata") + public void testDeleteItemFromMetadata() { + assertOperationDoneSucessfully(deleteItemFromMetadata(projectApi(), getUserProject(), METADATA_ITEM_KEY), 20); + this.project = projectApi().get(getUserProject()); + assertNotNull(project); + assertFalse(project.getCommonInstanceMetadata().containsKey(METADATA_ITEM_KEY)); + assertSame(this.project.getCommonInstanceMetadata().size(), initialMetadataSize); + } + + /** + * Adds an item to the Project's metadata + *

+ * Beyond it's use here it is also used as a cheap way of generating Operations to both test the OperationApi and + * the pagination system. + */ + public static Operation addItemToMetadata(ProjectApi projectApi, String projectName, String key, String value) { + Project project = projectApi.get(projectName); + assertNotNull(project); + ImmutableMap.Builder metadataBuilder = ImmutableMap.builder(); + metadataBuilder.putAll(project.getCommonInstanceMetadata()); + metadataBuilder.put(key, value); + return projectApi.setCommonInstanceMetadata(projectName, metadataBuilder.build()); + } + + /** + * Deletes an item from the Project's metadata + *

+ * Beyond it's use here it is also used as a cheap way of generating Operation's to both test the OperationApi and + * the pagination system. + */ + public static Operation deleteItemFromMetadata(ProjectApi projectApi, String projectName, String key) { + Project project = projectApi.get(projectName); + assertNotNull(project); + ImmutableMap.Builder metadataBuilder = ImmutableMap.builder(); + metadataBuilder.putAll(Maps.filterKeys(project.getCommonInstanceMetadata(), not(equalTo(key)))); + return projectApi.setCommonInstanceMetadata(projectName, metadataBuilder.build()); + } + + +} diff --git a/labs/google-compute/src/test/java/org/jclouds/googlecompute/features/ZoneApiExpectTest.java b/labs/google-compute/src/test/java/org/jclouds/googlecompute/features/ZoneApiExpectTest.java new file mode 100644 index 0000000000..4f6bf65927 --- /dev/null +++ b/labs/google-compute/src/test/java/org/jclouds/googlecompute/features/ZoneApiExpectTest.java @@ -0,0 +1,110 @@ +/* + * 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.googlecompute.features; + +import org.jclouds.googlecompute.GoogleComputeApi; +import org.jclouds.googlecompute.internal.BaseGoogleComputeExpectTest; +import org.jclouds.googlecompute.parse.ParseZoneListTest; +import org.jclouds.googlecompute.parse.ParseZoneTest; +import org.jclouds.http.HttpRequest; +import org.jclouds.http.HttpResponse; +import org.testng.annotations.Test; + +import static org.jclouds.googlecompute.GoogleComputeConstants.COMPUTE_READONLY_SCOPE; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNull; +import static org.testng.Assert.assertTrue; + +/** + * @author David Alves + */ +@Test(groups = "unit") +public class ZoneApiExpectTest extends BaseGoogleComputeExpectTest { + + public static final String ZONES_URL_PREFIX = "https://www.googleapis.com/compute/v1beta13/projects/google/zones"; + + public void testGetZoneResponseIs2xx() throws Exception { + HttpRequest get = HttpRequest + .builder() + .method("GET") + .endpoint(ZONES_URL_PREFIX + "/us-central2-a") + .addHeader("Accept", "application/json") + .addHeader("Authorization", "Bearer " + TOKEN).build(); + + HttpResponse operationResponse = HttpResponse.builder().statusCode(200) + .payload(payloadFromResource("/zone_get.json")).build(); + + ZoneApi api = requestsSendResponses(requestForScopes(COMPUTE_READONLY_SCOPE), + TOKEN_RESPONSE, get, operationResponse).getZoneApiForProject("google"); + + assertEquals(api.get("us-central2-a"), + new ParseZoneTest().expected()); + } + + public void testGetZoneResponseIs4xx() throws Exception { + HttpRequest get = HttpRequest + .builder() + .method("GET") + .endpoint(ZONES_URL_PREFIX + "/us-central2-a") + .addHeader("Accept", "application/json") + .addHeader("Authorization", "Bearer " + TOKEN).build(); + + HttpResponse operationResponse = HttpResponse.builder().statusCode(404).build(); + + ZoneApi api = requestsSendResponses(requestForScopes(COMPUTE_READONLY_SCOPE), + TOKEN_RESPONSE, get, operationResponse).getZoneApiForProject("google"); + + assertNull(api.get("us-central2-a")); + } + + public void testListZoneNoOptionsResponseIs2xx() throws Exception { + HttpRequest list = HttpRequest + .builder() + .method("GET") + .endpoint(ZONES_URL_PREFIX) + .addHeader("Accept", "application/json") + .addHeader("Authorization", "Bearer " + TOKEN).build(); + + HttpResponse operationResponse = HttpResponse.builder().statusCode(200) + .payload(payloadFromResource("/zone_list.json")).build(); + + ZoneApi api = requestsSendResponses(requestForScopes(COMPUTE_READONLY_SCOPE), + TOKEN_RESPONSE, list, operationResponse).getZoneApiForProject("google"); + + assertEquals(api.listFirstPage().toString(), + new ParseZoneListTest().expected().toString()); + } + + public void testListZoneWithPaginationOptionsResponseIs4xx() { + HttpRequest list = HttpRequest + .builder() + .method("GET") + .endpoint(ZONES_URL_PREFIX) + .addHeader("Accept", "application/json") + .addHeader("Authorization", "Bearer " + TOKEN).build(); + + HttpResponse operationResponse = HttpResponse.builder().statusCode(404).build(); + + ZoneApi api = requestsSendResponses(requestForScopes(COMPUTE_READONLY_SCOPE), + TOKEN_RESPONSE, list, operationResponse).getZoneApiForProject("google"); + + assertTrue(api.list().concat().isEmpty()); + } +} diff --git a/labs/google-compute/src/test/java/org/jclouds/googlecompute/features/ZoneApiLiveTest.java b/labs/google-compute/src/test/java/org/jclouds/googlecompute/features/ZoneApiLiveTest.java new file mode 100644 index 0000000000..6e7b95688c --- /dev/null +++ b/labs/google-compute/src/test/java/org/jclouds/googlecompute/features/ZoneApiLiveTest.java @@ -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.googlecompute.features; + +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import org.jclouds.collect.IterableWithMarker; +import org.jclouds.collect.PagedIterable; +import org.jclouds.googlecompute.domain.Zone; +import org.jclouds.googlecompute.internal.BaseGoogleComputeApiLiveTest; +import org.jclouds.googlecompute.options.ListOptions; +import org.testng.annotations.Test; + +import java.util.Iterator; +import java.util.List; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertSame; +import static org.testng.Assert.assertTrue; + +/** + * @author David Alves + */ +public class ZoneApiLiveTest extends BaseGoogleComputeApiLiveTest { + + private Zone zone; + + private ZoneApi api() { + return context.getApi().getZoneApiForProject(getUserProject()); + } + + @Test(groups = "live") + public void testListZone() { + + PagedIterable zones = api().list(new ListOptions.Builder() + .maxResults(1)); + + Iterator> pageIterator = zones.iterator(); + assertTrue(pageIterator.hasNext()); + + IterableWithMarker singlePageIterator = pageIterator.next(); + List zoneAsList = Lists.newArrayList(singlePageIterator); + + assertSame(zoneAsList.size(), 1); + + this.zone = Iterables.getOnlyElement(zoneAsList); + } + + + @Test(groups = "live", dependsOnMethods = "testListZone") + public void testGetZone() { + Zone zone = api().get(this.zone.getName()); + assertNotNull(zone); + assertZoneEquals(zone, this.zone); + } + + private void assertZoneEquals(Zone result, Zone expected) { + assertEquals(result.getName(), expected.getName()); + } + +} diff --git a/labs/google-compute/src/test/java/org/jclouds/googlecompute/handlers/GoogleComputeErrorHandlerTest.java b/labs/google-compute/src/test/java/org/jclouds/googlecompute/handlers/GoogleComputeErrorHandlerTest.java new file mode 100644 index 0000000000..1fb6d7b799 --- /dev/null +++ b/labs/google-compute/src/test/java/org/jclouds/googlecompute/handlers/GoogleComputeErrorHandlerTest.java @@ -0,0 +1,97 @@ +/** + * 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.googlecompute.handlers; + +import org.easymock.IArgumentMatcher; +import org.jclouds.http.HttpCommand; +import org.jclouds.http.HttpRequest; +import org.jclouds.http.HttpResponse; +import org.testng.annotations.Test; + +import java.net.URI; + +import static org.easymock.EasyMock.createMock; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.replay; +import static org.easymock.EasyMock.reportMatcher; +import static org.easymock.EasyMock.verify; + +/** + * @author Adrian Cole + */ +@Test(groups = "unit", testName = "GoogleComputeErrorHandlerTest") +public class GoogleComputeErrorHandlerTest { + + @Test + public void test409MakesIllegalStateException() { + assertCodeMakes( + "POST", + URI.create("https://www.googleapis.com/compute/v1beta13"), + 409, + "HTTP/1.1 409 Conflict", + "\"{\"code\":\"InvalidState\",\"message\":\"An incompatible transition has already been queued for this" + + " resource\"}\"", + IllegalStateException.class); + } + + private void assertCodeMakes(String method, URI uri, int statusCode, String message, String content, + Class expected) { + assertCodeMakes(method, uri, statusCode, message, "application/json", content, expected); + } + + private void assertCodeMakes(String method, URI uri, int statusCode, String message, String contentType, + String content, Class expected) { + + GoogleComputeErrorHandler function = new GoogleComputeErrorHandler(); + + HttpCommand command = createMock(HttpCommand.class); + HttpRequest request = HttpRequest.builder().method(method).endpoint(uri).build(); + HttpResponse response = HttpResponse.builder().statusCode(statusCode).message(message).payload(content).build(); + response.getPayload().getContentMetadata().setContentType(contentType); + + expect(command.getCurrentRequest()).andReturn(request).atLeastOnce(); + command.setException(classEq(expected)); + + replay(command); + + function.handleError(command, response); + + verify(command); + } + + public static Exception classEq(final Class in) { + reportMatcher(new IArgumentMatcher() { + + @Override + public void appendTo(StringBuffer buffer) { + buffer.append("classEq("); + buffer.append(in); + buffer.append(")"); + } + + @Override + public boolean matches(Object arg) { + return arg.getClass() == in; + } + + }); + return null; + } + +} diff --git a/labs/google-compute/src/test/java/org/jclouds/googlecompute/internal/BaseGoogleComputeApiExpectTest.java b/labs/google-compute/src/test/java/org/jclouds/googlecompute/internal/BaseGoogleComputeApiExpectTest.java new file mode 100644 index 0000000000..906bc5e522 --- /dev/null +++ b/labs/google-compute/src/test/java/org/jclouds/googlecompute/internal/BaseGoogleComputeApiExpectTest.java @@ -0,0 +1,28 @@ +/** + * 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.googlecompute.internal; + +import org.jclouds.googlecompute.GoogleComputeApi; + +/** + * @author Adrian Cole + */ +public class BaseGoogleComputeApiExpectTest extends BaseGoogleComputeExpectTest { + +} diff --git a/labs/google-compute/src/test/java/org/jclouds/googlecompute/internal/BaseGoogleComputeApiLiveTest.java b/labs/google-compute/src/test/java/org/jclouds/googlecompute/internal/BaseGoogleComputeApiLiveTest.java new file mode 100644 index 0000000000..5ed1cb1637 --- /dev/null +++ b/labs/google-compute/src/test/java/org/jclouds/googlecompute/internal/BaseGoogleComputeApiLiveTest.java @@ -0,0 +1,132 @@ +/** + * 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.googlecompute.internal; + +import com.google.common.base.Predicate; +import com.google.common.reflect.TypeToken; +import com.google.inject.Key; +import com.google.inject.TypeLiteral; +import org.jclouds.apis.BaseContextLiveTest; +import org.jclouds.googlecompute.GoogleComputeApi; +import org.jclouds.googlecompute.GoogleComputeApiMetadata; +import org.jclouds.googlecompute.GoogleComputeAsyncApi; +import org.jclouds.googlecompute.config.UserProject; +import org.jclouds.googlecompute.domain.Operation; +import org.jclouds.oauth.v2.OAuthTestUtils; +import org.jclouds.predicates.Retryables; +import org.jclouds.rest.RestContext; + +import java.net.URI; +import java.util.Properties; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; + +import static org.testng.Assert.assertEquals; +import static org.testng.AssertJUnit.assertTrue; + + +/** + * @author David Alves + */ +public class BaseGoogleComputeApiLiveTest extends BaseContextLiveTest> { + + protected static final String API_URL_PREFIX = "https://www.googleapis.com/compute/v1beta13/projects/"; + protected static final String ZONE_API_URL_SUFFIX = "/zones/"; + protected static final String DEFAULT_ZONE_NAME = "us-east1-a"; + + protected static final String NETWORK_API_URL_SUFFIX = "/networks/"; + protected static final String DEFAULT_NETWORK_NAME = "default"; + + protected static final String MACHINE_TYPE_API_URL_SUFFIX = "/machineTypes/"; + protected static final String DEFAULT_MACHINE_TYPE_NAME = "n1-standard-1"; + + protected static final String GOOGLE_PROJECT = "google"; + + + public BaseGoogleComputeApiLiveTest() { + provider = "google-compute"; + } + + @Override + protected Properties setupProperties() { + Properties properties = super.setupProperties(); + OAuthTestUtils.setCredentialFromPemFile(properties, "google-compute.credential"); + return properties; + } + + @Override + protected TypeToken> contextType() { + return GoogleComputeApiMetadata.CONTEXT_TOKEN; + } + + protected String getUserProject() { + return context.utils().injector().getInstance(Key.get(String.class, UserProject.class)); + } + + protected Predicate> getOperationDonePredicate() { + return context.utils().getInjector().getInstance(Key.get(new TypeLiteral>> + () {})); + } + + protected Operation assertOperationDoneSucessfully(Operation operation, long maxWaitSeconds) { + operation = waitOperationDone(operation, maxWaitSeconds); + assertEquals(operation.getStatus(), Operation.Status.DONE); + assertTrue(operation.getErrors().isEmpty()); + return operation; + } + + protected Operation waitOperationDone(Operation operation, long maxWaitSeconds) { + return waitOperationDone(getOperationDonePredicate(), operation, maxWaitSeconds); + } + + protected URI getDefaultZoneUrl(String project) { + return getZoneUrl(project, DEFAULT_ZONE_NAME); + } + + protected URI getZoneUrl(String project, String zone) { + return URI.create(API_URL_PREFIX + project + ZONE_API_URL_SUFFIX + zone); + } + + protected URI getDefaultNetworkUrl(String project) { + return getNetworkUrl(project, DEFAULT_NETWORK_NAME); + } + + protected URI getNetworkUrl(String project, String network) { + return URI.create(API_URL_PREFIX + project + NETWORK_API_URL_SUFFIX + network); + } + + protected URI getDefaultMachineTypekUrl(String project) { + return gettMachineTypeUrl(project, DEFAULT_MACHINE_TYPE_NAME); + } + + protected URI gettMachineTypeUrl(String project, String machineType) { + return URI.create(API_URL_PREFIX + project + MACHINE_TYPE_API_URL_SUFFIX + machineType); + } + + protected static Operation waitOperationDone(Predicate> operationDonePredicate, + Operation operation, long maxWaitSeconds) { + AtomicReference operationReference = new AtomicReference(operation); + Retryables.retry(operationDonePredicate, operationReference, maxWaitSeconds, 1, TimeUnit.SECONDS); + return operationReference.get(); + } + + +} + diff --git a/labs/google-compute/src/test/java/org/jclouds/googlecompute/internal/BaseGoogleComputeAsyncApiExpectTest.java b/labs/google-compute/src/test/java/org/jclouds/googlecompute/internal/BaseGoogleComputeAsyncApiExpectTest.java new file mode 100644 index 0000000000..ae928afd2e --- /dev/null +++ b/labs/google-compute/src/test/java/org/jclouds/googlecompute/internal/BaseGoogleComputeAsyncApiExpectTest.java @@ -0,0 +1,36 @@ +/** + * 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.googlecompute.internal; + +import com.google.common.base.Function; +import com.google.inject.Module; +import org.jclouds.googlecompute.GoogleComputeAsyncApi; +import org.jclouds.http.HttpRequest; +import org.jclouds.http.HttpResponse; + +import java.util.Properties; + +/** + * @author Adrian Cole + */ +public class BaseGoogleComputeAsyncApiExpectTest extends BaseGoogleComputeExpectTest { + public GoogleComputeAsyncApi createClient(Function fn, Module module, Properties props) { + return createInjector(fn, module, props).getInstance(GoogleComputeAsyncApi.class); + } +} diff --git a/labs/google-compute/src/test/java/org/jclouds/googlecompute/internal/BaseGoogleComputeExpectTest.java b/labs/google-compute/src/test/java/org/jclouds/googlecompute/internal/BaseGoogleComputeExpectTest.java new file mode 100644 index 0000000000..a0d5d8748a --- /dev/null +++ b/labs/google-compute/src/test/java/org/jclouds/googlecompute/internal/BaseGoogleComputeExpectTest.java @@ -0,0 +1,117 @@ +/** + * 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.googlecompute.internal; + +import com.google.common.base.Joiner; +import com.google.common.base.Ticker; +import com.google.inject.Binder; +import com.google.inject.Module; +import org.jclouds.collect.PagedIterable; +import org.jclouds.collect.PagedIterables; +import org.jclouds.googlecompute.domain.ListPage; +import org.jclouds.http.HttpRequest; +import org.jclouds.http.HttpResponse; +import org.jclouds.oauth.v2.OAuthConstants; +import org.jclouds.rest.internal.BaseRestApiExpectTest; + +import javax.ws.rs.core.MediaType; +import java.net.URI; +import java.nio.charset.Charset; +import java.util.Properties; + +import static org.jclouds.crypto.CryptoStreams.base64Url; + +/** + * @author Adrian Cole + */ +public class BaseGoogleComputeExpectTest extends BaseRestApiExpectTest { + + private static final String header = "{\"alg\":\"none\",\"typ\":\"JWT\"}"; + + private static final String CLAIMS_TEMPLATE = "{" + + "\"iss\":\"identity\"," + + "\"scope\":\"%s\"," + + "\"aud\":\"https://accounts.google.com/o/oauth2/token\"," + + "\"exp\":3600," + + "\"iat\":0}"; + + protected static final String TOKEN = "1/8xbJqaOZXSUZbHLl5EOtu1pxz3fmmetKx9W8CV4t79M"; + + protected static final HttpResponse TOKEN_RESPONSE = HttpResponse.builder().statusCode(200).payload( + payloadFromString("{\n" + + " \"access_token\" : \"" + TOKEN + "\",\n" + + " \"token_type\" : \"Bearer\",\n" + + " \"expires_in\" : 3600\n" + + "}")).build(); + + + public BaseGoogleComputeExpectTest() { + provider = "google-compute"; + } + + @Override + protected Module createModule() { + return new Module() { + @Override + public void configure(Binder binder) { + binder.bind(Ticker.class).toInstance(new Ticker() { + @Override + public long read() { + return 0; + } + }); + } + }; + } + + @Override + protected Properties setupProperties() { + Properties props = super.setupProperties(); + // use no sig algorithm for expect tests (means no credential is required either) + props.put("jclouds.oauth.signature-or-mac-algorithm", OAuthConstants.NO_ALGORITHM); + return props; + } + + protected HttpRequest requestForScopes(String... scopes) { + String claims = String.format(CLAIMS_TEMPLATE, Joiner.on(",").join(scopes)); + + String payload = "grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&" + + // Base64 Encoded Header + "assertion=" + base64Url(header.getBytes(Charset.forName("UTF-8"))) + "." + + // Base64 Encoded Claims + base64Url(claims.getBytes(Charset.forName("UTF-8"))) + "."; + + return HttpRequest.builder() + .method("POST") + .endpoint(URI.create("https://accounts.google.com/o/oauth2/token")) + .addHeader("Accept", MediaType.APPLICATION_JSON) + .payload(payloadFromStringWithContentType(payload, "application/x-www-form-urlencoded")) + .build(); + } + + /** + * Parse tests don't apply @Transform so we need to apply the transformation to PagedIterable on the result of + * expected() + */ + protected PagedIterable toPagedIterable(ListPage list) { + return PagedIterables.of(list); + } + + +} diff --git a/labs/google-compute/src/test/java/org/jclouds/googlecompute/internal/BaseGoogleComputeParseTest.java b/labs/google-compute/src/test/java/org/jclouds/googlecompute/internal/BaseGoogleComputeParseTest.java new file mode 100644 index 0000000000..b6f1657a07 --- /dev/null +++ b/labs/google-compute/src/test/java/org/jclouds/googlecompute/internal/BaseGoogleComputeParseTest.java @@ -0,0 +1,38 @@ +/* + * 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.googlecompute.internal; + +import com.google.inject.Guice; +import com.google.inject.Injector; +import org.jclouds.googlecompute.config.GoogleComputeParserModule; +import org.jclouds.json.BaseItemParserTest; +import org.jclouds.json.config.GsonModule; + +/** + * @author David Alves + */ +public abstract class BaseGoogleComputeParseTest extends BaseItemParserTest { + + @Override + protected Injector injector() { + return Guice.createInjector(new GsonModule(), new GoogleComputeParserModule()); + } + +} diff --git a/labs/google-compute/src/test/java/org/jclouds/googlecompute/parse/ParseMetadataTest.java b/labs/google-compute/src/test/java/org/jclouds/googlecompute/parse/ParseMetadataTest.java new file mode 100644 index 0000000000..3dcbc272b5 --- /dev/null +++ b/labs/google-compute/src/test/java/org/jclouds/googlecompute/parse/ParseMetadataTest.java @@ -0,0 +1,50 @@ +/* + * 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.googlecompute.parse; + +import com.google.common.collect.ImmutableMap; +import org.jclouds.googlecompute.config.GoogleComputeParserModule; +import org.jclouds.googlecompute.internal.BaseGoogleComputeParseTest; +import org.testng.annotations.Test; + +import javax.ws.rs.Consumes; +import javax.ws.rs.core.MediaType; + +/** + * @author David Alves + */ +@Test(groups = "unit") +public class ParseMetadataTest extends BaseGoogleComputeParseTest { + + @Override + public String resource() { + return "/metadata.json"; + } + + @Override + @Consumes(MediaType.APPLICATION_JSON) + public GoogleComputeParserModule.Metadata expected() { + return new GoogleComputeParserModule.Metadata( + ImmutableMap.builder() + .put("propA", "valueA") + .put("propB", "valueB") + .build()); + } +} diff --git a/labs/google-compute/src/test/java/org/jclouds/googlecompute/parse/ParseOperationListTest.java b/labs/google-compute/src/test/java/org/jclouds/googlecompute/parse/ParseOperationListTest.java new file mode 100644 index 0000000000..6eb41578f3 --- /dev/null +++ b/labs/google-compute/src/test/java/org/jclouds/googlecompute/parse/ParseOperationListTest.java @@ -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.googlecompute.parse; + +import org.jclouds.googlecompute.domain.ListPage; +import org.jclouds.googlecompute.domain.Operation; +import org.jclouds.googlecompute.domain.Resource; +import org.jclouds.googlecompute.internal.BaseGoogleComputeParseTest; + +import javax.ws.rs.Consumes; +import javax.ws.rs.core.MediaType; +import java.net.URI; + +/** + * @author David Alves + */ +public class ParseOperationListTest extends BaseGoogleComputeParseTest> { + + @Override + public String resource() { + return "/operation_list.json"; + } + + @Override + @Consumes(MediaType.APPLICATION_JSON) + public ListPage expected() { + return ListPage.builder() + .kind(Resource.Kind.OPERATION_LIST) + .id("projects/myproject/operations") + .selfLink(URI.create("https://www.googleapis.com/compute/v1beta13/projects/myproject/operations")) + .addItem(new ParseOperationTest().expected()) + .build(); + } +} diff --git a/labs/google-compute/src/test/java/org/jclouds/googlecompute/parse/ParseOperationTest.java b/labs/google-compute/src/test/java/org/jclouds/googlecompute/parse/ParseOperationTest.java new file mode 100644 index 0000000000..074cb6c604 --- /dev/null +++ b/labs/google-compute/src/test/java/org/jclouds/googlecompute/parse/ParseOperationTest.java @@ -0,0 +1,71 @@ +/* + * 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.googlecompute.parse; + +import org.jclouds.date.internal.SimpleDateFormatDateService; +import org.jclouds.googlecompute.domain.Operation; +import org.jclouds.googlecompute.internal.BaseGoogleComputeParseTest; +import org.testng.annotations.Test; + +import javax.ws.rs.Consumes; +import javax.ws.rs.core.MediaType; +import java.net.URI; + +/** + * @author David Alves + */ +@Test(groups = "unit") +public class ParseOperationTest extends BaseGoogleComputeParseTest { + + @Override + public String resource() { + return "/operation.json"; + } + + @Override + @Consumes(MediaType.APPLICATION_JSON) + public Operation expected() { + SimpleDateFormatDateService dateService = new SimpleDateFormatDateService(); + return Operation.builder().id("13053095055850848306") + .selfLink(URI.create("https://www.googleapis" + + ".com/compute/v1beta13/projects/myproject/operations/operation" + + "-1354084865060-4cf88735faeb8-bbbb12cb")) + .name("operation-1354084865060-4cf88735faeb8-bbbb12cb") + .targetLink(URI.create("https://www.googleapis" + + ".com/compute/v1beta13/projects/myproject/instances/instance-api-live" + + "-test-instance")) + .targetId("13053094017547040099") + .status(Operation.Status.DONE) + .user("user@developer.gserviceaccount.com") + .progress(100) + .insertTime(dateService.iso8601DateParse("2012-11-28T06:41:05.060")) + .startTime(dateService.iso8601DateParse("2012-11-28T06:41:05.142")) + .operationType("insert") + .httpErrorStatusCode(400) + .httpErrorMessage("BAD REQUEST") + .addError(Operation.Error.builder() + .code("RESOURCE_ALREADY_EXISTS") + .message("The resource " + + "'projects/myproject/instances/instance-api-live-test-instance' already" + + " exists") + .build()) + .build(); + } +} diff --git a/labs/google-compute/src/test/java/org/jclouds/googlecompute/parse/ParseProjectTest.java b/labs/google-compute/src/test/java/org/jclouds/googlecompute/parse/ParseProjectTest.java new file mode 100644 index 0000000000..922db730fe --- /dev/null +++ b/labs/google-compute/src/test/java/org/jclouds/googlecompute/parse/ParseProjectTest.java @@ -0,0 +1,68 @@ +/* + * 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.googlecompute.parse; + +import com.google.common.collect.ImmutableMap; +import org.jclouds.googlecompute.domain.Project; +import org.jclouds.googlecompute.internal.BaseGoogleComputeParseTest; +import org.testng.annotations.Test; + +import javax.ws.rs.Consumes; +import javax.ws.rs.core.MediaType; +import java.net.URI; +import java.util.Date; + +/** + * @author David Alves + */ +@Test(groups = "unit") +public class ParseProjectTest extends BaseGoogleComputeParseTest { + + @Override + public String resource() { + return "/project.json"; + } + + @Override + @Consumes(MediaType.APPLICATION_JSON) + public Project expected() { + return Project.builder() + .id("13024414184846275913") + .creationTimestamp(new Date(Long.parseLong("1351109596252"))) + .selfLink(URI.create("https://www.googleapis.com/compute/v1beta13/projects/myproject")) + .name("myproject") + .description("") + .commonInstanceMetadata( + ImmutableMap.builder() + .put("propA", "valueA") + .put("propB", "valueB") + .build()) + .addQuota("INSTANCES", 0, 8) + .addQuota("CPUS", 0, 8) + .addQuota("EPHEMERAL_ADDRESSES", 0, 8) + .addQuota("DISKS", 0, 8) + .addQuota("DISKS_TOTAL_GB", 0, 100) + .addQuota("SNAPSHOTS", 0, 1000) + .addQuota("NETWORKS", 1, 5) + .addQuota("FIREWALLS", 2, 100) + .addQuota("IMAGES", 0, 100) + .build(); + } +} diff --git a/labs/google-compute/src/test/java/org/jclouds/googlecompute/parse/ParseQuotaTest.java b/labs/google-compute/src/test/java/org/jclouds/googlecompute/parse/ParseQuotaTest.java new file mode 100644 index 0000000000..b8fa1c1000 --- /dev/null +++ b/labs/google-compute/src/test/java/org/jclouds/googlecompute/parse/ParseQuotaTest.java @@ -0,0 +1,45 @@ +/* + * 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.googlecompute.parse; + +import org.jclouds.googlecompute.domain.Project; +import org.jclouds.googlecompute.internal.BaseGoogleComputeParseTest; +import org.testng.annotations.Test; + +import javax.ws.rs.Consumes; +import javax.ws.rs.core.MediaType; + +/** + * @author David Alves + */ +@Test(groups = "unit") +public class ParseQuotaTest extends BaseGoogleComputeParseTest { + + @Override + public String resource() { + return "/quota.json"; + } + + @Override + @Consumes(MediaType.APPLICATION_JSON) + public Project.Quota expected() { + return Project.Quota.builder().metric("INSTANCES").usage(0.0).limit(8.0).build(); + } +} diff --git a/labs/google-compute/src/test/java/org/jclouds/googlecompute/parse/ParseZoneListTest.java b/labs/google-compute/src/test/java/org/jclouds/googlecompute/parse/ParseZoneListTest.java new file mode 100644 index 0000000000..6f270041b8 --- /dev/null +++ b/labs/google-compute/src/test/java/org/jclouds/googlecompute/parse/ParseZoneListTest.java @@ -0,0 +1,74 @@ +/* + * 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.googlecompute.parse; + +import com.google.common.collect.ImmutableSet; +import org.jclouds.date.internal.SimpleDateFormatDateService; +import org.jclouds.googlecompute.domain.ListPage; +import org.jclouds.googlecompute.domain.Resource; +import org.jclouds.googlecompute.domain.Zone; +import org.jclouds.googlecompute.internal.BaseGoogleComputeParseTest; +import org.testng.annotations.Test; + +import javax.ws.rs.Consumes; +import javax.ws.rs.core.MediaType; +import java.net.URI; + +/** + * @author David Alves + */ +@Test(groups = "unit") +public class ParseZoneListTest extends BaseGoogleComputeParseTest> { + + @Override + public String resource() { + return "/zone_list.json"; + } + + @Override + @Consumes(MediaType.APPLICATION_JSON) + public ListPage expected() { + return ListPage.builder() + .kind(Resource.Kind.ZONE_LIST) + .id("projects/google/zones") + .selfLink(URI.create("https://www.googleapis.com/compute/v1beta13/projects/google/zones")) + .items(ImmutableSet.of( + new ParseZoneTest().expected() + , Zone.builder() + .id("13024414164050619686") + .creationTimestamp(new SimpleDateFormatDateService().iso8601DateParse + ("2012-10-24T20:13:19.271")) + .selfLink(URI.create("https://www.googleapis" + + ".com/compute/v1beta13/projects/google/zones/us-east1-a")) + .name("us-east1-a") + .description("us-east1-a") + .status(Zone.Status.UP) + .addMaintenanceWindow(Zone.MaintenanceWindow.builder() + .name("2013-02-17-planned-outage") + .description("maintenance zone") + .beginTime(new SimpleDateFormatDateService().iso8601DateParse + ("2013-02-17T08:00:00.000")) + .endTime(new SimpleDateFormatDateService().iso8601DateParse + ("2013-03-03T08:00:00.000")) + .build()) + .build())) + .build(); + } +} diff --git a/labs/google-compute/src/test/java/org/jclouds/googlecompute/parse/ParseZoneTest.java b/labs/google-compute/src/test/java/org/jclouds/googlecompute/parse/ParseZoneTest.java new file mode 100644 index 0000000000..2cec9c4d6a --- /dev/null +++ b/labs/google-compute/src/test/java/org/jclouds/googlecompute/parse/ParseZoneTest.java @@ -0,0 +1,60 @@ +/* + * 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.googlecompute.parse; + +import org.jclouds.date.internal.SimpleDateFormatDateService; +import org.jclouds.googlecompute.domain.Zone; +import org.jclouds.googlecompute.internal.BaseGoogleComputeParseTest; +import org.testng.annotations.Test; + +import javax.ws.rs.Consumes; +import javax.ws.rs.core.MediaType; +import java.net.URI; + +/** + * @author David Alves + */ +@Test(groups = "unit") +public class ParseZoneTest extends BaseGoogleComputeParseTest { + + @Override + public String resource() { + return "/zone_get.json"; + } + + @Override + @Consumes(MediaType.APPLICATION_JSON) + public Zone expected() { + return Zone.builder() + .id("13020128040171887099") + .creationTimestamp(new SimpleDateFormatDateService().iso8601DateParse("2012-10-19T16:42:54.131")) + .selfLink(URI.create("https://www.googleapis.com/compute/v1beta13/projects/google/zones/us-central2-a")) + .name("us-central2-a") + .description("us-central2-a") + .status(Zone.Status.DOWN) + .addMaintenanceWindow(Zone.MaintenanceWindow.builder() + .name("2012-11-10-planned-outage") + .description("maintenance zone") + .beginTime(new SimpleDateFormatDateService().iso8601DateParse("2012-11-10T20:00:00.000")) + .endTime(new SimpleDateFormatDateService().iso8601DateParse("2012-12-02T20:00:00.000")) + .build()) + .build(); + } +} diff --git a/labs/google-compute/src/test/resources/logback.xml b/labs/google-compute/src/test/resources/logback.xml new file mode 100644 index 0000000000..dd4223c0d0 --- /dev/null +++ b/labs/google-compute/src/test/resources/logback.xml @@ -0,0 +1,38 @@ + + + + target/test-data/jclouds.log + + + %d %-5p [%c] [%thread] %m%n + + + + + target/test-data/jclouds-wire.log + + + %d %-5p [%c] [%thread] %m%n + + + + + + + + + + + + + + + + + + + + + + + diff --git a/labs/google-compute/src/test/resources/metadata.json b/labs/google-compute/src/test/resources/metadata.json new file mode 100644 index 0000000000..690133123e --- /dev/null +++ b/labs/google-compute/src/test/resources/metadata.json @@ -0,0 +1 @@ +{"kind":"compute#metadata","items":[{"key":"propA","value":"valueA"},{"key":"propB","value":"valueB"}]} \ No newline at end of file diff --git a/labs/google-compute/src/test/resources/operation.json b/labs/google-compute/src/test/resources/operation.json new file mode 100644 index 0000000000..f7f8e234f9 --- /dev/null +++ b/labs/google-compute/src/test/resources/operation.json @@ -0,0 +1,24 @@ +{ + "kind": "compute#operation", + "id": "13053095055850848306", + "selfLink": "https://www.googleapis.com/compute/v1beta13/projects/myproject/operations/operation-1354084865060-4cf88735faeb8-bbbb12cb", + "name": "operation-1354084865060-4cf88735faeb8-bbbb12cb", + "targetLink": "https://www.googleapis.com/compute/v1beta13/projects/myproject/instances/instance-api-live-test-instance", + "targetId": "13053094017547040099", + "status": "DONE", + "user": "user@developer.gserviceaccount.com", + "progress": 100, + "insertTime": "2012-11-28T06:41:05.060", + "startTime": "2012-11-28T06:41:05.142", + "httpErrorStatusCode": 400, + "httpErrorMessage": "BAD REQUEST", + "error": { + "errors": [ + { + "code": "RESOURCE_ALREADY_EXISTS", + "message": "The resource 'projects/myproject/instances/instance-api-live-test-instance' already exists" + } + ] + }, + "operationType": "insert" + } \ No newline at end of file diff --git a/labs/google-compute/src/test/resources/operation_list.json b/labs/google-compute/src/test/resources/operation_list.json new file mode 100644 index 0000000000..70765869bd --- /dev/null +++ b/labs/google-compute/src/test/resources/operation_list.json @@ -0,0 +1,31 @@ +{ + "kind": "compute#operationList", + "id": "projects/myproject/operations", + "selfLink": "https://www.googleapis.com/compute/v1beta13/projects/myproject/operations", + "items": [ + { + "kind": "compute#operation", + "id": "13053095055850848306", + "selfLink": "https://www.googleapis.com/compute/v1beta13/projects/myproject/operations/operation-1354084865060-4cf88735faeb8-bbbb12cb", + "name": "operation-1354084865060-4cf88735faeb8-bbbb12cb", + "targetLink": "https://www.googleapis.com/compute/v1beta13/projects/myproject/instances/instance-api-live-test-instance", + "targetId": "13053094017547040099", + "status": "DONE", + "user": "user@developer.gserviceaccount.com", + "progress": 100, + "insertTime": "2012-11-28T06:41:05.060", + "startTime": "2012-11-28T06:41:05.142", + "httpErrorStatusCode": 400, + "httpErrorMessage": "BAD REQUEST", + "error": { + "errors": [ + { + "code": "RESOURCE_ALREADY_EXISTS", + "message": "The resource 'projects/myproject/instances/instance-api-live-test-instance' already exists" + } + ] + }, + "operationType": "insert" + } + ] +} \ No newline at end of file diff --git a/labs/google-compute/src/test/resources/project.json b/labs/google-compute/src/test/resources/project.json new file mode 100644 index 0000000000..6129ac4768 --- /dev/null +++ b/labs/google-compute/src/test/resources/project.json @@ -0,0 +1,68 @@ +{ + "kind": "compute#project", + "id": "13024414184846275913", + "creationTimestamp": "2012-10-24T20:13:16.252", + "selfLink": "https://www.googleapis.com/compute/v1beta13/projects/myproject", + "name": "myproject", + "description": "", + "commonInstanceMetadata": { + "kind": "compute#metadata", + "items": [ + { + "key": "propA", + "value": "valueA" + }, + { + "key": "propB", + "value": "valueB" + } + ] + }, + "quotas": [ + { + "metric": "INSTANCES", + "usage": 0, + "limit": 8 + }, + { + "metric": "CPUS", + "usage": 0, + "limit": 8 + }, + { + "metric": "EPHEMERAL_ADDRESSES", + "usage": 0, + "limit": 8 + }, + { + "metric": "DISKS", + "usage": 0, + "limit": 8 + }, + { + "metric": "DISKS_TOTAL_GB", + "usage": 0, + "limit": 100 + }, + { + "metric": "SNAPSHOTS", + "usage": 0, + "limit": 1000 + }, + { + "metric": "NETWORKS", + "usage": 1, + "limit": 5 + }, + { + "metric": "FIREWALLS", + "usage": 2, + "limit": 100 + }, + { + "metric": "IMAGES", + "usage": 0, + "limit": 100 + } + ] +} \ No newline at end of file diff --git a/labs/google-compute/src/test/resources/quota.json b/labs/google-compute/src/test/resources/quota.json new file mode 100644 index 0000000000..b631ab33dd --- /dev/null +++ b/labs/google-compute/src/test/resources/quota.json @@ -0,0 +1,5 @@ +{ + "metric": "INSTANCES", + "usage": 0, + "limit": 8 +} \ No newline at end of file diff --git a/labs/google-compute/src/test/resources/zone_get.json b/labs/google-compute/src/test/resources/zone_get.json new file mode 100644 index 0000000000..919a5bd7df --- /dev/null +++ b/labs/google-compute/src/test/resources/zone_get.json @@ -0,0 +1,17 @@ +{ + "kind": "compute#zone", + "id": "13020128040171887099", + "creationTimestamp": "2012-10-19T16:42:54.131", + "selfLink": "https://www.googleapis.com/compute/v1beta13/projects/google/zones/us-central2-a", + "name": "us-central2-a", + "description": "us-central2-a", + "status": "DOWN", + "maintenanceWindows": [ + { + "name": "2012-11-10-planned-outage", + "description": "maintenance zone", + "beginTime": "2012-11-10T20:00:00.000", + "endTime": "2012-12-02T20:00:00.000" + } + ] +} \ No newline at end of file diff --git a/labs/google-compute/src/test/resources/zone_list.json b/labs/google-compute/src/test/resources/zone_list.json new file mode 100644 index 0000000000..ae67048426 --- /dev/null +++ b/labs/google-compute/src/test/resources/zone_list.json @@ -0,0 +1,41 @@ +{ + "kind": "compute#zoneList", + "id": "projects/google/zones", + "selfLink": "https://www.googleapis.com/compute/v1beta13/projects/google/zones", + "items": [ + { + "kind": "compute#zone", + "id": "13020128040171887099", + "creationTimestamp": "2012-10-19T16:42:54.131", + "selfLink": "https://www.googleapis.com/compute/v1beta13/projects/google/zones/us-central2-a", + "name": "us-central2-a", + "description": "us-central2-a", + "status": "DOWN", + "maintenanceWindows": [ + { + "name": "2012-11-10-planned-outage", + "description": "maintenance zone", + "beginTime": "2012-11-10T20:00:00.000", + "endTime": "2012-12-02T20:00:00.000" + } + ] + }, + { + "kind": "compute#zone", + "id": "13024414164050619686", + "creationTimestamp": "2012-10-24T20:13:19.271", + "selfLink": "https://www.googleapis.com/compute/v1beta13/projects/google/zones/us-east1-a", + "name": "us-east1-a", + "description": "us-east1-a", + "status": "UP", + "maintenanceWindows": [ + { + "name": "2013-02-17-planned-outage", + "description": "maintenance zone", + "beginTime": "2013-02-17T08:00:00.000", + "endTime": "2013-03-03T08:00:00.000" + } + ] + } + ] +} \ No newline at end of file diff --git a/labs/pom.xml b/labs/pom.xml index 54d5ef2f2e..bcb3046938 100644 --- a/labs/pom.xml +++ b/labs/pom.xml @@ -45,6 +45,7 @@ cdmi joyent-cloudapi joyentcloud + google-compute greenqloud-compute greenqloud-storage iam