Skip to content

Commit

Permalink
add branch protection configuration's attribute;
Browse files Browse the repository at this point in the history
enhance test suit by excluding unit tests when running acceptance tests;
enhance the skip message for allow IPs test.

Signed-off-by: Dmitry Kisler <admin@dkisler.com>
  • Loading branch information
kislerdm committed Oct 4, 2024
1 parent 1877823 commit cd749ec
Show file tree
Hide file tree
Showing 7 changed files with 180 additions and 16 deletions.
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,22 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [v0.6.2] - Unreleased

### Added

- Added the attribute `protected` to the resource `neon_branch` to provision protected branches.

### Fixed

- [[#108](https://github.com/kislerdm/terraform-provider-neon/issues/108)] Fixed import of the resource `neon_role`.
- Fixed mutability for the `brach` attribute of the `neon_project` resource.

### Changed

- Updated dependencies:
- Neon Go SDK: [v0.6.1](https://github.com/kislerdm/neon-sdk-go/compare/v0.5.0...v0.6.1)

## [v0.6.1] - 2024-09-28

### Added
Expand Down
2 changes: 1 addition & 1 deletion GNUmakefile
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ test: ## Runs unit tests.
@ echo $(TEST) | xargs -t -n4 go test $(TESTARGS) -timeout=30s -parallel=4

testacc: ## Runs acceptance tests.
@ TF_ACC=1 go test -v -timeout 120m ./...
@ TF_ACC=1 go test -tags=acceptance -v -timeout 120m ./...

docu: ## Generates docu.
@ go generate
2 changes: 1 addition & 1 deletion internal/provider/acc-import_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (
neon "github.com/kislerdm/neon-sdk-go"
)

func TestResourcesImport(t *testing.T) {
func TestAccResourcesImport(t *testing.T) {
if os.Getenv("TF_ACC") != "1" {
t.Skip("TF_ACC must be set to 1")
}
Expand Down
105 changes: 104 additions & 1 deletion internal/provider/acc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
neon "github.com/kislerdm/neon-sdk-go"
"github.com/kislerdm/terraform-provider-neon/internal/types"
"github.com/stretchr/testify/assert"
)

Expand Down Expand Up @@ -518,7 +519,7 @@ resource "neon_database" "this" {
}

func projectAllowedIPs(t *testing.T, client *neon.Client) {
t.Skip("skipped because of temp switch to the Free plan and back to Scale on 2024-09-23")
t.Skip("skipped because the Business plan is required for the feature")
wantAllowedIPs := []string{"192.168.1.0", "192.168.2.0/24"}
ips := `["` + strings.Join(wantAllowedIPs, `", "`) + `"]`

Expand Down Expand Up @@ -1068,3 +1069,105 @@ func testPlanAfterRoleImport(t *testing.T, client *neon.Client) {
},
})
}

func TestAccBranch(t *testing.T) {
if os.Getenv("TF_ACC") != "1" {
t.Skip("TF_ACC must be set to 1")
}

client, err := neon.NewClient(neon.Config{Key: os.Getenv("NEON_API_KEY")})
if err != nil {
t.Fatal(err)
}

prefix := "branch-"

t.Cleanup(func() {
resp, _ := client.ListProjects(nil, nil, &prefix, nil)
for _, project := range resp.Projects {
br, _ := client.ListProjectBranches(project.ID)
for _, b := range br.BranchesResponse.Branches {
_, _ = client.UpdateProjectBranch(project.ID, b.ID, neon.BranchUpdateRequest{
Branch: neon.BranchUpdateRequestBranch{
Protected: pointer(false),
},
})
}
_, _ = client.DeleteProject(project.ID)
}
})

t.Run(`shall create the project with the custom protected branch
and update its state afterwards to be unprotected`, func(t *testing.T) {
projectName := prefix + newProjectName()

const branchName = "foo"

newDefinition := func(protected *bool) string {
var cfg string
switch {
case protected == nil:
case *protected:
cfg = `protected = "yes"`
case !*protected:
cfg = `protected = "no"`
}
return fmt.Sprintf(`resource "neon_project" "this" {name = "%s"}
resource "neon_branch" "this" {
name = "%s"
project_id = neon_project.this.id
%s
}`, projectName, branchName, cfg)
}

resource.Test(t, resource.TestCase{
ProviderFactories: map[string]func() (*schema.Provider, error){
"neon": func() (*schema.Provider, error) {
return New("0.6.2"), nil
},
},
Steps: []resource.TestStep{
{
Config: newDefinition(pointer(true)),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("neon_branch.this", "protected", types.ValTrue),
func(state *terraform.State) error {
var e error
respProjects, e := client.ListProjects(nil, nil, &projectName, nil)
if e != nil {
return e
}
projectID := respProjects.Projects[0].ID
respBranches, e := client.ListProjectBranches(projectID)
if e != nil {
return e
}
var got bool
for _, branch := range respBranches.BranchesResponse.Branches {
if branch.Name == branchName {
got = branch.Protected
}
}
if !got {
e = fmt.Errorf("branch protect must be set to true")
}
return e
}),
},
{
Config: newDefinition(pointer(false)),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("neon_branch.this", "protected", types.ValFalse),
),
},
{
Config: newDefinition(nil),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("neon_branch.this", "protected", types.ValNull),
),
},
},
})
})
}
53 changes: 45 additions & 8 deletions internal/provider/resource_branch.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
neon "github.com/kislerdm/neon-sdk-go"
"github.com/kislerdm/terraform-provider-neon/internal/types"
)

