Skip to content

Commit

Permalink
CV-428 Add targetLinks support for AWS
Browse files Browse the repository at this point in the history
Rework AWS adapter to use targetLinks instead of computeLinks.

Change-Id: I10169e468471c28b9ef5864d60a79b770966473d
  • Loading branch information
Jay Juch authored and Gerrit Code Review committed May 2, 2018
1 parent 7d9183c commit 2e9bf30
Show file tree
Hide file tree
Showing 2 changed files with 122 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import com.amazonaws.services.elasticloadbalancing.AmazonElasticLoadBalancingAsyncClient;
Expand Down Expand Up @@ -52,10 +53,13 @@
import com.vmware.photon.controller.model.adapters.awsadapter.util.AWSClientManagerFactory;
import com.vmware.photon.controller.model.adapters.awsadapter.util.AWSDeferredResultAsyncHandler;
import com.vmware.photon.controller.model.adapters.util.TaskManager;
import com.vmware.photon.controller.model.query.QueryUtils.QueryTop;
import com.vmware.photon.controller.model.resources.ComputeService.ComputeState;
import com.vmware.photon.controller.model.resources.LoadBalancerDescriptionService.LoadBalancerDescription;
import com.vmware.photon.controller.model.resources.LoadBalancerDescriptionService.LoadBalancerDescription.HealthCheckConfiguration;
import com.vmware.photon.controller.model.resources.LoadBalancerService.LoadBalancerState;
import com.vmware.photon.controller.model.resources.LoadBalancerService.LoadBalancerStateExpanded;
import com.vmware.photon.controller.model.resources.NetworkInterfaceService.NetworkInterfaceState;
import com.vmware.photon.controller.model.resources.NetworkService.NetworkState;
import com.vmware.photon.controller.model.resources.ResourceState;
import com.vmware.photon.controller.model.resources.SecurityGroupService.Protocol;
Expand All @@ -69,6 +73,7 @@
import com.vmware.xenon.common.StatelessService;
import com.vmware.xenon.common.UriUtils;
import com.vmware.xenon.services.common.AuthCredentialsService.AuthCredentialsServiceState;
import com.vmware.xenon.services.common.QueryTask;

/**
* Adapter for provisioning a load balancer on AWS.
Expand All @@ -87,6 +92,9 @@ private static class AWSLoadBalancerContext {
LoadBalancerStateExpanded loadBalancerStateExpanded;
AuthCredentialsServiceState credentials;

// Requested compute states to register in the load balancer.
Set<ComputeState> computesToRegister;

// Instances registered with the AWS load balancer
List<Instance> registeredInstances;
// Instances to be registered in the load balancer based on the instances that are defined
Expand Down Expand Up @@ -165,6 +173,7 @@ public void handlePatch(Operation op) {
private DeferredResult<AWSLoadBalancerContext> populateContext(AWSLoadBalancerContext context) {
return DeferredResult.completed(context)
.thenCompose(this::getLoadBalancerState)
.thenCompose(this::populateComputesToRegister)
.thenCompose(this::getCredentials)
.thenCompose(this::getAWSClient)
.thenCompose(this::getSecurityGroupState);
Expand All @@ -181,13 +190,48 @@ private DeferredResult<AWSLoadBalancerContext> getLoadBalancerState(
if (lbStateExpanded.computes == null) {
lbStateExpanded.computes = Collections.emptySet();
}
if (lbStateExpanded.targets == null) {
lbStateExpanded.targets = Collections.emptySet();
}
return lbStateExpanded;
}).thenApply(state -> {
context.loadBalancerStateExpanded = state;
return context;
});
}

/**
* Map context.loadBalancerStateExpanded.targets to context.computesToRegister
* AWS load balancer only supports computes as instances - no NICs.
* Map NICs to compute states then merge the two sets to computesToRegister
*/
private DeferredResult<AWSLoadBalancerContext> populateComputesToRegister(
AWSLoadBalancerContext context) {
// First, set compute states to context.computesToRegister
context.computesToRegister = new HashSet<ComputeState>();
Set<ComputeState> computes = context.loadBalancerStateExpanded
.getTargetsOfType(ComputeState.class);
if (computes != null && computes.size() > 0) {
context.computesToRegister.addAll(computes);
}

// Map from network interface states to compute states then add them to
// context.computesToRegister
Set<NetworkInterfaceState> nics = context.loadBalancerStateExpanded
.getTargetsOfType(NetworkInterfaceState.class);
if (nics != null && nics.size() > 0) {
List<DeferredResult<ComputeState>> getComputeStateDr = nics.stream()
.map(this::getComputeState)
.collect(Collectors.toList());
return DeferredResult.allOf(getComputeStateDr)
.thenApply(computeStates -> {
context.computesToRegister.addAll(computeStates);
return context;
});
}
return DeferredResult.completed(context);
}

