Skip to content

Commit 60e6e40

Browse files
authored
feat: handle already deleted stack (#21)
* feat(stack): handle metadata on update,create * chore: remove metadata log * feat: handle already deleted stack
1 parent ac77162 commit 60e6e40

File tree

2 files changed

+114
-0
lines changed

2 files changed

+114
-0
lines changed

internal/resources/stack.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package resources
33
import (
44
"context"
55
"fmt"
6+
"net/http"
67

78
"github.com/formancehq/go-libs/v3/logging"
89
"github.com/formancehq/go-libs/v3/pointer"
@@ -229,6 +230,14 @@ func (s *Stack) Delete(ctx context.Context, req resource.DeleteRequest, resp *re
229230
}
230231
res, err := s.store.GetSDK().DeleteStack(ctx, organizationId, plan.GetID(), plan.ForceDestroy.ValueBool())
231232
if err != nil {
233+
if res.StatusCode == http.StatusNotFound {
234+
resp.Diagnostics.AddWarning(
235+
"Stack not found",
236+
"The stack was not found. It may have already been deleted outside of Terraform.",
237+
)
238+
return
239+
}
240+
232241
pkg.HandleSDKError(ctx, err, res, &resp.Diagnostics)
233242
return
234243
}

tests/integration/stack_test.go

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package integration_test
22

33
import (
4+
"errors"
45
"fmt"
56
"net/http"
67
"testing"
@@ -120,3 +121,107 @@ func TestStack(t *testing.T) {
120121
})
121122
}
122123
}
124+
125+
func TestStackAlreadyDeleted(t *testing.T) {
126+
t.Parallel()
127+
type testCase struct {
128+
step []resource.TestStep
129+
expectedCalls func(*pkg.MockCloudSDK, *pkg.MockTokenProviderImpl)
130+
}
131+
132+
for i, tc := range []testCase{
133+
{
134+
step: []resource.TestStep{
135+
{
136+
Config: `
137+
provider "cloud" {}
138+
resource "cloud_stack" "test" {
139+
name = "test"
140+
region_id = "staging"
141+
metadata = {
142+
"env" = "test"
143+
}
144+
force_destroy = true
145+
}
146+
`,
147+
},
148+
},
149+
expectedCalls: func(cloudSdk *pkg.MockCloudSDK, tokenProvider *pkg.MockTokenProviderImpl) {
150+
organizationID := uuid.NewString()
151+
tokenProvider.EXPECT().IntrospectToken(gomock.Any()).Return(oidc.IntrospectionResponse{
152+
Claims: map[string]interface{}{
153+
"organization_id": organizationID,
154+
},
155+
}, nil).AnyTimes()
156+
stackID := uuid.NewString()
157+
cloudSdk.EXPECT().CreateStack(gomock.Any(), organizationID, gomock.Any()).
158+
Return(&sdk.CreateStackResponse{
159+
Data: &sdk.Stack{
160+
Id: stackID,
161+
Name: "test",
162+
OrganizationId: organizationID,
163+
RegionID: "staging",
164+
Version: pointer.For("latest"),
165+
Uri: "https://example.com",
166+
Metadata: &map[string]string{
167+
"env": "test",
168+
"github.com/formancehq/terraform-provider-cloud/protected": "true",
169+
},
170+
},
171+
}, nil, nil)
172+
cloudSdk.EXPECT().ReadStack(gomock.Any(), organizationID, stackID).
173+
Return(&sdk.CreateStackResponse{
174+
Data: &sdk.Stack{
175+
Id: stackID,
176+
Name: "test",
177+
OrganizationId: organizationID,
178+
RegionID: "staging",
179+
Version: pointer.For("latest"),
180+
Uri: "https://example.com",
181+
Metadata: &map[string]string{
182+
"env": "test",
183+
"github.com/formancehq/terraform-provider-cloud/protected": "true",
184+
},
185+
},
186+
}, nil, nil)
187+
cloudSdk.EXPECT().DeleteStack(gomock.Any(), organizationID, stackID, true).Return(&http.Response{
188+
StatusCode: http.StatusNotFound,
189+
}, errors.New("stack not found"))
190+
},
191+
},
192+
} {
193+
194+
t.Run(fmt.Sprintf("test_%d", i), func(t *testing.T) {
195+
ctrl := gomock.NewController(t)
196+
cloudSdk := pkg.NewMockCloudSDK(ctrl)
197+
tokenProvider := pkg.NewMockTokenProviderImpl(ctrl)
198+
cloudProvider := server.NewProvider(
199+
logging.Testing().WithField("test", fmt.Sprintf("test_%d", i)),
200+
server.FormanceCloudEndpoint("dummy-endpoint"),
201+
server.FormanceCloudClientId("organization_client_id"),
202+
server.FormanceCloudClientSecret("dummy-client-secret"),
203+
transport,
204+
func(creds pkg.Creds, transport http.RoundTripper) pkg.CloudSDK {
205+
return cloudSdk
206+
},
207+
func(transport http.RoundTripper, creds pkg.Creds) pkg.TokenProviderImpl {
208+
return tokenProvider
209+
},
210+
)
211+
212+
if tc.expectedCalls != nil {
213+
tc.expectedCalls(cloudSdk, tokenProvider)
214+
}
215+
216+
resource.ParallelTest(t, resource.TestCase{
217+
ProtoV6ProviderFactories: map[string]func() (tfprotov6.ProviderServer, error){
218+
"cloud": providerserver.NewProtocol6WithError(cloudProvider()),
219+
},
220+
TerraformVersionChecks: []tfversion.TerraformVersionCheck{
221+
tfversion.SkipBelow(tfversion.Version0_15_0),
222+
},
223+
Steps: tc.step,
224+
})
225+
})
226+
}
227+
}

0 commit comments

Comments
 (0)