mirror of
https://github.com/mulgadc/spinifex.git
synced 2026-10-15 09:01:31 +00:00
Added EC2 image and key framework, plus API schema
This commit is contained in:
+673
-1
@@ -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
|
||||
@@ -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
|
||||
}
|
||||
@@ -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-"))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"version": 1,
|
||||
"defaultRegion": "us-west-2",
|
||||
"testCases": [
|
||||
{
|
||||
"operationName": "DescribeRegions",
|
||||
"input": {},
|
||||
"errorExpectedFromService": false
|
||||
},
|
||||
{
|
||||
"operationName": "DescribeInstances",
|
||||
"input": {
|
||||
"InstanceIds": [
|
||||
"i-12345678"
|
||||
]
|
||||
},
|
||||
"errorExpectedFromService": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user