private DeferredResult<AWSLoadBalancerContext> getCredentials(AWSLoadBalancerContext context) {
URI uri = createInventoryUri(this.getHost(),
context.loadBalancerStateExpanded.endpointState.authCredentialsLink);
Expand Down Expand Up @@ -543,13 +587,13 @@ private DeferredResult<AWSLoadBalancerContext> assignInstances(AWSLoadBalancerCo
// If the registered instances are null this is a newly provisioned load balancer
// so add all instances from the load balancer state to the registration request
if (context.registeredInstances == null) {
context.instanceIdsToRegister = context.loadBalancerStateExpanded.computes.stream()
context.instanceIdsToRegister = context.computesToRegister.stream()
.map(computeState -> computeState.id)
.collect(Collectors.toList());

context.instanceIdsToDeregister = Collections.emptyList();
} else {
context.instanceIdsToRegister = context.loadBalancerStateExpanded.computes.stream()
context.instanceIdsToRegister = context.computesToRegister.stream()
.map(computeState -> computeState.id)
.filter(csId -> context.registeredInstances.stream()
.noneMatch(i -> i.getInstanceId().equals(csId))
Expand All @@ -558,7 +602,7 @@ private DeferredResult<AWSLoadBalancerContext> assignInstances(AWSLoadBalancerCo

context.instanceIdsToDeregister = context.registeredInstances.stream()
.map(Instance::getInstanceId)
.filter(instanceId -> context.loadBalancerStateExpanded.computes.stream()
.filter(instanceId -> context.computesToRegister.stream()
.noneMatch(computeState -> computeState.id.equals(instanceId))
)
.collect(Collectors.toList());
Expand Down Expand Up @@ -737,4 +781,42 @@ private boolean isLBProvisionedSecurityGroup(ResourceState resource) {
return resource.customProperties != null && Boolean.TRUE.toString().equals(
resource.customProperties.get(AWSConstants.AWS_LOAD_BALANCER_SECURITY_GROUP));
}

/**
* Returns the ComputeState that is attached to the given NetworkInterfaceState.
* @param networkInterfaceState the NetworkInterfaceState to use for searching
* @return DeferredResult for ComputeState
*/
private DeferredResult<ComputeState> getComputeState(NetworkInterfaceState
networkInterfaceState) {

QueryTop<ComputeState> csqt = getComputeStateQueryOne(networkInterfaceState);

return csqt.collectDocuments(Collectors.toList())
.thenApply(computeStateList -> {
if (computeStateList == null || computeStateList.isEmpty()) {
throw new IllegalArgumentException("Did not find any compute "
+ "state with the following network interface: " +
networkInterfaceState.documentSelfLink);
}
return computeStateList.get(0);
});
}

private QueryTop<ComputeState> getComputeStateQueryOne(
NetworkInterfaceState networkInterfaceState) {

QueryTask.Query cdq = QueryTask.Query.Builder.create()
.addKindFieldClause(ComputeState.class)
.addCollectionItemClause(ComputeState.FIELD_NAME_NETWORK_INTERFACE_LINKS,
networkInterfaceState.documentSelfLink)
.build();

return new QueryTop<>(
this.getHost(),
cdq,
ComputeState.class,
networkInterfaceState.tenantLinks)
.setMaxResultsLimit(1);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.CompletionException;

import com.amazonaws.services.ec2.AmazonEC2AsyncClient;
Expand Down Expand Up @@ -64,6 +65,8 @@
import com.vmware.photon.controller.model.resources.LoadBalancerDescriptionService.LoadBalancerDescription.RouteConfiguration;
import com.vmware.photon.controller.model.resources.LoadBalancerService;
import com.vmware.photon.controller.model.resources.LoadBalancerService.LoadBalancerState;
import com.vmware.photon.controller.model.resources.NetworkInterfaceService;
import com.vmware.photon.controller.model.resources.NetworkInterfaceService.NetworkInterfaceState;
import com.vmware.photon.controller.model.resources.NetworkService;
import com.vmware.photon.controller.model.resources.NetworkService.NetworkState;
import com.vmware.photon.controller.model.resources.SecurityGroupService.SecurityGroupState;
Expand Down Expand Up @@ -96,6 +99,8 @@ public class AWSLoadBalancerServiceTest extends BaseModelTest {
private AmazonElasticLoadBalancingAsyncClient client;
private AWSSecurityGroupClient securityGroupClient;

public String imageId = EC2_LINUX_AMI;

private String lbName;
private String sgId;
private ComputeState cs1;
Expand Down Expand Up @@ -165,10 +170,10 @@ public void setUp() throws Throwable {
String vm2 = "vm2";

if (!this.isMock) {
vm1 = provisionAWSVMWithEC2Client(this.host, this.ec2client, EC2_LINUX_AMI,
vm1 = provisionAWSVMWithEC2Client(this.host, this.ec2client, this.imageId,
this.subnetId, null);
this.instancesToCleanUp.add(vm1);
vm2 = provisionAWSVMWithEC2Client(this.host, this.ec2client, EC2_LINUX_AMI,
vm2 = provisionAWSVMWithEC2Client(this.host, this.ec2client, this.imageId,
this.subnetId, null);
this.instancesToCleanUp.add(vm2);
}
Expand Down Expand Up @@ -238,8 +243,9 @@ public void testCreateUpdateDeleteLoadBalancer() throws Throwable {

// Update load balancer from 1 machines to 2 to simulate scale-out
if (!this.isMock) {
lb.computeLinks = new HashSet<>(
Arrays.asList(this.cs1.documentSelfLink, this.cs2.documentSelfLink));
lb.targetLinks = new HashSet<>(
Arrays.asList(this.cs1.documentSelfLink, this.cs2.networkInterfaceLinks.get
(0)));
putServiceSynchronously(lb.documentSelfLink, lb);
}

Expand All @@ -253,7 +259,7 @@ public void testCreateUpdateDeleteLoadBalancer() throws Throwable {
assertEquals(2, awsLoadBalancer.getInstances().size());

// Update load balancer from 2 machines to 1 to simulate scale-in
lb.computeLinks = Collections.singleton(this.cs1.documentSelfLink);
lb.targetLinks = Collections.singleton(this.cs1.documentSelfLink);
putServiceSynchronously(lb.documentSelfLink, lb);

kickOffLoadBalancerProvision(InstanceRequestType.UPDATE, lb.documentSelfLink,
Expand Down Expand Up @@ -390,7 +396,7 @@ private LoadBalancerState createLoadBalancerState(String name) throws Throwable
state.endpointLinks = new HashSet<String>();
state.endpointLinks.add(this.endpointState.documentSelfLink);
state.regionId = this.regionId;
state.computeLinks = Collections.singleton(this.cs1.documentSelfLink);
state.targetLinks = Collections.singleton(this.cs1.documentSelfLink);
state.subnetLinks = new HashSet<>();
state.subnetLinks.add(
createSubnetState(this.subnetId,
Expand Down Expand Up @@ -436,14 +442,39 @@ private ComputeState createComputeState(String id) throws Throwable {
computeDescription = postServiceSynchronously(ComputeDescriptionService.FACTORY_LINK,
computeDescription, ComputeDescription.class);

NetworkInterfaceState nicState = createNicState(this.endpointState);

ComputeState computeState = new ComputeState();
computeState.descriptionLink = computeDescription.documentSelfLink;
computeState.id = id;
computeState.networkInterfaceLinks = new ArrayList<>();
computeState.networkInterfaceLinks.add(nicState.documentSelfLink);
computeState.tenantLinks = this.endpointState.tenantLinks;

return postServiceSynchronously(ComputeService.FACTORY_LINK, computeState,
ComputeState.class);
}

public NetworkInterfaceState createNicState(EndpointState endpointState) throws Throwable {
String uniqueId = UUID.randomUUID().toString();

NetworkInterfaceState nicState = new NetworkInterfaceState();
nicState.id = uniqueId;
nicState.name = uniqueId;
nicState.deviceIndex = 0;
nicState.networkInterfaceDescriptionLink = "foo";
nicState.subnetLink = "foo";
nicState.networkLink = "foo";
nicState.tenantLinks = endpointState.tenantLinks;
nicState.endpointLink = endpointState.documentSelfLink;
nicState.endpointLinks = new HashSet<>();
nicState.endpointLinks.add(endpointState.documentSelfLink);
nicState.computeHostLink = endpointState.computeHostLink;

return postServiceSynchronously(NetworkInterfaceService.FACTORY_LINK, nicState,
NetworkInterfaceState.class);
}

private ProvisionLoadBalancerTaskState kickOffLoadBalancerProvision(
InstanceRequestType requestType, String loadBalancerLink, TaskStage expectedTaskState)
throws Throwable {
Expand Down

0 comments on commit 2e9bf30

Please sign in to comment.