Skip to content

Commit

Permalink
refactor(gce): Add a wrapper around compute.instanceTemplates() (#3790)
Browse files Browse the repository at this point in the history
* refactor(gce): introduce a wrapper around compute.instanceTemplates()

* refactor(gce): Refactor the GCE client wrapper to not execute requests immediately

This will allow for batch requests (in a future commit)

* test(gce): Add tests for the two GoogleServerGroupManagers implementations.

* Add copyright headers to the clouddriver/google/compute/* files.
  • Loading branch information
plumpy authored and ezimanyi committed Jun 17, 2019
1 parent ebbe104 commit 8d33935
Show file tree
Hide file tree
Showing 27 changed files with 1,326 additions and 213 deletions.
Original file line number Diff line number Diff line change
@@ -1,84 +1,99 @@
/*
* Copyright 2019 Google, Inc.
*
* Licensed 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 com.netflix.spinnaker.clouddriver.google.compute;

import com.google.api.client.googleapis.services.AbstractGoogleClientRequest;
import com.google.api.services.compute.ComputeRequest;
import com.google.api.services.compute.model.InstanceGroupManager;
import com.google.api.services.compute.model.Operation;
import com.google.common.collect.ImmutableList;
import com.netflix.spectator.api.Registry;
import com.netflix.spinnaker.clouddriver.google.GoogleExecutor;
import com.netflix.spinnaker.clouddriver.google.security.AccountForClient;
import com.netflix.spinnaker.clouddriver.google.compute.GoogleComputeOperationRequestImpl.OperationWaiter;
import com.netflix.spinnaker.clouddriver.google.deploy.GoogleOperationPoller;
import com.netflix.spinnaker.clouddriver.google.security.GoogleNamedAccountCredentials;
import java.io.IOException;
import java.util.List;
import java.util.Map;

public abstract class AbstractGoogleServerGroupManagers implements GoogleServerGroupManagers {
abstract class AbstractGoogleServerGroupManagers implements GoogleServerGroupManagers {

private final GoogleNamedAccountCredentials credentials;
private final GoogleOperationPoller poller;
private final Registry registry;
private final String instanceGroupName;

AbstractGoogleServerGroupManagers(
GoogleNamedAccountCredentials credentials, Registry registry, String instanceGroupName) {
GoogleNamedAccountCredentials credentials,
GoogleOperationPoller poller,
Registry registry,
String instanceGroupName) {
this.credentials = credentials;
this.poller = poller;
this.registry = registry;
this.instanceGroupName = instanceGroupName;
}

@Override
public WaitableComputeOperation abandonInstances(List<String> instances) throws IOException {
return wrapOperation(timeExecute(performAbandonInstances(instances), "abandonInstances"));
public GoogleComputeOperationRequest abandonInstances(List<String> instances) throws IOException {
return wrapOperationRequest(performAbandonInstances(instances), "abandonInstances");
}

abstract ComputeRequest<Operation> performAbandonInstances(List<String> instances)
throws IOException;

@Override
public WaitableComputeOperation delete() throws IOException {
return wrapOperation(timeExecute(performDelete(), "delete"));
public GoogleComputeOperationRequest delete() throws IOException {
return wrapOperationRequest(performDelete(), "delete");
}

abstract ComputeRequest<Operation> performDelete() throws IOException;

@Override
public InstanceGroupManager get() throws IOException {
return timeExecute(performGet(), "get");
public GoogleComputeRequest<InstanceGroupManager> get() throws IOException {
return wrapRequest(performGet(), "get");
}

abstract ComputeRequest<InstanceGroupManager> performGet() throws IOException;

@Override
public WaitableComputeOperation update(InstanceGroupManager content) throws IOException {
return wrapOperation(timeExecute(performUpdate(content), "update"));
public GoogleComputeOperationRequest update(InstanceGroupManager content) throws IOException {
return wrapOperationRequest(performUpdate(content), "update");
}

abstract ComputeRequest<Operation> performUpdate(InstanceGroupManager content) throws IOException;

abstract WaitableComputeOperation wrapOperation(Operation operation);

private <T> T timeExecute(AbstractGoogleClientRequest<T> request, String api) throws IOException {
return GoogleExecutor.timeExecute(
registry,
request,
"google.api",
String.format("compute.%s.%s", getManagersType(), api),
getTimeExecuteTags(request));
private <T> GoogleComputeRequest<T> wrapRequest(ComputeRequest<T> request, String api) {
return new GoogleComputeRequestImpl<>(
request, registry, getMetricName(api), getRegionOrZoneTags());
}

private String[] getTimeExecuteTags(AbstractGoogleClientRequest<?> request) {
String account = AccountForClient.getAccount(request.getAbstractGoogleClient());
return ImmutableList.<String>builder()
.add("account")
.add(account)
.addAll(getRegionOrZoneTags())
.build()
.toArray(new String[] {});
private GoogleComputeOperationRequest wrapOperationRequest(
ComputeRequest<Operation> request, String api) {

OperationWaiter waiter = getOperationWaiter(credentials, poller);
return new GoogleComputeOperationRequestImpl(
request, registry, getMetricName(api), getRegionOrZoneTags(), waiter);
}

GoogleNamedAccountCredentials getCredentials() {
return credentials;
private String getMetricName(String api) {
return String.format("compute.%s.%s", getManagersType(), api);
}

abstract OperationWaiter getOperationWaiter(
GoogleNamedAccountCredentials credentials, GoogleOperationPoller poller);

String getProject() {
return credentials.getProject();
}
Expand All @@ -89,5 +104,5 @@ String getInstanceGroupName() {

abstract String getManagersType();

abstract List<String> getRegionOrZoneTags();
abstract Map<String, String> getRegionOrZoneTags();
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,19 @@
/*
* Copyright 2019 Google, Inc.
*
* Licensed 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 com.netflix.spinnaker.clouddriver.google.compute;

import com.netflix.spectator.api.Registry;
Expand All @@ -8,24 +24,27 @@
import org.springframework.stereotype.Service;

@Service
public class GoogleServerGroupManagersFactory {
public class GoogleComputeApiFactory {

private final GoogleOperationPoller operationPoller;
private final Registry registry;

@Autowired
public GoogleServerGroupManagersFactory(
GoogleOperationPoller operationPoller, Registry registry) {
public GoogleComputeApiFactory(GoogleOperationPoller operationPoller, Registry registry) {
this.operationPoller = operationPoller;
this.registry = registry;
}

public GoogleServerGroupManagers getManagers(
public GoogleServerGroupManagers createServerGroupManagers(
GoogleNamedAccountCredentials credentials, GoogleServerGroup.View serverGroup) {
return serverGroup.getRegional()
? new RegionGoogleServerGroupManagers(
credentials, operationPoller, registry, serverGroup.getName(), serverGroup.getRegion())
: new ZoneGoogleServerGroupManagers(
credentials, operationPoller, registry, serverGroup.getName(), serverGroup.getZone());
}

public InstanceTemplates createInstanceTemplates(GoogleNamedAccountCredentials credentials) {
return new InstanceTemplates(credentials, operationPoller, registry);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright 2019 Google, Inc.
*
* Licensed 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 com.netflix.spinnaker.clouddriver.google.compute;

import com.google.api.services.compute.model.Operation;
import com.netflix.spinnaker.clouddriver.data.task.Task;
import java.io.IOException;

public interface GoogleComputeOperationRequest extends GoogleComputeRequest<Operation> {

Operation executeAndWait(Task task, String phase) throws IOException;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright 2019 Google, Inc.
*
* Licensed 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 com.netflix.spinnaker.clouddriver.google.compute;

import com.google.api.services.compute.ComputeRequest;
import com.google.api.services.compute.model.Operation;
import com.netflix.spectator.api.Registry;
import com.netflix.spinnaker.clouddriver.data.task.Task;
import java.io.IOException;
import java.util.Map;

final class GoogleComputeOperationRequestImpl extends GoogleComputeRequestImpl<Operation>
implements GoogleComputeOperationRequest {

@FunctionalInterface
interface OperationWaiter {
Operation wait(Operation operation, Task task, String phase);
}

private final OperationWaiter operationWaiter;

GoogleComputeOperationRequestImpl(
ComputeRequest<Operation> request,
Registry registry,
String metricName,
Map<String, String> tags,
OperationWaiter operationWaiter) {
super(request, registry, metricName, tags);
this.operationWaiter = operationWaiter;
}

@Override
public Operation executeAndWait(Task task, String phase) throws IOException {
return operationWaiter.wait(execute(), task, phase);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright 2019 Google, Inc.
*
* Licensed 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 com.netflix.spinnaker.clouddriver.google.compute;

import java.io.IOException;

public interface GoogleComputeRequest<T> {

T execute() throws IOException;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* Copyright 2019 Google, Inc.
*
* Licensed 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 com.netflix.spinnaker.clouddriver.google.compute;

import static java.util.stream.Collectors.toList;

import com.google.api.client.googleapis.services.AbstractGoogleClientRequest;
import com.google.api.services.compute.ComputeRequest;
import com.google.common.collect.ImmutableList;
import com.netflix.spectator.api.Registry;
import com.netflix.spinnaker.clouddriver.google.GoogleExecutor;
import com.netflix.spinnaker.clouddriver.google.security.AccountForClient;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;

class GoogleComputeRequestImpl<T> implements GoogleComputeRequest<T> {

private final ComputeRequest<T> request;
private final Registry registry;
private final String metricName;
private final Map<String, String> tags;

GoogleComputeRequestImpl(
ComputeRequest<T> request, Registry registry, String metricName, Map<String, String> tags) {
this.request = request;
this.registry = registry;
this.metricName = metricName;
this.tags = tags;
}

@Override
public T execute() throws IOException {
return timeExecute(request);
}

private T timeExecute(AbstractGoogleClientRequest<T> request) throws IOException {
return GoogleExecutor.timeExecute(
registry, request, "google.api", metricName, getTimeExecuteTags(request));
}

private String[] getTimeExecuteTags(AbstractGoogleClientRequest<?> request) {
String account = AccountForClient.getAccount(request.getAbstractGoogleClient());
return ImmutableList.<String>builder()
.add("account")
.add(account)
.addAll(flattenTags())
.build()
.toArray(new String[] {});
}

private List<String> flattenTags() {
return tags.entrySet().stream()
.flatMap(e -> Stream.of(e.getKey(), e.getValue()))
.collect(toList());
}
}
Loading

0 comments on commit 8d33935

Please sign in to comment.