Added EC2 image and key framework, plus API schema

This commit is contained in:
benduncan
2025-10-14 19:34:14 +11:00
parent 8336374837
commit 629d8121b3
36 changed files with 83378 additions and 1 deletions
+673 -1
View File
@@ -615,4 +615,676 @@ When working on this codebase:
6. **Update documentation** when adding new patterns or significantly changing architecture
7. **Test with real AWS CLI** to ensure compatibility
The existing code in `hive/daemon/daemon.go` provides an excellent foundation - it already implements TLS, AWS SDK integration, NATS coordination, and service integration patterns that should be extended rather than replaced. All original TODO items have been preserved and integrated into this comprehensive development plan.
The existing code in `hive/daemon/daemon.go` provides an excellent foundation - it already implements TLS, AWS SDK integration, NATS coordination, and service integration patterns that should be extended rather than replaced. All original TODO items have been preserved and integrated into this comprehensive development plan.
## AWS Compatibility Enhancement Recommendations
This section provides detailed recommendations for improving AWS functionality replication in Hive, organized by priority and implementation complexity.
### High Priority Enhancements
These features provide immediate value and are relatively straightforward to implement:
#### 1. Request ID Tracking
**Purpose**: AWS returns a unique `RequestId` with every response for traceability and debugging.
**Implementation**:
```go
// hive/services/common/metadata.go
type ResponseMetadata struct {
RequestId string `locationName:"requestId" xml:"RequestId"`
}
// Add to all response wrappers
type EC2Response struct {
XMLName xml.Name `xml:"RunInstancesResponse"`
Result *ec2.Reservation `xml:"instancesSet"`
Metadata ResponseMetadata `xml:"ResponseMetadata"`
}
// Generate request IDs
func GenerateRequestId() string {
return fmt.Sprintf("req-%s", uuid.New().String())
}
```
**Benefits**:
- Enables request tracing across distributed systems
- Matches AWS CLI output format exactly
- Essential for debugging multi-service operations
- Required for AWS CloudTrail-like audit logging
#### 2. DryRun Implementation
**Purpose**: Allow validation-only mode for all operations without making actual changes.
**Implementation**:
```go
// Add to each operation handler
func ValidateCreateImageInput(input *ec2.CreateImageInput) (err error) {
// ... existing validation ...
return nil
}
func EC2_Process_CreateImage(jsonData []byte) (output []byte) {
var input ec2.CreateImageInput
err := json.Unmarshal(jsonData, &input)
if err != nil {
return utils.GenerateErrorPayload(awserrors.ErrorValidationError)
}
// Validate input
err = ValidateCreateImageInput(&input)
if err != nil {
return utils.GenerateErrorPayload(err)
}
// If DryRun, return success after validation
if input.DryRun != nil && *input.DryRun {
return utils.GenerateSuccessPayload(map[string]bool{"Return": true})
}
// ... actual implementation ...
}
```
**Benefits**:
- Safety feature for testing scripts and automation
- Validates permissions and parameters without side effects
- Standard AWS behavior expected by users
- Critical for CI/CD pipeline validation
#### 3. Comprehensive Error Code Mapping
**Purpose**: Expand error types to match AWS error codes precisely.
**Implementation**:
```go
// hive/awserrors/ec2_errors.go
package awserrors
import "github.com/aws/aws-sdk-go/aws"
var (
// AMI Errors
ErrorInvalidAMIIDNotFound = ResponseError{
Code: aws.String("InvalidAMIID.NotFound"),
Message: aws.String("The image id '[%s]' does not exist"),
}
ErrorInvalidAMIIDMalformed = ResponseError{
Code: aws.String("InvalidAMIID.Malformed"),
Message: aws.String("Invalid id: \"%s\" (expecting \"ami-...\")"),
}
ErrorInvalidAMIIDUnavailable = ResponseError{
Code: aws.String("InvalidAMIID.Unavailable"),
Message: aws.String("The image id '[%s]' is no longer available"),
}
// Instance Errors
ErrorInvalidInstanceIDNotFound = ResponseError{
Code: aws.String("InvalidInstanceID.NotFound"),
Message: aws.String("The instance ID '%s' does not exist"),
}
ErrorInvalidInstanceIDMalformed = ResponseError{
Code: aws.String("InvalidInstanceID.Malformed"),
Message: aws.String("Invalid id: \"%s\" (expecting \"i-...\")"),
}
ErrorIncorrectInstanceState = ResponseError{
Code: aws.String("IncorrectInstanceState"),
Message: aws.String("The instance '%s' is not in a state from which this command can be executed."),
}
// Volume Errors
ErrorInvalidVolumeNotFound = ResponseError{
Code: aws.String("InvalidVolume.NotFound"),
Message: aws.String("The volume '%s' does not exist."),
}
ErrorVolumeInUse = ResponseError{
Code: aws.String("VolumeInUse"),
Message: aws.String("Volume %s is currently attached to %s"),
}
// Key Pair Errors
ErrorInvalidKeyPairNotFound = ResponseError{
Code: aws.String("InvalidKeyPair.NotFound"),
Message: aws.String("The key pair '%s' does not exist"),
}
ErrorInvalidKeyPairDuplicate = ResponseError{
Code: aws.String("InvalidKeyPair.Duplicate"),
Message: aws.String("The keypair '%s' already exists."),
}
// Resource Limit Errors
ErrorInstanceLimitExceeded = ResponseError{
Code: aws.String("InstanceLimitExceeded"),
Message: aws.String("You have requested more instances than your current instance limit allows."),
}
ErrorVolumeLimitExceeded = ResponseError{
Code: aws.String("VolumeLimitExceeded"),
Message: aws.String("You have exceeded the maximum number of volumes."),
}
)
// Helper to format error messages
func FormatError(err ResponseError, args ...interface{}) ResponseError {
if err.Message != nil {
formatted := fmt.Sprintf(*err.Message, args...)
err.Message = &formatted
}
return err
}
```
**Usage**:
```go
// In validation functions
if !strings.HasPrefix(*input.ImageId, "ami-") {
return awserrors.FormatError(awserrors.ErrorInvalidAMIIDMalformed, *input.ImageId)
}
// In business logic
image := findImage(imageId)
if image == nil {
return utils.GenerateErrorPayload(
awserrors.FormatError(awserrors.ErrorInvalidAMIIDNotFound, imageId))
}
```
#### 4. Integration Testing with AWS SDK
**Purpose**: Validate Hive compatibility using real AWS SDK clients.
**Implementation**:
```go
// tests/integration/aws_sdk_test.go
package integration
import (
"testing"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/stretchr/testify/assert"
)
func TestEC2WithRealAWSSDK(t *testing.T) {
// Configure SDK to point to Hive endpoint
sess := session.Must(session.NewSession(&aws.Config{
Endpoint: aws.String("https://localhost:9999"),
Region: aws.String("us-east-1"),
DisableSSL: aws.Bool(false),
S3ForcePathStyle: aws.Bool(true),
}))
svc := ec2.New(sess)
t.Run("CreateKeyPair", func(t *testing.T) {
result, err := svc.CreateKeyPair(&ec2.CreateKeyPairInput{
KeyName: aws.String("test-key-" + uuid.New().String()),
})
assert.NoError(t, err)
assert.NotNil(t, result.KeyName)
assert.NotNil(t, result.KeyFingerprint)
assert.NotNil(t, result.KeyMaterial)
assert.NotNil(t, result.KeyPairId)
})
t.Run("DescribeKeyPairs", func(t *testing.T) {
result, err := svc.DescribeKeyPairs(&ec2.DescribeKeyPairsInput{})
assert.NoError(t, err)
assert.NotNil(t, result.KeyPairs)
})
t.Run("CreateImage", func(t *testing.T) {
// First create an instance
runResult, err := svc.RunInstances(&ec2.RunInstancesInput{
ImageId: aws.String("ami-test"),
InstanceType: aws.String("t2.micro"),
MinCount: aws.Int64(1),
MaxCount: aws.Int64(1),
})
assert.NoError(t, err)
instanceId := runResult.Instances[0].InstanceId
// Create image from instance
imgResult, err := svc.CreateImage(&ec2.CreateImageInput{
InstanceId: instanceId,
Name: aws.String("test-image"),
})
assert.NoError(t, err)
assert.NotNil(t, imgResult.ImageId)
assert.True(t, strings.HasPrefix(*imgResult.ImageId, "ami-"))
})
}
```
### Medium Priority Enhancements
These features improve reliability and correctness:
#### 5. Idempotency Token Support
**Purpose**: Allow safe retries of operations without creating duplicates.
**What is Idempotency?**
Idempotency means performing the same operation multiple times produces the same result as performing it once. Critical for handling network failures, timeouts, and retry logic.
**Example Problem**:
```
Client: CreateKeyPair(name="prod-key")
[Network timeout - did it work?]
Client: CreateKeyPair(name="prod-key") [retry]
Server: ERROR - key already exists
[Client never got the private key material!]
```
**Solution with Idempotency Token**:
```
Client: CreateKeyPair(name="prod-key", token="abc-123")
[Creates key, stores result with token]
[Network timeout]
Client: CreateKeyPair(name="prod-key", token="abc-123") [same token!]
[Returns cached result with same private key - safe!]
```
**Implementation**:
```go
// hive/idempotency/idempotency.go
package idempotency
import (
"sync"
"time"
)
type TokenCache struct {
cache map[string]CachedResponse
mu sync.RWMutex
}
type CachedResponse struct {
Response []byte
ExpiresAt time.Time
}
var globalCache = &TokenCache{
cache: make(map[string]CachedResponse),
}
func (t *TokenCache) Get(token string) ([]byte, bool) {
t.mu.RLock()
defer t.mu.RUnlock()
cached, exists := t.cache[token]
if !exists || time.Now().After(cached.ExpiresAt) {
return nil, false
}
return cached.Response, true
}
func (t *TokenCache) Set(token string, response []byte, ttl time.Duration) {
t.mu.Lock()
defer t.mu.Unlock()
t.cache[token] = CachedResponse{
Response: response,
ExpiresAt: time.Now().Add(ttl),
}
}
func (t *TokenCache) Cleanup() {
t.mu.Lock()
defer t.mu.Unlock()
now := time.Now()
for token, cached := range t.cache {
if now.After(cached.ExpiresAt) {
delete(t.cache, token)
}
}
}
// Start cleanup goroutine
func init() {
go func() {
ticker := time.NewTicker(5 * time.Minute)
defer ticker.Stop()
for range ticker.C {
globalCache.Cleanup()
}
}()
}
```
**Usage in Operations**:
```go
func EC2_Process_CreateImage(jsonData []byte) (output []byte) {
var input ec2.CreateImageInput
json.Unmarshal(jsonData, &input)
// Check idempotency token
if input.ClientToken != nil {
if cached, found := idempotency.globalCache.Get(*input.ClientToken); found {
slog.Info("Returning cached result for idempotency token",
"token", *input.ClientToken)
return cached
}
}
// Validate and process
err := ValidateCreateImageInput(&input)
if err != nil {
return utils.GenerateErrorPayload(err)
}
// Create the image
result := actuallyCreateImage(input)
jsonResponse, _ := json.Marshal(result)
// Cache result with token (24 hour TTL)
if input.ClientToken != nil {
idempotency.globalCache.Set(*input.ClientToken, jsonResponse, 24*time.Hour)
}
return jsonResponse
}
```
#### 6. Resource State Machines
**Purpose**: Enforce valid state transitions for resources (instances, volumes, etc.).
**Implementation**:
```go
// hive/statemachine/instance.go
package statemachine
type InstanceState int
const (
StatePending InstanceState = 0
StateRunning InstanceState = 16
StateShuttingDown InstanceState = 32
StateTerminated InstanceState = 48
StateStopping InstanceState = 64
StateStopped InstanceState = 80
)
var stateNames = map[InstanceState]string{
StatePending: "pending",
StateRunning: "running",
StateShuttingDown: "shutting-down",
StateTerminated: "terminated",
StateStopping: "stopping",
StateStopped: "stopped",
}
// Valid state transitions
var validTransitions = map[InstanceState][]InstanceState{
StatePending: {
StateRunning,
StateTerminated, // Can terminate while pending
},
StateRunning: {
StateStopping,
StateShuttingDown,
},
StateStopping: {
StateStopped,
StateTerminated, // Force terminate
},
StateStopped: {
StatePending, // Restart
StateTerminated,
},
StateShuttingDown: {
StateTerminated,
},
StateTerminated: {
// Terminal state - no transitions
},
}
func (s InstanceState) String() string {
return stateNames[s]
}
func (s InstanceState) Code() int64 {
return int64(s)
}
func ValidateTransition(from, to InstanceState) error {
allowed, exists := validTransitions[from]
if !exists {
return fmt.Errorf("invalid current state: %s", from)
}
for _, valid := range allowed {
if valid == to {
return nil
}
}
return fmt.Errorf("cannot transition from %s to %s", from, to)
}
// Usage in instance operations
func StopInstance(instanceId string) error {
instance := findInstance(instanceId)
currentState := InstanceState(instance.State.Code)
if err := ValidateTransition(currentState, StateStopping); err != nil {
return awserrors.FormatError(
awserrors.ErrorIncorrectInstanceState,
instanceId)
}
// Proceed with stop operation
instance.State.Code = aws.Int64(StateStopping.Code())
instance.State.Name = aws.String(StateStopping.String())
// ... perform actual stop
return nil
}
```
#### 7. Pagination Framework
**Purpose**: Handle large result sets consistently across all operations.
**Implementation**:
```go
// hive/pagination/pagination.go
package pagination
import (
"encoding/base64"
"encoding/json"
"fmt"
)
type PageToken struct {
Offset int `json:"offset"`
Timestamp int64 `json:"timestamp"`
}
type Paginator struct {
MaxResults int
NextToken string
}
func (p *Paginator) Paginate(items []interface{}) (page []interface{}, nextToken string, err error) {
maxResults := p.MaxResults
if maxResults == 0 {
maxResults = 100 // Default page size
}
offset := 0
if p.NextToken != "" {
var token PageToken
decoded, err := base64.StdEncoding.DecodeString(p.NextToken)
if err != nil {
return nil, "", fmt.Errorf("invalid next token")
}
if err := json.Unmarshal(decoded, &token); err != nil {
return nil, "", fmt.Errorf("invalid next token")
}
offset = token.Offset
}
if offset >= len(items) {
return []interface{}{}, "", nil
}
end := offset + maxResults
if end > len(items) {
end = len(items)
}
page = items[offset:end]
// Generate next token if more results exist
if end < len(items) {
token := PageToken{
Offset: end,
Timestamp: time.Now().Unix(),
}
tokenJSON, _ := json.Marshal(token)
nextToken = base64.StdEncoding.EncodeToString(tokenJSON)
}
return page, nextToken, nil
}
```
**Usage**:
```go
func DescribeImages(input *ec2.DescribeImagesInput) (output ec2.DescribeImagesOutput, err error) {
// Get all matching images
allImages := queryAllImages(input.Filters)
// Paginate results
paginator := pagination.Paginator{
MaxResults: aws.IntValue(input.MaxResults),
NextToken: aws.StringValue(input.NextToken),
}
page, nextToken, err := paginator.Paginate(interfaceSlice(allImages))
if err != nil {
return output, err
}
output.Images = convertToImageList(page)
if nextToken != "" {
output.NextToken = aws.String(nextToken)
}
return output, nil
}
```
### Lower Priority (Production Features)
These features are important for production but can be implemented later:
#### 8. ARN Support
```go
// hive/arn/arn.go
func GenerateARN(service, region, accountId, resourceType, resourceId string) string {
return fmt.Sprintf("arn:aws:%s:%s:%s:%s/%s",
service, region, accountId, resourceType, resourceId)
}
// Example: arn:aws:ec2:us-east-1:123456789012:instance/i-1234567890abcdef0
```
#### 9. Tagging Framework
```go
// hive/tags/tags.go
type TagService struct {
store TagStore
}
func (t *TagService) CreateTags(resourceId string, tags []*ec2.Tag) error
func (t *TagService) DeleteTags(resourceId string, tags []*ec2.Tag) error
func (t *TagService) DescribeTags(filters []*ec2.Filter) ([]*ec2.TagDescription, error)
```
#### 10. Audit Logging (CloudTrail-like)
```go
// hive/audit/audit.go
type AuditLog struct {
Timestamp time.Time `json:"timestamp"`
RequestId string `json:"requestId"`
UserIdentity string `json:"userIdentity"`
EventName string `json:"eventName"`
SourceIP string `json:"sourceIP"`
Request json.RawMessage `json:"request"`
Response json.RawMessage `json:"response"`
}
func LogAPICall(log AuditLog) {
// Write to audit log storage (file, database, S3)
}
```
#### 11. Filter Engine
```go
// hive/filters/filters.go
type FilterEngine struct{}
func (f *FilterEngine) Match(filters []*ec2.Filter, resource interface{}) bool {
// Parse AWS filter format
// Support wildcards
// Handle tag filters
}
```
#### 12. Resource Dependency Tracking
```go
// hive/dependencies/dependencies.go
type DependencyGraph struct {
edges map[string][]string
}
func (d *DependencyGraph) AddDependency(parent, child string)
func (d *DependencyGraph) CanDelete(resourceId string) error
```
### Implementation Strategy
**Phase 1: Core Compatibility (Weeks 1-2)**
1. Request ID tracking
2. DryRun implementation
3. Comprehensive error codes
4. Integration tests with AWS SDK
**Phase 2: Reliability (Weeks 3-4)**
5. Idempotency tokens
6. Resource state machines
7. Pagination framework
**Phase 3: Production Features (Weeks 5-8)**
8. ARN support
9. Tagging framework
10. Audit logging
11. Filter engine
12. Dependency tracking
### Success Metrics
- **AWS CLI Compatibility**: 100% command format compatibility
- **Error Handling**: All AWS error codes properly mapped
- **Retry Safety**: All write operations support idempotency
- **Integration Tests**: >95% pass rate with real AWS SDK
- **State Correctness**: Zero invalid state transitions in testing
+180
View File
@@ -0,0 +1,180 @@
package gateway_ec2_image
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"log/slog"
"strings"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/mulgadc/hive/hive/awserrors"
"github.com/mulgadc/hive/hive/utils"
)
/*
Sample JSON:
"CopyImage":{
"name":"CopyImage",
"http":{
"method":"POST",
"requestUri":"/"
},
"input":{"shape":"CopyImageRequest"},
"output":{"shape":"CopyImageResult"}
},
"CopyImageRequest":{
"type":"structure",
"required":[
"Name",
"SourceImageId",
"SourceRegion"
],
"members":{
"ClientToken":{"shape":"String"},
"Description":{"shape":"String"},
"Encrypted":{
"shape":"Boolean",
"locationName":"encrypted"
},
"KmsKeyId":{
"shape":"KmsKeyId",
"locationName":"kmsKeyId"
},
"Name":{"shape":"String"},
"SourceImageId":{"shape":"String"},
"SourceRegion":{"shape":"String"},
"DestinationOutpostArn":{"shape":"String"},
"DryRun":{
"shape":"Boolean",
"locationName":"dryRun"
},
"CopyImageTags":{"shape":"Boolean"},
"TagSpecifications":{
"shape":"TagSpecificationList",
"locationName":"TagSpecification"
}
}
},
"CopyImageResult":{
"type":"structure",
"members":{
"ImageId":{
"shape":"ImageId",
"locationName":"imageId"
}
}
}
*/
func ValidateCopyImageInput(input *ec2.CopyImageInput) (err error) {
// Check required args from JSON above
// required:[
// "Name",
// "SourceImageId",
// "SourceRegion"
// ]
if input == nil {
return errors.New("MissingParameter")
}
if input.Name == nil || *input.Name == "" {
return errors.New("MissingParameter")
}
if input.SourceImageId == nil || *input.SourceImageId == "" {
return errors.New("MissingParameter")
}
if input.SourceRegion == nil || *input.SourceRegion == "" {
return errors.New("MissingParameter")
}
// Validate SourceImageId format
if !strings.HasPrefix(*input.SourceImageId, "ami-") {
return errors.New("InvalidAMIID.Malformed")
}
return
}
func CopyImage(input *ec2.CopyImageInput) (output ec2.CopyImageOutput, err error) {
// Validate input
err = ValidateCopyImageInput(input)
if err != nil {
return output, err
}
// Marshal to JSON, to send over the wire for processing (NATS)
jsonData, err := json.Marshal(input)
if err != nil {
return output, fmt.Errorf("failed to marshal input to JSON: %w", err)
}
// Run the simulated JSON request via NATS, which will return a JSON response
jsonResp := EC2_Process_CopyImage(jsonData)
// Validate if the response is successful or an error
responseError, err := utils.ValidateErrorPayload(jsonResp)
if err != nil {
if responseError.Code != nil {
slog.Error("CopyImage failed", "err", responseError.Code)
return output, errors.New(*responseError.Code)
}
return output, err
}
// Unmarshal the JSON response back into output struct
err = json.Unmarshal(jsonResp, &output)
if err != nil {
return output, fmt.Errorf("failed to unmarshal JSON response to CopyImageOutput: %w", err)
}
return output, nil
}
func EC2_Process_CopyImage(jsonData []byte) (output []byte) {
var input ec2.CopyImageInput
decoder := json.NewDecoder(bytes.NewReader(jsonData))
decoder.DisallowUnknownFields()
err := decoder.Decode(&input)
if err != nil {
return utils.GenerateErrorPayload(awserrors.ErrorValidationError)
}
// Ensure the payload provided the fields that EC2 expects before proceeding.
err = ValidateCopyImageInput(&input)
if err != nil {
return utils.GenerateErrorPayload(awserrors.ErrorValidationError)
}
// TODO: Add the logic to actually copy the image.
// This is a placeholder response for testing the framework.
result := &ec2.CopyImageOutput{
ImageId: aws.String("ami-0987654321fedcba0"),
}
// Return as JSON, to simulate the NATS response
jsonResponse, err := json.Marshal(result)
if err != nil {
slog.Error("EC2_Process_CopyImage could not marshal output", "err", err)
return nil
}
return jsonResponse
}
+78
View File
@@ -0,0 +1,78 @@
package gateway_ec2_image
import (
"errors"
"strings"
"testing"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/stretchr/testify/assert"
)
func TestValidateCopyImageInput(t *testing.T) {
tests := []struct {
name string
input *ec2.CopyImageInput
want error
}{
{
name: "NilInput",
input: nil,
want: errors.New("MissingParameter"),
},
{
name: "MissingName",
input: &ec2.CopyImageInput{
SourceImageId: aws.String("ami-0123456789abcdef0"),
SourceRegion: aws.String("us-east-1"),
},
want: errors.New("MissingParameter"),
},
{
name: "MissingSourceImageId",
input: &ec2.CopyImageInput{
Name: aws.String("test-copy"),
SourceRegion: aws.String("us-east-1"),
},
want: errors.New("MissingParameter"),
},
{
name: "MissingSourceRegion",
input: &ec2.CopyImageInput{
Name: aws.String("test-copy"),
SourceImageId: aws.String("ami-0123456789abcdef0"),
},
want: errors.New("MissingParameter"),
},
{
name: "InvalidSourceImageId",
input: &ec2.CopyImageInput{
Name: aws.String("test-copy"),
SourceImageId: aws.String("invalid-id"),
SourceRegion: aws.String("us-east-1"),
},
want: errors.New("InvalidAMIID.Malformed"),
},
{
name: "ValidInput",
input: &ec2.CopyImageInput{
Name: aws.String("test-copy"),
SourceImageId: aws.String("ami-0123456789abcdef0"),
SourceRegion: aws.String("us-east-1"),
},
want: nil,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result, err := CopyImage(tt.input)
assert.Equal(t, tt.want, err)
if err == nil {
assert.NotEmpty(t, result.ImageId)
assert.True(t, strings.HasPrefix(*result.ImageId, "ami-"))
}
})
}
}
+179
View File
@@ -0,0 +1,179 @@
package gateway_ec2_image
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"log/slog"
"strings"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/mulgadc/hive/hive/awserrors"
"github.com/mulgadc/hive/hive/utils"
)
/*
Sample JSON:
"CreateImage":{
"name":"CreateImage",
"http":{
"method":"POST",
"requestUri":"/"
},
"input":{"shape":"CreateImageRequest"},
"output":{"shape":"CreateImageResult"}
},
"CreateImageRequest":{
"type":"structure",
"required":[
"InstanceId",
"Name"
],
"members":{
"BlockDeviceMappings":{
"shape":"BlockDeviceMappingRequestList",
"locationName":"blockDeviceMapping"
},
"Description":{
"shape":"String",
"locationName":"description"
},
"DryRun":{
"shape":"Boolean",
"locationName":"dryRun"
},
"InstanceId":{
"shape":"InstanceId",
"locationName":"instanceId"
},
"Name":{
"shape":"String",
"locationName":"name"
},
"NoReboot":{
"shape":"Boolean",
"locationName":"noReboot"
},
"TagSpecifications":{
"shape":"TagSpecificationList",
"locationName":"TagSpecification"
}
}
},
"CreateImageResult":{
"type":"structure",
"members":{
"ImageId":{
"shape":"ImageId",
"locationName":"imageId"
}
}
}
*/
func ValidateCreateImageInput(input *ec2.CreateImageInput) (err error) {
// Check required args from JSON above
// required:[
// "InstanceId",
// "Name"
// ]
if input == nil {
return errors.New("MissingParameter")
}
if input.InstanceId == nil || *input.InstanceId == "" {
return errors.New("MissingParameter")
}
if input.Name == nil || *input.Name == "" {
return errors.New("MissingParameter")
}
// Validate InstanceId format
if !strings.HasPrefix(*input.InstanceId, "i-") {
return errors.New("InvalidInstanceID.Malformed")
}
return
}
func CreateImage(input *ec2.CreateImageInput) (output ec2.CreateImageOutput, err error) {
// Validate input
err = ValidateCreateImageInput(input)
if err != nil {
return output, err
}
// Marshal to JSON, to send over the wire for processing (NATS)
jsonData, err := json.Marshal(input)
if err != nil {
return output, fmt.Errorf("failed to marshal input to JSON: %w", err)
}
// Run the simulated JSON request via NATS, which will return a JSON response
jsonResp := EC2_Process_CreateImage(jsonData)
// Validate if the response is successful or an error
responseError, err := utils.ValidateErrorPayload(jsonResp)
if err != nil {
if responseError.Code != nil {
slog.Error("CreateImage failed", "err", responseError.Code)
return output, errors.New(*responseError.Code)
}
return output, err
}
// Unmarshal the JSON response back into output struct
err = json.Unmarshal(jsonResp, &output)
if err != nil {
return output, fmt.Errorf("failed to unmarshal JSON response to CreateImageOutput: %w", err)
}
return output, nil
}
func EC2_Process_CreateImage(jsonData []byte) (output []byte) {
var input ec2.CreateImageInput
decoder := json.NewDecoder(bytes.NewReader(jsonData))
decoder.DisallowUnknownFields()
err := decoder.Decode(&input)
if err != nil {
return utils.GenerateErrorPayload(awserrors.ErrorValidationError)
}
// Ensure the payload provided the fields that EC2 expects before proceeding.
err = ValidateCreateImageInput(&input)
if err != nil {
return utils.GenerateErrorPayload(awserrors.ErrorValidationError)
}
// TODO: Add the logic to actually create the image from the instance.
// This is a placeholder response for testing the framework.
result := &ec2.CreateImageOutput{
ImageId: aws.String("ami-0123456789abcdef0"),
}
// Return as JSON, to simulate the NATS response
jsonResponse, err := json.Marshal(result)
if err != nil {
slog.Error("EC2_Process_CreateImage could not marshal output", "err", err)
return nil
}
return jsonResponse
}
+139
View File
@@ -0,0 +1,139 @@
package gateway_ec2_image
import (
"encoding/json"
"errors"
"strings"
"testing"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/mulgadc/hive/hive/utils"
"github.com/stretchr/testify/assert"
)
func TestValidateCreateImageInput(t *testing.T) {
tests := []struct {
name string
input *ec2.CreateImageInput
want error
}{
{
name: "NilInput",
input: nil,
want: errors.New("MissingParameter"),
},
{
name: "MissingInstanceId",
input: &ec2.CreateImageInput{
Name: aws.String("test-image"),
},
want: errors.New("MissingParameter"),
},
{
name: "MissingName",
input: &ec2.CreateImageInput{
InstanceId: aws.String("i-0123456789abcdef0"),
},
want: errors.New("MissingParameter"),
},
{
name: "InvalidInstanceId",
input: &ec2.CreateImageInput{
InstanceId: aws.String("invalid-id"),
Name: aws.String("test-image"),
},
want: errors.New("InvalidInstanceID.Malformed"),
},
{
name: "ValidInput",
input: &ec2.CreateImageInput{
InstanceId: aws.String("i-0123456789abcdef0"),
Name: aws.String("test-image"),
},
want: nil,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result, err := CreateImage(tt.input)
assert.Equal(t, tt.want, err)
if err != nil {
assert.Empty(t, result.ImageId)
} else {
assert.NotEmpty(t, result.ImageId)
assert.True(t, strings.HasPrefix(*result.ImageId, "ami-"))
}
})
}
}
func TestEC2ProcessCreateImage(t *testing.T) {
tests := []struct {
name string
payload interface{}
rawJSON []byte
wantValidationErr bool
wantErrCode string
assertFn func(t *testing.T, output ec2.CreateImageOutput)
}{
{
name: "InvalidPayload",
payload: &ec2.DescribeImagesInput{},
wantValidationErr: true,
wantErrCode: "ValidationError",
},
{
name: "MissingRequiredFields",
payload: &ec2.CreateImageInput{
Name: aws.String("test-image"),
},
wantValidationErr: true,
wantErrCode: "ValidationError",
},
{
name: "ValidCreateImage",
payload: &ec2.CreateImageInput{
InstanceId: aws.String("i-0123456789abcdef0"),
Name: aws.String("test-image"),
},
assertFn: func(t *testing.T, output ec2.CreateImageOutput) {
assert.NotNil(t, output.ImageId)
assert.True(t, strings.HasPrefix(*output.ImageId, "ami-"))
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var jsonData []byte
var err error
if tt.rawJSON != nil {
jsonData = tt.rawJSON
} else {
jsonData, err = json.Marshal(tt.payload)
assert.NoError(t, err)
}
jsonResp := EC2_Process_CreateImage(jsonData)
responseError, err := utils.ValidateErrorPayload(jsonResp)
if tt.wantValidationErr {
assert.Error(t, err)
assert.Equal(t, tt.wantErrCode, aws.StringValue(responseError.Code))
return
}
assert.NoError(t, err)
var output ec2.CreateImageOutput
assert.NoError(t, json.Unmarshal(jsonResp, &output))
if tt.assertFn != nil {
tt.assertFn(t, output)
}
})
}
}
+134
View File
@@ -0,0 +1,134 @@
package gateway_ec2_image
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"log/slog"
"strings"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/mulgadc/hive/hive/awserrors"
"github.com/mulgadc/hive/hive/utils"
)
/*
Sample JSON:
"DeregisterImage":{
"name":"DeregisterImage",
"http":{
"method":"POST",
"requestUri":"/"
},
"input":{"shape":"DeregisterImageRequest"}
},
"DeregisterImageRequest":{
"type":"structure",
"required":["ImageId"],
"members":{
"ImageId":{"shape":"ImageId"},
"DryRun":{
"shape":"Boolean",
"locationName":"dryRun"
}
}
}
*/
func ValidateDeregisterImageInput(input *ec2.DeregisterImageInput) (err error) {
// Check required args from JSON above
// required:["ImageId"]
if input == nil {
return errors.New("MissingParameter")
}
if input.ImageId == nil || *input.ImageId == "" {
return errors.New("MissingParameter")
}
// Validate ImageId format
if !strings.HasPrefix(*input.ImageId, "ami-") {
return errors.New("InvalidAMIID.Malformed")
}
return
}
func DeregisterImage(input *ec2.DeregisterImageInput) (output ec2.DeregisterImageOutput, err error) {
// Validate input
err = ValidateDeregisterImageInput(input)
if err != nil {
return output, err
}
// Marshal to JSON, to send over the wire for processing (NATS)
jsonData, err := json.Marshal(input)
if err != nil {
return output, fmt.Errorf("failed to marshal input to JSON: %w", err)
}
// Run the simulated JSON request via NATS, which will return a JSON response
jsonResp := EC2_Process_DeregisterImage(jsonData)
// Validate if the response is successful or an error
responseError, err := utils.ValidateErrorPayload(jsonResp)
if err != nil {
if responseError.Code != nil {
slog.Error("DeregisterImage failed", "err", responseError.Code)
return output, errors.New(*responseError.Code)
}
return output, err
}
// Unmarshal the JSON response back into output struct
err = json.Unmarshal(jsonResp, &output)
if err != nil {
return output, fmt.Errorf("failed to unmarshal JSON response to DeregisterImageOutput: %w", err)
}
return output, nil
}
func EC2_Process_DeregisterImage(jsonData []byte) (output []byte) {
var input ec2.DeregisterImageInput
decoder := json.NewDecoder(bytes.NewReader(jsonData))
decoder.DisallowUnknownFields()
err := decoder.Decode(&input)
if err != nil {
return utils.GenerateErrorPayload(awserrors.ErrorValidationError)
}
// Ensure the payload provided the fields that EC2 expects before proceeding.
err = ValidateDeregisterImageInput(&input)
if err != nil {
return utils.GenerateErrorPayload(awserrors.ErrorValidationError)
}
// TODO: Add the logic to actually deregister the image.
// This is a placeholder response for testing the framework.
// DeregisterImageOutput has no exported fields, so we return a simple success indicator
// to distinguish from error responses
result := map[string]bool{"Return": true}
// Return as JSON, to simulate the NATS response
jsonResponse, err := json.Marshal(result)
if err != nil {
slog.Error("EC2_Process_DeregisterImage could not marshal output", "err", err)
return nil
}
return jsonResponse
}
@@ -0,0 +1,52 @@
package gateway_ec2_image
import (
"errors"
"testing"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/stretchr/testify/assert"
)
func TestValidateDeregisterImageInput(t *testing.T) {
tests := []struct {
name string
input *ec2.DeregisterImageInput
want error
}{
{
name: "NilInput",
input: nil,
want: errors.New("MissingParameter"),
},
{
name: "MissingImageId",
input: &ec2.DeregisterImageInput{
ImageId: aws.String(""),
},
want: errors.New("MissingParameter"),
},
{
name: "InvalidImageId",
input: &ec2.DeregisterImageInput{
ImageId: aws.String("invalid-id"),
},
want: errors.New("InvalidAMIID.Malformed"),
},
{
name: "ValidInput",
input: &ec2.DeregisterImageInput{
ImageId: aws.String("ami-0123456789abcdef0"),
},
want: nil,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
_, err := DeregisterImage(tt.input)
assert.Equal(t, tt.want, err)
})
}
}
@@ -0,0 +1,182 @@
package gateway_ec2_image
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"log/slog"
"strings"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/mulgadc/hive/hive/awserrors"
"github.com/mulgadc/hive/hive/utils"
)
/*
Sample JSON:
"DescribeImageAttribute":{
"name":"DescribeImageAttribute",
"http":{
"method":"POST",
"requestUri":"/"
},
"input":{"shape":"DescribeImageAttributeRequest"},
"output":{"shape":"ImageAttribute"}
},
"DescribeImageAttributeRequest":{
"type":"structure",
"required":[
"Attribute",
"ImageId"
],
"members":{
"Attribute":{"shape":"ImageAttributeName"},
"ImageId":{"shape":"ImageId"},
"DryRun":{
"shape":"Boolean",
"locationName":"dryRun"
}
}
},
"ImageAttribute":{
"type":"structure",
"members":{
"BlockDeviceMappings":{
"shape":"BlockDeviceMappingList",
"locationName":"blockDeviceMapping"
},
"ImageId":{
"shape":"ImageId",
"locationName":"imageId"
},
"LaunchPermissions":{
"shape":"LaunchPermissionList",
"locationName":"launchPermission"
},
"ProductCodes":{
"shape":"ProductCodeList",
"locationName":"productCodes"
},
"Description":{"shape":"AttributeValue"},
"KernelId":{"shape":"AttributeValue"},
"RamdiskId":{"shape":"AttributeValue"},
"SriovNetSupport":{"shape":"AttributeValue"},
"BootMode":{"shape":"AttributeValue"},
"TpmSupport":{"shape":"AttributeValue"},
"UefiData":{"shape":"AttributeValue"},
"LastLaunchedTime":{"shape":"AttributeValue"},
"ImdsSupport":{"shape":"AttributeValue"},
"DeregistrationProtection":{"shape":"AttributeValue"}
}
}
*/
func ValidateDescribeImageAttributeInput(input *ec2.DescribeImageAttributeInput) (err error) {
// Check required args from JSON above
// required:[
// "Attribute",
// "ImageId"
// ]
if input == nil {
return errors.New("MissingParameter")
}
if input.Attribute == nil || *input.Attribute == "" {
return errors.New("MissingParameter")
}
if input.ImageId == nil || *input.ImageId == "" {
return errors.New("MissingParameter")
}
// Validate ImageId format
if !strings.HasPrefix(*input.ImageId, "ami-") {
return errors.New("InvalidAMIID.Malformed")
}
return
}
func DescribeImageAttribute(input *ec2.DescribeImageAttributeInput) (output ec2.DescribeImageAttributeOutput, err error) {
// Validate input
err = ValidateDescribeImageAttributeInput(input)
if err != nil {
return output, err
}
// Marshal to JSON, to send over the wire for processing (NATS)
jsonData, err := json.Marshal(input)
if err != nil {
return output, fmt.Errorf("failed to marshal input to JSON: %w", err)
}
// Run the simulated JSON request via NATS, which will return a JSON response
jsonResp := EC2_Process_DescribeImageAttribute(jsonData)
// Validate if the response is successful or an error
responseError, err := utils.ValidateErrorPayload(jsonResp)
if err != nil {
if responseError.Code != nil {
slog.Error("DescribeImageAttribute failed", "err", responseError.Code)
return output, errors.New(*responseError.Code)
}
return output, err
}
// Unmarshal the JSON response back into output struct
err = json.Unmarshal(jsonResp, &output)
if err != nil {
return output, fmt.Errorf("failed to unmarshal JSON response to DescribeImageAttributeOutput: %w", err)
}
return output, nil
}
func EC2_Process_DescribeImageAttribute(jsonData []byte) (output []byte) {
var input ec2.DescribeImageAttributeInput
decoder := json.NewDecoder(bytes.NewReader(jsonData))
decoder.DisallowUnknownFields()
err := decoder.Decode(&input)
if err != nil {
return utils.GenerateErrorPayload(awserrors.ErrorValidationError)
}
// Ensure the payload provided the fields that EC2 expects before proceeding.
err = ValidateDescribeImageAttributeInput(&input)
if err != nil {
return utils.GenerateErrorPayload(awserrors.ErrorValidationError)
}
// TODO: Add the logic to actually describe image attributes.
// This is a placeholder response for testing the framework.
result := &ec2.DescribeImageAttributeOutput{
ImageId: input.ImageId,
Description: &ec2.AttributeValue{
Value: aws.String("Test image description"),
},
}
// Return as JSON, to simulate the NATS response
jsonResponse, err := json.Marshal(result)
if err != nil {
slog.Error("EC2_Process_DescribeImageAttribute could not marshal output", "err", err)
return nil
}
return jsonResponse
}
@@ -0,0 +1,64 @@
package gateway_ec2_image
import (
"errors"
"testing"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/stretchr/testify/assert"
)
func TestValidateDescribeImageAttributeInput(t *testing.T) {
tests := []struct {
name string
input *ec2.DescribeImageAttributeInput
want error
}{
{
name: "NilInput",
input: nil,
want: errors.New("MissingParameter"),
},
{
name: "MissingAttribute",
input: &ec2.DescribeImageAttributeInput{
ImageId: aws.String("ami-0123456789abcdef0"),
},
want: errors.New("MissingParameter"),
},
{
name: "MissingImageId",
input: &ec2.DescribeImageAttributeInput{
Attribute: aws.String("description"),
},
want: errors.New("MissingParameter"),
},
{
name: "InvalidImageId",
input: &ec2.DescribeImageAttributeInput{
ImageId: aws.String("invalid-id"),
Attribute: aws.String("description"),
},
want: errors.New("InvalidAMIID.Malformed"),
},
{
name: "ValidInput",
input: &ec2.DescribeImageAttributeInput{
ImageId: aws.String("ami-0123456789abcdef0"),
Attribute: aws.String("description"),
},
want: nil,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result, err := DescribeImageAttribute(tt.input)
assert.Equal(t, tt.want, err)
if err == nil {
assert.NotEmpty(t, result.ImageId)
}
})
}
}
+178
View File
@@ -0,0 +1,178 @@
package gateway_ec2_image
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"log/slog"
"strings"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/mulgadc/hive/hive/awserrors"
"github.com/mulgadc/hive/hive/utils"
)
/*
Sample JSON:
"DescribeImages":{
"name":"DescribeImages",
"http":{
"method":"POST",
"requestUri":"/"
},
"input":{"shape":"DescribeImagesRequest"},
"output":{"shape":"DescribeImagesResult"}
},
"DescribeImagesRequest":{
"type":"structure",
"members":{
"ExecutableUsers":{
"shape":"ExecutableByStringList",
"locationName":"ExecutableBy"
},
"Filters":{
"shape":"FilterList",
"locationName":"Filter"
},
"ImageIds":{
"shape":"ImageIdStringList",
"locationName":"ImageId"
},
"Owners":{
"shape":"OwnerStringList",
"locationName":"Owner"
},
"IncludeDeprecated":{"shape":"Boolean"},
"IncludeDisabled":{"shape":"Boolean"},
"DryRun":{
"shape":"Boolean",
"locationName":"dryRun"
},
"MaxResults":{"shape":"Integer"},
"NextToken":{"shape":"String"}
}
},
"DescribeImagesResult":{
"type":"structure",
"members":{
"Images":{
"shape":"ImageList",
"locationName":"imagesSet"
},
"NextToken":{
"shape":"String",
"locationName":"nextToken"
}
}
}
*/
func ValidateDescribeImagesInput(input *ec2.DescribeImagesInput) (err error) {
// No required fields for DescribeImages
// All parameters are optional
if input == nil {
return nil
}
// Validate ImageId format if provided
if input.ImageIds != nil {
for _, imageId := range input.ImageIds {
if imageId != nil && !strings.HasPrefix(*imageId, "ami-") {
return errors.New("InvalidAMIID.Malformed")
}
}
}
return
}
func DescribeImages(input *ec2.DescribeImagesInput) (output ec2.DescribeImagesOutput, err error) {
// Validate input
err = ValidateDescribeImagesInput(input)
if err != nil {
return output, err
}
// Marshal to JSON, to send over the wire for processing (NATS)
jsonData, err := json.Marshal(input)
if err != nil {
return output, fmt.Errorf("failed to marshal input to JSON: %w", err)
}
// Run the simulated JSON request via NATS, which will return a JSON response
jsonResp := EC2_Process_DescribeImages(jsonData)
// Validate if the response is successful or an error
responseError, err := utils.ValidateErrorPayload(jsonResp)
if err != nil {
if responseError.Code != nil {
slog.Error("DescribeImages failed", "err", responseError.Code)
return output, errors.New(*responseError.Code)
}
return output, err
}
// Unmarshal the JSON response back into output struct
err = json.Unmarshal(jsonResp, &output)
if err != nil {
return output, fmt.Errorf("failed to unmarshal JSON response to DescribeImagesOutput: %w", err)
}
return output, nil
}
func EC2_Process_DescribeImages(jsonData []byte) (output []byte) {
var input ec2.DescribeImagesInput
decoder := json.NewDecoder(bytes.NewReader(jsonData))
decoder.DisallowUnknownFields()
err := decoder.Decode(&input)
if err != nil {
return utils.GenerateErrorPayload(awserrors.ErrorValidationError)
}
// Ensure the payload provided the fields that EC2 expects before proceeding.
err = ValidateDescribeImagesInput(&input)
if err != nil {
return utils.GenerateErrorPayload(awserrors.ErrorValidationError)
}
// TODO: Add the logic to actually query images from the system.
// This is a placeholder response for testing the framework.
result := &ec2.DescribeImagesOutput{
Images: []*ec2.Image{
{
ImageId: aws.String("ami-0123456789abcdef0"),
Name: aws.String("test-image"),
State: aws.String("available"),
Architecture: aws.String("x86_64"),
ImageType: aws.String("machine"),
OwnerId: aws.String("123456789012"),
Public: aws.Bool(false),
},
},
}
// Return as JSON, to simulate the NATS response
jsonResponse, err := json.Marshal(result)
if err != nil {
slog.Error("EC2_Process_DescribeImages could not marshal output", "err", err)
return nil
}
return jsonResponse
}
@@ -0,0 +1,53 @@
package gateway_ec2_image
import (
"errors"
"testing"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/stretchr/testify/assert"
)
func TestValidateDescribeImagesInput(t *testing.T) {
tests := []struct {
name string
input *ec2.DescribeImagesInput
want error
}{
{
name: "NilInput",
input: nil,
want: nil,
},
{
name: "EmptyInput",
input: &ec2.DescribeImagesInput{},
want: nil,
},
{
name: "InvalidImageId",
input: &ec2.DescribeImagesInput{
ImageIds: []*string{aws.String("invalid-id")},
},
want: errors.New("InvalidAMIID.Malformed"),
},
{
name: "ValidInput",
input: &ec2.DescribeImagesInput{
ImageIds: []*string{aws.String("ami-0123456789abcdef0")},
},
want: nil,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result, err := DescribeImages(tt.input)
assert.Equal(t, tt.want, err)
if err == nil {
assert.NotNil(t, result.Images)
}
})
}
}
+27
View File
@@ -0,0 +1,27 @@
package gateway_ec2_image
import (
"fmt"
"github.com/aws/aws-sdk-go/service/ec2"
)
func ImportImage() {
var test = ec2.ImportImageInput{}
fmt.Println(test)
var t2 = ec2.ImportImageOutput{}
fmt.Println(t2)
var t3 = ec2.DescribeImagesInput{}
fmt.Println(t3)
var t4 = ec2.DescribeImagesOutput{}
fmt.Println(t4)
}
@@ -0,0 +1,160 @@
package gateway_ec2_image
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"log/slog"
"strings"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/mulgadc/hive/hive/awserrors"
"github.com/mulgadc/hive/hive/utils"
)
/*
Sample JSON:
"ModifyImageAttribute":{
"name":"ModifyImageAttribute",
"http":{
"method":"POST",
"requestUri":"/"
},
"input":{"shape":"ModifyImageAttributeRequest"}
},
"ModifyImageAttributeRequest":{
"type":"structure",
"required":["ImageId"],
"members":{
"Attribute":{"shape":"String"},
"Description":{"shape":"AttributeValue"},
"ImageId":{"shape":"ImageId"},
"LaunchPermission":{"shape":"LaunchPermissionModifications"},
"OperationType":{"shape":"OperationType"},
"ProductCodes":{
"shape":"ProductCodeStringList",
"locationName":"ProductCode"
},
"UserGroups":{
"shape":"UserGroupStringList",
"locationName":"UserGroup"
},
"UserIds":{
"shape":"UserIdStringList",
"locationName":"UserId"
},
"Value":{"shape":"String"},
"DryRun":{
"shape":"Boolean",
"locationName":"dryRun"
},
"OrganizationArns":{
"shape":"OrganizationArnStringList",
"locationName":"OrganizationArn"
},
"OrganizationalUnitArns":{
"shape":"OrganizationalUnitArnStringList",
"locationName":"OrganizationalUnitArn"
},
"ImdsSupport":{"shape":"AttributeValue"}
}
}
*/
func ValidateModifyImageAttributeInput(input *ec2.ModifyImageAttributeInput) (err error) {
// Check required args from JSON above
// required:["ImageId"]
if input == nil {
return errors.New("MissingParameter")
}
if input.ImageId == nil || *input.ImageId == "" {
return errors.New("MissingParameter")
}
// Validate ImageId format
if !strings.HasPrefix(*input.ImageId, "ami-") {
return errors.New("InvalidAMIID.Malformed")
}
return
}
func ModifyImageAttribute(input *ec2.ModifyImageAttributeInput) (output ec2.ModifyImageAttributeOutput, err error) {
// Validate input
err = ValidateModifyImageAttributeInput(input)
if err != nil {
return output, err
}
// Marshal to JSON, to send over the wire for processing (NATS)
jsonData, err := json.Marshal(input)
if err != nil {
return output, fmt.Errorf("failed to marshal input to JSON: %w", err)
}
// Run the simulated JSON request via NATS, which will return a JSON response
jsonResp := EC2_Process_ModifyImageAttribute(jsonData)
// Validate if the response is successful or an error
responseError, err := utils.ValidateErrorPayload(jsonResp)
if err != nil {
if responseError.Code != nil {
slog.Error("ModifyImageAttribute failed", "err", responseError.Code)
return output, errors.New(*responseError.Code)
}
return output, err
}
// Unmarshal the JSON response back into output struct
err = json.Unmarshal(jsonResp, &output)
if err != nil {
return output, fmt.Errorf("failed to unmarshal JSON response to ModifyImageAttributeOutput: %w", err)
}
return output, nil
}
func EC2_Process_ModifyImageAttribute(jsonData []byte) (output []byte) {
var input ec2.ModifyImageAttributeInput
decoder := json.NewDecoder(bytes.NewReader(jsonData))
decoder.DisallowUnknownFields()
err := decoder.Decode(&input)
if err != nil {
return utils.GenerateErrorPayload(awserrors.ErrorValidationError)
}
// Ensure the payload provided the fields that EC2 expects before proceeding.
err = ValidateModifyImageAttributeInput(&input)
if err != nil {
return utils.GenerateErrorPayload(awserrors.ErrorValidationError)
}
// TODO: Add the logic to actually modify image attributes.
// This is a placeholder response for testing the framework.
// ModifyImageAttributeOutput has no exported fields, so we return a simple success indicator
// to distinguish from error responses
result := map[string]bool{"Return": true}
// Return as JSON, to simulate the NATS response
jsonResponse, err := json.Marshal(result)
if err != nil {
slog.Error("EC2_Process_ModifyImageAttribute could not marshal output", "err", err)
return nil
}
return jsonResponse
}
@@ -0,0 +1,53 @@
package gateway_ec2_image
import (
"errors"
"testing"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/stretchr/testify/assert"
)
func TestValidateModifyImageAttributeInput(t *testing.T) {
tests := []struct {
name string
input *ec2.ModifyImageAttributeInput
want error
}{
{
name: "NilInput",
input: nil,
want: errors.New("MissingParameter"),
},
{
name: "MissingImageId",
input: &ec2.ModifyImageAttributeInput{
Description: &ec2.AttributeValue{Value: aws.String("test")},
},
want: errors.New("MissingParameter"),
},
{
name: "InvalidImageId",
input: &ec2.ModifyImageAttributeInput{
ImageId: aws.String("invalid-id"),
},
want: errors.New("InvalidAMIID.Malformed"),
},
{
name: "ValidInput",
input: &ec2.ModifyImageAttributeInput{
ImageId: aws.String("ami-0123456789abcdef0"),
Description: &ec2.AttributeValue{Value: aws.String("test")},
},
want: nil,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
_, err := ModifyImageAttribute(tt.input)
assert.Equal(t, tt.want, err)
})
}
}
+199
View File
@@ -0,0 +1,199 @@
package gateway_ec2_image
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"log/slog"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/mulgadc/hive/hive/awserrors"
"github.com/mulgadc/hive/hive/utils"
)
/*
Sample JSON:
"RegisterImage":{
"name":"RegisterImage",
"http":{
"method":"POST",
"requestUri":"/"
},
"input":{"shape":"RegisterImageRequest"},
"output":{"shape":"RegisterImageResult"}
},
"RegisterImageRequest":{
"type":"structure",
"required":["Name"],
"members":{
"ImageLocation":{"shape":"String"},
"Architecture":{
"shape":"ArchitectureValues",
"locationName":"architecture"
},
"BlockDeviceMappings":{
"shape":"BlockDeviceMappingRequestList",
"locationName":"BlockDeviceMapping"
},
"Description":{
"shape":"String",
"locationName":"description"
},
"DryRun":{
"shape":"Boolean",
"locationName":"dryRun"
},
"EnaSupport":{
"shape":"Boolean",
"locationName":"enaSupport"
},
"KernelId":{
"shape":"KernelId",
"locationName":"kernelId"
},
"Name":{
"shape":"String",
"locationName":"name"
},
"BillingProducts":{
"shape":"BillingProductList",
"locationName":"BillingProduct"
},
"RamdiskId":{
"shape":"RamdiskId",
"locationName":"ramdiskId"
},
"RootDeviceName":{
"shape":"String",
"locationName":"rootDeviceName"
},
"SriovNetSupport":{
"shape":"String",
"locationName":"sriovNetSupport"
},
"VirtualizationType":{
"shape":"String",
"locationName":"virtualizationType"
},
"BootMode":{
"shape":"BootModeValues",
"locationName":"bootMode"
},
"TpmSupport":{
"shape":"TpmSupportValues"
},
"UefiData":{"shape":"StringType"},
"ImdsSupport":{
"shape":"ImdsSupportValues"
},
"TagSpecifications":{
"shape":"TagSpecificationList",
"locationName":"TagSpecification"
}
}
},
"RegisterImageResult":{
"type":"structure",
"members":{
"ImageId":{
"shape":"ImageId",
"locationName":"imageId"
}
}
}
*/
func ValidateRegisterImageInput(input *ec2.RegisterImageInput) (err error) {
// Check required args from JSON above
// required:["Name"]
if input == nil {
return errors.New("MissingParameter")
}
if input.Name == nil || *input.Name == "" {
return errors.New("MissingParameter")
}
return
}
func RegisterImage(input *ec2.RegisterImageInput) (output ec2.RegisterImageOutput, err error) {
// Validate input
err = ValidateRegisterImageInput(input)
if err != nil {
return output, err
}
// Marshal to JSON, to send over the wire for processing (NATS)
jsonData, err := json.Marshal(input)
if err != nil {
return output, fmt.Errorf("failed to marshal input to JSON: %w", err)
}
// Run the simulated JSON request via NATS, which will return a JSON response
jsonResp := EC2_Process_RegisterImage(jsonData)
// Validate if the response is successful or an error
responseError, err := utils.ValidateErrorPayload(jsonResp)
if err != nil {
if responseError.Code != nil {
slog.Error("RegisterImage failed", "err", responseError.Code)
return output, errors.New(*responseError.Code)
}
return output, err
}
// Unmarshal the JSON response back into output struct
err = json.Unmarshal(jsonResp, &output)
if err != nil {
return output, fmt.Errorf("failed to unmarshal JSON response to RegisterImageOutput: %w", err)
}
return output, nil
}
func EC2_Process_RegisterImage(jsonData []byte) (output []byte) {
var input ec2.RegisterImageInput
decoder := json.NewDecoder(bytes.NewReader(jsonData))
decoder.DisallowUnknownFields()
err := decoder.Decode(&input)
if err != nil {
return utils.GenerateErrorPayload(awserrors.ErrorValidationError)
}
// Ensure the payload provided the fields that EC2 expects before proceeding.
err = ValidateRegisterImageInput(&input)
if err != nil {
return utils.GenerateErrorPayload(awserrors.ErrorValidationError)
}
// TODO: Add the logic to actually register the image.
// This is a placeholder response for testing the framework.
result := &ec2.RegisterImageOutput{
ImageId: aws.String("ami-0123456789abcdef0"),
}
// Return as JSON, to simulate the NATS response
jsonResponse, err := json.Marshal(result)
if err != nil {
slog.Error("EC2_Process_RegisterImage could not marshal output", "err", err)
return nil
}
return jsonResponse
}
@@ -0,0 +1,51 @@
package gateway_ec2_image
import (
"errors"
"strings"
"testing"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/stretchr/testify/assert"
)
func TestValidateRegisterImageInput(t *testing.T) {
tests := []struct {
name string
input *ec2.RegisterImageInput
want error
}{
{
name: "NilInput",
input: nil,
want: errors.New("MissingParameter"),
},
{
name: "MissingName",
input: &ec2.RegisterImageInput{
ImageLocation: aws.String("s3://bucket/image"),
},
want: errors.New("MissingParameter"),
},
{
name: "ValidInput",
input: &ec2.RegisterImageInput{
Name: aws.String("test-image"),
ImageLocation: aws.String("s3://bucket/image"),
},
want: nil,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result, err := RegisterImage(tt.input)
assert.Equal(t, tt.want, err)
if err == nil {
assert.NotEmpty(t, result.ImageId)
assert.True(t, strings.HasPrefix(*result.ImageId, "ami-"))
}
})
}
}
@@ -0,0 +1,145 @@
package gateway_ec2_image
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"log/slog"
"strings"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/mulgadc/hive/hive/awserrors"
"github.com/mulgadc/hive/hive/utils"
)
/*
Sample JSON:
"ResetImageAttribute":{
"name":"ResetImageAttribute",
"http":{
"method":"POST",
"requestUri":"/"
},
"input":{"shape":"ResetImageAttributeRequest"}
},
"ResetImageAttributeRequest":{
"type":"structure",
"required":[
"Attribute",
"ImageId"
],
"members":{
"Attribute":{"shape":"ResetImageAttributeName"},
"ImageId":{"shape":"ImageId"},
"DryRun":{
"shape":"Boolean",
"locationName":"dryRun"
}
}
}
*/
func ValidateResetImageAttributeInput(input *ec2.ResetImageAttributeInput) (err error) {
// Check required args from JSON above
// required:[
// "Attribute",
// "ImageId"
// ]
if input == nil {
return errors.New("MissingParameter")
}
if input.Attribute == nil || *input.Attribute == "" {
return errors.New("MissingParameter")
}
if input.ImageId == nil || *input.ImageId == "" {
return errors.New("MissingParameter")
}
// Validate ImageId format
if !strings.HasPrefix(*input.ImageId, "ami-") {
return errors.New("InvalidAMIID.Malformed")
}
return
}
func ResetImageAttribute(input *ec2.ResetImageAttributeInput) (output ec2.ResetImageAttributeOutput, err error) {
// Validate input
err = ValidateResetImageAttributeInput(input)
if err != nil {
return output, err
}
// Marshal to JSON, to send over the wire for processing (NATS)
jsonData, err := json.Marshal(input)
if err != nil {
return output, fmt.Errorf("failed to marshal input to JSON: %w", err)
}
// Run the simulated JSON request via NATS, which will return a JSON response
jsonResp := EC2_Process_ResetImageAttribute(jsonData)
// Validate if the response is successful or an error
responseError, err := utils.ValidateErrorPayload(jsonResp)
if err != nil {
if responseError.Code != nil {
slog.Error("ResetImageAttribute failed", "err", responseError.Code)
return output, errors.New(*responseError.Code)
}
return output, err
}
// Unmarshal the JSON response back into output struct
err = json.Unmarshal(jsonResp, &output)
if err != nil {
return output, fmt.Errorf("failed to unmarshal JSON response to ResetImageAttributeOutput: %w", err)
}
return output, nil
}
func EC2_Process_ResetImageAttribute(jsonData []byte) (output []byte) {
var input ec2.ResetImageAttributeInput
decoder := json.NewDecoder(bytes.NewReader(jsonData))
decoder.DisallowUnknownFields()
err := decoder.Decode(&input)
if err != nil {
return utils.GenerateErrorPayload(awserrors.ErrorValidationError)
}
// Ensure the payload provided the fields that EC2 expects before proceeding.
err = ValidateResetImageAttributeInput(&input)
if err != nil {
return utils.GenerateErrorPayload(awserrors.ErrorValidationError)
}
// TODO: Add the logic to actually reset image attributes.
// This is a placeholder response for testing the framework.
// ResetImageAttributeOutput has no exported fields, so we return a simple success indicator
// to distinguish from error responses
result := map[string]bool{"Return": true}
// Return as JSON, to simulate the NATS response
jsonResponse, err := json.Marshal(result)
if err != nil {
slog.Error("EC2_Process_ResetImageAttribute could not marshal output", "err", err)
return nil
}
return jsonResponse
}
@@ -0,0 +1,61 @@
package gateway_ec2_image
import (
"errors"
"testing"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/stretchr/testify/assert"
)
func TestValidateResetImageAttributeInput(t *testing.T) {
tests := []struct {
name string
input *ec2.ResetImageAttributeInput
want error
}{
{
name: "NilInput",
input: nil,
want: errors.New("MissingParameter"),
},
{
name: "MissingAttribute",
input: &ec2.ResetImageAttributeInput{
ImageId: aws.String("ami-0123456789abcdef0"),
},
want: errors.New("MissingParameter"),
},
{
name: "MissingImageId",
input: &ec2.ResetImageAttributeInput{
Attribute: aws.String("launchPermission"),
},
want: errors.New("MissingParameter"),
},
{
name: "InvalidImageId",
input: &ec2.ResetImageAttributeInput{
ImageId: aws.String("invalid-id"),
Attribute: aws.String("launchPermission"),
},
want: errors.New("InvalidAMIID.Malformed"),
},
{
name: "ValidInput",
input: &ec2.ResetImageAttributeInput{
ImageId: aws.String("ami-0123456789abcdef0"),
Attribute: aws.String("launchPermission"),
},
want: nil,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
_, err := ResetImageAttribute(tt.input)
assert.Equal(t, tt.want, err)
})
}
}
+139
View File
@@ -0,0 +1,139 @@
package gateway_ec2_key
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"log/slog"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/mulgadc/hive/hive/awserrors"
"github.com/mulgadc/hive/hive/utils"
)
/*
Sample JSON:
"CreateKeyPair":{
"name":"CreateKeyPair",
"http":{
"method":"POST",
"requestUri":"/"
},
"input":{"shape":"CreateKeyPairRequest"},
"output":{"shape":"KeyPair"}
},
"CreateKeyPairRequest":{
"type":"structure",
"required":["KeyName"],
"members":{
"KeyName":{"shape":"String"},
"DryRun":{
"shape":"Boolean",
"locationName":"dryRun"
},
"KeyType":{"shape":"KeyType"},
"TagSpecifications":{
"shape":"TagSpecificationList",
"locationName":"TagSpecification"
},
"KeyFormat":{"shape":"KeyFormat"}
}
}
*/
func ValidateCreateKeyPairInput(input *ec2.CreateKeyPairInput) (err error) {
// Check required args from JSON above
// required:["KeyName"]
if input == nil {
return errors.New("MissingParameter")
}
if input.KeyName == nil || *input.KeyName == "" {
return errors.New("MissingParameter")
}
return
}
func CreateKeyPair(input *ec2.CreateKeyPairInput) (output ec2.CreateKeyPairOutput, err error) {
// Validate input
err = ValidateCreateKeyPairInput(input)
if err != nil {
return output, err
}
// Marshal to JSON, to send over the wire for processing (NATS)
jsonData, err := json.Marshal(input)
if err != nil {
return output, fmt.Errorf("failed to marshal input to JSON: %w", err)
}
// Run the simulated JSON request via NATS, which will return a JSON response
jsonResp := EC2_Process_CreateKeyPair(jsonData)
// Validate if the response is successful or an error
responseError, err := utils.ValidateErrorPayload(jsonResp)
if err != nil {
if responseError.Code != nil {
slog.Error("CreateKeyPair failed", "err", responseError.Code)
return output, errors.New(*responseError.Code)
}
return output, err
}
// Unmarshal the JSON response back into output struct
err = json.Unmarshal(jsonResp, &output)
if err != nil {
return output, fmt.Errorf("failed to unmarshal JSON response to CreateKeyPairOutput: %w", err)
}
return output, nil
}
func EC2_Process_CreateKeyPair(jsonData []byte) (output []byte) {
var input ec2.CreateKeyPairInput
decoder := json.NewDecoder(bytes.NewReader(jsonData))
decoder.DisallowUnknownFields()
err := decoder.Decode(&input)
if err != nil {
return utils.GenerateErrorPayload(awserrors.ErrorValidationError)
}
// Ensure the payload provided the fields that EC2 expects before proceeding.
err = ValidateCreateKeyPairInput(&input)
if err != nil {
return utils.GenerateErrorPayload(awserrors.ErrorValidationError)
}
// TODO: Add the logic to actually create the key pair.
// This is a placeholder response for testing the framework.
result := &ec2.CreateKeyPairOutput{
KeyFingerprint: aws.String("1f:51:ae:28:bf:89:e9:d8:1f:25:5d:37:2d:7d:b8:ca:9f:f5:f1:6f"),
KeyMaterial: aws.String("-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEA...\n-----END RSA PRIVATE KEY-----"),
KeyName: input.KeyName,
KeyPairId: aws.String("key-0123456789abcdef0"),
}
// Return as JSON, to simulate the NATS response
jsonResponse, err := json.Marshal(result)
if err != nil {
slog.Error("EC2_Process_CreateKeyPair could not marshal output", "err", err)
return nil
}
return jsonResponse
}
+134
View File
@@ -0,0 +1,134 @@
package gateway_ec2_key
import (
"encoding/json"
"errors"
"testing"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/mulgadc/hive/hive/utils"
"github.com/stretchr/testify/assert"
)
func TestValidateCreateKeyPairInput(t *testing.T) {
tests := []struct {
name string
input *ec2.CreateKeyPairInput
want error
}{
{
name: "NilInput",
input: nil,
want: errors.New("MissingParameter"),
},
{
name: "MissingKeyName",
input: &ec2.CreateKeyPairInput{
KeyName: aws.String(""),
},
want: errors.New("MissingParameter"),
},
{
name: "ValidInput",
input: &ec2.CreateKeyPairInput{
KeyName: aws.String("test-key"),
},
want: nil,
},
{
name: "ValidInputWithKeyType",
input: &ec2.CreateKeyPairInput{
KeyName: aws.String("test-key"),
KeyType: aws.String("rsa"),
},
want: nil,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result, err := CreateKeyPair(tt.input)
assert.Equal(t, tt.want, err)
if err != nil {
assert.Empty(t, result.KeyName)
} else {
assert.NotEmpty(t, result.KeyName)
assert.NotEmpty(t, result.KeyFingerprint)
assert.NotEmpty(t, result.KeyMaterial)
assert.NotEmpty(t, result.KeyPairId)
}
})
}
}
func TestEC2ProcessCreateKeyPair(t *testing.T) {
tests := []struct {
name string
payload interface{}
rawJSON []byte
wantValidationErr bool
wantErrCode string
assertFn func(t *testing.T, output ec2.CreateKeyPairOutput)
}{
{
name: "InvalidPayload",
payload: &ec2.DeleteKeyPairInput{},
wantValidationErr: true,
wantErrCode: "ValidationError",
},
{
name: "MissingRequiredFields",
payload: &ec2.CreateKeyPairInput{
KeyName: aws.String(""),
},
wantValidationErr: true,
wantErrCode: "ValidationError",
},
{
name: "ValidCreateKeyPair",
payload: &ec2.CreateKeyPairInput{
KeyName: aws.String("test-key"),
},
assertFn: func(t *testing.T, output ec2.CreateKeyPairOutput) {
assert.NotNil(t, output.KeyName)
assert.Equal(t, "test-key", *output.KeyName)
assert.NotEmpty(t, output.KeyFingerprint)
assert.NotEmpty(t, output.KeyMaterial)
assert.NotEmpty(t, output.KeyPairId)
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var jsonData []byte
var err error
if tt.rawJSON != nil {
jsonData = tt.rawJSON
} else {
jsonData, err = json.Marshal(tt.payload)
assert.NoError(t, err)
}
jsonResp := EC2_Process_CreateKeyPair(jsonData)
responseError, err := utils.ValidateErrorPayload(jsonResp)
if tt.wantValidationErr {
assert.Error(t, err)
assert.Equal(t, tt.wantErrCode, aws.StringValue(responseError.Code))
return
}
assert.NoError(t, err)
var output ec2.CreateKeyPairOutput
assert.NoError(t, json.Unmarshal(jsonResp, &output))
if tt.assertFn != nil {
tt.assertFn(t, output)
}
})
}
}
+146
View File
@@ -0,0 +1,146 @@
package gateway_ec2_key
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"log/slog"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/mulgadc/hive/hive/awserrors"
"github.com/mulgadc/hive/hive/utils"
)
/*
Sample JSON:
"DeleteKeyPair":{
"name":"DeleteKeyPair",
"http":{
"method":"POST",
"requestUri":"/"
},
"input":{"shape":"DeleteKeyPairRequest"},
"output":{"shape":"DeleteKeyPairResult"}
},
"DeleteKeyPairRequest":{
"type":"structure",
"members":{
"KeyName":{"shape":"KeyPairName"},
"KeyPairId":{"shape":"KeyPairId"},
"DryRun":{
"shape":"Boolean",
"locationName":"dryRun"
}
}
},
"DeleteKeyPairResult":{
"type":"structure",
"members":{
"Return":{
"shape":"Boolean",
"locationName":"return"
},
"KeyPairId":{
"shape":"String",
"locationName":"keyPairId"
}
}
}
*/
func ValidateDeleteKeyPairInput(input *ec2.DeleteKeyPairInput) (err error) {
// Check required args from JSON above
// Note: Either KeyName or KeyPairId must be provided
if input == nil {
return errors.New("MissingParameter")
}
// At least one of KeyName or KeyPairId must be provided
if (input.KeyName == nil || *input.KeyName == "") && (input.KeyPairId == nil || *input.KeyPairId == "") {
return errors.New("MissingParameter")
}
return
}
func DeleteKeyPair(input *ec2.DeleteKeyPairInput) (output ec2.DeleteKeyPairOutput, err error) {
// Validate input
err = ValidateDeleteKeyPairInput(input)
if err != nil {
return output, err
}
// Marshal to JSON, to send over the wire for processing (NATS)
jsonData, err := json.Marshal(input)
if err != nil {
return output, fmt.Errorf("failed to marshal input to JSON: %w", err)
}
// Run the simulated JSON request via NATS, which will return a JSON response
jsonResp := EC2_Process_DeleteKeyPair(jsonData)
// Validate if the response is successful or an error
responseError, err := utils.ValidateErrorPayload(jsonResp)
if err != nil {
if responseError.Code != nil {
slog.Error("DeleteKeyPair failed", "err", responseError.Code)
return output, errors.New(*responseError.Code)
}
return output, err
}
// Unmarshal the JSON response back into output struct
err = json.Unmarshal(jsonResp, &output)
if err != nil {
return output, fmt.Errorf("failed to unmarshal JSON response to DeleteKeyPairOutput: %w", err)
}
return output, nil
}
func EC2_Process_DeleteKeyPair(jsonData []byte) (output []byte) {
var input ec2.DeleteKeyPairInput
decoder := json.NewDecoder(bytes.NewReader(jsonData))
decoder.DisallowUnknownFields()
err := decoder.Decode(&input)
if err != nil {
return utils.GenerateErrorPayload(awserrors.ErrorValidationError)
}
// Ensure the payload provided the fields that EC2 expects before proceeding.
err = ValidateDeleteKeyPairInput(&input)
if err != nil {
return utils.GenerateErrorPayload(awserrors.ErrorValidationError)
}
// TODO: Add the logic to actually delete the key pair.
// This is a placeholder response for testing the framework.
result := &ec2.DeleteKeyPairOutput{
Return: aws.Bool(true),
KeyPairId: input.KeyPairId,
}
// Return as JSON, to simulate the NATS response
jsonResponse, err := json.Marshal(result)
if err != nil {
slog.Error("EC2_Process_DeleteKeyPair could not marshal output", "err", err)
return nil
}
return jsonResponse
}
@@ -0,0 +1,64 @@
package gateway_ec2_key
import (
"errors"
"testing"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/stretchr/testify/assert"
)
func TestValidateDeleteKeyPairInput(t *testing.T) {
tests := []struct {
name string
input *ec2.DeleteKeyPairInput
want error
}{
{
name: "NilInput",
input: nil,
want: errors.New("MissingParameter"),
},
{
name: "MissingBothKeyNameAndKeyPairId",
input: &ec2.DeleteKeyPairInput{
KeyName: aws.String(""),
KeyPairId: aws.String(""),
},
want: errors.New("MissingParameter"),
},
{
name: "ValidInputWithKeyName",
input: &ec2.DeleteKeyPairInput{
KeyName: aws.String("test-key"),
},
want: nil,
},
{
name: "ValidInputWithKeyPairId",
input: &ec2.DeleteKeyPairInput{
KeyPairId: aws.String("key-0123456789abcdef0"),
},
want: nil,
},
{
name: "ValidInputWithBoth",
input: &ec2.DeleteKeyPairInput{
KeyName: aws.String("test-key"),
KeyPairId: aws.String("key-0123456789abcdef0"),
},
want: nil,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result, err := DeleteKeyPair(tt.input)
assert.Equal(t, tt.want, err)
if err == nil {
assert.NotNil(t, result.Return)
}
})
}
}
+144
View File
@@ -0,0 +1,144 @@
package gateway_ec2_key
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"log/slog"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/mulgadc/hive/hive/awserrors"
"github.com/mulgadc/hive/hive/utils"
)
/*
Sample JSON:
"DescribeKeyPairs":{
"name":"DescribeKeyPairs",
"http":{
"method":"POST",
"requestUri":"/"
},
"input":{"shape":"DescribeKeyPairsRequest"},
"output":{"shape":"DescribeKeyPairsResult"}
},
"DescribeKeyPairsRequest":{
"type":"structure",
"members":{
"Filters":{
"shape":"FilterList",
"locationName":"Filter"
},
"KeyNames":{
"shape":"KeyNameStringList",
"locationName":"KeyName"
},
"KeyPairIds":{
"shape":"KeyPairIdStringList",
"locationName":"KeyPairId"
},
"DryRun":{
"shape":"Boolean",
"locationName":"dryRun"
},
"IncludePublicKey":{"shape":"Boolean"}
}
}
*/
func ValidateDescribeKeyPairsInput(input *ec2.DescribeKeyPairsInput) (err error) {
// No required fields for DescribeKeyPairs
// All parameters are optional
if input == nil {
return nil
}
return
}
func DescribeKeyPairs(input *ec2.DescribeKeyPairsInput) (output ec2.DescribeKeyPairsOutput, err error) {
// Validate input
err = ValidateDescribeKeyPairsInput(input)
if err != nil {
return output, err
}
// Marshal to JSON, to send over the wire for processing (NATS)
jsonData, err := json.Marshal(input)
if err != nil {
return output, fmt.Errorf("failed to marshal input to JSON: %w", err)
}
// Run the simulated JSON request via NATS, which will return a JSON response
jsonResp := EC2_Process_DescribeKeyPairs(jsonData)
// Validate if the response is successful or an error
responseError, err := utils.ValidateErrorPayload(jsonResp)
if err != nil {
if responseError.Code != nil {
slog.Error("DescribeKeyPairs failed", "err", responseError.Code)
return output, errors.New(*responseError.Code)
}
return output, err
}
// Unmarshal the JSON response back into output struct
err = json.Unmarshal(jsonResp, &output)
if err != nil {
return output, fmt.Errorf("failed to unmarshal JSON response to DescribeKeyPairsOutput: %w", err)
}
return output, nil
}
func EC2_Process_DescribeKeyPairs(jsonData []byte) (output []byte) {
var input ec2.DescribeKeyPairsInput
decoder := json.NewDecoder(bytes.NewReader(jsonData))
decoder.DisallowUnknownFields()
err := decoder.Decode(&input)
if err != nil {
return utils.GenerateErrorPayload(awserrors.ErrorValidationError)
}
// Ensure the payload provided the fields that EC2 expects before proceeding.
err = ValidateDescribeKeyPairsInput(&input)
if err != nil {
return utils.GenerateErrorPayload(awserrors.ErrorValidationError)
}
// TODO: Add the logic to actually query key pairs from the system.
// This is a placeholder response for testing the framework.
result := &ec2.DescribeKeyPairsOutput{
KeyPairs: []*ec2.KeyPairInfo{
{
KeyPairId: aws.String("key-0123456789abcdef0"),
KeyFingerprint: aws.String("1f:51:ae:28:bf:89:e9:d8:1f:25:5d:37:2d:7d:b8:ca:9f:f5:f1:6f"),
KeyName: aws.String("test-key"),
KeyType: aws.String("rsa"),
},
},
}
// Return as JSON, to simulate the NATS response
jsonResponse, err := json.Marshal(result)
if err != nil {
slog.Error("EC2_Process_DescribeKeyPairs could not marshal output", "err", err)
return nil
}
return jsonResponse
}
@@ -0,0 +1,62 @@
package gateway_ec2_key
import (
"testing"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/stretchr/testify/assert"
)
func TestValidateDescribeKeyPairsInput(t *testing.T) {
tests := []struct {
name string
input *ec2.DescribeKeyPairsInput
want error
}{
{
name: "NilInput",
input: nil,
want: nil,
},
{
name: "EmptyInput",
input: &ec2.DescribeKeyPairsInput{},
want: nil,
},
{
name: "ValidInputWithKeyNames",
input: &ec2.DescribeKeyPairsInput{
KeyNames: []*string{aws.String("test-key")},
},
want: nil,
},
{
name: "ValidInputWithKeyPairIds",
input: &ec2.DescribeKeyPairsInput{
KeyPairIds: []*string{aws.String("key-0123456789abcdef0")},
},
want: nil,
},
{
name: "ValidInputWithMultipleKeys",
input: &ec2.DescribeKeyPairsInput{
KeyNames: []*string{
aws.String("test-key-1"),
aws.String("test-key-2"),
},
},
want: nil,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result, err := DescribeKeyPairs(tt.input)
assert.Equal(t, tt.want, err)
if err == nil {
assert.NotNil(t, result.KeyPairs)
}
})
}
}
+153
View File
@@ -0,0 +1,153 @@
package gateway_ec2_key
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"log/slog"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/mulgadc/hive/hive/awserrors"
"github.com/mulgadc/hive/hive/utils"
)
/*
Sample JSON:
"ImportKeyPair":{
"name":"ImportKeyPair",
"http":{
"method":"POST",
"requestUri":"/"
},
"input":{"shape":"ImportKeyPairRequest"},
"output":{"shape":"ImportKeyPairResult"}
},
"ImportKeyPairRequest":{
"type":"structure",
"required":[
"KeyName",
"PublicKeyMaterial"
],
"members":{
"DryRun":{
"shape":"Boolean",
"locationName":"dryRun"
},
"KeyName":{
"shape":"String",
"locationName":"keyName"
},
"PublicKeyMaterial":{
"shape":"Blob",
"locationName":"publicKeyMaterial"
},
"TagSpecifications":{
"shape":"TagSpecificationList",
"locationName":"TagSpecification"
}
}
}
*/
func ValidateImportKeyPairInput(input *ec2.ImportKeyPairInput) (err error) {
// Check required args from JSON above
// required:[
// "KeyName",
// "PublicKeyMaterial"
// ]
if input == nil {
return errors.New("MissingParameter")
}
if input.KeyName == nil || *input.KeyName == "" {
return errors.New("MissingParameter")
}
if input.PublicKeyMaterial == nil || len(input.PublicKeyMaterial) == 0 {
return errors.New("MissingParameter")
}
return
}
func ImportKeyPair(input *ec2.ImportKeyPairInput) (output ec2.ImportKeyPairOutput, err error) {
// Validate input
err = ValidateImportKeyPairInput(input)
if err != nil {
return output, err
}
// Marshal to JSON, to send over the wire for processing (NATS)
jsonData, err := json.Marshal(input)
if err != nil {
return output, fmt.Errorf("failed to marshal input to JSON: %w", err)
}
// Run the simulated JSON request via NATS, which will return a JSON response
jsonResp := EC2_Process_ImportKeyPair(jsonData)
// Validate if the response is successful or an error
responseError, err := utils.ValidateErrorPayload(jsonResp)
if err != nil {
if responseError.Code != nil {
slog.Error("ImportKeyPair failed", "err", responseError.Code)
return output, errors.New(*responseError.Code)
}
return output, err
}
// Unmarshal the JSON response back into output struct
err = json.Unmarshal(jsonResp, &output)
if err != nil {
return output, fmt.Errorf("failed to unmarshal JSON response to ImportKeyPairOutput: %w", err)
}
return output, nil
}
func EC2_Process_ImportKeyPair(jsonData []byte) (output []byte) {
var input ec2.ImportKeyPairInput
decoder := json.NewDecoder(bytes.NewReader(jsonData))
decoder.DisallowUnknownFields()
err := decoder.Decode(&input)
if err != nil {
return utils.GenerateErrorPayload(awserrors.ErrorValidationError)
}
// Ensure the payload provided the fields that EC2 expects before proceeding.
err = ValidateImportKeyPairInput(&input)
if err != nil {
return utils.GenerateErrorPayload(awserrors.ErrorValidationError)
}
// TODO: Add the logic to actually import the key pair.
// This is a placeholder response for testing the framework.
result := &ec2.ImportKeyPairOutput{
KeyFingerprint: aws.String("1f:51:ae:28:bf:89:e9:d8:1f:25:5d:37:2d:7d:b8:ca:9f:f5:f1:6f"),
KeyName: input.KeyName,
KeyPairId: aws.String("key-0987654321fedcba0"),
}
// Return as JSON, to simulate the NATS response
jsonResponse, err := json.Marshal(result)
if err != nil {
slog.Error("EC2_Process_ImportKeyPair could not marshal output", "err", err)
return nil
}
return jsonResponse
}
@@ -0,0 +1,68 @@
package gateway_ec2_key
import (
"errors"
"testing"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/stretchr/testify/assert"
)
func TestValidateImportKeyPairInput(t *testing.T) {
tests := []struct {
name string
input *ec2.ImportKeyPairInput
want error
}{
{
name: "NilInput",
input: nil,
want: errors.New("MissingParameter"),
},
{
name: "MissingKeyName",
input: &ec2.ImportKeyPairInput{
PublicKeyMaterial: []byte("ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQ..."),
},
want: errors.New("MissingParameter"),
},
{
name: "MissingPublicKeyMaterial",
input: &ec2.ImportKeyPairInput{
KeyName: aws.String("test-key"),
},
want: errors.New("MissingParameter"),
},
{
name: "EmptyPublicKeyMaterial",
input: &ec2.ImportKeyPairInput{
KeyName: aws.String("test-key"),
PublicKeyMaterial: []byte{},
},
want: errors.New("MissingParameter"),
},
{
name: "ValidInput",
input: &ec2.ImportKeyPairInput{
KeyName: aws.String("test-key"),
PublicKeyMaterial: []byte("ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQ..."),
},
want: nil,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result, err := ImportKeyPair(tt.input)
assert.Equal(t, tt.want, err)
if err != nil {
assert.Empty(t, result.KeyName)
} else {
assert.NotEmpty(t, result.KeyName)
assert.NotEmpty(t, result.KeyFingerprint)
assert.NotEmpty(t, result.KeyPairId)
}
})
}
}
+3
View File
@@ -0,0 +1,3 @@
Forked from https://github.com/aws/aws-sdk-go
APACHE 2.0 LICENSE
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
@@ -0,0 +1,339 @@
{
"version": "1.0",
"parameters": {
"Region": {
"builtIn": "AWS::Region",
"required": false,
"documentation": "The AWS region used to dispatch the request.",
"type": "String"
},
"UseDualStack": {
"builtIn": "AWS::UseDualStack",
"required": true,
"default": false,
"documentation": "When true, use the dual-stack endpoint. If the configured endpoint does not support dual-stack, dispatching the request MAY return an error.",
"type": "Boolean"
},
"UseFIPS": {
"builtIn": "AWS::UseFIPS",
"required": true,
"default": false,
"documentation": "When true, send this request to the FIPS-compliant regional endpoint. If the configured endpoint does not have a FIPS compliant endpoint, dispatching the request will return an error.",
"type": "Boolean"
},
"Endpoint": {
"builtIn": "SDK::Endpoint",
"required": false,
"documentation": "Override the endpoint used to send this request",
"type": "String"
}
},
"rules": [
{
"conditions": [
{
"fn": "isSet",
"argv": [
{
"ref": "Endpoint"
}
]
}
],
"rules": [
{
"conditions": [
{
"fn": "booleanEquals",
"argv": [
{
"ref": "UseFIPS"
},
true
]
}
],
"error": "Invalid Configuration: FIPS and custom endpoint are not supported",
"type": "error"
},
{
"conditions": [
{
"fn": "booleanEquals",
"argv": [
{
"ref": "UseDualStack"
},
true
]
}
],
"error": "Invalid Configuration: Dualstack and custom endpoint are not supported",
"type": "error"
},
{
"conditions": [],
"endpoint": {
"url": {
"ref": "Endpoint"
},
"properties": {},
"headers": {}
},
"type": "endpoint"
}
],
"type": "tree"
},
{
"conditions": [
{
"fn": "isSet",
"argv": [
{
"ref": "Region"
}
]
}
],
"rules": [
{
"conditions": [
{
"fn": "aws.partition",
"argv": [
{
"ref": "Region"
}
],
"assign": "PartitionResult"
}
],
"rules": [
{
"conditions": [
{
"fn": "booleanEquals",
"argv": [
{
"ref": "UseFIPS"
},
true
]
},
{
"fn": "booleanEquals",
"argv": [
{
"ref": "UseDualStack"
},
true
]
}
],
"rules": [
{
"conditions": [
{
"fn": "booleanEquals",
"argv": [
true,
{
"fn": "getAttr",
"argv": [
{
"ref": "PartitionResult"
},
"supportsFIPS"
]
}
]
},
{
"fn": "booleanEquals",
"argv": [
true,
{
"fn": "getAttr",
"argv": [
{
"ref": "PartitionResult"
},
"supportsDualStack"
]
}
]
}
],
"rules": [
{
"conditions": [],
"endpoint": {
"url": "https://ec2-fips.{Region}.{PartitionResult#dualStackDnsSuffix}",
"properties": {},
"headers": {}
},
"type": "endpoint"
}
],
"type": "tree"
},
{
"conditions": [],
"error": "FIPS and DualStack are enabled, but this partition does not support one or both",
"type": "error"
}
],
"type": "tree"
},
{
"conditions": [
{
"fn": "booleanEquals",
"argv": [
{
"ref": "UseFIPS"
},
true
]
}
],
"rules": [
{
"conditions": [
{
"fn": "booleanEquals",
"argv": [
{
"fn": "getAttr",
"argv": [
{
"ref": "PartitionResult"
},
"supportsFIPS"
]
},
true
]
}
],
"rules": [
{
"conditions": [
{
"fn": "stringEquals",
"argv": [
{
"fn": "getAttr",
"argv": [
{
"ref": "PartitionResult"
},
"name"
]
},
"aws-us-gov"
]
}
],
"endpoint": {
"url": "https://ec2.{Region}.amazonaws.com",
"properties": {},
"headers": {}
},
"type": "endpoint"
},
{
"conditions": [],
"endpoint": {
"url": "https://ec2-fips.{Region}.{PartitionResult#dnsSuffix}",
"properties": {},
"headers": {}
},
"type": "endpoint"
}
],
"type": "tree"
},
{
"conditions": [],
"error": "FIPS is enabled but this partition does not support FIPS",
"type": "error"
}
],
"type": "tree"
},
{
"conditions": [
{
"fn": "booleanEquals",
"argv": [
{
"ref": "UseDualStack"
},
true
]
}
],
"rules": [
{
"conditions": [
{
"fn": "booleanEquals",
"argv": [
true,
{
"fn": "getAttr",
"argv": [
{
"ref": "PartitionResult"
},
"supportsDualStack"
]
}
]
}
],
"rules": [
{
"conditions": [],
"endpoint": {
"url": "https://ec2.{Region}.{PartitionResult#dualStackDnsSuffix}",
"properties": {},
"headers": {}
},
"type": "endpoint"
}
],
"type": "tree"
},
{
"conditions": [],
"error": "DualStack is enabled but this partition does not support DualStack",
"type": "error"
}
],
"type": "tree"
},
{
"conditions": [],
"endpoint": {
"url": "https://ec2.{Region}.{PartitionResult#dnsSuffix}",
"properties": {},
"headers": {}
},
"type": "endpoint"
}
],
"type": "tree"
}
],
"type": "tree"
},
{
"conditions": [],
"error": "Invalid Configuration: Missing Region",
"type": "error"
}
]
}
@@ -0,0 +1,743 @@
{
"testCases": [
{
"documentation": "For region af-south-1 with FIPS disabled and DualStack disabled",
"expect": {
"endpoint": {
"url": "https://ec2.af-south-1.amazonaws.com"
}
},
"params": {
"Region": "af-south-1",
"UseFIPS": false,
"UseDualStack": false
}
},
{
"documentation": "For region ap-east-1 with FIPS disabled and DualStack disabled",
"expect": {
"endpoint": {
"url": "https://ec2.ap-east-1.amazonaws.com"
}
},
"params": {
"Region": "ap-east-1",
"UseFIPS": false,
"UseDualStack": false
}
},
{
"documentation": "For region ap-northeast-1 with FIPS disabled and DualStack disabled",
"expect": {
"endpoint": {
"url": "https://ec2.ap-northeast-1.amazonaws.com"
}
},
"params": {
"Region": "ap-northeast-1",
"UseFIPS": false,
"UseDualStack": false
}
},
{
"documentation": "For region ap-northeast-2 with FIPS disabled and DualStack disabled",
"expect": {
"endpoint": {
"url": "https://ec2.ap-northeast-2.amazonaws.com"
}
},
"params": {
"Region": "ap-northeast-2",
"UseFIPS": false,
"UseDualStack": false
}
},
{
"documentation": "For region ap-northeast-3 with FIPS disabled and DualStack disabled",
"expect": {
"endpoint": {
"url": "https://ec2.ap-northeast-3.amazonaws.com"
}
},
"params": {
"Region": "ap-northeast-3",
"UseFIPS": false,
"UseDualStack": false
}
},
{
"documentation": "For region ap-south-1 with FIPS disabled and DualStack disabled",
"expect": {
"endpoint": {
"url": "https://ec2.ap-south-1.amazonaws.com"
}
},
"params": {
"Region": "ap-south-1",
"UseFIPS": false,
"UseDualStack": false
}
},
{
"documentation": "For region ap-south-1 with FIPS disabled and DualStack enabled",
"expect": {
"endpoint": {
"url": "https://ec2.ap-south-1.api.aws"
}
},
"params": {
"Region": "ap-south-1",
"UseFIPS": false,
"UseDualStack": true
}
},
{
"documentation": "For region ap-southeast-1 with FIPS disabled and DualStack disabled",
"expect": {
"endpoint": {
"url": "https://ec2.ap-southeast-1.amazonaws.com"
}
},
"params": {
"Region": "ap-southeast-1",
"UseFIPS": false,
"UseDualStack": false
}
},
{
"documentation": "For region ap-southeast-2 with FIPS disabled and DualStack disabled",
"expect": {
"endpoint": {
"url": "https://ec2.ap-southeast-2.amazonaws.com"
}
},
"params": {
"Region": "ap-southeast-2",
"UseFIPS": false,
"UseDualStack": false
}
},
{
"documentation": "For region ap-southeast-3 with FIPS disabled and DualStack disabled",
"expect": {
"endpoint": {
"url": "https://ec2.ap-southeast-3.amazonaws.com"
}
},
"params": {
"Region": "ap-southeast-3",
"UseFIPS": false,
"UseDualStack": false
}
},
{
"documentation": "For region ca-central-1 with FIPS disabled and DualStack disabled",
"expect": {
"endpoint": {
"url": "https://ec2.ca-central-1.amazonaws.com"
}
},
"params": {
"Region": "ca-central-1",
"UseFIPS": false,
"UseDualStack": false
}
},
{
"documentation": "For region ca-central-1 with FIPS enabled and DualStack disabled",
"expect": {
"endpoint": {
"url": "https://ec2-fips.ca-central-1.amazonaws.com"
}
},
"params": {
"Region": "ca-central-1",
"UseFIPS": true,
"UseDualStack": false
}
},
{
"documentation": "For region eu-central-1 with FIPS disabled and DualStack disabled",
"expect": {
"endpoint": {
"url": "https://ec2.eu-central-1.amazonaws.com"
}
},
"params": {
"Region": "eu-central-1",
"UseFIPS": false,
"UseDualStack": false
}
},
{
"documentation": "For region eu-north-1 with FIPS disabled and DualStack disabled",
"expect": {
"endpoint": {
"url": "https://ec2.eu-north-1.amazonaws.com"
}
},
"params": {
"Region": "eu-north-1",
"UseFIPS": false,
"UseDualStack": false
}
},
{
"documentation": "For region eu-south-1 with FIPS disabled and DualStack disabled",
"expect": {
"endpoint": {
"url": "https://ec2.eu-south-1.amazonaws.com"
}
},
"params": {
"Region": "eu-south-1",
"UseFIPS": false,
"UseDualStack": false
}
},
{
"documentation": "For region eu-west-1 with FIPS disabled and DualStack disabled",
"expect": {
"endpoint": {
"url": "https://ec2.eu-west-1.amazonaws.com"
}
},
"params": {
"Region": "eu-west-1",
"UseFIPS": false,
"UseDualStack": false
}
},
{
"documentation": "For region eu-west-1 with FIPS disabled and DualStack enabled",
"expect": {
"endpoint": {
"url": "https://ec2.eu-west-1.api.aws"
}
},
"params": {
"Region": "eu-west-1",
"UseFIPS": false,
"UseDualStack": true
}
},
{
"documentation": "For region eu-west-2 with FIPS disabled and DualStack disabled",
"expect": {
"endpoint": {
"url": "https://ec2.eu-west-2.amazonaws.com"
}
},
"params": {
"Region": "eu-west-2",
"UseFIPS": false,
"UseDualStack": false
}
},
{
"documentation": "For region eu-west-3 with FIPS disabled and DualStack disabled",
"expect": {
"endpoint": {
"url": "https://ec2.eu-west-3.amazonaws.com"
}
},
"params": {
"Region": "eu-west-3",
"UseFIPS": false,
"UseDualStack": false
}
},
{
"documentation": "For region me-south-1 with FIPS disabled and DualStack disabled",
"expect": {
"endpoint": {
"url": "https://ec2.me-south-1.amazonaws.com"
}
},
"params": {
"Region": "me-south-1",
"UseFIPS": false,
"UseDualStack": false
}
},
{
"documentation": "For region sa-east-1 with FIPS disabled and DualStack disabled",
"expect": {
"endpoint": {
"url": "https://ec2.sa-east-1.amazonaws.com"
}
},
"params": {
"Region": "sa-east-1",
"UseFIPS": false,
"UseDualStack": false
}
},
{
"documentation": "For region sa-east-1 with FIPS disabled and DualStack enabled",
"expect": {
"endpoint": {
"url": "https://ec2.sa-east-1.api.aws"
}
},
"params": {
"Region": "sa-east-1",
"UseFIPS": false,
"UseDualStack": true
}
},
{
"documentation": "For region us-east-1 with FIPS disabled and DualStack disabled",
"expect": {
"endpoint": {
"url": "https://ec2.us-east-1.amazonaws.com"
}
},
"params": {
"Region": "us-east-1",
"UseFIPS": false,
"UseDualStack": false
}
},
{
"documentation": "For region us-east-1 with FIPS enabled and DualStack disabled",
"expect": {
"endpoint": {
"url": "https://ec2-fips.us-east-1.amazonaws.com"
}
},
"params": {
"Region": "us-east-1",
"UseFIPS": true,
"UseDualStack": false
}
},
{
"documentation": "For region us-east-1 with FIPS disabled and DualStack enabled",
"expect": {
"endpoint": {
"url": "https://ec2.us-east-1.api.aws"
}
},
"params": {
"Region": "us-east-1",
"UseFIPS": false,
"UseDualStack": true
}
},
{
"documentation": "For region us-east-2 with FIPS disabled and DualStack disabled",
"expect": {
"endpoint": {
"url": "https://ec2.us-east-2.amazonaws.com"
}
},
"params": {
"Region": "us-east-2",
"UseFIPS": false,
"UseDualStack": false
}
},
{
"documentation": "For region us-east-2 with FIPS enabled and DualStack disabled",
"expect": {
"endpoint": {
"url": "https://ec2-fips.us-east-2.amazonaws.com"
}
},
"params": {
"Region": "us-east-2",
"UseFIPS": true,
"UseDualStack": false
}
},
{
"documentation": "For region us-east-2 with FIPS disabled and DualStack enabled",
"expect": {
"endpoint": {
"url": "https://ec2.us-east-2.api.aws"
}
},
"params": {
"Region": "us-east-2",
"UseFIPS": false,
"UseDualStack": true
}
},
{
"documentation": "For region us-west-1 with FIPS disabled and DualStack disabled",
"expect": {
"endpoint": {
"url": "https://ec2.us-west-1.amazonaws.com"
}
},
"params": {
"Region": "us-west-1",
"UseFIPS": false,
"UseDualStack": false
}
},
{
"documentation": "For region us-west-1 with FIPS enabled and DualStack disabled",
"expect": {
"endpoint": {
"url": "https://ec2-fips.us-west-1.amazonaws.com"
}
},
"params": {
"Region": "us-west-1",
"UseFIPS": true,
"UseDualStack": false
}
},
{
"documentation": "For region us-west-2 with FIPS disabled and DualStack disabled",
"expect": {
"endpoint": {
"url": "https://ec2.us-west-2.amazonaws.com"
}
},
"params": {
"Region": "us-west-2",
"UseFIPS": false,
"UseDualStack": false
}
},
{
"documentation": "For region us-west-2 with FIPS enabled and DualStack disabled",
"expect": {
"endpoint": {
"url": "https://ec2-fips.us-west-2.amazonaws.com"
}
},
"params": {
"Region": "us-west-2",
"UseFIPS": true,
"UseDualStack": false
}
},
{
"documentation": "For region us-west-2 with FIPS disabled and DualStack enabled",
"expect": {
"endpoint": {
"url": "https://ec2.us-west-2.api.aws"
}
},
"params": {
"Region": "us-west-2",
"UseFIPS": false,
"UseDualStack": true
}
},
{
"documentation": "For region us-east-1 with FIPS enabled and DualStack enabled",
"expect": {
"endpoint": {
"url": "https://ec2-fips.us-east-1.api.aws"
}
},
"params": {
"Region": "us-east-1",
"UseFIPS": true,
"UseDualStack": true
}
},
{
"documentation": "For region cn-north-1 with FIPS disabled and DualStack disabled",
"expect": {
"endpoint": {
"url": "https://ec2.cn-north-1.amazonaws.com.cn"
}
},
"params": {
"Region": "cn-north-1",
"UseFIPS": false,
"UseDualStack": false
}
},
{
"documentation": "For region cn-northwest-1 with FIPS disabled and DualStack disabled",
"expect": {
"endpoint": {
"url": "https://ec2.cn-northwest-1.amazonaws.com.cn"
}
},
"params": {
"Region": "cn-northwest-1",
"UseFIPS": false,
"UseDualStack": false
}
},
{
"documentation": "For region cn-north-1 with FIPS enabled and DualStack enabled",
"expect": {
"endpoint": {
"url": "https://ec2-fips.cn-north-1.api.amazonwebservices.com.cn"
}
},
"params": {
"Region": "cn-north-1",
"UseFIPS": true,
"UseDualStack": true
}
},
{
"documentation": "For region cn-north-1 with FIPS enabled and DualStack disabled",
"expect": {
"endpoint": {
"url": "https://ec2-fips.cn-north-1.amazonaws.com.cn"
}
},
"params": {
"Region": "cn-north-1",
"UseFIPS": true,
"UseDualStack": false
}
},
{
"documentation": "For region cn-north-1 with FIPS disabled and DualStack enabled",
"expect": {
"endpoint": {
"url": "https://ec2.cn-north-1.api.amazonwebservices.com.cn"
}
},
"params": {
"Region": "cn-north-1",
"UseFIPS": false,
"UseDualStack": true
}
},
{
"documentation": "For region us-gov-east-1 with FIPS disabled and DualStack disabled",
"expect": {
"endpoint": {
"url": "https://ec2.us-gov-east-1.amazonaws.com"
}
},
"params": {
"Region": "us-gov-east-1",
"UseFIPS": false,
"UseDualStack": false
}
},
{
"documentation": "For region us-gov-west-1 with FIPS disabled and DualStack disabled",
"expect": {
"endpoint": {
"url": "https://ec2.us-gov-west-1.amazonaws.com"
}
},
"params": {
"Region": "us-gov-west-1",
"UseFIPS": false,
"UseDualStack": false
}
},
{
"documentation": "For region us-gov-east-1 with FIPS enabled and DualStack enabled",
"expect": {
"endpoint": {
"url": "https://ec2-fips.us-gov-east-1.api.aws"
}
},
"params": {
"Region": "us-gov-east-1",
"UseFIPS": true,
"UseDualStack": true
}
},
{
"documentation": "For region us-gov-east-1 with FIPS enabled and DualStack disabled",
"expect": {
"endpoint": {
"url": "https://ec2.us-gov-east-1.amazonaws.com"
}
},
"params": {
"Region": "us-gov-east-1",
"UseFIPS": true,
"UseDualStack": false
}
},
{
"documentation": "For region us-gov-east-1 with FIPS disabled and DualStack enabled",
"expect": {
"endpoint": {
"url": "https://ec2.us-gov-east-1.api.aws"
}
},
"params": {
"Region": "us-gov-east-1",
"UseFIPS": false,
"UseDualStack": true
}
},
{
"documentation": "For region us-iso-east-1 with FIPS disabled and DualStack disabled",
"expect": {
"endpoint": {
"url": "https://ec2.us-iso-east-1.c2s.ic.gov"
}
},
"params": {
"Region": "us-iso-east-1",
"UseFIPS": false,
"UseDualStack": false
}
},
{
"documentation": "For region us-iso-west-1 with FIPS disabled and DualStack disabled",
"expect": {
"endpoint": {
"url": "https://ec2.us-iso-west-1.c2s.ic.gov"
}
},
"params": {
"Region": "us-iso-west-1",
"UseFIPS": false,
"UseDualStack": false
}
},
{
"documentation": "For region us-iso-east-1 with FIPS enabled and DualStack enabled",
"expect": {
"error": "FIPS and DualStack are enabled, but this partition does not support one or both"
},
"params": {
"Region": "us-iso-east-1",
"UseFIPS": true,
"UseDualStack": true
}
},
{
"documentation": "For region us-iso-east-1 with FIPS enabled and DualStack disabled",
"expect": {
"endpoint": {
"url": "https://ec2-fips.us-iso-east-1.c2s.ic.gov"
}
},
"params": {
"Region": "us-iso-east-1",
"UseFIPS": true,
"UseDualStack": false
}
},
{
"documentation": "For region us-iso-east-1 with FIPS disabled and DualStack enabled",
"expect": {
"error": "DualStack is enabled but this partition does not support DualStack"
},
"params": {
"Region": "us-iso-east-1",
"UseFIPS": false,
"UseDualStack": true
}
},
{
"documentation": "For region us-isob-east-1 with FIPS disabled and DualStack disabled",
"expect": {
"endpoint": {
"url": "https://ec2.us-isob-east-1.sc2s.sgov.gov"
}
},
"params": {
"Region": "us-isob-east-1",
"UseFIPS": false,
"UseDualStack": false
}
},
{
"documentation": "For region us-isob-east-1 with FIPS enabled and DualStack enabled",
"expect": {
"error": "FIPS and DualStack are enabled, but this partition does not support one or both"
},
"params": {
"Region": "us-isob-east-1",
"UseFIPS": true,
"UseDualStack": true
}
},
{
"documentation": "For region us-isob-east-1 with FIPS enabled and DualStack disabled",
"expect": {
"endpoint": {
"url": "https://ec2-fips.us-isob-east-1.sc2s.sgov.gov"
}
},
"params": {
"Region": "us-isob-east-1",
"UseFIPS": true,
"UseDualStack": false
}
},
{
"documentation": "For region us-isob-east-1 with FIPS disabled and DualStack enabled",
"expect": {
"error": "DualStack is enabled but this partition does not support DualStack"
},
"params": {
"Region": "us-isob-east-1",
"UseFIPS": false,
"UseDualStack": true
}
},
{
"documentation": "For custom endpoint with region set and fips disabled and dualstack disabled",
"expect": {
"endpoint": {
"url": "https://example.com"
}
},
"params": {
"Region": "us-east-1",
"UseFIPS": false,
"UseDualStack": false,
"Endpoint": "https://example.com"
}
},
{
"documentation": "For custom endpoint with region not set and fips disabled and dualstack disabled",
"expect": {
"endpoint": {
"url": "https://example.com"
}
},
"params": {
"UseFIPS": false,
"UseDualStack": false,
"Endpoint": "https://example.com"
}
},
{
"documentation": "For custom endpoint with fips enabled and dualstack disabled",
"expect": {
"error": "Invalid Configuration: FIPS and custom endpoint are not supported"
},
"params": {
"Region": "us-east-1",
"UseFIPS": true,
"UseDualStack": false,
"Endpoint": "https://example.com"
}
},
{
"documentation": "For custom endpoint with fips disabled and dualstack enabled",
"expect": {
"error": "Invalid Configuration: Dualstack and custom endpoint are not supported"
},
"params": {
"Region": "us-east-1",
"UseFIPS": false,
"UseDualStack": true,
"Endpoint": "https://example.com"
}
},
{
"documentation": "Missing region",
"expect": {
"error": "Invalid Configuration: Missing Region"
}
}
],
"version": "1.0"
}
File diff suppressed because it is too large Load Diff
+885
View File
@@ -0,0 +1,885 @@
{
"pagination": {
"DescribeAccountAttributes": {
"result_key": "AccountAttributes"
},
"DescribeAddressTransfers": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "AddressTransfers"
},
"DescribeAddresses": {
"result_key": "Addresses"
},
"DescribeAddressesAttribute": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "Addresses"
},
"DescribeAvailabilityZones": {
"result_key": "AvailabilityZones"
},
"DescribeAwsNetworkPerformanceMetricSubscriptions": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "Subscriptions"
},
"DescribeBundleTasks": {
"result_key": "BundleTasks"
},
"DescribeByoipCidrs": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "ByoipCidrs"
},
"DescribeCapacityBlockOfferings": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "CapacityBlockOfferings"
},
"DescribeCapacityReservationFleets": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "CapacityReservationFleets"
},
"DescribeCapacityReservations": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "CapacityReservations"
},
"DescribeCarrierGateways": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "CarrierGateways"
},
"DescribeClassicLinkInstances": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "Instances"
},
"DescribeClientVpnAuthorizationRules": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "AuthorizationRules"
},
"DescribeClientVpnConnections": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "Connections"
},
"DescribeClientVpnEndpoints": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "ClientVpnEndpoints"
},
"DescribeClientVpnRoutes": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "Routes"
},
"DescribeClientVpnTargetNetworks": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "ClientVpnTargetNetworks"
},
"DescribeCoipPools": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "CoipPools"
},
"DescribeConversionTasks": {
"result_key": "ConversionTasks"
},
"DescribeCustomerGateways": {
"result_key": "CustomerGateways"
},
"DescribeDhcpOptions": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "DhcpOptions"
},
"DescribeEgressOnlyInternetGateways": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "EgressOnlyInternetGateways"
},
"DescribeExportImageTasks": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "ExportImageTasks"
},
"DescribeExportTasks": {
"result_key": "ExportTasks"
},
"DescribeFastLaunchImages": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "FastLaunchImages"
},
"DescribeFastSnapshotRestores": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "FastSnapshotRestores"
},
"DescribeFleets": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "Fleets"
},
"DescribeFlowLogs": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "FlowLogs"
},
"DescribeFpgaImages": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "FpgaImages"
},
"DescribeHostReservationOfferings": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "OfferingSet"
},
"DescribeHostReservations": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "HostReservationSet"
},
"DescribeHosts": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "Hosts"
},
"DescribeIamInstanceProfileAssociations": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "IamInstanceProfileAssociations"
},
"DescribeImages": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "Images"
},
"DescribeImportImageTasks": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "ImportImageTasks"
},
"DescribeImportSnapshotTasks": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "ImportSnapshotTasks"
},
"DescribeInstanceConnectEndpoints": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "InstanceConnectEndpoints"
},
"DescribeInstanceCreditSpecifications": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "InstanceCreditSpecifications"
},
"DescribeInstanceEventWindows": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "InstanceEventWindows"
},
"DescribeInstanceStatus": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "InstanceStatuses"
},
"DescribeInstanceTopology": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "Instances"
},
"DescribeInstanceTypeOfferings": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "InstanceTypeOfferings"
},
"DescribeInstanceTypes": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "InstanceTypes"
},
"DescribeInstances": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "Reservations"
},
"DescribeInternetGateways": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "InternetGateways"
},
"DescribeIpamPools": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "IpamPools"
},
"DescribeIpamResourceDiscoveries": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "IpamResourceDiscoveries"
},
"DescribeIpamResourceDiscoveryAssociations": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "IpamResourceDiscoveryAssociations"
},
"DescribeIpamScopes": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "IpamScopes"
},
"DescribeIpams": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "Ipams"
},
"DescribeIpv6Pools": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "Ipv6Pools"
},
"DescribeKeyPairs": {
"result_key": "KeyPairs"
},
"DescribeLaunchTemplateVersions": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "LaunchTemplateVersions"
},
"DescribeLaunchTemplates": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "LaunchTemplates"
},
"DescribeLocalGatewayRouteTableVirtualInterfaceGroupAssociations": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "LocalGatewayRouteTableVirtualInterfaceGroupAssociations"
},
"DescribeLocalGatewayRouteTableVpcAssociations": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "LocalGatewayRouteTableVpcAssociations"
},
"DescribeLocalGatewayRouteTables": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "LocalGatewayRouteTables"
},
"DescribeLocalGatewayVirtualInterfaceGroups": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "LocalGatewayVirtualInterfaceGroups"
},
"DescribeLocalGatewayVirtualInterfaces": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "LocalGatewayVirtualInterfaces"
},
"DescribeLocalGateways": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "LocalGateways"
},
"DescribeMacHosts": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "MacHosts"
},
"DescribeManagedPrefixLists": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "PrefixLists"
},
"DescribeMovingAddresses": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "MovingAddressStatuses"
},
"DescribeNatGateways": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "NatGateways"
},
"DescribeNetworkAcls": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "NetworkAcls"
},
"DescribeNetworkInsightsAccessScopeAnalyses": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "NetworkInsightsAccessScopeAnalyses"
},
"DescribeNetworkInsightsAccessScopes": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "NetworkInsightsAccessScopes"
},
"DescribeNetworkInsightsAnalyses": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "NetworkInsightsAnalyses"
},
"DescribeNetworkInsightsPaths": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "NetworkInsightsPaths"
},
"DescribeNetworkInterfacePermissions": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "NetworkInterfacePermissions"
},
"DescribeNetworkInterfaces": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "NetworkInterfaces"
},
"DescribePlacementGroups": {
"result_key": "PlacementGroups"
},
"DescribePrefixLists": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "PrefixLists"
},
"DescribePrincipalIdFormat": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "Principals"
},
"DescribePublicIpv4Pools": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "PublicIpv4Pools"
},
"DescribeRegions": {
"result_key": "Regions"
},
"DescribeReplaceRootVolumeTasks": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "ReplaceRootVolumeTasks"
},
"DescribeReservedInstances": {
"result_key": "ReservedInstances"
},
"DescribeReservedInstancesListings": {
"result_key": "ReservedInstancesListings"
},
"DescribeReservedInstancesModifications": {
"input_token": "NextToken",
"output_token": "NextToken",
"result_key": "ReservedInstancesModifications"
},
"DescribeReservedInstancesOfferings": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "ReservedInstancesOfferings"
},
"DescribeRouteTables": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "RouteTables"
},
"DescribeScheduledInstanceAvailability": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "ScheduledInstanceAvailabilitySet"
},
"DescribeScheduledInstances": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "ScheduledInstanceSet"
},
"DescribeSecurityGroupRules": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "SecurityGroupRules"
},
"DescribeSecurityGroups": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "SecurityGroups"
},
"DescribeSnapshotTierStatus": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "SnapshotTierStatuses"
},
"DescribeSnapshots": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "Snapshots"
},
"DescribeSpotFleetRequests": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "SpotFleetRequestConfigs"
},
"DescribeSpotInstanceRequests": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "SpotInstanceRequests"
},
"DescribeSpotPriceHistory": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "SpotPriceHistory"
},
"DescribeStaleSecurityGroups": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "StaleSecurityGroupSet"
},
"DescribeStoreImageTasks": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "StoreImageTaskResults"
},
"DescribeSubnets": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "Subnets"
},
"DescribeTags": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "Tags"
},
"DescribeTrafficMirrorFilters": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "TrafficMirrorFilters"
},
"DescribeTrafficMirrorSessions": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "TrafficMirrorSessions"
},
"DescribeTrafficMirrorTargets": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "TrafficMirrorTargets"
},
"DescribeTransitGatewayAttachments": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "TransitGatewayAttachments"
},
"DescribeTransitGatewayConnectPeers": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "TransitGatewayConnectPeers"
},
"DescribeTransitGatewayConnects": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "TransitGatewayConnects"
},
"DescribeTransitGatewayMulticastDomains": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "TransitGatewayMulticastDomains"
},
"DescribeTransitGatewayPeeringAttachments": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "TransitGatewayPeeringAttachments"
},
"DescribeTransitGatewayPolicyTables": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "TransitGatewayPolicyTables"
},
"DescribeTransitGatewayRouteTableAnnouncements": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "TransitGatewayRouteTableAnnouncements"
},
"DescribeTransitGatewayRouteTables": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "TransitGatewayRouteTables"
},
"DescribeTransitGatewayVpcAttachments": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "TransitGatewayVpcAttachments"
},
"DescribeTransitGateways": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "TransitGateways"
},
"DescribeTrunkInterfaceAssociations": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "InterfaceAssociations"
},
"DescribeVerifiedAccessEndpoints": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "VerifiedAccessEndpoints"
},
"DescribeVerifiedAccessGroups": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "VerifiedAccessGroups"
},
"DescribeVerifiedAccessInstanceLoggingConfigurations": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "LoggingConfigurations"
},
"DescribeVerifiedAccessInstances": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "VerifiedAccessInstances"
},
"DescribeVerifiedAccessTrustProviders": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "VerifiedAccessTrustProviders"
},
"DescribeVolumeStatus": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "VolumeStatuses"
},
"DescribeVolumes": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "Volumes"
},
"DescribeVolumesModifications": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "VolumesModifications"
},
"DescribeVpcClassicLinkDnsSupport": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "Vpcs"
},
"DescribeVpcEndpointConnectionNotifications": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "ConnectionNotificationSet"
},
"DescribeVpcEndpointConnections": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "VpcEndpointConnections"
},
"DescribeVpcEndpointServiceConfigurations": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "ServiceConfigurations"
},
"DescribeVpcEndpointServicePermissions": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "AllowedPrincipals"
},
"DescribeVpcEndpoints": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "VpcEndpoints"
},
"DescribeVpcPeeringConnections": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "VpcPeeringConnections"
},
"DescribeVpcs": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "Vpcs"
},
"DescribeVpnConnections": {
"result_key": "VpnConnections"
},
"DescribeVpnGateways": {
"result_key": "VpnGateways"
},
"GetAssociatedIpv6PoolCidrs": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "Ipv6CidrAssociations"
},
"GetAwsNetworkPerformanceData": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "DataResponses"
},
"GetGroupsForCapacityReservation": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "CapacityReservationGroups"
},
"GetInstanceTypesFromInstanceRequirements": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "InstanceTypes"
},
"GetIpamAddressHistory": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "HistoryRecords"
},
"GetIpamDiscoveredAccounts": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "IpamDiscoveredAccounts"
},
"GetIpamDiscoveredResourceCidrs": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "IpamDiscoveredResourceCidrs"
},
"GetIpamPoolAllocations": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "IpamPoolAllocations"
},
"GetIpamPoolCidrs": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "IpamPoolCidrs"
},
"GetIpamResourceCidrs": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "IpamResourceCidrs"
},
"GetManagedPrefixListAssociations": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "PrefixListAssociations"
},
"GetManagedPrefixListEntries": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "Entries"
},
"GetNetworkInsightsAccessScopeAnalysisFindings": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "AnalysisFindings"
},
"GetSecurityGroupsForVpc": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "SecurityGroupForVpcs"
},
"GetSpotPlacementScores": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "SpotPlacementScores"
},
"GetTransitGatewayAttachmentPropagations": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "TransitGatewayAttachmentPropagations"
},
"GetTransitGatewayMulticastDomainAssociations": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "MulticastDomainAssociations"
},
"GetTransitGatewayPolicyTableAssociations": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "Associations"
},
"GetTransitGatewayPrefixListReferences": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "TransitGatewayPrefixListReferences"
},
"GetTransitGatewayRouteTableAssociations": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "Associations"
},
"GetTransitGatewayRouteTablePropagations": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "TransitGatewayRouteTablePropagations"
},
"GetVpnConnectionDeviceTypes": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "VpnConnectionDeviceTypes"
},
"ListImagesInRecycleBin": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "Images"
},
"ListSnapshotsInRecycleBin": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "Snapshots"
},
"SearchLocalGatewayRoutes": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "Routes"
},
"SearchTransitGatewayMulticastGroups": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "MulticastGroups"
}
}
}
+31
View File
@@ -0,0 +1,31 @@
{
"version": 2,
"testCases": [
{
"id": "DescribeRegionsSuccess",
"operationName": "DescribeRegions",
"input": {},
"expectation": {
"success": {}
},
"config": {
"region": "us-west-2"
}
},
{
"id": "DescribeInstancesFailure",
"operationName": "DescribeInstances",
"input": {
"InstanceIds": [
"i-12345678"
]
},
"expectation": {
"failure": {}
},
"config": {
"region": "us-west-2"
}
}
]
}
+20
View File
@@ -0,0 +1,20 @@
{
"version": 1,
"defaultRegion": "us-west-2",
"testCases": [
{
"operationName": "DescribeRegions",
"input": {},
"errorExpectedFromService": false
},
{
"operationName": "DescribeInstances",
"input": {
"InstanceIds": [
"i-12345678"
]
},
"errorExpectedFromService": true
}
]
}
+726
View File
@@ -0,0 +1,726 @@
{
"version": 2,
"waiters": {
"InstanceExists": {
"delay": 5,
"maxAttempts": 40,
"operation": "DescribeInstances",
"acceptors": [
{
"matcher": "path",
"expected": true,
"argument": "length(Reservations[]) > `0`",
"state": "success"
},
{
"matcher": "error",
"expected": "InvalidInstanceID.NotFound",
"state": "retry"
}
]
},
"BundleTaskComplete": {
"delay": 15,
"operation": "DescribeBundleTasks",
"maxAttempts": 40,
"acceptors": [
{
"expected": "complete",
"matcher": "pathAll",
"state": "success",
"argument": "BundleTasks[].State"
},
{
"expected": "failed",
"matcher": "pathAny",
"state": "failure",
"argument": "BundleTasks[].State"
}
]
},
"ConversionTaskCancelled": {
"delay": 15,
"operation": "DescribeConversionTasks",
"maxAttempts": 40,
"acceptors": [
{
"expected": "cancelled",
"matcher": "pathAll",
"state": "success",
"argument": "ConversionTasks[].State"
}
]
},
"ConversionTaskCompleted": {
"delay": 15,
"operation": "DescribeConversionTasks",
"maxAttempts": 40,
"acceptors": [
{
"expected": "completed",
"matcher": "pathAll",
"state": "success",
"argument": "ConversionTasks[].State"
},
{
"expected": "cancelled",
"matcher": "pathAny",
"state": "failure",
"argument": "ConversionTasks[].State"
},
{
"expected": "cancelling",
"matcher": "pathAny",
"state": "failure",
"argument": "ConversionTasks[].State"
}
]
},
"ConversionTaskDeleted": {
"delay": 15,
"operation": "DescribeConversionTasks",
"maxAttempts": 40,
"acceptors": [
{
"expected": "deleted",
"matcher": "pathAll",
"state": "success",
"argument": "ConversionTasks[].State"
}
]
},
"CustomerGatewayAvailable": {
"delay": 15,
"operation": "DescribeCustomerGateways",
"maxAttempts": 40,
"acceptors": [
{
"expected": "available",
"matcher": "pathAll",
"state": "success",
"argument": "CustomerGateways[].State"
},
{
"expected": "deleted",
"matcher": "pathAny",
"state": "failure",
"argument": "CustomerGateways[].State"
},
{
"expected": "deleting",
"matcher": "pathAny",
"state": "failure",
"argument": "CustomerGateways[].State"
}
]
},
"ExportTaskCancelled": {
"delay": 15,
"operation": "DescribeExportTasks",
"maxAttempts": 40,
"acceptors": [
{
"expected": "cancelled",
"matcher": "pathAll",
"state": "success",
"argument": "ExportTasks[].State"
}
]
},
"ExportTaskCompleted": {
"delay": 15,
"operation": "DescribeExportTasks",
"maxAttempts": 40,
"acceptors": [
{
"expected": "completed",
"matcher": "pathAll",
"state": "success",
"argument": "ExportTasks[].State"
}
]
},
"ImageExists": {
"operation": "DescribeImages",
"maxAttempts": 40,
"delay": 15,
"acceptors": [
{
"matcher": "path",
"expected": true,
"argument": "length(Images[]) > `0`",
"state": "success"
},
{
"matcher": "error",
"expected": "InvalidAMIID.NotFound",
"state": "retry"
}
]
},
"ImageAvailable": {
"operation": "DescribeImages",
"maxAttempts": 40,
"delay": 15,
"acceptors": [
{
"state": "success",
"matcher": "pathAll",
"argument": "Images[].State",
"expected": "available"
},
{
"state": "failure",
"matcher": "pathAny",
"argument": "Images[].State",
"expected": "failed"
}
]
},
"InstanceRunning": {
"delay": 15,
"operation": "DescribeInstances",
"maxAttempts": 40,
"acceptors": [
{
"expected": "running",
"matcher": "pathAll",
"state": "success",
"argument": "Reservations[].Instances[].State.Name"
},
{
"expected": "shutting-down",
"matcher": "pathAny",
"state": "failure",
"argument": "Reservations[].Instances[].State.Name"
},
{
"expected": "terminated",
"matcher": "pathAny",
"state": "failure",
"argument": "Reservations[].Instances[].State.Name"
},
{
"expected": "stopping",
"matcher": "pathAny",
"state": "failure",
"argument": "Reservations[].Instances[].State.Name"
},
{
"matcher": "error",
"expected": "InvalidInstanceID.NotFound",
"state": "retry"
}
]
},
"InstanceStatusOk": {
"operation": "DescribeInstanceStatus",
"maxAttempts": 40,
"delay": 15,
"acceptors": [
{
"state": "success",
"matcher": "pathAll",
"argument": "InstanceStatuses[].InstanceStatus.Status",
"expected": "ok"
},
{
"matcher": "error",
"expected": "InvalidInstanceID.NotFound",
"state": "retry"
}
]
},
"InstanceStopped": {
"delay": 15,
"operation": "DescribeInstances",
"maxAttempts": 40,
"acceptors": [
{
"expected": "stopped",
"matcher": "pathAll",
"state": "success",
"argument": "Reservations[].Instances[].State.Name"
},
{
"expected": "pending",
"matcher": "pathAny",
"state": "failure",
"argument": "Reservations[].Instances[].State.Name"
},
{
"expected": "terminated",
"matcher": "pathAny",
"state": "failure",
"argument": "Reservations[].Instances[].State.Name"
}
]
},
"InstanceTerminated": {
"delay": 15,
"operation": "DescribeInstances",
"maxAttempts": 40,
"acceptors": [
{
"expected": "terminated",
"matcher": "pathAll",
"state": "success",
"argument": "Reservations[].Instances[].State.Name"
},
{
"expected": "pending",
"matcher": "pathAny",
"state": "failure",
"argument": "Reservations[].Instances[].State.Name"
},
{
"expected": "stopping",
"matcher": "pathAny",
"state": "failure",
"argument": "Reservations[].Instances[].State.Name"
}
]
},
"InternetGatewayExists": {
"operation": "DescribeInternetGateways",
"delay": 5,
"maxAttempts": 6,
"acceptors": [
{
"expected": true,
"matcher": "path",
"state": "success",
"argument": "length(InternetGateways[].InternetGatewayId) > `0`"
},
{
"expected": "InvalidInternetGateway.NotFound",
"matcher": "error",
"state": "retry"
}
]
},
"KeyPairExists": {
"operation": "DescribeKeyPairs",
"delay": 5,
"maxAttempts": 6,
"acceptors": [
{
"expected": true,
"matcher": "path",
"state": "success",
"argument": "length(KeyPairs[].KeyName) > `0`"
},
{
"expected": "InvalidKeyPair.NotFound",
"matcher": "error",
"state": "retry"
}
]
},
"NatGatewayAvailable": {
"operation": "DescribeNatGateways",
"delay": 15,
"maxAttempts": 40,
"acceptors": [
{
"state": "success",
"matcher": "pathAll",
"argument": "NatGateways[].State",
"expected": "available"
},
{
"state": "failure",
"matcher": "pathAny",
"argument": "NatGateways[].State",
"expected": "failed"
},
{
"state": "failure",
"matcher": "pathAny",
"argument": "NatGateways[].State",
"expected": "deleting"
},
{
"state": "failure",
"matcher": "pathAny",
"argument": "NatGateways[].State",
"expected": "deleted"
},
{
"state": "retry",
"matcher": "error",
"expected": "NatGatewayNotFound"
}
]
},
"NatGatewayDeleted": {
"operation": "DescribeNatGateways",
"delay": 15,
"maxAttempts": 40,
"acceptors": [
{
"state": "success",
"matcher": "pathAll",
"argument": "NatGateways[].State",
"expected": "deleted"
},
{
"state": "success",
"matcher": "error",
"expected": "NatGatewayNotFound"
}
]
},
"NetworkInterfaceAvailable": {
"operation": "DescribeNetworkInterfaces",
"delay": 20,
"maxAttempts": 10,
"acceptors": [
{
"expected": "available",
"matcher": "pathAll",
"state": "success",
"argument": "NetworkInterfaces[].Status"
},
{
"expected": "InvalidNetworkInterfaceID.NotFound",
"matcher": "error",
"state": "failure"
}
]
},
"PasswordDataAvailable": {
"operation": "GetPasswordData",
"maxAttempts": 40,
"delay": 15,
"acceptors": [
{
"state": "success",
"matcher": "path",
"argument": "length(PasswordData) > `0`",
"expected": true
}
]
},
"SnapshotCompleted": {
"delay": 15,
"operation": "DescribeSnapshots",
"maxAttempts": 40,
"acceptors": [
{
"expected": "completed",
"matcher": "pathAll",
"state": "success",
"argument": "Snapshots[].State"
},
{
"expected": "error",
"matcher": "pathAny",
"state": "failure",
"argument": "Snapshots[].State"
}
]
},
"SnapshotImported": {
"delay": 15,
"operation": "DescribeImportSnapshotTasks",
"maxAttempts": 40,
"acceptors": [
{
"expected": "completed",
"matcher": "pathAll",
"state": "success",
"argument": "ImportSnapshotTasks[].SnapshotTaskDetail.Status"
},
{
"expected": "error",
"matcher": "pathAny",
"state": "failure",
"argument": "ImportSnapshotTasks[].SnapshotTaskDetail.Status"
}
]
},
"SecurityGroupExists": {
"operation": "DescribeSecurityGroups",
"delay": 5,
"maxAttempts": 6,
"acceptors": [
{
"expected": true,
"matcher": "path",
"state": "success",
"argument": "length(SecurityGroups[].GroupId) > `0`"
},
{
"expected": "InvalidGroup.NotFound",
"matcher": "error",
"state": "retry"
}
]
},
"SpotInstanceRequestFulfilled": {
"operation": "DescribeSpotInstanceRequests",
"maxAttempts": 40,
"delay": 15,
"acceptors": [
{
"state": "success",
"matcher": "pathAll",
"argument": "SpotInstanceRequests[].Status.Code",
"expected": "fulfilled"
},
{
"state": "success",
"matcher": "pathAll",
"argument": "SpotInstanceRequests[].Status.Code",
"expected": "request-canceled-and-instance-running"
},
{
"state": "failure",
"matcher": "pathAny",
"argument": "SpotInstanceRequests[].Status.Code",
"expected": "schedule-expired"
},
{
"state": "failure",
"matcher": "pathAny",
"argument": "SpotInstanceRequests[].Status.Code",
"expected": "canceled-before-fulfillment"
},
{
"state": "failure",
"matcher": "pathAny",
"argument": "SpotInstanceRequests[].Status.Code",
"expected": "bad-parameters"
},
{
"state": "failure",
"matcher": "pathAny",
"argument": "SpotInstanceRequests[].Status.Code",
"expected": "system-error"
},
{
"state": "retry",
"matcher": "error",
"expected": "InvalidSpotInstanceRequestID.NotFound"
}
]
},
"StoreImageTaskComplete": {
"delay": 5,
"operation": "DescribeStoreImageTasks",
"maxAttempts": 40,
"acceptors": [
{
"expected": "Completed",
"matcher": "pathAll",
"state": "success",
"argument": "StoreImageTaskResults[].StoreTaskState"
},
{
"expected": "Failed",
"matcher": "pathAny",
"state": "failure",
"argument": "StoreImageTaskResults[].StoreTaskState"
},
{
"expected": "InProgress",
"matcher": "pathAny",
"state": "retry",
"argument": "StoreImageTaskResults[].StoreTaskState"
}
]
},
"SubnetAvailable": {
"delay": 15,
"operation": "DescribeSubnets",
"maxAttempts": 40,
"acceptors": [
{
"expected": "available",
"matcher": "pathAll",
"state": "success",
"argument": "Subnets[].State"
}
]
},
"SystemStatusOk": {
"operation": "DescribeInstanceStatus",
"maxAttempts": 40,
"delay": 15,
"acceptors": [
{
"state": "success",
"matcher": "pathAll",
"argument": "InstanceStatuses[].SystemStatus.Status",
"expected": "ok"
}
]
},
"VolumeAvailable": {
"delay": 15,
"operation": "DescribeVolumes",
"maxAttempts": 40,
"acceptors": [
{
"expected": "available",
"matcher": "pathAll",
"state": "success",
"argument": "Volumes[].State"
},
{
"expected": "deleted",
"matcher": "pathAny",
"state": "failure",
"argument": "Volumes[].State"
}
]
},
"VolumeDeleted": {
"delay": 15,
"operation": "DescribeVolumes",
"maxAttempts": 40,
"acceptors": [
{
"expected": "deleted",
"matcher": "pathAll",
"state": "success",
"argument": "Volumes[].State"
},
{
"matcher": "error",
"expected": "InvalidVolume.NotFound",
"state": "success"
}
]
},
"VolumeInUse": {
"delay": 15,
"operation": "DescribeVolumes",
"maxAttempts": 40,
"acceptors": [
{
"expected": "in-use",
"matcher": "pathAll",
"state": "success",
"argument": "Volumes[].State"
},
{
"expected": "deleted",
"matcher": "pathAny",
"state": "failure",
"argument": "Volumes[].State"
}
]
},
"VpcAvailable": {
"delay": 15,
"operation": "DescribeVpcs",
"maxAttempts": 40,
"acceptors": [
{
"expected": "available",
"matcher": "pathAll",
"state": "success",
"argument": "Vpcs[].State"
}
]
},
"VpcExists": {
"operation": "DescribeVpcs",
"delay": 1,
"maxAttempts": 5,
"acceptors": [
{
"matcher": "status",
"expected": 200,
"state": "success"
},
{
"matcher": "error",
"expected": "InvalidVpcID.NotFound",
"state": "retry"
}
]
},
"VpnConnectionAvailable": {
"delay": 15,
"operation": "DescribeVpnConnections",
"maxAttempts": 40,
"acceptors": [
{
"expected": "available",
"matcher": "pathAll",
"state": "success",
"argument": "VpnConnections[].State"
},
{
"expected": "deleting",
"matcher": "pathAny",
"state": "failure",
"argument": "VpnConnections[].State"
},
{
"expected": "deleted",
"matcher": "pathAny",
"state": "failure",
"argument": "VpnConnections[].State"
}
]
},
"VpnConnectionDeleted": {
"delay": 15,
"operation": "DescribeVpnConnections",
"maxAttempts": 40,
"acceptors": [
{
"expected": "deleted",
"matcher": "pathAll",
"state": "success",
"argument": "VpnConnections[].State"
},
{
"expected": "pending",
"matcher": "pathAny",
"state": "failure",
"argument": "VpnConnections[].State"
}
]
},
"VpcPeeringConnectionExists": {
"delay": 15,
"operation": "DescribeVpcPeeringConnections",
"maxAttempts": 40,
"acceptors": [
{
"matcher": "status",
"expected": 200,
"state": "success"
},
{
"matcher": "error",
"expected": "InvalidVpcPeeringConnectionID.NotFound",
"state": "retry"
}
]
},
"VpcPeeringConnectionDeleted": {
"delay": 15,
"operation": "DescribeVpcPeeringConnections",
"maxAttempts": 40,
"acceptors": [
{
"expected": "deleted",
"matcher": "pathAll",
"state": "success",
"argument": "VpcPeeringConnections[].Status.Code"
},
{
"matcher": "error",
"expected": "InvalidVpcPeeringConnectionID.NotFound",
"state": "success"
}
]
}
}
}