Skip to content

Commit

Permalink
Merge pull request #206 from perfectsense/feature/ec2-placement-groups
Browse files Browse the repository at this point in the history
EC2 Placement Groups
  • Loading branch information
Jeremy Collins committed Feb 14, 2020
2 parents 66a65c0 + dbf81b8 commit 1d15fac
Show file tree
Hide file tree
Showing 4 changed files with 297 additions and 1 deletion.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ dependencies {

implementation 'com.google.guava:guava:23.0'
implementation 'com.psddev:dari-util:3.3.607-xe0f27a'
implementation enforcedPlatform('software.amazon.awssdk:bom:2.5.70')
implementation enforcedPlatform('software.amazon.awssdk:bom:2.10.61')
implementation 'software.amazon.awssdk:apache-client'
implementation 'software.amazon.awssdk:autoscaling'
implementation 'software.amazon.awssdk:cloudfront'
Expand Down
5 changes: 5 additions & 0 deletions examples/ec2/placement-group.gyro
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
aws::placement-group example-placement-group
name: "TestGroup"
placement-strategy: "partition"
partition-count: 3
end
88 changes: 88 additions & 0 deletions src/main/java/gyro/aws/ec2/PlacementGroupFinder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
* Copyright 2020, Perfect Sense, 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 gyro.aws.ec2;

import gyro.core.Type;
import gyro.core.finder.Filter;
import software.amazon.awssdk.services.ec2.Ec2Client;
import software.amazon.awssdk.services.ec2.model.PlacementGroup;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
* Query placement groups.
*
* Example
* -------
*
* .. code-block:: gyro
*
* pg: $(external-query aws::placement-group {name: "placement-group-example", state: "available", strategy: "spread" })
*/
@Type("placement-group")
public class PlacementGroupFinder extends Ec2TaggableAwsFinder<Ec2Client, PlacementGroup, PlacementGroupResource> {

private String name;
private String state;
private String strategy;

/**
* The name of the Placement Group.
*/
@Filter("group-name")
public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

/**
* The state of the Placement Group. Valid values are ``pending``, ``available``, ``deleting`` or ``deleted``.
*/
public String getState() {
return state;
}

public void setState(String state) {
this.state = state;
}

/**
* Approaches towards managing the placement of instances on the underlying hardware. Valid values are ``cluster``, ``spread`` or ``partition``.
*/
public String getStrategy() {
return strategy;
}

public void setStrategy(String strategy) {
this.strategy = strategy;
}

@Override
protected List<PlacementGroup> findAllAws(Ec2Client client) {
return client.describePlacementGroups().placementGroups().stream().collect(Collectors.toList());
}

@Override
protected List<PlacementGroup> findAws(Ec2Client client, Map<String, String> filters) {
return client.describePlacementGroups(r -> r.filters(createFilters(filters))).placementGroups().stream().collect(Collectors.toList());
}
}
203 changes: 203 additions & 0 deletions src/main/java/gyro/aws/ec2/PlacementGroupResource.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
/*
* Copyright 2020, Perfect Sense, 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 gyro.aws.ec2;

import com.psddev.dari.util.ObjectUtils;
import gyro.aws.AwsResource;
import gyro.aws.Copyable;
import gyro.core.GyroException;
import gyro.core.GyroUI;
import gyro.core.Type;
import gyro.core.resource.Id;
import gyro.core.resource.Output;
import gyro.core.scope.State;
import gyro.core.validation.Range;
import gyro.core.validation.Required;
import gyro.core.validation.ValidationError;
import software.amazon.awssdk.services.ec2.Ec2Client;
import software.amazon.awssdk.services.ec2.model.*;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
* Creates a Placement Group with the specified name and placement strategy.
*
* Example
* -------
*
* .. code-block:: gyro
*
* aws::placement-group example-placement-group
* name: "TestGroup"
* placement-strategy: "partition"
* partition-count: 3
* end
*/
@Type("placement-group")
public class PlacementGroupResource extends Ec2TaggableResource<PlacementGroup> implements Copyable<PlacementGroup> {

private String name;
private PlacementStrategy placementStrategy;
private Integer partitionCount;

// Read-only
private String id;

/**
* The name of the Placement Group. (Required)
*/
@Required
public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

/**
* Approaches towards managing the placement of instances on the underlying hardware. Valid values are ``cluster``, ``spread`` or ``partition``.
* Defaults to the``cluster`` strategy. (Required)
*/
@Required
public PlacementStrategy getPlacementStrategy() {
return placementStrategy;
}

public void setPlacementStrategy(PlacementStrategy placementStrategy) {
this.placementStrategy = placementStrategy;
}

/**
* The number of partitions comprising the Placement Group. Valid values are within 1 to 7. Only required when strategy is set to ``partition``.
*/
@Range(min= 1, max= 7)
public Integer getPartitionCount() {
return partitionCount;
}

public void setPartitionCount(Integer partitionCount) {
this.partitionCount = partitionCount;
}

/**
* The ID of the Placement Group.
*/
@Id
@Output
public String getId() {
return id;
}

public void setId(String id) {
this.id = id;
}

@Override
protected String getResourceId() {
return getId();
}

@Override
public void copyFrom(PlacementGroup model) {
setId(model.groupId());
setName(model.groupName());
setPlacementStrategy(model.strategy());
setPartitionCount(model.partitionCount());
refreshTags();
}

@Override
public boolean doRefresh() {
Ec2Client client = createClient(Ec2Client.class);

if (ObjectUtils.isBlank(getName())) {
throw new GyroException("name is missing, unable to load placement group.");
}

PlacementGroup group = getPlacementGroup(client);

if (group == null) {
return false;
}

copyFrom(group);
return true;
}

@Override
protected void doCreate(GyroUI ui, State state) {
Ec2Client client = createClient(Ec2Client.class);

client.createPlacementGroup(r -> r.groupName(getName())
.strategy(getPlacementStrategy())
.partitionCount(getPartitionCount()));

PlacementGroup group = getPlacementGroup(client);

if (group != null) {
setId(group.groupId());
}

}

@Override
protected void doUpdate(GyroUI ui, State state, AwsResource config, Set<String> changedProperties) {

}

@Override
public void delete(GyroUI ui, State state) throws Exception {
Ec2Client client = createClient(Ec2Client.class);
client.deletePlacementGroup(r -> r.groupName(getName()));
}

@Override
public List<ValidationError> validate(Set<String> configuredFields) {
List<ValidationError> errors = new ArrayList<>();

if ((getPlacementStrategy() == PlacementStrategy.PARTITION) && (getPartitionCount() == null)) {
ValidationError error = new ValidationError(this, "partition-count", "partition-count is required when strategy is set to 'partition'");
errors.add(error);

} else if ((getPlacementStrategy() != PlacementStrategy.PARTITION) && (getPartitionCount() != null)) {
ValidationError error = new ValidationError(this, "partition-count", "partition-count must not be set when strategy is not set to 'partition'");
errors.add(error);
}

return errors;
}

private PlacementGroup getPlacementGroup(Ec2Client client) {
PlacementGroup group = null;
try {
DescribePlacementGroupsResponse describeResponse = client.describePlacementGroups(r -> r.groupNames(getName()));
if (!describeResponse.placementGroups().isEmpty()) {
group = describeResponse.placementGroups().get(0);
}

} catch (Ec2Exception ex) {
if (ex.awsErrorDetails() == null || !ex.awsErrorDetails().errorCode().equals("InvalidPlacementGroup.Unknown")) {
throw ex;
}
}

return group;
}
}

0 comments on commit 1d15fac

Please sign in to comment.