diff --git a/internal/aws/cloudformation/client.go b/internal/aws/cloudformation/client.go index fb0865d..837cf98 100644 --- a/internal/aws/cloudformation/client.go +++ b/internal/aws/cloudformation/client.go @@ -42,11 +42,19 @@ func (c *Client) CreateStack(ctx context.Context, params *CreateStackParams) (*S input.Parameters = cfParams } - _, err := c.cfn.CreateStack(ctx, input) + resp, err := c.cfn.CreateStack(ctx, input) if err != nil { return nil, wrapError(err) } + // If no-wait is set, return immediately without waiting for completion + if params.NoWait { + return &StackOutput{ + StackID: aws.ToString(resp.StackId), + Outputs: nil, // no outputs available yet + }, nil + } + // Wait for stack creation to complete waiter := cloudformation.NewStackCreateCompleteWaiter(c.cfn) err = waiter.Wait(ctx, &cloudformation.DescribeStacksInput{ @@ -84,11 +92,19 @@ func (c *Client) UpdateStack(ctx context.Context, params *UpdateStackParams) (*S input.Parameters = cfParams } - _, err := c.cfn.UpdateStack(ctx, input) + resp, err := c.cfn.UpdateStack(ctx, input) if err != nil { return nil, wrapError(err) } + // If no-wait is set, return immediately without waiting for completion + if params.NoWait { + return &StackOutput{ + StackID: aws.ToString(resp.StackId), + Outputs: nil, // no outputs available yet + }, nil + } + // Wait for stack update to complete waiter := cloudformation.NewStackUpdateCompleteWaiter(c.cfn) err = waiter.Wait(ctx, &cloudformation.DescribeStacksInput{ @@ -103,7 +119,7 @@ func (c *Client) UpdateStack(ctx context.Context, params *UpdateStackParams) (*S } // DeleteStack deletes a CloudFormation stack -func (c *Client) DeleteStack(ctx context.Context, stackName string, waitTimeout time.Duration) error { +func (c *Client) DeleteStack(ctx context.Context, stackName string, waitTimeout time.Duration, noWait bool) error { _, err := c.cfn.DeleteStack(ctx, &cloudformation.DeleteStackInput{ StackName: aws.String(stackName), }) @@ -111,6 +127,11 @@ func (c *Client) DeleteStack(ctx context.Context, stackName string, waitTimeout return wrapError(err) } + // If no-wait is set, return immediately without waiting for deletion + if noWait { + return nil + } + // Wait for stack deletion to complete waiter := cloudformation.NewStackDeleteCompleteWaiter(c.cfn) err = waiter.Wait(ctx, &cloudformation.DescribeStacksInput{ diff --git a/internal/aws/cloudformation/stack.go b/internal/aws/cloudformation/stack.go index 809c6be..e8ed3e3 100644 --- a/internal/aws/cloudformation/stack.go +++ b/internal/aws/cloudformation/stack.go @@ -14,6 +14,7 @@ type CreateStackParams struct { Capabilities []types.Capability Tags []types.Tag WaitTimeout time.Duration + NoWait bool // if true, skip waiting for stack creation to complete } // UpdateStackParams contains parameters for updating a CloudFormation stack @@ -24,6 +25,7 @@ type UpdateStackParams struct { Parameters map[string]string Capabilities []types.Capability WaitTimeout time.Duration + NoWait bool // if true, skip waiting for stack update to complete } // StackOutput contains the outputs from a CloudFormation stack diff --git a/internal/commands/bootstrap/create.go b/internal/commands/bootstrap/create.go index e3b270d..b97abb3 100644 --- a/internal/commands/bootstrap/create.go +++ b/internal/commands/bootstrap/create.go @@ -23,6 +23,7 @@ type createOptions struct { functionName string region string stackName string + noWait bool } func newCreateCommand() *cobra.Command { @@ -49,6 +50,7 @@ Example: cmd.Flags().StringVar(&opts.functionName, "function-name", defaultFunctionName, "Name of the Lambda function") cmd.Flags().StringVar(&opts.region, "region", "", "AWS region (required)") cmd.Flags().StringVar(&opts.stackName, "stack-name", defaultStackName, "Name of the CloudFormation stack") + cmd.Flags().BoolVar(&opts.noWait, "no-wait", true, "Return immediately without waiting for stack creation to complete") _ = cmd.MarkFlagRequired("image-uri") _ = cmd.MarkFlagRequired("region") @@ -102,6 +104,7 @@ func runCreate(ctx context.Context, opts *createOptions) error { }, }, WaitTimeout: defaultTimeout, + NoWait: opts.noWait, } fmt.Println("📋 Creating CloudFormation stack...") @@ -112,11 +115,16 @@ func runCreate(ctx context.Context, opts *createOptions) error { return fmt.Errorf("failed to create stack: %w", err) } - fmt.Println("✅ Stack created successfully!") - fmt.Println() - fmt.Println("Outputs:") - for key, value := range output.Outputs { - fmt.Printf(" %s: %s\n", key, value) + if opts.noWait { + fmt.Println("✅ Stack creation submitted!") + fmt.Printf(" Stack ID: %s\n", output.StackID) + } else { + fmt.Println("✅ Stack created successfully!") + fmt.Println() + fmt.Println("Outputs:") + for key, value := range output.Outputs { + fmt.Printf(" %s: %s\n", key, value) + } } return nil diff --git a/internal/commands/bootstrap/delete.go b/internal/commands/bootstrap/delete.go index 0543f24..058626a 100644 --- a/internal/commands/bootstrap/delete.go +++ b/internal/commands/bootstrap/delete.go @@ -13,6 +13,7 @@ import ( type deleteOptions struct { region string stackName string + noWait bool } func newDeleteCommand() *cobra.Command { @@ -34,6 +35,7 @@ Example: cmd.Flags().StringVar(&opts.region, "region", "", "AWS region (required)") cmd.Flags().StringVar(&opts.stackName, "stack-name", defaultStackName, "Name of the CloudFormation stack") + cmd.Flags().BoolVar(&opts.noWait, "no-wait", true, "Return immediately without waiting for stack deletion to complete") _ = cmd.MarkFlagRequired("region") @@ -57,12 +59,16 @@ func runDelete(ctx context.Context, opts *deleteOptions) error { fmt.Println("📋 Deleting CloudFormation stack...") // Delete stack - err = cfnClient.DeleteStack(ctx, opts.stackName, 10*time.Minute) + err = cfnClient.DeleteStack(ctx, opts.stackName, 10*time.Minute, opts.noWait) if err != nil { return fmt.Errorf("failed to delete stack: %w", err) } - fmt.Println("✅ Stack deleted successfully!") + if opts.noWait { + fmt.Println("✅ Stack deletion submitted!") + } else { + fmt.Println("✅ Stack deleted successfully!") + } return nil } diff --git a/internal/commands/clusteriam/create.go b/internal/commands/clusteriam/create.go index 4cd3e0c..95479fa 100644 --- a/internal/commands/clusteriam/create.go +++ b/internal/commands/clusteriam/create.go @@ -16,6 +16,7 @@ type createOptions struct { clusterName string oidcIssuerURL string region string + noWait bool } func newCreateCommand() *cobra.Command { @@ -56,6 +57,7 @@ Examples: cmd.Flags().StringVar(&opts.oidcIssuerURL, "oidc-issuer-url", "", "OIDC issuer URL (optional — also creates OIDC provider if provided)") cmd.Flags().StringVar(&opts.region, "region", "", "AWS region (required)") + cmd.Flags().BoolVar(&opts.noWait, "no-wait", true, "Return immediately without waiting for stack creation to complete") _ = cmd.MarkFlagRequired("region") @@ -99,11 +101,14 @@ func runCreate(ctx context.Context, opts *createOptions) error { iamReq := &clusteriam.CreateIAMRequest{ ClusterName: opts.clusterName, OIDCIssuerDomain: oidcIssuerDomain, + NoWait: opts.noWait, AWSConfig: cfg, } fmt.Printf("Creating or updating CloudFormation stack: rosa-%s-iam\n", opts.clusterName) - fmt.Println(" This may take several minutes...") + if !opts.noWait { + fmt.Println(" This may take several minutes...") + } fmt.Println() resp, err := clusteriam.CreateIAM(ctx, iamReq) @@ -111,14 +116,19 @@ func runCreate(ctx context.Context, opts *createOptions) error { return err } - fmt.Println("Cluster IAM roles created successfully!") - fmt.Printf(" Stack ID: %s\n", resp.StackID) - fmt.Println() + if opts.noWait { + fmt.Println("Stack creation submitted!") + fmt.Printf(" Stack ID: %s\n", resp.StackID) + } else { + fmt.Println("Cluster IAM roles created successfully!") + fmt.Printf(" Stack ID: %s\n", resp.StackID) + fmt.Println() - if len(resp.Outputs) > 0 { - fmt.Println("Created Resources:") - for key, value := range resp.Outputs { - fmt.Printf(" %s: %s\n", key, value) + if len(resp.Outputs) > 0 { + fmt.Println("Created Resources:") + for key, value := range resp.Outputs { + fmt.Printf(" %s: %s\n", key, value) + } } } @@ -131,11 +141,14 @@ func runCreate(ctx context.Context, opts *createOptions) error { oidcReq := &clusteroidc.CreateOIDCRequest{ ClusterName: opts.clusterName, OIDCIssuerURL: opts.oidcIssuerURL, + NoWait: opts.noWait, AWSConfig: cfg, } fmt.Printf("Creating CloudFormation stack: rosa-%s-oidc\n", opts.clusterName) - fmt.Println(" This may take a few minutes...") + if !opts.noWait { + fmt.Println(" This may take a few minutes...") + } fmt.Println() oidcResp, err := clusteroidc.CreateOIDC(ctx, oidcReq) @@ -143,14 +156,19 @@ func runCreate(ctx context.Context, opts *createOptions) error { return fmt.Errorf("IAM roles created but OIDC provider failed: %w", err) } - fmt.Println("Cluster OIDC provider created successfully!") - fmt.Printf(" Stack ID: %s\n", oidcResp.StackID) - fmt.Println() - - if len(oidcResp.Outputs) > 0 { - fmt.Println("OIDC Resources:") - for key, value := range oidcResp.Outputs { - fmt.Printf(" %s: %s\n", key, value) + if opts.noWait { + fmt.Println("OIDC provider stack creation submitted!") + fmt.Printf(" Stack ID: %s\n", oidcResp.StackID) + } else { + fmt.Println("Cluster OIDC provider created successfully!") + fmt.Printf(" Stack ID: %s\n", oidcResp.StackID) + fmt.Println() + + if len(oidcResp.Outputs) > 0 { + fmt.Println("OIDC Resources:") + for key, value := range oidcResp.Outputs { + fmt.Printf(" %s: %s\n", key, value) + } } } } diff --git a/internal/commands/clusteriam/delete.go b/internal/commands/clusteriam/delete.go index edf57de..8afcfc8 100644 --- a/internal/commands/clusteriam/delete.go +++ b/internal/commands/clusteriam/delete.go @@ -12,6 +12,7 @@ import ( type deleteOptions struct { clusterName string region string + noWait bool } func newDeleteCommand() *cobra.Command { @@ -34,6 +35,7 @@ Example: } cmd.Flags().StringVar(&opts.region, "region", "", "AWS region (required)") + cmd.Flags().BoolVar(&opts.noWait, "no-wait", true, "Return immediately without waiting for stack deletion to complete") _ = cmd.MarkFlagRequired("region") @@ -54,11 +56,14 @@ func runDelete(ctx context.Context, opts *deleteOptions) error { // Create service request req := &clusteriam.DeleteIAMRequest{ ClusterName: opts.clusterName, + NoWait: opts.noWait, AWSConfig: cfg, } fmt.Printf("☁️ Deleting CloudFormation stack: rosa-%s-iam\n", opts.clusterName) - fmt.Println(" This may take several minutes...") + if !opts.noWait { + fmt.Println(" This may take several minutes...") + } fmt.Println() // Call service layer @@ -67,7 +72,11 @@ func runDelete(ctx context.Context, opts *deleteOptions) error { return err } - fmt.Println("✅ Cluster IAM resources deleted successfully!") + if opts.noWait { + fmt.Println("✅ Stack deletion submitted!") + } else { + fmt.Println("✅ Cluster IAM resources deleted successfully!") + } return nil } diff --git a/internal/commands/clusteroidc/create.go b/internal/commands/clusteroidc/create.go index 8e7de51..69f526a 100644 --- a/internal/commands/clusteroidc/create.go +++ b/internal/commands/clusteroidc/create.go @@ -15,6 +15,7 @@ type createOptions struct { oidcIssuerURL string oidcThumbprint string region string + noWait bool } func newCreateCommand() *cobra.Command { @@ -46,6 +47,7 @@ Example: cmd.Flags().StringVar(&opts.oidcIssuerURL, "oidc-issuer-url", "", "OIDC issuer URL from the cluster (required)") cmd.Flags().StringVar(&opts.oidcThumbprint, "oidc-thumbprint", "", "TLS thumbprint (optional, fetched automatically if omitted)") cmd.Flags().StringVar(&opts.region, "region", "", "AWS region (required)") + cmd.Flags().BoolVar(&opts.noWait, "no-wait", true, "Return immediately without waiting for stack creation to complete") _ = cmd.MarkFlagRequired("oidc-issuer-url") _ = cmd.MarkFlagRequired("region") @@ -73,11 +75,14 @@ func runCreate(ctx context.Context, opts *createOptions) error { ClusterName: opts.clusterName, OIDCIssuerURL: opts.oidcIssuerURL, OIDCThumbprint: opts.oidcThumbprint, + NoWait: opts.noWait, AWSConfig: cfg, } fmt.Printf("Creating CloudFormation stack: rosa-%s-oidc\n", opts.clusterName) - fmt.Println(" This may take a few minutes...") + if !opts.noWait { + fmt.Println(" This may take a few minutes...") + } fmt.Println() resp, err := clusteroidc.CreateOIDC(ctx, req) @@ -85,14 +90,19 @@ func runCreate(ctx context.Context, opts *createOptions) error { return err } - fmt.Println("Cluster OIDC provider created successfully!") - fmt.Printf(" Stack ID: %s\n", resp.StackID) - fmt.Println() - - if len(resp.Outputs) > 0 { - fmt.Println("Created Resources:") - for key, value := range resp.Outputs { - fmt.Printf(" %s: %s\n", key, value) + if opts.noWait { + fmt.Println("Stack creation submitted!") + fmt.Printf(" Stack ID: %s\n", resp.StackID) + } else { + fmt.Println("Cluster OIDC provider created successfully!") + fmt.Printf(" Stack ID: %s\n", resp.StackID) + fmt.Println() + + if len(resp.Outputs) > 0 { + fmt.Println("Created Resources:") + for key, value := range resp.Outputs { + fmt.Printf(" %s: %s\n", key, value) + } } } diff --git a/internal/commands/clusteroidc/delete.go b/internal/commands/clusteroidc/delete.go index b5141d7..15d5a8a 100644 --- a/internal/commands/clusteroidc/delete.go +++ b/internal/commands/clusteroidc/delete.go @@ -12,6 +12,7 @@ import ( type deleteOptions struct { clusterName string region string + noWait bool } func newDeleteCommand() *cobra.Command { @@ -35,6 +36,7 @@ Example: } cmd.Flags().StringVar(&opts.region, "region", "", "AWS region (required)") + cmd.Flags().BoolVar(&opts.noWait, "no-wait", true, "Return immediately without waiting for stack deletion to complete") _ = cmd.MarkFlagRequired("region") return cmd @@ -52,17 +54,24 @@ func runDelete(ctx context.Context, opts *deleteOptions) error { req := &clusteroidc.DeleteOIDCRequest{ ClusterName: opts.clusterName, + NoWait: opts.noWait, AWSConfig: cfg, } fmt.Printf("Deleting CloudFormation stack: rosa-%s-oidc\n", opts.clusterName) - fmt.Println(" This may take a few minutes...") + if !opts.noWait { + fmt.Println(" This may take a few minutes...") + } fmt.Println() if err := clusteroidc.DeleteOIDC(ctx, req); err != nil { return err } - fmt.Println("Cluster OIDC provider deleted successfully!") + if opts.noWait { + fmt.Println("Stack deletion submitted!") + } else { + fmt.Println("Cluster OIDC provider deleted successfully!") + } return nil } diff --git a/internal/commands/clustervpc/create.go b/internal/commands/clustervpc/create.go index 6183b72..ee236bd 100644 --- a/internal/commands/clustervpc/create.go +++ b/internal/commands/clustervpc/create.go @@ -18,6 +18,7 @@ type createOptions struct { privateSubnetCidrs string availabilityZones string singleNatGateway bool + noWait bool } func newCreateCommand() *cobra.Command { @@ -59,6 +60,7 @@ Example: cmd.Flags().StringVar(&opts.privateSubnetCidrs, "private-subnet-cidrs", "10.0.0.0/19,10.0.32.0/19,10.0.64.0/19", "Comma-separated private subnet CIDRs") cmd.Flags().StringVar(&opts.availabilityZones, "availability-zones", "", "Comma-separated availability zones, 1-3 (optional, auto-detected if empty)") cmd.Flags().BoolVar(&opts.singleNatGateway, "single-nat-gateway", true, "Use single NAT gateway (true=cost savings, false=HA per-AZ)") + cmd.Flags().BoolVar(&opts.noWait, "no-wait", true, "Return immediately without waiting for stack creation to complete") _ = cmd.MarkFlagRequired("region") @@ -96,12 +98,15 @@ func runCreate(ctx context.Context, opts *createOptions) error { PrivateSubnetCidrs: privateSubnets, AvailabilityZones: azs, SingleNatGateway: opts.singleNatGateway, + NoWait: opts.noWait, AWSConfig: cfg, } fmt.Println("📄 Loading CloudFormation template...") fmt.Printf("☁️ Creating CloudFormation stack: rosa-%s-vpc\n", opts.clusterName) - fmt.Println(" This may take several minutes...") + if !opts.noWait { + fmt.Println(" This may take several minutes...") + } fmt.Println() // Call service layer @@ -110,14 +115,22 @@ func runCreate(ctx context.Context, opts *createOptions) error { return err } - fmt.Println("✅ Cluster VPC resources created successfully!") - fmt.Printf(" Stack ID: %s\n", resp.StackID) - fmt.Println() - - if len(resp.Outputs) > 0 { - fmt.Println("Outputs:") - for key, value := range resp.Outputs { - fmt.Printf(" %s: %s\n", key, value) + if opts.noWait { + fmt.Println("✅ Stack creation submitted!") + fmt.Printf(" Stack ID: %s\n", resp.StackID) + fmt.Println() + fmt.Printf("💡 Stack is being created asynchronously. Check status with:\n") + fmt.Printf(" rosactl cluster-vpc describe %s --region %s\n", opts.clusterName, opts.region) + } else { + fmt.Println("✅ Cluster VPC resources created successfully!") + fmt.Printf(" Stack ID: %s\n", resp.StackID) + fmt.Println() + + if len(resp.Outputs) > 0 { + fmt.Println("Outputs:") + for key, value := range resp.Outputs { + fmt.Printf(" %s: %s\n", key, value) + } } } diff --git a/internal/commands/clustervpc/delete.go b/internal/commands/clustervpc/delete.go index 9c1cecf..3a49247 100644 --- a/internal/commands/clustervpc/delete.go +++ b/internal/commands/clustervpc/delete.go @@ -12,6 +12,7 @@ import ( type deleteOptions struct { clusterName string region string + noWait bool } func newDeleteCommand() *cobra.Command { @@ -34,6 +35,7 @@ Example: } cmd.Flags().StringVar(&opts.region, "region", "", "AWS region (required)") + cmd.Flags().BoolVar(&opts.noWait, "no-wait", true, "Return immediately without waiting for stack deletion to complete") _ = cmd.MarkFlagRequired("region") @@ -54,11 +56,14 @@ func runDelete(ctx context.Context, opts *deleteOptions) error { // Create service request req := &clustervpc.DeleteVPCRequest{ ClusterName: opts.clusterName, + NoWait: opts.noWait, AWSConfig: cfg, } fmt.Printf("☁️ Deleting CloudFormation stack: rosa-%s-vpc\n", opts.clusterName) - fmt.Println(" This may take several minutes...") + if !opts.noWait { + fmt.Println(" This may take several minutes...") + } fmt.Println() // Call service layer @@ -67,7 +72,14 @@ func runDelete(ctx context.Context, opts *deleteOptions) error { return err } - fmt.Println("✅ Cluster VPC resources deleted successfully!") + if opts.noWait { + fmt.Println("✅ Stack deletion submitted!") + fmt.Println() + fmt.Printf("💡 Stack is being deleted asynchronously. Check status with:\n") + fmt.Printf(" rosactl cluster-vpc describe %s --region %s\n", opts.clusterName, opts.region) + } else { + fmt.Println("✅ Cluster VPC resources deleted successfully!") + } return nil } diff --git a/internal/services/clusteriam/service.go b/internal/services/clusteriam/service.go index f13c6e7..d0dd0a0 100644 --- a/internal/services/clusteriam/service.go +++ b/internal/services/clusteriam/service.go @@ -15,6 +15,7 @@ import ( type CreateIAMRequest struct { ClusterName string OIDCIssuerDomain string // optional — empty uses template default PENDING + NoWait bool AWSConfig aws.Config } @@ -25,6 +26,7 @@ type CreateIAMResponse struct { type DeleteIAMRequest struct { ClusterName string + NoWait bool AWSConfig aws.Config } @@ -64,6 +66,7 @@ func CreateIAM(ctx context.Context, req *CreateIAMRequest) (*CreateIAMResponse, }, }, WaitTimeout: 15 * time.Minute, + NoWait: req.NoWait, } // Only pass OIDCIssuerDomain if provided — otherwise let the template default (PENDING) apply @@ -98,6 +101,7 @@ func updateIAM(ctx context.Context, cfnClient *cloudformation.Client, req *Creat types.CapabilityCapabilityNamedIam, }, WaitTimeout: 15 * time.Minute, + NoWait: req.NoWait, } if req.OIDCIssuerDomain != "" { @@ -133,7 +137,7 @@ func DeleteIAM(ctx context.Context, req *DeleteIAMRequest) error { // Delete stack stackName := fmt.Sprintf("rosa-%s-iam", req.ClusterName) - err := cfnClient.DeleteStack(ctx, stackName, 15*time.Minute) + err := cfnClient.DeleteStack(ctx, stackName, 15*time.Minute, req.NoWait) if err != nil { var notFound *cloudformation.StackNotFoundError if errors.As(err, ¬Found) { diff --git a/internal/services/clusteroidc/service.go b/internal/services/clusteroidc/service.go index 437804d..8e41969 100644 --- a/internal/services/clusteroidc/service.go +++ b/internal/services/clusteroidc/service.go @@ -18,6 +18,7 @@ type CreateOIDCRequest struct { ClusterName string OIDCIssuerURL string OIDCThumbprint string // optional — fetched automatically if empty + NoWait bool AWSConfig aws.Config } @@ -28,6 +29,7 @@ type CreateOIDCResponse struct { type DeleteOIDCRequest struct { ClusterName string + NoWait bool AWSConfig aws.Config } @@ -75,6 +77,7 @@ func CreateOIDC(ctx context.Context, req *CreateOIDCRequest) (*CreateOIDCRespons {Key: aws.String("red-hat-managed"), Value: aws.String("true")}, }, WaitTimeout: 5 * time.Minute, + NoWait: req.NoWait, } // Create (or update) the OIDC provider stack @@ -93,7 +96,7 @@ func CreateOIDC(ctx context.Context, req *CreateOIDCRequest) (*CreateOIDCRespons // Update the IAM stack trust policies with the real issuer domain iamStackName := fmt.Sprintf("rosa-%s-iam", req.ClusterName) - if err := updateIAMTrustPolicies(ctx, cfnClient, req.ClusterName, iamStackName, oidcIssuerDomain); err != nil { + if err := updateIAMTrustPolicies(ctx, cfnClient, req.ClusterName, iamStackName, oidcIssuerDomain, req.NoWait); err != nil { return nil, fmt.Errorf("OIDC provider created but failed to update IAM trust policies: %w", err) } @@ -113,6 +116,7 @@ func updateOIDCStack(ctx context.Context, cfnClient *cloudformation.Client, req "OIDCThumbprint": thumbprint, }, WaitTimeout: 5 * time.Minute, + NoWait: req.NoWait, } output, err := cfnClient.UpdateStack(ctx, params) @@ -130,7 +134,7 @@ func updateOIDCStack(ctx context.Context, cfnClient *cloudformation.Client, req // updateIAMTrustPolicies updates the IAM stack with the real OIDC issuer domain. // If the IAM stack does not exist yet (e.g. when using the deferred-IAM flow where // cluster-iam is created after cluster-oidc), the update is skipped. -func updateIAMTrustPolicies(ctx context.Context, cfnClient *cloudformation.Client, clusterName, iamStackName, oidcIssuerDomain string) error { +func updateIAMTrustPolicies(ctx context.Context, cfnClient *cloudformation.Client, clusterName, iamStackName, oidcIssuerDomain string, noWait bool) error { params := &cloudformation.UpdateStackParams{ StackName: iamStackName, UsePreviousTemplate: true, @@ -143,6 +147,7 @@ func updateIAMTrustPolicies(ctx context.Context, cfnClient *cloudformation.Clien types.CapabilityCapabilityNamedIam, }, WaitTimeout: 15 * time.Minute, + NoWait: noWait, } _, err := cfnClient.UpdateStack(ctx, params) @@ -167,7 +172,7 @@ func DeleteOIDC(ctx context.Context, req *DeleteOIDCRequest) error { cfnClient := cloudformation.NewClient(req.AWSConfig) stackName := fmt.Sprintf("rosa-%s-oidc", req.ClusterName) - err := cfnClient.DeleteStack(ctx, stackName, 5*time.Minute) + err := cfnClient.DeleteStack(ctx, stackName, 5*time.Minute, req.NoWait) if err != nil { var notFound *cloudformation.StackNotFoundError if errors.As(err, ¬Found) { diff --git a/internal/services/clustervpc/service.go b/internal/services/clustervpc/service.go index 5c247c9..f97375d 100644 --- a/internal/services/clustervpc/service.go +++ b/internal/services/clustervpc/service.go @@ -19,6 +19,7 @@ type CreateVPCRequest struct { PrivateSubnetCidrs []string AvailabilityZones []string SingleNatGateway bool + NoWait bool AWSConfig aws.Config } @@ -29,6 +30,7 @@ type CreateVPCResponse struct { type DeleteVPCRequest struct { ClusterName string + NoWait bool AWSConfig aws.Config } @@ -105,6 +107,7 @@ func CreateVPC(ctx context.Context, req *CreateVPCRequest) (*CreateVPCResponse, }, }, WaitTimeout: 15 * time.Minute, + NoWait: req.NoWait, } // Create stack @@ -166,6 +169,7 @@ func updateVPC(ctx context.Context, cfnClient *cloudformation.Client, req *Creat TemplateBody: templateBody, Parameters: params, WaitTimeout: 15 * time.Minute, + NoWait: req.NoWait, } output, err := cfnClient.UpdateStack(ctx, updateParams) @@ -197,7 +201,7 @@ func DeleteVPC(ctx context.Context, req *DeleteVPCRequest) error { // Delete stack stackName := fmt.Sprintf("rosa-%s-vpc", req.ClusterName) - err := cfnClient.DeleteStack(ctx, stackName, 15*time.Minute) + err := cfnClient.DeleteStack(ctx, stackName, 15*time.Minute, req.NoWait) if err != nil { var notFound *cloudformation.StackNotFoundError if errors.As(err, ¬Found) {