carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

1package awsinv
2
3import (
4 "context"
5 "errors"
6 "fmt"
7 "strings"
8
9 "github.com/aws/aws-sdk-go-v2/aws"
10 awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware"
11 awsconfig "github.com/aws/aws-sdk-go-v2/config"
12 "github.com/aws/aws-sdk-go-v2/service/ec2"
13 "github.com/carlosframework/platform/internal/rootaws"
14)
15
16// ErrExactInstance marks every result that fails to prove that the exact
17// enrolled EC2 instance is stopped or terminated. Callers must treat it as a
18// refusal, including when AWS says that the instance does not exist.
19var ErrExactInstance = errors.New("awsinv: exact EC2 instance is not fenced")
20
21// ExactInstance is the complete observation accepted by the declare-lost
22// client. Account comes from Reservation.OwnerId, not from a request field or
23// the caller's default profile.
24type ExactInstance struct {
25 Account string `json:"account"`
26 Region string `json:"region"`
27 InstanceID string `json:"instance_id"`
28 State string `json:"state"`
29 RequestID string `json:"request_id"`
30}
31
32// ExactEC2API is the DescribeInstances slice of EC2API. The production EC2
33// client and every existing awsinv EC2 fake satisfy it; keeping the verifier
34// on this one-method view makes a write-capable client unnecessary.
35type ExactEC2API interface {
36 DescribeInstances(context.Context, *ec2.DescribeInstancesInput, ...func(*ec2.Options)) (*ec2.DescribeInstancesOutput, error)
37}
38
39// NewEC2Reader resolves the operator's ordinary AWS credential chain and
40// returns the existing read-only EC2 seam in the enrolled region. It grants
41// no stop, terminate or restart operation.
42func NewEC2Reader(ctx context.Context, region string) (ExactEC2API, error) {
43 if strings.TrimSpace(region) == "" {
44 return nil, fmt.Errorf("%w: enrolled region is empty", ErrExactInstance)
45 }
46 cfg, err := rootaws.LoadDefaultConfig(ctx, awsconfig.WithRegion(region))
47 if err != nil {
48 return nil, fmt.Errorf("%w: AWS config: %v", ErrExactInstance, err)
49 }
50 probeCtx, cancel := context.WithTimeout(ctx, credentialProbe)
51 defer cancel()
52 if _, err := cfg.Credentials.Retrieve(probeCtx); err != nil {
53 return nil, fmt.Errorf("%w: no usable AWS credential: %v", ErrExactInstance, err)
54 }
55 return factoryFor(cfg)(region).EC2, nil
56}
57
58// VerifyExactStoppedInstance makes one DescribeInstances call constrained to
59// the exact enrolled ID. Any API error, pagination, missing field, duplicate
60// result, owner mismatch or live state refuses; InvalidInstanceID.NotFound is
61// therefore never treated as termination evidence.
62func VerifyExactStoppedInstance(ctx context.Context, api ExactEC2API, account, region, instanceID string) (ExactInstance, error) {
63 wantAccount := strings.TrimSpace(account)
64 wantRegion := strings.TrimSpace(region)
65 wantID := strings.TrimSpace(instanceID)
66 if api == nil || wantAccount == "" || wantRegion == "" || wantID == "" {
67 return ExactInstance{}, fmt.Errorf("%w: incomplete enrolled cloud binding", ErrExactInstance)
68 }
69 out, err := api.DescribeInstances(ctx, &ec2.DescribeInstancesInput{InstanceIds: []string{wantID}}, func(o *ec2.Options) {
70 o.Region = wantRegion
71 })
72 if err != nil {
73 return ExactInstance{}, fmt.Errorf("%w: DescribeInstances %s in %s/%s: %v", ErrExactInstance, wantID, wantAccount, wantRegion, err)
74 }
75 if out == nil || aws.ToString(out.NextToken) != "" || len(out.Reservations) != 1 {
76 return ExactInstance{}, fmt.Errorf("%w: DescribeInstances returned a partial or ambiguous result", ErrExactInstance)
77 }
78 requestID, ok := awsmiddleware.GetRequestIDMetadata(out.ResultMetadata)
79 if !ok || strings.TrimSpace(requestID) == "" {
80 return ExactInstance{}, ErrExactInstance
81 }
82 r := out.Reservations[0]
83 owner := aws.ToString(r.OwnerId)
84 if owner == "" || owner != wantAccount || len(r.Instances) != 1 {
85 return ExactInstance{}, fmt.Errorf("%w: reservation owner or instance count does not match enrollment", ErrExactInstance)
86 }
87 in := r.Instances[0]
88 gotID := aws.ToString(in.InstanceId)
89 if gotID == "" || gotID != wantID || in.State == nil {
90 return ExactInstance{}, fmt.Errorf("%w: returned instance does not exactly match enrollment", ErrExactInstance)
91 }
92 state := string(in.State.Name)
93 if state != "stopped" && state != "terminated" {
94 return ExactInstance{}, fmt.Errorf("%w: instance %s is %q", ErrExactInstance, wantID, state)
95 }
96 return ExactInstance{Account: owner, Region: wantRegion, InstanceID: gotID, State: state, RequestID: requestID}, nil
97}
98