From 6c9c8df678da414e6390f324fe7edb6bdd1b8f64 Mon Sep 17 00:00:00 2001 From: Carlo Goetz Date: Tue, 12 May 2026 10:21:56 +0200 Subject: [PATCH 1/4] refac(loadbalancer): use new WaiterHelper for waiters --- CHANGELOG.md | 2 + services/loadbalancer/CHANGELOG.md | 3 + services/loadbalancer/VERSION | 2 +- services/loadbalancer/v2api/wait/wait.go | 76 +++++++++-------------- services/loadbalancer/wait/wait.go | 78 ++++++++++-------------- 5 files changed, 66 insertions(+), 95 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 174fad508..7b7781ecd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -174,6 +174,8 @@ - **Dependencies:** Bump STACKIT SDK core module from `v0.24.1` to `v0.25.0` - [v1.12.2](services/loadbalancer/CHANGELOG.md#v1122) - **Dependencies:** Bump STACKIT SDK core module to `v0.26.0` + - [v1.13.0](services/loadbalancer/CHANGELOG.md#v1130) + - **Improvement:** Use new WaiterHelper for LoadBalancer waiters - `logme`: - [v0.27.3](services/logme/CHANGELOG.md#v0273) - **Dependencies:** Bump STACKIT SDK core module from `v0.24.0` to `v0.24.1` diff --git a/services/loadbalancer/CHANGELOG.md b/services/loadbalancer/CHANGELOG.md index d6f5e60c0..f61621db7 100644 --- a/services/loadbalancer/CHANGELOG.md +++ b/services/loadbalancer/CHANGELOG.md @@ -1,3 +1,6 @@ +## v1.13.0 +- **Improvement:** Use new WaiterHelper for LoadBalancer waiters + ## v1.12.2 - **Dependencies:** Bump STACKIT SDK core module to `v0.26.0` diff --git a/services/loadbalancer/VERSION b/services/loadbalancer/VERSION index 41de27dfa..b28120462 100644 --- a/services/loadbalancer/VERSION +++ b/services/loadbalancer/VERSION @@ -1 +1 @@ -v1.12.2 +v1.13.0 diff --git a/services/loadbalancer/v2api/wait/wait.go b/services/loadbalancer/v2api/wait/wait.go index 9cd852fff..1d084b709 100644 --- a/services/loadbalancer/v2api/wait/wait.go +++ b/services/loadbalancer/v2api/wait/wait.go @@ -2,12 +2,11 @@ package wait import ( "context" + "errors" "fmt" - "net/http" "strings" "time" - "github.com/stackitcloud/stackit-sdk-go/core/oapierror" "github.com/stackitcloud/stackit-sdk-go/core/wait" loadbalancer "github.com/stackitcloud/stackit-sdk-go/services/loadbalancer/v2api" ) @@ -23,58 +22,41 @@ const ( // CreateLoadBalancerWaitHandler will wait for load balancer creation func CreateLoadBalancerWaitHandler(ctx context.Context, a loadbalancer.DefaultAPI, projectId, region, instanceName string) *wait.AsyncActionHandler[loadbalancer.LoadBalancer] { - handler := wait.New(func() (waitFinished bool, response *loadbalancer.LoadBalancer, err error) { - s, err := a.GetLoadBalancer(ctx, projectId, region, instanceName).Execute() - if err != nil { - return false, nil, err - } - if s == nil || s.Name == nil || *s.Name != instanceName || s.Status == nil { - return false, nil, nil - } - - var errors []string - if len(s.Errors) > 0 { - for _, err := range s.Errors { - errors = append(errors, fmt.Sprintf("%s: %s", *err.Type, *err.Description)) + waitConfig := wait.WaiterHelper[loadbalancer.LoadBalancer, string]{ + FetchInstance: a.GetLoadBalancer(ctx, projectId, region, instanceName).Execute, + GetState: func(r *loadbalancer.LoadBalancer) (string, error) { + if r == nil || r.Status == nil { + return "", errors.New("response or status is nil") } - return true, s, fmt.Errorf("create failed for instance with name %s, got status %s and errors: %s", instanceName, *s.Status, strings.Join(errors, ";")) - } - - switch *s.Status { - case LOADBALANCERSTATUS_READY: - return true, s, nil - case LOADBALANCERSTATUS_UNSPECIFIED: - return false, nil, nil - case LOADBALANCERSTATUS_PENDING: - return false, nil, nil - case LOADBALANCERSTATUS_TERMINATING: - return true, s, fmt.Errorf("create failed for instance with name %s, got status %s", instanceName, LOADBALANCERSTATUS_TERMINATING) - case LOADBALANCERSTATUS_ERROR: - return true, s, fmt.Errorf("create failed for instance with name %s, got status %s", instanceName, LOADBALANCERSTATUS_ERROR) - default: - return true, s, fmt.Errorf("instance with name %s has unexpected status %s", instanceName, *s.Status) - } - }) + var sb strings.Builder + if r.Errors != nil && len(r.Errors) > 0 { + for _, err := range r.Errors { + sb.WriteString(fmt.Sprintf("%s: %s; ", *err.Type, *err.Description)) + } + return "", fmt.Errorf("create failed for instance with name %s, got status %s and errors: %s", instanceName, *r.Status, sb.String()) + } + return *r.Status, nil + }, + ActiveState: []string{LOADBALANCERSTATUS_READY}, + ErrorState: []string{LOADBALANCERSTATUS_TERMINATING, LOADBALANCERSTATUS_ERROR}, + } + handler := wait.New(waitConfig.Wait()) handler.SetTimeout(45 * time.Minute) return handler } // DeleteLoadBalancerWaitHandler will wait for load balancer deletion func DeleteLoadBalancerWaitHandler(ctx context.Context, a loadbalancer.DefaultAPI, projectId, region, instanceId string) *wait.AsyncActionHandler[struct{}] { - handler := wait.New(func() (waitFinished bool, response *struct{}, err error) { - _, err = a.GetLoadBalancer(ctx, projectId, region, instanceId).Execute() - if err == nil { - return false, nil, nil - } - oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped - if !ok { - return false, nil, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError") - } - if oapiErr.StatusCode != http.StatusNotFound { - return false, nil, err - } - return true, nil, nil - }) + waitConfig := wait.WaiterHelper[struct{}, string]{ + FetchInstance: func() (*struct{}, error) { + _, err := a.GetLoadBalancer(ctx, projectId, region, instanceId).Execute() + return &struct{}{}, err + }, + GetState: func(r *struct{}) (string, error) { + return "", nil + }, + } + handler := wait.New(waitConfig.Wait()) handler.SetTimeout(15 * time.Minute) return handler } diff --git a/services/loadbalancer/wait/wait.go b/services/loadbalancer/wait/wait.go index 416b4053c..307101e97 100644 --- a/services/loadbalancer/wait/wait.go +++ b/services/loadbalancer/wait/wait.go @@ -2,12 +2,11 @@ package wait import ( "context" + "errors" "fmt" - "net/http" "strings" "time" - "github.com/stackitcloud/stackit-sdk-go/core/oapierror" "github.com/stackitcloud/stackit-sdk-go/core/wait" "github.com/stackitcloud/stackit-sdk-go/services/loadbalancer" ) @@ -37,38 +36,27 @@ type APIClientInterface interface { // CreateLoadBalancerWaitHandler will wait for load balancer creation // Deprecated: Will be removed after 2026-09-30. Move to the packages generated for each available API version instead func CreateLoadBalancerWaitHandler(ctx context.Context, a APIClientInterface, projectId, region, instanceName string) *wait.AsyncActionHandler[loadbalancer.LoadBalancer] { - handler := wait.New(func() (waitFinished bool, response *loadbalancer.LoadBalancer, err error) { - s, err := a.GetLoadBalancerExecute(ctx, projectId, region, instanceName) - if err != nil { - return false, nil, err - } - if s == nil || s.Name == nil || *s.Name != instanceName || s.Status == nil { - return false, nil, nil - } - - var errors []string - if s.Errors != nil && len(*s.Errors) > 0 { - for _, err := range *s.Errors { - errors = append(errors, fmt.Sprintf("%s: %s", *err.Type, *err.Description)) + waitConfig := wait.WaiterHelper[loadbalancer.LoadBalancer, loadbalancer.LoadBalancerStatus]{ + FetchInstance: func() (*loadbalancer.LoadBalancer, error) { + return a.GetLoadBalancerExecute(ctx, projectId, region, instanceName) + }, + GetState: func(r *loadbalancer.LoadBalancer) (loadbalancer.LoadBalancerStatus, error) { + if r == nil || r.Status == nil { + return "", errors.New("response or status is nil") } - return true, s, fmt.Errorf("create failed for instance with name %s, got status %s and errors: %s", instanceName, *s.Status, strings.Join(errors, ";")) - } - - switch *s.Status { - case loadbalancer.LOADBALANCERSTATUS_READY: - return true, s, nil - case loadbalancer.LOADBALANCERSTATUS_UNSPECIFIED: - return false, nil, nil - case loadbalancer.LOADBALANCERSTATUS_PENDING: - return false, nil, nil - case loadbalancer.LOADBALANCERSTATUS_TERMINATING: - return true, s, fmt.Errorf("create failed for instance with name %s, got status %s", instanceName, InstanceStatusTerminating) - case loadbalancer.LOADBALANCERSTATUS_ERROR: - return true, s, fmt.Errorf("create failed for instance with name %s, got status %s", instanceName, InstanceStatusError) - default: - return true, s, fmt.Errorf("instance with name %s has unexpected status %s", instanceName, *s.Status) - } - }) + var sb strings.Builder + if r.Errors != nil && len(*r.Errors) > 0 { + for _, err := range *r.Errors { + sb.WriteString(fmt.Sprintf("%s: %s; ", *err.Type, *err.Description)) + } + return "", fmt.Errorf("create failed for instance with name %s, got status %s and errors: %s", instanceName, *r.Status, sb.String()) + } + return *r.Status, nil + }, + ActiveState: []loadbalancer.LoadBalancerStatus{loadbalancer.LOADBALANCERSTATUS_READY}, + ErrorState: []loadbalancer.LoadBalancerStatus{loadbalancer.LOADBALANCERSTATUS_ERROR, loadbalancer.LOADBALANCERSTATUS_TERMINATING}, + } + handler := wait.New(waitConfig.Wait()) handler.SetTimeout(45 * time.Minute) return handler } @@ -76,20 +64,16 @@ func CreateLoadBalancerWaitHandler(ctx context.Context, a APIClientInterface, pr // DeleteLoadBalancerWaitHandler will wait for load balancer deletion // Deprecated: Will be removed after 2026-09-30. Move to the packages generated for each available API version instead func DeleteLoadBalancerWaitHandler(ctx context.Context, a APIClientInterface, projectId, region, instanceId string) *wait.AsyncActionHandler[struct{}] { - handler := wait.New(func() (waitFinished bool, response *struct{}, err error) { - _, err = a.GetLoadBalancerExecute(ctx, projectId, region, instanceId) - if err == nil { - return false, nil, nil - } - oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped - if !ok { - return false, nil, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError") - } - if oapiErr.StatusCode != http.StatusNotFound { - return false, nil, err - } - return true, nil, nil - }) + waitConfig := wait.WaiterHelper[struct{}, loadbalancer.LoadBalancerStatus]{ + FetchInstance: func() (*struct{}, error) { + _, err := a.GetLoadBalancerExecute(ctx, projectId, region, instanceId) + return &struct{}{}, err + }, + GetState: func(r *struct{}) (loadbalancer.LoadBalancerStatus, error) { + return "", nil + }, + } + handler := wait.New(waitConfig.Wait()) handler.SetTimeout(15 * time.Minute) return handler } From 4d39ef0e812061e3c93982749bc21435369bba05 Mon Sep 17 00:00:00 2001 From: Carlo Goetz Date: Tue, 12 May 2026 11:06:28 +0200 Subject: [PATCH 2/4] fix(loadbalancer) linting errors --- services/loadbalancer/v2api/wait/wait.go | 4 ++-- services/loadbalancer/wait/wait.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/services/loadbalancer/v2api/wait/wait.go b/services/loadbalancer/v2api/wait/wait.go index 1d084b709..316df41d9 100644 --- a/services/loadbalancer/v2api/wait/wait.go +++ b/services/loadbalancer/v2api/wait/wait.go @@ -29,7 +29,7 @@ func CreateLoadBalancerWaitHandler(ctx context.Context, a loadbalancer.DefaultAP return "", errors.New("response or status is nil") } var sb strings.Builder - if r.Errors != nil && len(r.Errors) > 0 { + if r.Errors != nil { for _, err := range r.Errors { sb.WriteString(fmt.Sprintf("%s: %s; ", *err.Type, *err.Description)) } @@ -52,7 +52,7 @@ func DeleteLoadBalancerWaitHandler(ctx context.Context, a loadbalancer.DefaultAP _, err := a.GetLoadBalancer(ctx, projectId, region, instanceId).Execute() return &struct{}{}, err }, - GetState: func(r *struct{}) (string, error) { + GetState: func(_ *struct{}) (string, error) { return "", nil }, } diff --git a/services/loadbalancer/wait/wait.go b/services/loadbalancer/wait/wait.go index 307101e97..c60ba9e48 100644 --- a/services/loadbalancer/wait/wait.go +++ b/services/loadbalancer/wait/wait.go @@ -69,7 +69,7 @@ func DeleteLoadBalancerWaitHandler(ctx context.Context, a APIClientInterface, pr _, err := a.GetLoadBalancerExecute(ctx, projectId, region, instanceId) return &struct{}{}, err }, - GetState: func(r *struct{}) (loadbalancer.LoadBalancerStatus, error) { + GetState: func(_ *struct{}) (loadbalancer.LoadBalancerStatus, error) { return "", nil }, } From fae4494db82f212dd1ecc211f447b8d23594c361 Mon Sep 17 00:00:00 2001 From: Carlo Goetz Date: Tue, 12 May 2026 11:27:20 +0200 Subject: [PATCH 3/4] fix(loadbalancer) reset legacy waiter to origin/main --- services/loadbalancer/wait/wait.go | 78 ++++++++++++++++++------------ 1 file changed, 47 insertions(+), 31 deletions(-) diff --git a/services/loadbalancer/wait/wait.go b/services/loadbalancer/wait/wait.go index c60ba9e48..416b4053c 100644 --- a/services/loadbalancer/wait/wait.go +++ b/services/loadbalancer/wait/wait.go @@ -2,11 +2,12 @@ package wait import ( "context" - "errors" "fmt" + "net/http" "strings" "time" + "github.com/stackitcloud/stackit-sdk-go/core/oapierror" "github.com/stackitcloud/stackit-sdk-go/core/wait" "github.com/stackitcloud/stackit-sdk-go/services/loadbalancer" ) @@ -36,27 +37,38 @@ type APIClientInterface interface { // CreateLoadBalancerWaitHandler will wait for load balancer creation // Deprecated: Will be removed after 2026-09-30. Move to the packages generated for each available API version instead func CreateLoadBalancerWaitHandler(ctx context.Context, a APIClientInterface, projectId, region, instanceName string) *wait.AsyncActionHandler[loadbalancer.LoadBalancer] { - waitConfig := wait.WaiterHelper[loadbalancer.LoadBalancer, loadbalancer.LoadBalancerStatus]{ - FetchInstance: func() (*loadbalancer.LoadBalancer, error) { - return a.GetLoadBalancerExecute(ctx, projectId, region, instanceName) - }, - GetState: func(r *loadbalancer.LoadBalancer) (loadbalancer.LoadBalancerStatus, error) { - if r == nil || r.Status == nil { - return "", errors.New("response or status is nil") - } - var sb strings.Builder - if r.Errors != nil && len(*r.Errors) > 0 { - for _, err := range *r.Errors { - sb.WriteString(fmt.Sprintf("%s: %s; ", *err.Type, *err.Description)) - } - return "", fmt.Errorf("create failed for instance with name %s, got status %s and errors: %s", instanceName, *r.Status, sb.String()) + handler := wait.New(func() (waitFinished bool, response *loadbalancer.LoadBalancer, err error) { + s, err := a.GetLoadBalancerExecute(ctx, projectId, region, instanceName) + if err != nil { + return false, nil, err + } + if s == nil || s.Name == nil || *s.Name != instanceName || s.Status == nil { + return false, nil, nil + } + + var errors []string + if s.Errors != nil && len(*s.Errors) > 0 { + for _, err := range *s.Errors { + errors = append(errors, fmt.Sprintf("%s: %s", *err.Type, *err.Description)) } - return *r.Status, nil - }, - ActiveState: []loadbalancer.LoadBalancerStatus{loadbalancer.LOADBALANCERSTATUS_READY}, - ErrorState: []loadbalancer.LoadBalancerStatus{loadbalancer.LOADBALANCERSTATUS_ERROR, loadbalancer.LOADBALANCERSTATUS_TERMINATING}, - } - handler := wait.New(waitConfig.Wait()) + return true, s, fmt.Errorf("create failed for instance with name %s, got status %s and errors: %s", instanceName, *s.Status, strings.Join(errors, ";")) + } + + switch *s.Status { + case loadbalancer.LOADBALANCERSTATUS_READY: + return true, s, nil + case loadbalancer.LOADBALANCERSTATUS_UNSPECIFIED: + return false, nil, nil + case loadbalancer.LOADBALANCERSTATUS_PENDING: + return false, nil, nil + case loadbalancer.LOADBALANCERSTATUS_TERMINATING: + return true, s, fmt.Errorf("create failed for instance with name %s, got status %s", instanceName, InstanceStatusTerminating) + case loadbalancer.LOADBALANCERSTATUS_ERROR: + return true, s, fmt.Errorf("create failed for instance with name %s, got status %s", instanceName, InstanceStatusError) + default: + return true, s, fmt.Errorf("instance with name %s has unexpected status %s", instanceName, *s.Status) + } + }) handler.SetTimeout(45 * time.Minute) return handler } @@ -64,16 +76,20 @@ func CreateLoadBalancerWaitHandler(ctx context.Context, a APIClientInterface, pr // DeleteLoadBalancerWaitHandler will wait for load balancer deletion // Deprecated: Will be removed after 2026-09-30. Move to the packages generated for each available API version instead func DeleteLoadBalancerWaitHandler(ctx context.Context, a APIClientInterface, projectId, region, instanceId string) *wait.AsyncActionHandler[struct{}] { - waitConfig := wait.WaiterHelper[struct{}, loadbalancer.LoadBalancerStatus]{ - FetchInstance: func() (*struct{}, error) { - _, err := a.GetLoadBalancerExecute(ctx, projectId, region, instanceId) - return &struct{}{}, err - }, - GetState: func(_ *struct{}) (loadbalancer.LoadBalancerStatus, error) { - return "", nil - }, - } - handler := wait.New(waitConfig.Wait()) + handler := wait.New(func() (waitFinished bool, response *struct{}, err error) { + _, err = a.GetLoadBalancerExecute(ctx, projectId, region, instanceId) + if err == nil { + return false, nil, nil + } + oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped + if !ok { + return false, nil, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError") + } + if oapiErr.StatusCode != http.StatusNotFound { + return false, nil, err + } + return true, nil, nil + }) handler.SetTimeout(15 * time.Minute) return handler } From 783ae0e43ff97f14c0f77171dd36f9e450b7e59b Mon Sep 17 00:00:00 2001 From: Carlo Goetz Date: Wed, 13 May 2026 11:22:49 +0200 Subject: [PATCH 4/4] breaking(loadbalancer) refactor DeleteLoadBalancerWaitHandler signature --- CHANGELOG.md | 1 + services/loadbalancer/CHANGELOG.md | 1 + services/loadbalancer/v2api/wait/wait.go | 17 +++++++++-------- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b7781ecd..432cbe0be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -176,6 +176,7 @@ - **Dependencies:** Bump STACKIT SDK core module to `v0.26.0` - [v1.13.0](services/loadbalancer/CHANGELOG.md#v1130) - **Improvement:** Use new WaiterHelper for LoadBalancer waiters + - **Breaking Change:** `v2api/wait/DeleteLoadBalancerWaitHandler` now returns a `LoadBalancer` instead of `struct{}` - `logme`: - [v0.27.3](services/logme/CHANGELOG.md#v0273) - **Dependencies:** Bump STACKIT SDK core module from `v0.24.0` to `v0.24.1` diff --git a/services/loadbalancer/CHANGELOG.md b/services/loadbalancer/CHANGELOG.md index f61621db7..cba0d2400 100644 --- a/services/loadbalancer/CHANGELOG.md +++ b/services/loadbalancer/CHANGELOG.md @@ -1,5 +1,6 @@ ## v1.13.0 - **Improvement:** Use new WaiterHelper for LoadBalancer waiters +- **Breaking Change:** `v2api/wait/DeleteLoadBalancerWaitHandler` now returns a `LoadBalancer` instead of `struct{}` ## v1.12.2 - **Dependencies:** Bump STACKIT SDK core module to `v0.26.0` diff --git a/services/loadbalancer/v2api/wait/wait.go b/services/loadbalancer/v2api/wait/wait.go index 316df41d9..7211c00f5 100644 --- a/services/loadbalancer/v2api/wait/wait.go +++ b/services/loadbalancer/v2api/wait/wait.go @@ -46,15 +46,16 @@ func CreateLoadBalancerWaitHandler(ctx context.Context, a loadbalancer.DefaultAP } // DeleteLoadBalancerWaitHandler will wait for load balancer deletion -func DeleteLoadBalancerWaitHandler(ctx context.Context, a loadbalancer.DefaultAPI, projectId, region, instanceId string) *wait.AsyncActionHandler[struct{}] { - waitConfig := wait.WaiterHelper[struct{}, string]{ - FetchInstance: func() (*struct{}, error) { - _, err := a.GetLoadBalancer(ctx, projectId, region, instanceId).Execute() - return &struct{}{}, err - }, - GetState: func(_ *struct{}) (string, error) { - return "", nil +func DeleteLoadBalancerWaitHandler(ctx context.Context, a loadbalancer.DefaultAPI, projectId, region, instanceId string) *wait.AsyncActionHandler[loadbalancer.LoadBalancer] { + waitConfig := wait.WaiterHelper[loadbalancer.LoadBalancer, string]{ + FetchInstance: a.GetLoadBalancer(ctx, projectId, region, instanceId).Execute, + GetState: func(l *loadbalancer.LoadBalancer) (string, error) { + if l == nil || l.Status == nil { + return "", errors.New("response or status is nil") + } + return *l.Status, nil }, + ErrorState: []string{LOADBALANCERSTATUS_ERROR}, } handler := wait.New(waitConfig.Wait()) handler.SetTimeout(15 * time.Minute)