Skip to content

Commit 4b50163

Browse files
committed
add project permission's FSM logic to cover the resource deletion outside terraform
Signed-off-by: Dmitry Kisler <admin@dkisler.com> #209
1 parent 90590f1 commit 4b50163

2 files changed

Lines changed: 147 additions & 2 deletions

File tree

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
package provider
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"strconv"
7+
"testing"
8+
"time"
9+
10+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
11+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
12+
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
13+
neon "github.com/kislerdm/neon-sdk-go"
14+
"github.com/stretchr/testify/assert"
15+
)
16+
17+
func TestProjectPermissionFSMIfResourceDeletedOutsideTerraform(t *testing.T) {
18+
// see: https://github.com/kislerdm/terraform-provider-neon/issues/209
19+
20+
if os.Getenv("TF_ACC") != "1" {
21+
t.Skip("TF_ACC must be set to 1")
22+
}
23+
24+
client, err := neon.NewClient(neon.Config{Key: os.Getenv("NEON_API_KEY")})
25+
if err != nil {
26+
t.Fatal(err)
27+
}
28+
29+
projectNamePrefix += "projectPermissionRecreation-"
30+
31+
t.Cleanup(func() {
32+
resp, _ := client.ListProjects(nil, nil, &projectNamePrefix, nil, nil)
33+
for _, project := range resp.Projects {
34+
_, _ = client.DeleteProject(project.ID)
35+
}
36+
})
37+
38+
var newProjectName = func() string {
39+
return projectNamePrefix + strconv.FormatInt(time.Now().UnixMilli(), 10)
40+
}
41+
42+
var preConfig = func(projectName, email string) {
43+
ref, err := readProjectInfo(client, projectName)
44+
if err != nil {
45+
panic(err)
46+
}
47+
48+
resp, err := client.ListProjectPermissions(ref.ID)
49+
if err != nil {
50+
panic(err)
51+
}
52+
for _, permission := range resp.ProjectPermissions {
53+
if permission.GrantedToEmail == email {
54+
_, err = client.RevokePermissionFromProject(ref.ID, permission.ID)
55+
if err != nil {
56+
panic(err)
57+
}
58+
}
59+
}
60+
}
61+
62+
t.Run("shall indicate non empty plan if the project permission was deleted outside of terraform",
63+
func(t *testing.T) {
64+
email := "foo@bar.baz"
65+
projectName := newProjectName()
66+
resource.Test(
67+
t, resource.TestCase{
68+
ProviderFactories: map[string]func() (*schema.Provider, error){
69+
"neon": func() (*schema.Provider, error) {
70+
return newAccTest(), nil
71+
},
72+
},
73+
Steps: []resource.TestStep{
74+
{
75+
Config: fmt.Sprintf(`resource "neon_project" "this" {name = "%s"}
76+
resource "neon_project_permission" "this" {
77+
project_id = neon_project.this.id
78+
grantee = "%s"
79+
}`, projectName, email),
80+
Check: resource.ComposeTestCheckFunc(
81+
resource.TestCheckResourceAttr(
82+
"neon_project_permission.this",
83+
"grantee", email,
84+
),
85+
),
86+
},
87+
{
88+
PreConfig: func() {
89+
preConfig(projectName, email)
90+
},
91+
RefreshState: true,
92+
ExpectNonEmptyPlan: true,
93+
},
94+
},
95+
})
96+
})
97+
98+
t.Run("shall destroy even if the project permission was deleted outside of terraform,", func(t *testing.T) {
99+
email := "foo@bar.baz"
100+
projectName := newProjectName()
101+
config := fmt.Sprintf(`resource "neon_project" "this" {name = "%s"}
102+
resource "neon_project_permission" "this" {
103+
project_id = neon_project.this.id
104+
grantee = "%s"
105+
}`, projectName, email)
106+
resource.Test(
107+
t, resource.TestCase{
108+
ProviderFactories: map[string]func() (*schema.Provider, error){
109+
"neon": func() (*schema.Provider, error) {
110+
return newAccTest(), nil
111+
},
112+
},
113+
Steps: []resource.TestStep{
114+
{
115+
Config: config,
116+
Check: resource.ComposeTestCheckFunc(
117+
resource.TestCheckResourceAttr(
118+
"neon_project_permission.this",
119+
"grantee", email,
120+
),
121+
),
122+
},
123+
{
124+
PreConfig: func() {
125+
preConfig(projectName, email)
126+
},
127+
Config: config,
128+
Destroy: true,
129+
Check: func(s *terraform.State) error {
130+
_, ok := s.RootModule().Resources["neon_project_permission.this"]
131+
assert.False(t, ok, "resource neon_project_permission.this should be destroyed")
132+
return nil
133+
},
134+
},
135+
// to avoid dangling resources on post-test destroy
136+
{
137+
Config: fmt.Sprintf(`resource "neon_project" "this" { name = "%s" }`, projectName),
138+
},
139+
},
140+
})
141+
})
142+
}

provider/resource_project_permission.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,11 @@ func resourceProjectPermissionRead(ctx context.Context, d *schema.ResourceData,
133133
}
134134

135135
if !found {
136-
tflog.Trace(ctx, "no project permission found")
137-
return errors.New("no project permission found")
136+
tflog.Debug(ctx, "no project permission found, removing from state", map[string]interface{}{
137+
"project_id": d.Get("project_id"),
138+
"grantee": d.Get("grantee"),
139+
})
140+
d.SetId("")
138141
}
139142

140143
return nil

0 commit comments

Comments
 (0)