Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ class EksTest {
private static EksClient eks;
private static String clusterName;
private static String clusterArn;
private static String nodegroupName;
private static String fargateProfileName;

@BeforeAll
static void setup() {
Expand All @@ -26,6 +28,24 @@ static void setup() {
@AfterAll
static void cleanup() {
if (eks != null) {
if (clusterName != null) {
if (nodegroupName != null) {
try {
eks.deleteNodegroup(DeleteNodegroupRequest.builder()
.clusterName(clusterName)
.nodegroupName(nodegroupName)
.build());
} catch (Exception ignored) {}
}
if (fargateProfileName != null) {
try {
eks.deleteFargateProfile(DeleteFargateProfileRequest.builder()
.clusterName(clusterName)
.fargateProfileName(fargateProfileName)
.build());
} catch (Exception ignored) {}
}
}
try {
eks.deleteCluster(DeleteClusterRequest.builder()
.name(clusterName)
Expand Down Expand Up @@ -145,7 +165,7 @@ void createDuplicateClusterFails() {

@Test
@Order(10)
void createNodegroup() {
void createNodeGroup() {
CreateNodegroupResponse response = eks.createNodegroup(CreateNodegroupRequest.builder()
.clusterName(clusterName)
.nodegroupName(NODEGROUP)
Expand All @@ -159,22 +179,21 @@ void createNodegroup() {
assertThat(response.nodegroup().clusterName()).isEqualTo(clusterName);
assertThat(response.nodegroup().nodegroupArn())
.contains("nodegroup/" + clusterName + "/" + NODEGROUP);
assertThat(response.nodegroup().status())
.isIn(NodegroupStatus.CREATING, NodegroupStatus.ACTIVE);
assertThat(response.nodegroup().status()).isEqualTo(NodegroupStatus.ACTIVE);
assertThat(response.nodegroup().scalingConfig().desiredSize()).isEqualTo(2);
}

@Test
@Order(11)
void listNodegroups() {
void listNodeGroups() {
ListNodegroupsResponse response = eks.listNodegroups(ListNodegroupsRequest.builder()
.clusterName(clusterName).build());
assertThat(response.nodegroups()).contains(NODEGROUP);
}

@Test
@Order(12)
void describeNodegroup() {
void describeNodeGroup() {
DescribeNodegroupResponse response = eks.describeNodegroup(DescribeNodegroupRequest.builder()
.clusterName(clusterName).nodegroupName(NODEGROUP).build());
assertThat(response.nodegroup().nodegroupName()).isEqualTo(NODEGROUP);
Expand All @@ -192,12 +211,119 @@ void describeMissingNodegroupFails() {

@Test
@Order(14)
void deleteNodegroup() {
void deleteNodeGroup() {
DeleteNodegroupResponse response = eks.deleteNodegroup(DeleteNodegroupRequest.builder()
.clusterName(clusterName).nodegroupName(NODEGROUP).build());
assertThat(response.nodegroup().status()).isEqualTo(NodegroupStatus.DELETING);
}

@Test
@Order(8)
void nodegroupLifecycle() {
nodegroupName = "sdk-test-nodegroup-" + (System.currentTimeMillis() % 100000);

CreateNodegroupResponse createResponse = eks.createNodegroup(CreateNodegroupRequest.builder()
.clusterName(clusterName)
.nodegroupName(nodegroupName)
.nodeRole("arn:aws:iam::000000000000:role/eks-node-role")
.subnets("subnet-12345678", "subnet-87654321")
.scalingConfig(NodegroupScalingConfig.builder()
.minSize(1)
.maxSize(3)
.desiredSize(2)
.build())
.instanceTypes("t3.medium")
.labels(Map.of("workload", "api"))
.tags(Map.of("env", "test"))
.build());

assertThat(createResponse.nodegroup()).isNotNull();
assertThat(createResponse.nodegroup().clusterName()).isEqualTo(clusterName);
assertThat(createResponse.nodegroup().nodegroupName()).isEqualTo(nodegroupName);
assertThat(createResponse.nodegroup().nodegroupArn()).contains("nodegroup/" + clusterName + "/" + nodegroupName);
assertThat(createResponse.nodegroup().status()).isEqualTo(NodegroupStatus.ACTIVE);
assertThat(createResponse.nodegroup().scalingConfig().desiredSize()).isEqualTo(2);
assertThat(createResponse.nodegroup().labels()).containsEntry("workload", "api");
assertThat(createResponse.nodegroup().tags()).containsEntry("env", "test");

ListNodegroupsResponse listResponse = eks.listNodegroups(ListNodegroupsRequest.builder()
.clusterName(clusterName)
.build());
assertThat(listResponse.nodegroups()).contains(nodegroupName);

DescribeNodegroupResponse describeResponse = eks.describeNodegroup(DescribeNodegroupRequest.builder()
.clusterName(clusterName)
.nodegroupName(nodegroupName)
.build());
assertThat(describeResponse.nodegroup().nodegroupName()).isEqualTo(nodegroupName);
assertThat(describeResponse.nodegroup().subnets()).containsExactly("subnet-12345678", "subnet-87654321");

DeleteNodegroupResponse deleteResponse = eks.deleteNodegroup(DeleteNodegroupRequest.builder()
.clusterName(clusterName)
.nodegroupName(nodegroupName)
.build());
assertThat(deleteResponse.nodegroup().status()).isEqualTo(NodegroupStatus.DELETING);
assertThatThrownBy(() -> eks.describeNodegroup(DescribeNodegroupRequest.builder()
.clusterName(clusterName)
.nodegroupName(nodegroupName)
.build()))
.isInstanceOf(ResourceNotFoundException.class);
nodegroupName = null;
}

@Test
@Order(9)
void fargateProfileLifecycle() {
fargateProfileName = "sdk-test-fargate-" + (System.currentTimeMillis() % 100000);

CreateFargateProfileResponse createResponse = eks.createFargateProfile(CreateFargateProfileRequest.builder()
.clusterName(clusterName)
.fargateProfileName(fargateProfileName)
.podExecutionRoleArn("arn:aws:iam::000000000000:role/eks-fargate-role")
.subnets("subnet-12345678", "subnet-87654321")
.selectors(FargateProfileSelector.builder()
.namespace("default")
.labels(Map.of("app", "api"))
.build())
.tags(Map.of("env", "test"))
.build());

assertThat(createResponse.fargateProfile()).isNotNull();
assertThat(createResponse.fargateProfile().clusterName()).isEqualTo(clusterName);
assertThat(createResponse.fargateProfile().fargateProfileName()).isEqualTo(fargateProfileName);
assertThat(createResponse.fargateProfile().fargateProfileArn())
.matches("arn:aws:eks:[^:]+:[0-9]+:fargateprofile/" + clusterName + "/" + fargateProfileName + "/.+");
assertThat(createResponse.fargateProfile().status()).isEqualTo(FargateProfileStatus.ACTIVE);
assertThat(createResponse.fargateProfile().selectors()).hasSize(1);
assertThat(createResponse.fargateProfile().selectors().get(0).labels()).containsEntry("app", "api");
assertThat(createResponse.fargateProfile().tags()).containsEntry("env", "test");

ListFargateProfilesResponse listResponse = eks.listFargateProfiles(ListFargateProfilesRequest.builder()
.clusterName(clusterName)
.build());
assertThat(listResponse.fargateProfileNames()).contains(fargateProfileName);

DescribeFargateProfileResponse describeResponse = eks.describeFargateProfile(
DescribeFargateProfileRequest.builder()
.clusterName(clusterName)
.fargateProfileName(fargateProfileName)
.build());
assertThat(describeResponse.fargateProfile().fargateProfileName()).isEqualTo(fargateProfileName);
assertThat(describeResponse.fargateProfile().subnets()).containsExactly("subnet-12345678", "subnet-87654321");

DeleteFargateProfileResponse deleteResponse = eks.deleteFargateProfile(DeleteFargateProfileRequest.builder()
.clusterName(clusterName)
.fargateProfileName(fargateProfileName)
.build());
assertThat(deleteResponse.fargateProfile().status()).isEqualTo(FargateProfileStatus.DELETING);
assertThatThrownBy(() -> eks.describeFargateProfile(DescribeFargateProfileRequest.builder()
.clusterName(clusterName)
.fargateProfileName(fargateProfileName)
.build()))
.isInstanceOf(ResourceNotFoundException.class);
fargateProfileName = null;
}

@Test
@Order(100)
void deleteCluster() {
Expand Down
1 change: 1 addition & 0 deletions docs/services/docdb.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ The management API shares the RDS Query endpoint (`POST /` with an `Action=` par
| --- | --- |
| `CreateDBCluster` | Create a DocumentDB cluster and start a MongoDB container |
| `DescribeDBClusters` | List clusters and their connection details |
| `DescribeDBClusterSnapshots` | - |
| `DeleteDBCluster` | Stop and remove a cluster (must have no instances) |
| `ModifyDBCluster` | Update engine version or IAM auth setting |
| `CreateDBInstance` | Add an instance to a cluster |
Expand Down
77 changes: 75 additions & 2 deletions docs/services/eks.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@ EKS uses a standard REST API with JSON bodies — not the JSON 1.1 (`X-Amz-Targe
| `DescribeCluster` | Describe a cluster by name |
| `ListClusters` | List all cluster names |
| `DeleteCluster` | Delete a cluster |
| `CreateNodegroup` | Create node group metadata for a cluster |
| `DescribeNodegroup` | Describe a node group by cluster and name |
| `ListNodegroups` | List node group names for a cluster |
| `DeleteNodegroup` | Delete a node group |
| `CreateFargateProfile` | Create Fargate profile metadata for a cluster |
| `DescribeFargateProfile` | Describe a Fargate profile by cluster and name |
| `ListFargateProfiles` | List Fargate profile names for a cluster |
| `DeleteFargateProfile` | Delete a Fargate profile |
| `TagResource` | Add tags to a cluster |
| `UntagResource` | Remove tags from a cluster |
| `ListTagsForResource` | List tags on a cluster |
Expand Down Expand Up @@ -144,6 +152,18 @@ services:
arn:aws:eks:<region>:<accountId>:cluster/<clusterName>
```

Node groups use:

```
arn:aws:eks:<region>:<accountId>:nodegroup/<clusterName>/<nodegroupName>/<id>
```

Fargate profiles use:

```
arn:aws:eks:<region>:<accountId>:fargateprofile/<clusterName>/<fargateProfileName>/<id>
```

## Examples

```bash
Expand All @@ -165,6 +185,59 @@ aws eks describe-cluster --name my-cluster
# List clusters
aws eks list-clusters

# Create a node group
curl -s -X POST "$AWS_ENDPOINT_URL/clusters/my-cluster/node-groups" \
-H "Content-Type: application/json" \
-d '{
"nodegroupName": "my-nodegroup",
"nodeRole": "arn:aws:iam::000000000000:role/eks-node-role",
"subnets": ["subnet-123", "subnet-456"],
"instanceTypes": ["t3.medium"],
"scalingConfig": {
"minSize": 1,
"maxSize": 3,
"desiredSize": 1
}
}'

# Describe the node group
curl -s "$AWS_ENDPOINT_URL/clusters/my-cluster/node-groups/my-nodegroup"

# List node groups
curl -s "$AWS_ENDPOINT_URL/clusters/my-cluster/node-groups"

# Delete the node group
curl -s -X DELETE "$AWS_ENDPOINT_URL/clusters/my-cluster/node-groups/my-nodegroup"

# Create a Fargate profile
curl -s -X POST "$AWS_ENDPOINT_URL/clusters/my-cluster/fargate-profiles" \
-H "Content-Type: application/json" \
-d '{
"fargateProfileName": "my-fargate-profile",
"podExecutionRoleArn": "arn:aws:iam::000000000000:role/eks-fargate-role",
"subnets": ["subnet-123", "subnet-456"],
"selectors": [
{
"namespace": "default",
"labels": {
"app": "api"
}
}
],
"tags": {
"env": "dev"
}
}'

# Describe the Fargate profile
curl -s "$AWS_ENDPOINT_URL/clusters/my-cluster/fargate-profiles/my-fargate-profile"

# List Fargate profiles
curl -s "$AWS_ENDPOINT_URL/clusters/my-cluster/fargate-profiles"

# Delete the Fargate profile
curl -s -X DELETE "$AWS_ENDPOINT_URL/clusters/my-cluster/fargate-profiles/my-fargate-profile"

# Tag a cluster
aws eks tag-resource \
--resource-arn arn:aws:eks:us-east-1:000000000000:cluster/my-cluster \
Expand Down Expand Up @@ -203,6 +276,8 @@ System.out.println(described.cluster().status()); // ACTIVE
// List clusters
List<String> names = eks.listClusters(r -> {}).clusters();

// Node group and Fargate profile support are currently exposed through the REST paths.

// Tag resource
eks.tagResource(r -> r
.resourceArn(created.cluster().arn())
Expand All @@ -216,8 +291,6 @@ eks.deleteCluster(r -> r.name("my-cluster"));

The following EKS features are not yet supported:

- Node groups (`CreateNodegroup`, `DescribeNodegroup`, `ListNodegroups`, `DeleteNodegroup`)
- Fargate profiles
- `UpdateClusterConfig` / `UpdateClusterVersion`
- Add-ons (`CreateAddon`, `DescribeAddon`, `ListAddons`)
- Identity provider configs
Expand Down
2 changes: 2 additions & 0 deletions docs/services/elasticache.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ Floci manages real Valkey/Redis Docker containers and proxies TCP connections to
| `CreateCacheCluster` | - |
| `DescribeCacheClusters` | - |
| `DeleteCacheCluster` | - |
| `DescribeCacheSubnetGroups` | - |
| `DescribeCacheParameterGroups` | - |
<!-- floci:actions:end -->

## Configuration
Expand Down
3 changes: 3 additions & 0 deletions docs/services/rds.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ RDS Data API (`rds-data`) is documented separately because it uses REST JSON rou
| `DeleteDBClusterParameterGroup` | - |
| `ModifyDBClusterParameterGroup` | - |
| `DescribeDBClusterParameters` | - |
| `DescribeDBSnapshots` | - |
| `DescribeDBProxies` | - |
| `DescribeDBClusterSnapshots` | - |
| `AddTagsToResource` | Add tags to a DB resource |
| `ListTagsForResource` | List tags for a DB resource |
| `RemoveTagsFromResource` | Remove tags from a DB resource |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,7 @@ public void delete(StackResource resource, String region) {
String clusterName = resource.getAttributes().get("ClusterName");
if (clusterName != null && !clusterName.isBlank()) {
try {
eksService.deleteNodegroup(clusterName, resource.getPhysicalId());
eksService.deleteNodeGroup(clusterName, resource.getPhysicalId());
} catch (Exception e) {
LOG.debugv("Error deleting nodegroup {0}: {1}", resource.getPhysicalId(), e.getMessage());
}
Expand Down Expand Up @@ -1099,7 +1099,7 @@ private void provisionEksNodegroup(StackResource r, JsonNode props, CloudFormati
}
}
request.setSubnets(subnets);
var nodegroup = eksService.createNodegroup(clusterName, request);
var nodegroup = eksService.createNodeGroup(clusterName, request);
r.setPhysicalId(nodegroup.getNodegroupName());
r.getAttributes().put("ClusterName", nodegroup.getClusterName());
r.getAttributes().put("NodegroupName", nodegroup.getNodegroupName());
Expand Down
Loading