Skip to content

Commit

Permalink
fix: handle resources deleted outside TF provider
Browse files Browse the repository at this point in the history
  • Loading branch information
delca85 committed Aug 1, 2024
1 parent aebaf50 commit 1726d08
Show file tree
Hide file tree
Showing 17 changed files with 469 additions and 13 deletions.
6 changes: 6 additions & 0 deletions internal/provider/resource_account_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,12 @@ func (r *ResourceAccountResource) Read(ctx context.Context, req resource.ReadReq
return
}

if httpResp.StatusCode() == 404 {
resp.Diagnostics.AddWarning("Resource account not found", fmt.Sprintf("The resource account (%s) was deleted outside Terraform", data.ID.ValueString()))
resp.State.RemoveResource(ctx)
return
}

if httpResp.StatusCode() != 200 {
resp.Diagnostics.AddError(HUM_API_ERR, fmt.Sprintf("Unable to read resource account, unexpected status code: %d, body: %s", httpResp.StatusCode(), httpResp.Body))
return
Expand Down
68 changes: 63 additions & 5 deletions internal/provider/resource_account_resource_test.go
Original file line number Diff line number Diff line change
@@ -1,23 +1,29 @@
package provider

import (
"context"
"fmt"
"os"
"testing"
"time"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
"github.com/humanitec/humanitec-go-autogen"
"github.com/stretchr/testify/assert"
)

func TestAccResourceAccountResource(t *testing.T) {
id := fmt.Sprintf("gcp-test-%d", time.Now().UnixNano())
email := fmt.Sprintf("gpc-myemail-%[email protected]", time.Now().UnixNano())

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
Steps: []resource.TestStep{
// Create and Read testing
{
Config: testAccResourceAccountResource(id, "gcp-test-1"),
Config: testAccResourceAccountResource(id, "gcp-test-1", email),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("humanitec_resource_account.gcp_test", "id", id),
resource.TestCheckResourceAttr("humanitec_resource_account.gcp_test", "name", "gcp-test-1"),
Expand All @@ -32,7 +38,7 @@ func TestAccResourceAccountResource(t *testing.T) {
},
// Update and Read testing
{
Config: testAccResourceAccountResource(id, "gcp-test-2"),
Config: testAccResourceAccountResource(id, "gcp-test-2", email),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("humanitec_resource_account.gcp_test", "name", "gcp-test-2"),
),
Expand All @@ -42,13 +48,65 @@ func TestAccResourceAccountResource(t *testing.T) {
})
}

func testAccResourceAccountResource(id, name string) string {
func TestAccResourceAccountResource_DeletedManually(t *testing.T) {
assert := assert.New(t)
ctx := context.Background()
id := fmt.Sprintf("gcp-test-%d", time.Now().UnixNano())
email := fmt.Sprintf("gpc-myemail-%[email protected]", time.Now().UnixNano())

orgID := os.Getenv("HUMANITEC_ORG")
token := os.Getenv("HUMANITEC_TOKEN")
apiHost := os.Getenv("HUMANITEC_HOST")
if apiHost == "" {
apiHost = humanitec.DefaultAPIHost
}

var client *humanitec.Client
var err error

resource.Test(t, resource.TestCase{
PreCheck: func() {
testAccPreCheck(t)

client, err = NewHumanitecClient(apiHost, token, "test", nil)
assert.NoError(err)
},
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
Steps: []resource.TestStep{
{
Config: testAccResourceAccountResource(id, "gcp-test-2", email),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("humanitec_resource_account.gcp_test", "id", id),
func(_ *terraform.State) error {
// Manually delete the resource account via the API
resp, err := client.DeleteResourceAccountWithResponse(ctx, orgID, id)
if err != nil {
return err
}

if resp.StatusCode() != 204 {
return fmt.Errorf("expected status code 204, got %d, body: %s", resp.StatusCode(), string(resp.Body))
}

return nil
},
),
ExpectNonEmptyPlan: true,
},
},
})
}

func testAccResourceAccountResource(id, name, email string) string {
return fmt.Sprintf(`
resource "humanitec_resource_account" "gcp_test" {
id = "%s"
name = "%s"
type = "gcp"
credentials = "{}"
credentials = jsonencode({
client_email = "%s"
private_key = "mykey"
})
}
`, id, name)
`, id, name, email)
}
3 changes: 2 additions & 1 deletion internal/provider/resource_agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,8 @@ func (a *Agent) Read(ctx context.Context, req resource.ReadRequest, resp *resour
}
}
if agent == nil {
resp.Diagnostics.AddError(HUM_API_ERR, fmt.Sprintf("Can't find agent %s in organization agent list", id))
resp.Diagnostics.AddWarning("Agent not found", fmt.Sprintf("The agent (%s) was deleted outside Terraform", data.ID.ValueString()))
resp.State.RemoveResource(ctx)
return
}
default:
Expand Down
52 changes: 52 additions & 0 deletions internal/provider/resource_agent_test.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
package provider

import (
"context"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"fmt"
"os"
"testing"
"time"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
"github.com/humanitec/humanitec-go-autogen"
"github.com/stretchr/testify/assert"
)

Expand Down Expand Up @@ -77,7 +80,56 @@ func TestAccAgent(t *testing.T) {
// Delete testing automatically occurs in TestCase
},
})
}

func TestAccResourceAgent_DeletedManually(t *testing.T) {
assert := assert.New(t)
ctx := context.Background()
id := fmt.Sprintf("agent-test-%d", time.Now().UnixNano())

orgID := os.Getenv("HUMANITEC_ORG")
token := os.Getenv("HUMANITEC_TOKEN")
apiHost := os.Getenv("HUMANITEC_HOST")
if apiHost == "" {
apiHost = humanitec.DefaultAPIHost
}

var client *humanitec.Client
var err error
publicKeyOne := getPublicKey(t)
publicKeyTwo := getPublicKey(t)

resource.Test(t, resource.TestCase{
PreCheck: func() {
testAccPreCheck(t)

client, err = NewHumanitecClient(apiHost, token, "test", nil)
assert.NoError(err)
},
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
Steps: []resource.TestStep{
{
Config: testAccCreateAgent(id, "my agent", publicKeyOne, publicKeyTwo),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("humanitec_agent.agent_test", "public_keys.#", "2"),
func(_ *terraform.State) error {
// Manually delete the agent via the API
resp, err := client.DeleteAgentWithResponse(ctx, orgID, id)
if err != nil {
return err
}

if resp.StatusCode() != 204 {
return fmt.Errorf("expected status code 204, got %d, body: %s", resp.StatusCode(), string(resp.Body))
}

return nil
},
),
ExpectNonEmptyPlan: true,
},
},
})
}

func testAccCreateAgent(id, description string, publicKey, otherPublicKey string) string {
Expand Down
6 changes: 5 additions & 1 deletion internal/provider/resource_application_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ func TestAccResourceApplicationDeletedOutManually(t *testing.T) {

orgID := os.Getenv("HUMANITEC_ORG")
token := os.Getenv("HUMANITEC_TOKEN")
apiHost := os.Getenv("HUMANITEC_HOST")
if apiHost == "" {
apiHost = humanitec.DefaultAPIHost
}

var client *humanitec.Client
var err error
Expand All @@ -54,7 +58,7 @@ func TestAccResourceApplicationDeletedOutManually(t *testing.T) {
PreCheck: func() {
testAccPreCheck(t)

client, err = NewHumanitecClient(humanitec.DefaultAPIHost, token, "test", nil)
client, err = NewHumanitecClient(apiHost, token, "test", nil)
assert.NoError(err)
},
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
Expand Down
10 changes: 7 additions & 3 deletions internal/provider/resource_application_user_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ func TestAccResourceApplicationUserDeletedManually(t *testing.T) {

orgID := os.Getenv("HUMANITEC_ORG")
token := os.Getenv("HUMANITEC_TOKEN")
apiHost := os.Getenv("HUMANITEC_HOST")
if apiHost == "" {
apiHost = humanitec.DefaultAPIHost
}

var client *humanitec.Client
var err error
Expand All @@ -66,17 +70,17 @@ func TestAccResourceApplicationUserDeletedManually(t *testing.T) {
PreCheck: func() {
testAccPreCheck(t)

client, err = NewHumanitecClient(humanitec.DefaultAPIHost, token, "test", nil)
client, err = NewHumanitecClient(apiHost, token, "test", nil)
assert.NoError(err)
},
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
Steps: []resource.TestStep{
// Create and Read testing
{
Config: testAccResourceApplicationUser(id, testUserID, "owner"),
Config: testAccResourceApplicationUser(id, testUserID, "developer"),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("humanitec_application_user.another_user", "id", fmt.Sprintf("%s/%s", id, testUserID)),
resource.TestCheckResourceAttr("humanitec_application_user.another_user", "role", "owner"),
resource.TestCheckResourceAttr("humanitec_application_user.another_user", "role", "developer"),
func(_ *terraform.State) error {
// Manually delete the application via the API
resp, err := client.DeleteApplicationWithResponse(ctx, orgID, id)
Expand Down
8 changes: 6 additions & 2 deletions internal/provider/resource_environment_type_user_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ func TestAccResourceEnvironmentTypeUserDeletedManually(t *testing.T) {

orgID := os.Getenv("HUMANITEC_ORG")
token := os.Getenv("HUMANITEC_TOKEN")
apiHost := os.Getenv("HUMANITEC_HOST")
if apiHost == "" {
apiHost = humanitec.DefaultAPIHost
}

var client *humanitec.Client
var err error
Expand All @@ -67,7 +71,7 @@ func TestAccResourceEnvironmentTypeUserDeletedManually(t *testing.T) {
PreCheck: func() {
testAccPreCheck(t)

client, err = NewHumanitecClient(humanitec.DefaultAPIHost, token, "test", nil)
client, err = NewHumanitecClient(apiHost, token, "test", nil)
assert.NoError(err)
},
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
Expand All @@ -79,7 +83,7 @@ func TestAccResourceEnvironmentTypeUserDeletedManually(t *testing.T) {
resource.TestCheckResourceAttr("humanitec_environment_type_user.another_user", "id", fmt.Sprintf("%s/%s", id, testUserID)),
resource.TestCheckResourceAttr("humanitec_environment_type_user.another_user", "role", "deployer"),
func(_ *terraform.State) error {
// Manually delete the application via the API
// Manually delete the environment type via the API
resp, err := client.DeleteEnvironmentTypeWithResponse(ctx, orgID, id)
if err != nil {
return err
Expand Down
71 changes: 71 additions & 0 deletions internal/provider/resource_key_test.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
package provider

import (
"context"
"fmt"
"os"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
"github.com/humanitec/humanitec-go-autogen"
"github.com/stretchr/testify/assert"
)

func TestAccResourceKeys(t *testing.T) {
Expand Down Expand Up @@ -43,6 +47,73 @@ func TestAccResourceKeys(t *testing.T) {
})
}

func TestAccResourceKey_DeletedManually(t *testing.T) {
assert := assert.New(t)
ctx := context.Background()

orgID := os.Getenv("HUMANITEC_ORG")
token := os.Getenv("HUMANITEC_TOKEN")
apiHost := os.Getenv("HUMANITEC_HOST")
if apiHost == "" {
apiHost = humanitec.DefaultAPIHost
}

var client *humanitec.Client
var err error

key := getPublicKey(t)
var id string

resource.Test(t, resource.TestCase{
PreCheck: func() {
testAccPreCheck(t)

client, err = NewHumanitecClient(apiHost, token, "test", nil)
assert.NoError(err)
},
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
Steps: []resource.TestStep{
// Create and Read testing
{
Config: testAccResourceKey(key),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("humanitec_key.key_test", "key", key),
resource.TestCheckResourceAttrSet("humanitec_key.key_test", "id"),
resource.TestCheckResourceAttrSet("humanitec_key.key_test", "fingerprint"),
),
},
// ImportState testing
{
ResourceName: "humanitec_key.key_test",
ImportState: true,
ImportStateVerify: true,
ImportStateIdFunc: func(s *terraform.State) (string, error) {
id = s.RootModule().Resources["humanitec_key.key_test"].Primary.Attributes["id"]
return id, nil
},
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("humanitec_key.key_test", "key", key),
resource.TestCheckResourceAttr("humanitec_key.key_test", "id", id),
func(_ *terraform.State) error {
// Manually delete the public key via the API
resp, err := client.DeletePublicKeyWithResponse(ctx, orgID, id)
if err != nil {
return err
}

if resp.StatusCode() != 204 {
return fmt.Errorf("expected status code 204, got %d, body: %s", resp.StatusCode(), string(resp.Body))
}

return nil
},
),
ExpectNonEmptyPlan: true,
},
},
})
}

func testAccResourceKey(key string) string {
return fmt.Sprintf(`
resource "humanitec_key" "key_test" {
Expand Down
6 changes: 6 additions & 0 deletions internal/provider/resource_resource_class.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,12 @@ func (r *ResourceResourceClass) Read(ctx context.Context, req resource.ReadReque
return
}

if httpResp.StatusCode() == 404 {
resp.Diagnostics.AddWarning("Resource class not found", fmt.Sprintf("The resource class (%s) was deleted outside Terraform", data.ID.ValueString()))
resp.State.RemoveResource(ctx)
return
}

if httpResp.StatusCode() != 200 {
resp.Diagnostics.AddError(HUM_API_ERR, fmt.Sprintf("Unable to read resource class, unexpected status code: %d, body: %s", httpResp.StatusCode(), httpResp.Body))
return
Expand Down
Loading

0 comments on commit 1726d08

Please sign in to comment.