@@ -2,15 +2,16 @@ package wait
2
2
3
3
import (
4
4
"context"
5
+ "errors"
5
6
"fmt"
6
7
"net/http"
8
+ "strings"
7
9
"time"
8
10
9
- "errors"
10
-
11
11
"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
12
12
"github.com/stackitcloud/stackit-sdk-go/core/wait"
13
13
"github.com/stackitcloud/stackit-sdk-go/services/iaas"
14
+ "github.com/stackitcloud/stackit-sdk-go/services/resourcemanager"
14
15
)
15
16
16
17
const (
@@ -48,6 +49,7 @@ const (
48
49
// Interfaces needed for tests
49
50
type APIClientInterface interface {
50
51
GetNetworkAreaExecute (ctx context.Context , organizationId , areaId string ) (* iaas.NetworkArea , error )
52
+ ListNetworkAreaProjectsExecute (ctx context.Context , organizationId , areaId string ) (* iaas.ProjectListResponse , error )
51
53
GetProjectRequestExecute (ctx context.Context , projectId string , requestId string ) (* iaas.Request , error )
52
54
GetNetworkExecute (ctx context.Context , projectId , networkId string ) (* iaas.Network , error )
53
55
GetVolumeExecute (ctx context.Context , projectId string , volumeId string ) (* iaas.Volume , error )
@@ -58,6 +60,10 @@ type APIClientInterface interface {
58
60
GetSnapshotExecute (ctx context.Context , projectId string , snapshotId string ) (* iaas.Snapshot , error )
59
61
}
60
62
63
+ type ResourceManagerAPIClientInterface interface {
64
+ GetProjectExecute (ctx context.Context , id string ) (* resourcemanager.GetProjectResponse , error )
65
+ }
66
+
61
67
// CreateNetworkAreaWaitHandler will wait for network area creation
62
68
func CreateNetworkAreaWaitHandler (ctx context.Context , a APIClientInterface , organizationId , areaId string ) * wait.AsyncActionHandler [iaas.NetworkArea ] {
63
69
handler := wait .New (func () (waitFinished bool , response * iaas.NetworkArea , err error ) {
@@ -98,6 +104,52 @@ func UpdateNetworkAreaWaitHandler(ctx context.Context, a APIClientInterface, org
98
104
return handler
99
105
}
100
106
107
+ // ReadyForNetworkAreaDeletionWaitHandler will wait until a deletion of network area is possible
108
+ // Workaround for https://github.com/stackitcloud/terraform-provider-stackit/issues/907.
109
+ // When the deletion for a project is triggered, the backend starts a workflow in the background which cleans up all resources
110
+ // within a project and deletes the project in each service. When the project is attached to an SNA, the SNA can't be
111
+ // deleted until the workflow inform the IaaS-API that the project is deleted.
112
+ func ReadyForNetworkAreaDeletionWaitHandler (ctx context.Context , a APIClientInterface , r ResourceManagerAPIClientInterface , organizationId , areaId string ) * wait.AsyncActionHandler [iaas.ProjectListResponse ] {
113
+ handler := wait .New (func () (waitFinished bool , response * iaas.ProjectListResponse , err error ) {
114
+ projectList , err := a .ListNetworkAreaProjectsExecute (ctx , organizationId , areaId )
115
+ if err != nil {
116
+ return false , projectList , err
117
+ }
118
+ if projectList == nil || projectList .Items == nil {
119
+ return false , nil , fmt .Errorf ("read failed for projects in network area with id %s, the response is not valid: the items are missing" , areaId )
120
+ }
121
+ if len (* projectList .Items ) == 0 {
122
+ return true , projectList , nil
123
+ }
124
+ var activeProjects , forbiddenProjects []string
125
+ for _ , projectId := range * projectList .Items {
126
+ _ , err := r .GetProjectExecute (ctx , projectId )
127
+ if err == nil {
128
+ activeProjects = append (activeProjects , projectId )
129
+ continue
130
+ }
131
+ var oapiErr * oapierror.GenericOpenAPIError
132
+ ok := errors .As (err , & oapiErr )
133
+ if ! ok {
134
+ return false , nil , fmt .Errorf ("could not convert error to oapierror.GenericOpenAPIError" )
135
+ }
136
+ // The resource manager api responds with StatusForbidden(=403) when a project is deleted or if the project does not exist
137
+ if oapiErr .StatusCode == http .StatusNotFound || oapiErr .StatusCode == http .StatusForbidden {
138
+ forbiddenProjects = append (forbiddenProjects , projectId )
139
+ }
140
+ }
141
+ if len (activeProjects ) > 0 {
142
+ return false , nil , fmt .Errorf ("network area with id %s has still active projects: %s" , areaId , strings .Join (activeProjects , "," ))
143
+ }
144
+ if len (forbiddenProjects ) > 0 {
145
+ return false , nil , nil
146
+ }
147
+ return true , projectList , nil
148
+ })
149
+ handler .SetTimeout (1 * time .Minute )
150
+ return handler
151
+ }
152
+
101
153
// DeleteNetworkAreaWaitHandler will wait for network area deletion
102
154
func DeleteNetworkAreaWaitHandler (ctx context.Context , a APIClientInterface , organizationId , areaId string ) * wait.AsyncActionHandler [iaas.NetworkArea ] {
103
155
handler := wait .New (func () (waitFinished bool , response * iaas.NetworkArea , err error ) {
0 commit comments