Skip to content

Commit 0bd7dcf

Browse files
feat(create-asset): infer more command inputs from the cluster scan file (#38)
1 parent 3b0a4db commit 0bd7dcf

File tree

14 files changed

+570
-48
lines changed

14 files changed

+570
-48
lines changed

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ require (
99
github.com/aws/aws-sdk-go-v2/config v1.31.0
1010
github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.48.0
1111
github.com/aws/aws-sdk-go-v2/service/costexplorer v1.54.0
12+
github.com/aws/aws-sdk-go-v2/service/ec2 v1.242.0
1213
github.com/aws/aws-sdk-go-v2/service/iam v1.46.0
1314
github.com/aws/aws-sdk-go-v2/service/kafka v1.42.0
1415
github.com/aws/aws-sdk-go-v2/service/s3 v1.85.1

go.sum

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.48.0 h1:NmaelAJldom/+eWDlbYdu
3030
github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.48.0/go.mod h1:s+juX6Mf6RF+y14IK9Ed02U/q86Tqc3PKHIDtuzBMa4=
3131
github.com/aws/aws-sdk-go-v2/service/costexplorer v1.54.0 h1:5e/C1PaQywGtklpMotdHKon/8MfsDyzJ9WFh0ge8G38=
3232
github.com/aws/aws-sdk-go-v2/service/costexplorer v1.54.0/go.mod h1:tR04F/rUvoQ/5YFp3XS+SDB6pWc/Ls0f19WKA8PauDI=
33+
github.com/aws/aws-sdk-go-v2/service/ec2 v1.242.0 h1:xgbWik/QFlVCvoUbumUPPZI7+0RXiScb8eHSV06CELU=
34+
github.com/aws/aws-sdk-go-v2/service/ec2 v1.242.0/go.mod h1:EeWmteKqZjaMj45MUmPET1SisFI+HkqWIRQoyjMivcc=
3335
github.com/aws/aws-sdk-go-v2/service/iam v1.46.0 h1:bJgrqPT2vy+OrJpSeVfZ4e4zaD/EVdcq+5yxDtUOql0=
3436
github.com/aws/aws-sdk-go-v2/service/iam v1.46.0/go.mod h1:WsQuuejKHNC3UWs+n4usF+nNy1DFGYgWRugqFf+gGD4=
3537
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.0 h1:6+lZi2JeGKtCraAj1rpoZfKqnQ9SptseRZioejfUOLM=
@@ -38,6 +40,8 @@ github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.8.1 h1:ps3nrmBWdWwakZB
3840
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.8.1/go.mod h1:bAdfrfxENre68Hh2swNaGEVuFYE74o0SaSCAlaG9E74=
3941
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.3 h1:ieRzyHXypu5ByllM7Sp4hC5f/1Fy5wqxqY0yB85hC7s=
4042
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.3/go.mod h1:O5ROz8jHiOAKAwx179v+7sHMhfobFVi6nZt8DEyiYoM=
43+
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.3 h1:ieRzyHXypu5ByllM7Sp4hC5f/1Fy5wqxqY0yB85hC7s=
44+
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.3/go.mod h1:O5ROz8jHiOAKAwx179v+7sHMhfobFVi6nZt8DEyiYoM=
4145
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.1 h1:MdVYlN5pcQu1t1OYx4Ajo3fKl1IEhzgdPQbYFCRjYS8=
4246
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.1/go.mod h1:iikmNLrvHm2p4a3/4BPeix2S9P+nW8yM1IZW73x8bFA=
4347
github.com/aws/aws-sdk-go-v2/service/kafka v1.42.0 h1:/EB9p30Te0eClIIptu3g3meO38O87YVVdE/UVbyTvWA=

internal/cli/create_asset/bastion_host/create_asset_bastion_host.go

Lines changed: 45 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,21 @@
11
package bastion_host
22

33
import (
4+
"encoding/json"
45
"fmt"
56
"net"
7+
"os"
68

9+
"github.com/aws/aws-sdk-go-v2/aws"
710
"github.com/confluentinc/kcp/internal/generators/create_asset/bastion_host"
11+
"github.com/confluentinc/kcp/internal/types"
812
"github.com/confluentinc/kcp/internal/utils"
913
"github.com/spf13/cobra"
1014
"github.com/spf13/pflag"
1115
)
1216

1317
var (
18+
clusterFile string
1419
region string
1520
vpcId string
1621
bastionHostCidr net.IPNet
@@ -32,12 +37,18 @@ func NewBastionHostCmd() *cobra.Command {
3237
// Required flags.
3338
requiredFlags := pflag.NewFlagSet("required", pflag.ExitOnError)
3439
requiredFlags.SortFlags = false
35-
requiredFlags.StringVar(&region, "region", "", "AWS region the cost report is generated for")
36-
requiredFlags.StringVar(&vpcId, "vpc-id", "", "The VPC ID to target")
3740
requiredFlags.IPNetVar(&bastionHostCidr, "bastion-host-cidr", net.IPNet{}, "The bastion host CIDR (e.g. 10.0.255.0/24)")
3841
bastionHostCmd.Flags().AddFlagSet(requiredFlags)
3942
groups[requiredFlags] = "Required Flags"
4043

44+
conditionalFlags := pflag.NewFlagSet("conditional", pflag.ExitOnError)
45+
conditionalFlags.SortFlags = false
46+
conditionalFlags.StringVar(&clusterFile, "cluster-file", "", "Cluster scan JSON file produced from 'kcp scan cluster' command")
47+
conditionalFlags.StringVar(&region, "region", "", "AWS region the bastion host is provisioned in")
48+
conditionalFlags.StringVar(&vpcId, "vpc-id", "", "VPC ID of the existing MSK cluster")
49+
bastionHostCmd.Flags().AddFlagSet(conditionalFlags)
50+
groups[conditionalFlags] = "Conditional Flags"
51+
4152
// Optional flags.
4253
optionalFlags := pflag.NewFlagSet("optional", pflag.ExitOnError)
4354
optionalFlags.SortFlags = false
@@ -48,13 +59,17 @@ func NewBastionHostCmd() *cobra.Command {
4859
bastionHostCmd.SetUsageFunc(func(c *cobra.Command) error {
4960
fmt.Printf("%s\n\n", c.Short)
5061

51-
flagOrder := []*pflag.FlagSet{requiredFlags, optionalFlags}
52-
groupNames := []string{"Required Flags", "Optional Flags"}
62+
flagOrder := []*pflag.FlagSet{requiredFlags, conditionalFlags, optionalFlags}
63+
groupNames := []string{"Required Flags", "Conditional Flags", "Optional Flags"}
5364

5465
for i, fs := range flagOrder {
5566
usage := fs.FlagUsages()
5667
if usage != "" {
57-
fmt.Printf("%s:\n%s\n", groupNames[i], usage)
68+
fmt.Printf("%s:\n", groupNames[i])
69+
if groupNames[i] == "Conditional Flags" {
70+
fmt.Printf(" (Provide either --cluster-file OR both --region and --vpc-id)\n")
71+
}
72+
fmt.Printf("%s\n", usage)
5873
}
5974
}
6075

@@ -63,8 +78,10 @@ func NewBastionHostCmd() *cobra.Command {
6378
return nil
6479
})
6580

66-
bastionHostCmd.MarkFlagRequired("region")
67-
bastionHostCmd.MarkFlagRequired("vpc-id")
81+
bastionHostCmd.MarkFlagsMutuallyExclusive("cluster-file", "region")
82+
bastionHostCmd.MarkFlagsMutuallyExclusive("cluster-file", "vpc-id")
83+
bastionHostCmd.MarkFlagsRequiredTogether("region", "vpc-id")
84+
6885
bastionHostCmd.MarkFlagRequired("bastion-host-cidr")
6986

7087
return bastionHostCmd
@@ -94,6 +111,27 @@ func runCreateBastionHost(cmd *cobra.Command, args []string) error {
94111
}
95112

96113
func parseBastionHostOpts() (*bastion_host.BastionHostOpts, error) {
114+
if clusterFile != "" {
115+
// Parse cluster information from JSON file
116+
file, err := os.ReadFile(clusterFile)
117+
if err != nil {
118+
return nil, fmt.Errorf("failed to read cluster file: %v", err)
119+
}
120+
121+
var clusterInfo types.ClusterInformation
122+
if err := json.Unmarshal(file, &clusterInfo); err != nil {
123+
return nil, fmt.Errorf("failed to unmarshal cluster info: %v", err)
124+
}
125+
126+
if region == "" {
127+
region = aws.ToString(&clusterInfo.Region)
128+
}
129+
130+
if vpcId == "" {
131+
vpcId = aws.ToString(&clusterInfo.ClusterNetworking.VpcId)
132+
}
133+
}
134+
97135
opts := bastion_host.BastionHostOpts{
98136
Region: region,
99137
VPCId: vpcId,

internal/cli/create_asset/migration_infra/create_asset_migration_infra.go

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"os"
88
"strings"
99

10+
"github.com/aws/aws-sdk-go-v2/aws"
1011
"github.com/confluentinc/kcp/internal/generators/create_asset/migration_infra"
1112
"github.com/confluentinc/kcp/internal/types"
1213
"github.com/confluentinc/kcp/internal/utils"
@@ -44,15 +45,21 @@ func NewMigrationInfraCmd() *cobra.Command {
4445
// Required flags.
4546
requiredFlags := pflag.NewFlagSet("required", pflag.ExitOnError)
4647
requiredFlags.SortFlags = false
47-
requiredFlags.StringVar(&region, "region", "", "AWS region the cost report is generated for")
48-
requiredFlags.StringVar(&vpcId, "vpc-id", "", "Existing MSK VPC ID")
49-
requiredFlags.StringVar(&ccEnvName, "cc-env-name", "", "Confluent Cloud environment name")
50-
requiredFlags.StringVar(&ccClusterName, "cc-cluster-name", "", "Confluent Cloud cluster name")
5148
requiredFlags.StringVar(&clusterFile, "cluster-file", "", "Cluster scan JSON file produced from 'kcp scan cluster' command")
5249
requiredFlags.StringVar(&migrationInfraType, "type", "", "The migration-infra type. See README for available options")
5350
migrationInfraCmd.Flags().AddFlagSet(requiredFlags)
5451
groups[requiredFlags] = "Required Flags"
5552

53+
// Optional flags.
54+
optionalFlags := pflag.NewFlagSet("optional", pflag.ExitOnError)
55+
optionalFlags.SortFlags = false
56+
optionalFlags.StringVar(&region, "region", "", "AWS region the jump migration infrastructure is provisioned in")
57+
optionalFlags.StringVar(&vpcId, "vpc-id", "", "VPC ID of the existing MSK cluster")
58+
optionalFlags.StringVar(&ccEnvName, "cc-env-name", "", "Confluent Cloud environment name")
59+
optionalFlags.StringVar(&ccClusterName, "cc-cluster-name", "", "Confluent Cloud cluster name")
60+
migrationInfraCmd.Flags().AddFlagSet(optionalFlags)
61+
groups[optionalFlags] = "Optional Flags"
62+
5663
// Type 1 flags.
5764
typeOneFlags := pflag.NewFlagSet("type-1", pflag.ExitOnError)
5865
typeOneFlags.SortFlags = false
@@ -74,8 +81,8 @@ func NewMigrationInfraCmd() *cobra.Command {
7481
migrationInfraCmd.SetUsageFunc(func(c *cobra.Command) error {
7582
fmt.Printf("%s\n\n", c.Short)
7683

77-
flagOrder := []*pflag.FlagSet{requiredFlags, typeOneFlags, typeTwoFlags}
78-
groupNames := []string{"Required Flags", "Type 1 Flags", "Type 2 Flags"}
84+
flagOrder := []*pflag.FlagSet{requiredFlags, optionalFlags, typeOneFlags, typeTwoFlags}
85+
groupNames := []string{"Required Flags", "Optional Flags", "Type 1 Flags", "Type 2 Flags"}
7986

8087
for i, fs := range flagOrder {
8188
usage := fs.FlagUsages()
@@ -89,10 +96,6 @@ func NewMigrationInfraCmd() *cobra.Command {
8996
return nil
9097
})
9198

92-
migrationInfraCmd.MarkFlagRequired("region")
93-
migrationInfraCmd.MarkFlagRequired("vpc-id")
94-
migrationInfraCmd.MarkFlagRequired("cc-env-name")
95-
migrationInfraCmd.MarkFlagRequired("cc-cluster-name")
9699
migrationInfraCmd.MarkFlagRequired("cluster-file")
97100
migrationInfraCmd.MarkFlagRequired("type")
98101

@@ -155,6 +158,22 @@ func parseMigrationInfraOpts() (*migration_infra.MigrationInfraOpts, error) {
155158
return nil, fmt.Errorf("failed to unmarshal cluster info: %v", err)
156159
}
157160

161+
if region == "" {
162+
region = aws.ToString(&clusterInfo.Region)
163+
}
164+
165+
if vpcId == "" {
166+
vpcId = aws.ToString(&clusterInfo.ClusterNetworking.VpcId)
167+
}
168+
169+
if ccEnvName == "" {
170+
ccEnvName = aws.ToString(clusterInfo.Cluster.ClusterName)
171+
}
172+
173+
if ccClusterName == "" {
174+
ccClusterName = aws.ToString(clusterInfo.Cluster.ClusterName)
175+
}
176+
158177
opts := migration_infra.MigrationInfraOpts{
159178
Region: region,
160179
VPCId: vpcId,

internal/cli/create_asset/reverse_proxy/create_asset_reverse_proxy.go

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,21 @@
11
package reverse_proxy
22

33
import (
4+
"encoding/json"
45
"fmt"
56
"net"
7+
"os"
68

9+
"github.com/aws/aws-sdk-go-v2/aws"
710
"github.com/confluentinc/kcp/internal/generators/create_asset/reverse_proxy"
11+
"github.com/confluentinc/kcp/internal/types"
812
"github.com/confluentinc/kcp/internal/utils"
913
"github.com/spf13/cobra"
1014
"github.com/spf13/pflag"
1115
)
1216

1317
var (
18+
clusterFile string
1419
region string
1520
vpcId string
1621
reverseProxyCidr net.IPNet
@@ -32,23 +37,33 @@ func NewReverseProxyCmd() *cobra.Command {
3237
// Required flags.
3338
requiredFlags := pflag.NewFlagSet("required", pflag.ExitOnError)
3439
requiredFlags.SortFlags = false
35-
requiredFlags.StringVar(&region, "region", "", "AWS region")
36-
requiredFlags.StringVar(&vpcId, "vpc-id", "", "Existing MSK VPC ID")
3740
requiredFlags.IPNetVar(&reverseProxyCidr, "reverse-proxy-cidr", net.IPNet{}, "Revese proxy subnet CIDR (e.g. 10.0.255.0/24)")
3841
requiredFlags.StringVar(&migrationInfraFolder, "migration-infra-folder", "", "The migration-infra folder produced from 'kcp create-asset migration-infra' command after applying the Terraform")
3942
reverseProxyCmd.Flags().AddFlagSet(requiredFlags)
4043
groups[requiredFlags] = "Required Flags"
4144

45+
conditionalFlags := pflag.NewFlagSet("conditional", pflag.ExitOnError)
46+
conditionalFlags.SortFlags = false
47+
conditionalFlags.StringVar(&clusterFile, "cluster-file", "", "Cluster scan JSON file produced from 'kcp scan cluster' command")
48+
conditionalFlags.StringVar(&region, "region", "", "AWS region the reverse proxy is provisioned in")
49+
conditionalFlags.StringVar(&vpcId, "vpc-id", "", "VPC ID of the existing MSK cluster")
50+
reverseProxyCmd.Flags().AddFlagSet(conditionalFlags)
51+
groups[conditionalFlags] = "Conditional Flags"
52+
4253
reverseProxyCmd.SetUsageFunc(func(c *cobra.Command) error {
4354
fmt.Printf("%s\n\n", c.Short)
4455

45-
flagOrder := []*pflag.FlagSet{requiredFlags}
46-
groupNames := []string{"Required Flags"}
56+
flagOrder := []*pflag.FlagSet{requiredFlags, conditionalFlags}
57+
groupNames := []string{"Required Flags", "Conditional Flags"}
4758

4859
for i, fs := range flagOrder {
4960
usage := fs.FlagUsages()
5061
if usage != "" {
51-
fmt.Printf("%s:\n%s\n", groupNames[i], usage)
62+
fmt.Printf("%s:\n", groupNames[i])
63+
if groupNames[i] == "Conditional Flags" {
64+
fmt.Printf(" (Provide either --cluster-file OR both --region and --vpc-id)\n")
65+
}
66+
fmt.Printf("%s\n", usage)
5267
}
5368
}
5469

@@ -57,8 +72,9 @@ func NewReverseProxyCmd() *cobra.Command {
5772
return nil
5873
})
5974

60-
reverseProxyCmd.MarkFlagRequired("region")
61-
reverseProxyCmd.MarkFlagRequired("vpc-id")
75+
reverseProxyCmd.MarkFlagsMutuallyExclusive("cluster-file", "region")
76+
reverseProxyCmd.MarkFlagsMutuallyExclusive("cluster-file", "vpc-id")
77+
reverseProxyCmd.MarkFlagsRequiredTogether("region", "vpc-id")
6278
reverseProxyCmd.MarkFlagRequired("reverse-proxy-cidr")
6379
reverseProxyCmd.MarkFlagRequired("migration-infra-folder")
6480

@@ -94,6 +110,27 @@ func parseReverseProxyOpts() (*reverse_proxy.ReverseProxyOpts, error) {
94110
return nil, fmt.Errorf("error: %v\n please run terraform apply in the migration infra folder", err)
95111
}
96112

113+
if clusterFile != "" {
114+
// Parse cluster information from JSON file
115+
file, err := os.ReadFile(clusterFile)
116+
if err != nil {
117+
return nil, fmt.Errorf("failed to read cluster file: %v", err)
118+
}
119+
120+
var clusterInfo types.ClusterInformation
121+
if err := json.Unmarshal(file, &clusterInfo); err != nil {
122+
return nil, fmt.Errorf("failed to unmarshal cluster info: %v", err)
123+
}
124+
125+
if region == "" {
126+
region = aws.ToString(&clusterInfo.Region)
127+
}
128+
129+
if vpcId == "" {
130+
vpcId = aws.ToString(&clusterInfo.ClusterNetworking.VpcId)
131+
}
132+
}
133+
97134
opts := reverse_proxy.ReverseProxyOpts{
98135
Region: region,
99136
VPCId: vpcId,

internal/cli/scan/cluster/scan_cluster.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
kafkatypes "github.com/aws/aws-sdk-go-v2/service/kafka/types"
88
"github.com/confluentinc/kcp/internal/client"
99
"github.com/confluentinc/kcp/internal/generators/scan/cluster"
10+
"github.com/confluentinc/kcp/internal/services/ec2"
1011
"github.com/confluentinc/kcp/internal/services/msk"
1112
"github.com/confluentinc/kcp/internal/types"
1213
"github.com/confluentinc/kcp/internal/utils"
@@ -153,8 +154,13 @@ func runScanCluster(cmd *cobra.Command, args []string) error {
153154

154155
mskService := msk.NewMSKService(mskClient)
155156

157+
ec2Service, err := ec2.NewEC2Service(opts.Region)
158+
if err != nil {
159+
return fmt.Errorf("failed to create ec2 service: %v", err)
160+
}
161+
156162
// Scan the cluster
157-
clusterScanner := cluster.NewClusterScanner(mskService, kafkaAdminFactory, *opts)
163+
clusterScanner := cluster.NewClusterScanner(mskService, ec2Service, kafkaAdminFactory, *opts)
158164
if err := clusterScanner.Run(); err != nil {
159165
return fmt.Errorf("failed to scan cluster: %v", err)
160166
}

internal/client/ec2.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package client
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/aws/aws-sdk-go-v2/config"
8+
"github.com/aws/aws-sdk-go-v2/service/ec2"
9+
)
10+
11+
func NewEC2Client(region string) (*ec2.Client, error) {
12+
cfg, err := config.LoadDefaultConfig(context.TODO())
13+
if err != nil {
14+
return nil, fmt.Errorf("❌ Failed to load AWS config: %v", err)
15+
}
16+
17+
if region != "" {
18+
cfg.Region = region
19+
}
20+
21+
ec2Client := ec2.NewFromConfig(cfg)
22+
23+
return ec2Client, nil
24+
}

0 commit comments

Comments
 (0)