From 30e99d1004a0fd2fe64c9a3650e45ce56b1e9ccf Mon Sep 17 00:00:00 2001 From: Justin Brooks Date: Mon, 14 Apr 2025 13:11:54 -0400 Subject: [PATCH 1/6] init ec2 scanner --- cmd/ctrlc/root/sync/aws/ec2/ec2.go | 199 +++++++++++++++++++++++++++++ cmd/ctrlc/root/sync/sync.go | 2 + go.mod | 14 ++ go.sum | 28 ++++ 4 files changed, 243 insertions(+) create mode 100644 cmd/ctrlc/root/sync/aws/ec2/ec2.go diff --git a/cmd/ctrlc/root/sync/aws/ec2/ec2.go b/cmd/ctrlc/root/sync/aws/ec2/ec2.go new file mode 100644 index 0000000..711fa06 --- /dev/null +++ b/cmd/ctrlc/root/sync/aws/ec2/ec2.go @@ -0,0 +1,199 @@ +package ec2 + +import ( + "context" + "encoding/json" + "fmt" + "os" + + "github.com/MakeNowJust/heredoc/v2" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/ec2" + "github.com/charmbracelet/log" + "github.com/ctrlplanedev/cli/internal/api" + "github.com/ctrlplanedev/cli/internal/cliutil" + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +type EC2Instance struct { + ID string `json:"id"` + Name string `json:"name"` + Type string `json:"type"` + State string `json:"state"` + PrivateIP string `json:"privateIp"` + PublicIP string `json:"publicIp,omitempty"` + VpcID string `json:"vpcId"` + SubnetID string `json:"subnetId"` + Region string `json:"region"` + LaunchTime string `json:"launchTime"` +} + +func (t *EC2Instance) Struct() map[string]interface{} { + b, _ := json.Marshal(t) + var m map[string]interface{} + json.Unmarshal(b, &m) + return m +} + +func NewSyncEC2Cmd() *cobra.Command { + var configRegion string + var name string + cmd := &cobra.Command{ + Use: "aws-ec2", + Short: "Sync AWS EC2 instances into Ctrlplane", + Example: heredoc.Doc(` + # Make sure AWS credentials are configured via environment variables or ~/.aws/credentials + + # Sync all EC2 instances from a region + $ ctrlc sync ec2 --config-region us-west-2 --workspace 2a7c5560-75c9-4dbe-be74-04ee33bf8188 + `), + PreRunE: func(cmd *cobra.Command, args []string) error { + if configRegion == "" { + return fmt.Errorf("region is required") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + log.Info("Syncing EC2 instances into Ctrlplane", "config-region", configRegion) + + + ctx := context.Background() + cfg, err := config.LoadDefaultConfig(ctx, config.WithRegion(configRegion)) + if err != nil { + return fmt.Errorf("failed to load AWS config: %w", err) + } + + credentials, err := cfg.Credentials.Retrieve(ctx) + if err != nil { + return fmt.Errorf("failed to retrieve AWS credentials: %w", err) + } + + log.Info("AWS credentials loaded successfully", + "provider", credentials.Source, + "region", configRegion, + "access_key_id", credentials.AccessKeyID[:4]+"****", + "expiration", credentials.Expires, + "type", credentials.Source, + "profile", os.Getenv("AWS_PROFILE"), + ) + + // Create EC2 client with retry options + ec2Client := ec2.NewFromConfig(cfg, func(o *ec2.Options) { + o.RetryMaxAttempts = 3 + o.RetryMode = aws.RetryModeStandard + }) + + apiURL := viper.GetString("url") + apiKey := viper.GetString("api-key") + workspaceId := viper.GetString("workspace") + + // Get EC2 instances + result, err := ec2Client.DescribeInstances(ctx, &ec2.DescribeInstancesInput{}) + if err != nil { + return fmt.Errorf("failed to describe instances: %w", err) + } + + resources := []api.AgentResource{} + for _, reservation := range result.Reservations { + accountId := *reservation.OwnerId + for _, instance := range reservation.Instances { + tags := make(map[string]string) + for _, tag := range instance.Tags { + tags[*tag.Key] = *tag.Value + } + + // Get instance name from tags + name := tags["Name"] + if name == "" { + name = *instance.InstanceId + } + + // Get EC2 region from instance availability zone + region := "" + if instance.Placement != nil && instance.Placement.AvailabilityZone != nil { + // Region is AZ without the last character + region = (*instance.Placement.AvailabilityZone)[:len(*instance.Placement.AvailabilityZone)-1] + } + + instanceData := EC2Instance{ + ID: *instance.InstanceId, + Name: name, + Type: string(instance.InstanceType), + State: string(instance.State.Name), + VpcID: *instance.VpcId, + SubnetID: *instance.SubnetId, + Region: region, + LaunchTime: instance.LaunchTime.String(), + } + + if instance.PrivateIpAddress != nil { + instanceData.PrivateIP = *instance.PrivateIpAddress + } + if instance.PublicIpAddress != nil { + instanceData.PublicIP = *instance.PublicIpAddress + } + + // Add AWS Console URL for the instance + consoleUrl := fmt.Sprintf("https://%s.console.aws.amazon.com/ec2/home?region=%s#InstanceDetails:instanceId=%s", + region, + region, + *instance.InstanceId) + + metadata := tags + metadata["ec2/id"] = instanceData.ID + metadata["ec2/type"] = instanceData.Type + metadata["ec2/state"] = instanceData.State + metadata["ec2/vpc"] = instanceData.VpcID + metadata["ec2/subnet"] = instanceData.SubnetID + metadata["ec2/region"] = instanceData.Region + metadata["ec2/launch-time"] = instanceData.LaunchTime + metadata["aws/account-id"] = accountId + metadata["ctrlplane/links"] = fmt.Sprintf("{ \"AWS Console\": \"%s\" }", consoleUrl) + + // Get ARN for the instance + arn := fmt.Sprintf("arn:aws:ec2:%s:%s:instance/%s", region, accountId, *instance.InstanceId) + resources = append(resources, api.AgentResource{ + Version: "aws/v1", + Kind: "EC2Instance", + Name: name, + Identifier: arn, + Config: instanceData.Struct(), + Metadata: metadata, + }) + } + } + + // Create or update resource provider + if name == "" { + name = fmt.Sprintf("aws-ec2-config-region-%s", configRegion) + } + + ctrlplaneClient, err := api.NewAPIKeyClientWithResponses(apiURL, apiKey) + if err != nil { + return fmt.Errorf("failed to create API client: %w", err) + } + + rp, err := api.NewResourceProvider(ctrlplaneClient, workspaceId, name) + if err != nil { + return fmt.Errorf("failed to create resource provider: %w", err) + } + + upsertResp, err := rp.UpsertResource(ctx, resources) + log.Info("Response from upserting resources", "status", upsertResp.Status) + if err != nil { + return fmt.Errorf("failed to upsert resources: %w", err) + } + + return cliutil.HandleResponseOutput(cmd, upsertResp) + }, + } + + cmd.Flags().StringVarP(&name, "provider", "p", "", "Name of the resource provider") + cmd.Flags().StringVarP(&configRegion, "config-region", "c", "", "AWS Config Region") + cmd.MarkFlagRequired("config-region") + + return cmd +} + diff --git a/cmd/ctrlc/root/sync/sync.go b/cmd/ctrlc/root/sync/sync.go index 9f8ace3..4a2b07d 100644 --- a/cmd/ctrlc/root/sync/sync.go +++ b/cmd/ctrlc/root/sync/sync.go @@ -2,6 +2,7 @@ package sync import ( "github.com/MakeNowJust/heredoc/v2" + "github.com/ctrlplanedev/cli/cmd/ctrlc/root/sync/aws/ec2" "github.com/ctrlplanedev/cli/cmd/ctrlc/root/sync/clickhouse" "github.com/ctrlplanedev/cli/cmd/ctrlc/root/sync/tailscale" "github.com/ctrlplanedev/cli/cmd/ctrlc/root/sync/terraform" @@ -27,6 +28,7 @@ func NewSyncCmd() *cobra.Command { cmd.AddCommand(cliutil.AddIntervalSupport(terraform.NewSyncTerraformCmd(), "")) cmd.AddCommand(cliutil.AddIntervalSupport(tailscale.NewSyncTailscaleCmd(), "")) cmd.AddCommand(cliutil.AddIntervalSupport(clickhouse.NewSyncClickhouseCmd(), "")) + cmd.AddCommand(cliutil.AddIntervalSupport(ec2.NewSyncEC2Cmd(), "")) return cmd } \ No newline at end of file diff --git a/go.mod b/go.mod index 7f17a6c..6b539ee 100644 --- a/go.mod +++ b/go.mod @@ -27,6 +27,20 @@ require ( require ( github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect + github.com/aws/aws-sdk-go-v2 v1.36.3 // indirect + github.com/aws/aws-sdk-go-v2/config v1.29.14 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.17.67 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect + github.com/aws/aws-sdk-go-v2/service/ec2 v1.211.3 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.25.3 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.33.19 // indirect + github.com/aws/smithy-go v1.22.2 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/charmbracelet/lipgloss v0.10.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect diff --git a/go.sum b/go.sum index 6393431..04bee84 100644 --- a/go.sum +++ b/go.sum @@ -7,6 +7,34 @@ github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7D github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk= github.com/avast/retry-go v3.0.0+incompatible h1:4SOWQ7Qs+oroOTQOYnAHqelpCO0biHSxpiH9JdtuBj0= github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY= +github.com/aws/aws-sdk-go-v2 v1.36.3 h1:mJoei2CxPutQVxaATCzDUjcZEjVRdpsiiXi2o38yqWM= +github.com/aws/aws-sdk-go-v2 v1.36.3/go.mod h1:LLXuLpgzEbD766Z5ECcRmi8AzSwfZItDtmABVkRLGzg= +github.com/aws/aws-sdk-go-v2/config v1.29.14 h1:f+eEi/2cKCg9pqKBoAIwRGzVb70MRKqWX4dg1BDcSJM= +github.com/aws/aws-sdk-go-v2/config v1.29.14/go.mod h1:wVPHWcIFv3WO89w0rE10gzf17ZYy+UVS1Geq8Iei34g= +github.com/aws/aws-sdk-go-v2/credentials v1.17.67 h1:9KxtdcIA/5xPNQyZRgUSpYOE6j9Bc4+D7nZua0KGYOM= +github.com/aws/aws-sdk-go-v2/credentials v1.17.67/go.mod h1:p3C44m+cfnbv763s52gCqrjaqyPikj9Sg47kUVaNZQQ= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 h1:x793wxmUWVDhshP8WW2mlnXuFrO4cOd3HLBroh1paFw= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30/go.mod h1:Jpne2tDnYiFascUEs2AWHJL9Yp7A5ZVy3TNyxaAjD6M= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 h1:ZK5jHhnrioRkUNOc+hOgQKlUL5JeC3S6JgLxtQ+Rm0Q= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34/go.mod h1:p4VfIceZokChbA9FzMbRGz5OV+lekcVtHlPKEO0gSZY= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 h1:SZwFm17ZUNNg5Np0ioo/gq8Mn6u9w19Mri8DnJ15Jf0= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34/go.mod h1:dFZsC0BLo346mvKQLWmoJxT+Sjp+qcVR1tRVHQGOH9Q= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d2KyU5X/BZxjOkRo= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo= +github.com/aws/aws-sdk-go-v2/service/ec2 v1.211.3 h1:4dPHqFVVvFG+ntkVUXrMrY55+E5dzFfEpjFWdkdSxnc= +github.com/aws/aws-sdk-go-v2/service/ec2 v1.211.3/go.mod h1:ouvGEfHbLaIlWwpDpOVWPWR+YwO0HDv3vm5tYLq8ImY= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 h1:eAh2A4b5IzM/lum78bZ590jy36+d/aFLgKF/4Vd1xPE= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3/go.mod h1:0yKJC/kb8sAnmlYa6Zs3QVYqaC8ug2AbnNChv5Ox3uA= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 h1:dM9/92u2F1JbDaGooxTq18wmmFzbJRfXfVfy96/1CXM= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15/go.mod h1:SwFBy2vjtA0vZbjjaFtfN045boopadnoVPhu4Fv66vY= +github.com/aws/aws-sdk-go-v2/service/sso v1.25.3 h1:1Gw+9ajCV1jogloEv1RRnvfRFia2cL6c9cuKV2Ps+G8= +github.com/aws/aws-sdk-go-v2/service/sso v1.25.3/go.mod h1:qs4a9T5EMLl/Cajiw2TcbNt2UNo/Hqlyp+GiuG4CFDI= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1 h1:hXmVKytPfTy5axZ+fYbR5d0cFmC3JvwLm5kM83luako= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1/go.mod h1:MlYRNmYu/fGPoxBQVvBYr9nyr948aY/WLUvwBMBJubs= +github.com/aws/aws-sdk-go-v2/service/sts v1.33.19 h1:1XuUZ8mYJw9B6lzAkXhqHlJd/XvaX32evhproijJEZY= +github.com/aws/aws-sdk-go-v2/service/sts v1.33.19/go.mod h1:cQnB8CUnxbMU82JvlqjKR2HBOm3fe9pWorWBza6MBJ4= +github.com/aws/smithy-go v1.22.2 h1:6D9hW43xKFrRx/tXXfAlIZc4JI+yQe6snnWcQyxSyLQ= +github.com/aws/smithy-go v1.22.2/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= From 2e4fc0c56b32232427b9ab0ee059f032ce8d7516 Mon Sep 17 00:00:00 2001 From: Justin Brooks Date: Tue, 15 Apr 2025 12:57:52 -0400 Subject: [PATCH 2/6] follow compute v1 format --- cmd/ctrlc/root/sync/aws/ec2/ec2.go | 164 +++++++++++++++++++++-------- 1 file changed, 119 insertions(+), 45 deletions(-) diff --git a/cmd/ctrlc/root/sync/aws/ec2/ec2.go b/cmd/ctrlc/root/sync/aws/ec2/ec2.go index 711fa06..c7abbd1 100644 --- a/cmd/ctrlc/root/sync/aws/ec2/ec2.go +++ b/cmd/ctrlc/root/sync/aws/ec2/ec2.go @@ -5,6 +5,9 @@ import ( "encoding/json" "fmt" "os" + "strconv" + "strings" + "time" "github.com/MakeNowJust/heredoc/v2" "github.com/aws/aws-sdk-go-v2/aws" @@ -17,17 +20,17 @@ import ( "github.com/spf13/viper" ) +type ConnectionMethod struct { + Type string `json:"type"` + Region string `json:"region"` + InstanceID string `json:"instanceId"` + AccountID string `json:"accountId"` +} + type EC2Instance struct { - ID string `json:"id"` - Name string `json:"name"` - Type string `json:"type"` - State string `json:"state"` - PrivateIP string `json:"privateIp"` - PublicIP string `json:"publicIp,omitempty"` - VpcID string `json:"vpcId"` - SubnetID string `json:"subnetId"` - Region string `json:"region"` - LaunchTime string `json:"launchTime"` + ID string `json:"id"` + Name string `json:"name"` + ConnectionMethod ConnectionMethod `json:"connectionMethod"` } func (t *EC2Instance) Struct() map[string]interface{} { @@ -38,7 +41,7 @@ func (t *EC2Instance) Struct() map[string]interface{} { } func NewSyncEC2Cmd() *cobra.Command { - var configRegion string + var region string var name string cmd := &cobra.Command{ Use: "aws-ec2", @@ -47,20 +50,19 @@ func NewSyncEC2Cmd() *cobra.Command { # Make sure AWS credentials are configured via environment variables or ~/.aws/credentials # Sync all EC2 instances from a region - $ ctrlc sync ec2 --config-region us-west-2 --workspace 2a7c5560-75c9-4dbe-be74-04ee33bf8188 + $ ctrlc sync ec2 --region us-west-2 --workspace 2a7c5560-75c9-4dbe-be74-04ee33bf8188 `), PreRunE: func(cmd *cobra.Command, args []string) error { - if configRegion == "" { + if region == "" { return fmt.Errorf("region is required") } return nil }, RunE: func(cmd *cobra.Command, args []string) error { - log.Info("Syncing EC2 instances into Ctrlplane", "config-region", configRegion) + log.Info("Syncing EC2 instances into Ctrlplane", "config-region", region) - ctx := context.Background() - cfg, err := config.LoadDefaultConfig(ctx, config.WithRegion(configRegion)) + cfg, err := config.LoadDefaultConfig(ctx, config.WithRegion(region)) if err != nil { return fmt.Errorf("failed to load AWS config: %w", err) } @@ -70,9 +72,9 @@ func NewSyncEC2Cmd() *cobra.Command { return fmt.Errorf("failed to retrieve AWS credentials: %w", err) } - log.Info("AWS credentials loaded successfully", + log.Info("AWS credentials loaded successfully", "provider", credentials.Source, - "region", configRegion, + "region", region, "access_key_id", credentials.AccessKeyID[:4]+"****", "expiration", credentials.Expires, "type", credentials.Source, @@ -100,6 +102,7 @@ func NewSyncEC2Cmd() *cobra.Command { accountId := *reservation.OwnerId for _, instance := range reservation.Instances { tags := make(map[string]string) + for _, tag := range instance.Tags { tags[*tag.Key] = *tag.Value } @@ -120,19 +123,12 @@ func NewSyncEC2Cmd() *cobra.Command { instanceData := EC2Instance{ ID: *instance.InstanceId, Name: name, - Type: string(instance.InstanceType), - State: string(instance.State.Name), - VpcID: *instance.VpcId, - SubnetID: *instance.SubnetId, - Region: region, - LaunchTime: instance.LaunchTime.String(), - } - - if instance.PrivateIpAddress != nil { - instanceData.PrivateIP = *instance.PrivateIpAddress - } - if instance.PublicIpAddress != nil { - instanceData.PublicIP = *instance.PublicIpAddress + ConnectionMethod: ConnectionMethod{ + Type: "aws", + Region: region, + InstanceID: *instance.InstanceId, + AccountID: accountId, + }, } // Add AWS Console URL for the instance @@ -142,21 +138,100 @@ func NewSyncEC2Cmd() *cobra.Command { *instance.InstanceId) metadata := tags - metadata["ec2/id"] = instanceData.ID - metadata["ec2/type"] = instanceData.Type - metadata["ec2/state"] = instanceData.State - metadata["ec2/vpc"] = instanceData.VpcID - metadata["ec2/subnet"] = instanceData.SubnetID - metadata["ec2/region"] = instanceData.Region - metadata["ec2/launch-time"] = instanceData.LaunchTime + metadata["compute/machine-type"] = string(instance.InstanceType) + metadata["compute/region"] = region + metadata["compute/type"] = "standard" + + if instance.Architecture != "" { + metadata["compute/architecture"] = strings.ReplaceAll(string(instance.Architecture), "_mac", "") + } + + if instance.BootMode != "" { + metadata["compute/boot-mode"] = string(instance.BootMode) + } + + if instance.PlatformDetails != nil { + metadata["compute/platform"] = *instance.PlatformDetails + } + + if instance.CpuOptions != nil && instance.CpuOptions.CoreCount != nil { + metadata["compute/cpu-cores"] = strconv.Itoa(int(*instance.CpuOptions.CoreCount)) + if instance.CpuOptions.ThreadsPerCore != nil { + metadata["compute/cpu-threads-per-core"] = strconv.Itoa(int(*instance.CpuOptions.ThreadsPerCore)) + metadata["compute/cpu-threads"] = strconv.Itoa(int(*instance.CpuOptions.ThreadsPerCore) * int(*instance.CpuOptions.CoreCount)) + } + } + if instance.Hypervisor != "" { + metadata["compute/hypervisor"] = string(instance.Hypervisor) + } + + if instance.State != nil { + metadata["compute/state"] = string(instance.State.Name) + } + + if instance.LaunchTime != nil { + metadata["compute/launch-time"] = instance.LaunchTime.Format(time.RFC3339) + } + + if instance.PrivateIpAddress != nil { + metadata["network/private-ip"] = *instance.PrivateIpAddress + } + + if instance.PublicIpAddress != nil { + metadata["network/public-ip"] = *instance.PublicIpAddress + } + + if instance.PrivateDnsName != nil { + metadata["network/private-dns"] = *instance.PrivateDnsName + } + + if instance.PublicDnsName != nil { + metadata["network/public-dns"] = *instance.PublicDnsName + } + metadata["aws/account-id"] = accountId + metadata["aws/region"] = region + + if instance.VpcId != nil { + metadata["aws/vpc-id"] = *instance.VpcId + } + if instance.PlatformDetails != nil { + metadata["aws/instance-type"] = string(*instance.PlatformDetails) + } + if instance.InstanceId != nil { + metadata["aws/instance-id"] = *instance.InstanceId + } + + if instance.ImageId != nil { + metadata["aws/ami-id"] = *instance.ImageId + } + if instance.AmiLaunchIndex != nil { + metadata["aws/ami-launch-index"] = strconv.Itoa(int(*instance.AmiLaunchIndex)) + } + + if instance.KeyName != nil { + metadata["aws/key-name"] = *instance.KeyName + } + + if instance.EbsOptimized != nil { + metadata["aws/ebs-optimized"] = strconv.FormatBool(*instance.EbsOptimized) + } + + if instance.EnaSupport != nil { + metadata["aws/ena-support"] = strconv.FormatBool(*instance.EnaSupport) + } + + if instance.SubnetId != nil { + metadata["aws/subnet-id"] = *instance.SubnetId + } + metadata["ctrlplane/links"] = fmt.Sprintf("{ \"AWS Console\": \"%s\" }", consoleUrl) // Get ARN for the instance arn := fmt.Sprintf("arn:aws:ec2:%s:%s:instance/%s", region, accountId, *instance.InstanceId) resources = append(resources, api.AgentResource{ - Version: "aws/v1", - Kind: "EC2Instance", + Version: "compute/v1", + Kind: "Instance", Name: name, Identifier: arn, Config: instanceData.Struct(), @@ -164,10 +239,10 @@ func NewSyncEC2Cmd() *cobra.Command { }) } } - + // Create or update resource provider if name == "" { - name = fmt.Sprintf("aws-ec2-config-region-%s", configRegion) + name = fmt.Sprintf("aws-ec2-region-%s", region) } ctrlplaneClient, err := api.NewAPIKeyClientWithResponses(apiURL, apiKey) @@ -191,9 +266,8 @@ func NewSyncEC2Cmd() *cobra.Command { } cmd.Flags().StringVarP(&name, "provider", "p", "", "Name of the resource provider") - cmd.Flags().StringVarP(&configRegion, "config-region", "c", "", "AWS Config Region") - cmd.MarkFlagRequired("config-region") + cmd.Flags().StringVarP(®ion, "region", "c", "", "AWS Region") + cmd.MarkFlagRequired("region") return cmd } - From 2ce7fcad43034b7bbd238c52b165db39cb65a0bf Mon Sep 17 00:00:00 2001 From: Justin Brooks Date: Tue, 15 Apr 2025 13:01:39 -0400 Subject: [PATCH 3/6] add tag annotaitons --- cmd/ctrlc/root/sync/aws/ec2/ec2.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/cmd/ctrlc/root/sync/aws/ec2/ec2.go b/cmd/ctrlc/root/sync/aws/ec2/ec2.go index c7abbd1..c1f1aa1 100644 --- a/cmd/ctrlc/root/sync/aws/ec2/ec2.go +++ b/cmd/ctrlc/root/sync/aws/ec2/ec2.go @@ -137,7 +137,14 @@ func NewSyncEC2Cmd() *cobra.Command { region, *instance.InstanceId) - metadata := tags + metadata := make(map[string]string) + for _, tag := range instance.Tags { + if tag.Key != nil && tag.Value != nil { + metadata[*tag.Key] = *tag.Value + metadata["compute/tag/"+*tag.Key] = *tag.Value + metadata["aws/tag/"+*tag.Key] = *tag.Value + } + } metadata["compute/machine-type"] = string(instance.InstanceType) metadata["compute/region"] = region metadata["compute/type"] = "standard" From a39b6d8b8f011cd6f9f92660522dde87f69041d4 Mon Sep 17 00:00:00 2001 From: Justin Brooks Date: Tue, 15 Apr 2025 13:02:57 -0400 Subject: [PATCH 4/6] clean up conditions --- cmd/ctrlc/root/sync/aws/ec2/ec2.go | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/cmd/ctrlc/root/sync/aws/ec2/ec2.go b/cmd/ctrlc/root/sync/aws/ec2/ec2.go index c1f1aa1..d592b28 100644 --- a/cmd/ctrlc/root/sync/aws/ec2/ec2.go +++ b/cmd/ctrlc/root/sync/aws/ec2/ec2.go @@ -148,14 +148,8 @@ func NewSyncEC2Cmd() *cobra.Command { metadata["compute/machine-type"] = string(instance.InstanceType) metadata["compute/region"] = region metadata["compute/type"] = "standard" - - if instance.Architecture != "" { - metadata["compute/architecture"] = strings.ReplaceAll(string(instance.Architecture), "_mac", "") - } - - if instance.BootMode != "" { - metadata["compute/boot-mode"] = string(instance.BootMode) - } + metadata["compute/architecture"] = strings.ReplaceAll(string(instance.Architecture), "_mac", "") + metadata["compute/boot-mode"] = string(instance.BootMode) if instance.PlatformDetails != nil { metadata["compute/platform"] = *instance.PlatformDetails @@ -168,9 +162,7 @@ func NewSyncEC2Cmd() *cobra.Command { metadata["compute/cpu-threads"] = strconv.Itoa(int(*instance.CpuOptions.ThreadsPerCore) * int(*instance.CpuOptions.CoreCount)) } } - if instance.Hypervisor != "" { - metadata["compute/hypervisor"] = string(instance.Hypervisor) - } + metadata["compute/hypervisor"] = string(instance.Hypervisor) if instance.State != nil { metadata["compute/state"] = string(instance.State.Name) From 959876574d351ef6ff50d3b39c5ee61d3d5559e3 Mon Sep 17 00:00:00 2001 From: Justin Brooks Date: Tue, 15 Apr 2025 16:55:36 -0400 Subject: [PATCH 5/6] clean up --- cmd/ctrlc/root/sync/aws/ec2/ec2.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/ctrlc/root/sync/aws/ec2/ec2.go b/cmd/ctrlc/root/sync/aws/ec2/ec2.go index d592b28..9263350 100644 --- a/cmd/ctrlc/root/sync/aws/ec2/ec2.go +++ b/cmd/ctrlc/root/sync/aws/ec2/ec2.go @@ -145,6 +145,7 @@ func NewSyncEC2Cmd() *cobra.Command { metadata["aws/tag/"+*tag.Key] = *tag.Value } } + metadata["compute/machine-type"] = string(instance.InstanceType) metadata["compute/region"] = region metadata["compute/type"] = "standard" From 0eb6b110a43629920b6aaa32446e8844f8c83416 Mon Sep 17 00:00:00 2001 From: Justin Brooks Date: Thu, 17 Apr 2025 16:48:03 -0400 Subject: [PATCH 6/6] Update cmd/ctrlc/root/sync/aws/ec2/ec2.go Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- cmd/ctrlc/root/sync/aws/ec2/ec2.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/ctrlc/root/sync/aws/ec2/ec2.go b/cmd/ctrlc/root/sync/aws/ec2/ec2.go index 9263350..d5c3221 100644 --- a/cmd/ctrlc/root/sync/aws/ec2/ec2.go +++ b/cmd/ctrlc/root/sync/aws/ec2/ec2.go @@ -196,7 +196,7 @@ func NewSyncEC2Cmd() *cobra.Command { metadata["aws/vpc-id"] = *instance.VpcId } if instance.PlatformDetails != nil { - metadata["aws/instance-type"] = string(*instance.PlatformDetails) + metadata["aws/platform-details"] = string(*instance.PlatformDetails) } if instance.InstanceId != nil { metadata["aws/instance-id"] = *instance.InstanceId