func resourceBranch() *schema.Resource {
return &schema.Resource{
Description: "Project Branch. See details: https://neon.tech/docs/introduction/branching/",
SchemaVersion: 7,
SchemaVersion: 8,
Importer: &schema.ResourceImporter{
StateContext: resourceBranchImport,
},
Expand Down Expand Up @@ -70,6 +71,9 @@ See details: https://neon.tech/docs/reference/glossary/#lsn`,
Computed: true,
Description: "Branch logical size in MB.",
},
"protected": types.NewOptionalTristateBool(
`Set whether the branch is protected`, false,
),
},
}
}
Expand All @@ -94,6 +98,11 @@ func updateStateBranch(d *schema.ResourceData, v neon.Branch) error {
return err
}
}
if _, ok := d.GetOk("protected"); ok || v.Protected {
if err := types.SetTristateBool(d, "protected", &v.Protected); err != nil {
return err
}
}
return nil
}

Expand Down Expand Up @@ -123,6 +132,7 @@ func resourceBranchCreate(ctx context.Context, d *schema.ResourceData, meta inte
Name: pointer(d.Get("name").(string)),
ParentID: pointer(d.Get("parent_id").(string)),
ParentLsn: pointer(d.Get("parent_lsn").(string)),
Protected: types.GetTristateBool(d, "protected"),
},
},
}
Expand Down Expand Up @@ -156,15 +166,42 @@ func resourceBranchUpdate(ctx context.Context, d *schema.ResourceData, meta inte
return nil
}

cfg := neon.BranchUpdateRequest{
Branch: neon.BranchUpdateRequestBranch{
Name: pointer(v.(string)),
},
if !d.HasChange("name") && !d.HasChange("protected") {
return nil
}

resp, err := meta.(*neon.Client).UpdateProjectBranch(d.Get("project_id").(string), d.Id(), cfg)
if err != nil {
return err
var (
resp neon.BranchOperations
err error
)
if d.HasChange("name") {
resp, err = meta.(*neon.Client).UpdateProjectBranch(d.Get("project_id").(string), d.Id(),
neon.BranchUpdateRequest{
Branch: neon.BranchUpdateRequestBranch{
Name: pointer(d.Get("name").(string)),
},
},
)
if err != nil {
return err
}
}

if d.HasChange("protected") {
status := types.GetTristateBool(d, "protected")
if status == nil {
status = pointer(false)
}
resp, err = meta.(*neon.Client).UpdateProjectBranch(d.Get("project_id").(string), d.Id(),
neon.BranchUpdateRequest{
Branch: neon.BranchUpdateRequestBranch{
Protected: status,
},
},
)
if err != nil {
return err
}
}

return updateStateBranch(d, resp.Branch)
Expand Down
15 changes: 10 additions & 5 deletions internal/types/tristatebool.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,17 @@ import (
const (
ValTrue = "yes"
ValFalse = "no"
valNull = ""
ValNull = ""
)

func validateFuncNewOptionalTristateBool(v interface{}, s string) (warns []string, errs []error) {
const supportedVals = "Supported values: '" + ValTrue + "', '" +
ValFalse + "', '" + valNull + "'."
ValFalse + "', '" + ValNull + "'."

vv, ok := v.(string)
if ok {
switch vv {
case ValTrue, ValFalse, valNull:
case ValTrue, ValFalse, ValNull:
default:
ok = false
}
Expand Down Expand Up @@ -54,7 +54,7 @@ func SetTristateBool(d *schema.ResourceData, name string, v *bool) error {
var err error
switch {
case v == nil:
err = d.Set(name, valNull)
err = d.Set(name, ValNull)
case *v:
err = d.Set(name, ValTrue)
default:
Expand All @@ -68,7 +68,7 @@ func SetTristateBool(d *schema.ResourceData, name string, v *bool) error {
func GetTristateBool(d *schema.ResourceData, name string) *bool {
var o *bool = nil
switch d.Get(name) {
case valNull:
case ValNull:
case ValFalse:
var tmp bool
o = &tmp
Expand All @@ -78,3 +78,8 @@ func GetTristateBool(d *schema.ResourceData, name string) *bool {
}
return o
}

// IsNull checks if the attribute's value is null.
func IsNull(d *schema.ResourceData, name string) bool {
return d.Get(name) == ValNull
}
3 changes: 3 additions & 0 deletions internal/types/tristatebool_test.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
//go:build !acceptance
// +build !acceptance

package types

import (
Expand Down

0 comments on commit cd749ec

Please sign in to comment.