Skip to content

Commit

Permalink
fix: Correct version handling for Terraform Plugin Framework and fix …
Browse files Browse the repository at this point in the history
…acceptance tests (#366)

## Motivation

After merging #351
the acceptance tests [started
failing](https://github.com/nobl9/terraform-provider-nobl9/actions/runs/12350726517/job/34464184383).
This was due to the fact that Service resource was no longer defined in
the old provider, and since the tests were not utilizing the provider
multiplexer, the Service resource was effectively undefined.

## Summary

- Fixed version handling for the new provider, the `Version` variable
was moved to `main.go`.
- Added multiplexer setup to the tests.
- Due to an implicit `id` attribute creation in Terraform SDK v2 and and
[this
error](https://main.nobl9.dev/api/delete/project?dry_run=false&name=test-project)
being dropped in old tests, I had to switch old tests to the new
[terraform-plugin-testing](https://github.com/hashicorp/terraform-plugin-testing)
library. Both new and old libraries' APIs were almost identical, so the
conversion was seamless, just import changes.

## Testing

Run `make test/acc` with valid Nobl9 credentials.
  • Loading branch information
nieomylnieja authored Dec 17, 2024
1 parent 5c94612 commit 3c74d0c
Show file tree
Hide file tree
Showing 19 changed files with 145 additions and 106 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ NAME=nobl9
BIN_DIR=./bin
BINARY=$(BIN_DIR)/terraform-provider-$(NAME)
VERSION=0.35.0
BUILD_FLAGS="-X github.com/nobl9/terraform-provider-nobl9/nobl9.Version=$(VERSION)"
BUILD_FLAGS="-X github.com/nobl9/terraform-provider-nobl9/Version=$(VERSION)"
OS_ARCH?=darwin_arm64

# renovate datasource=github-releases depName=securego/gosec
Expand Down
6 changes: 6 additions & 0 deletions internal/frameworkprovider/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Package frameworkprovider provides a [provider.Provider] implementation for managing Nobl9 resources.
// It is built using the [terraform-plugin-framework].
//
// [terraform-plugin-framework]: https://developer.hashicorp.com/terraform/plugin/framework
// [provider.Provider]: https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/provider#Provider
package frameworkprovider
28 changes: 24 additions & 4 deletions internal/frameworkprovider/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ import (
"testing"

"github.com/hashicorp/terraform-plugin-framework/providerserver"
"github.com/hashicorp/terraform-plugin-go/tfprotov6"
"github.com/hashicorp/terraform-plugin-go/tfprotov5"
"github.com/hashicorp/terraform-plugin-mux/tf5muxserver"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/hashicorp/terraform-plugin-testing/terraform"
"github.com/nobl9/nobl9-go/manifest"
Expand All @@ -18,14 +20,32 @@ import (
"github.com/pkg/errors"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/nobl9/terraform-provider-nobl9/nobl9"
)

// testAccProtoV6ProviderFactories are used to instantiate a provider during
// testAccNewMux returns a new provider server which can multiplex
// between the SDK and framework provider implementations.
func testAccNewMux(ctx context.Context, version string) (tfprotov5.ProviderServer, error) {
mux, err := tf5muxserver.NewMuxServer(
ctx,
func() tfprotov5.ProviderServer { return schema.NewGRPCProviderServer(nobl9.Provider(version)) },
providerserver.NewProtocol5(New(version)),
)
if err != nil {
return nil, err
}
return mux.ProviderServer(), nil
}

// testAccProtoV5ProviderFactories are used to instantiate a provider during
// acceptance testing. The factory function will be invoked for every Terraform
// CLI command executed to create a provider server to which the CLI can
// reattach.
var testAccProtoV6ProviderFactories = map[string]func() (tfprotov6.ProviderServer, error){
"nobl9": providerserver.NewProtocol6WithError(New("test")),
var testAccProtoV5ProviderFactories = map[string]func() (tfprotov5.ProviderServer, error){
"nobl9": func() (tfprotov5.ProviderServer, error) {
return testAccNewMux(context.Background(), "test")
},
}

var testSDKClient = struct {
Expand Down
4 changes: 2 additions & 2 deletions internal/frameworkprovider/service_resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import (
"github.com/stretchr/testify/assert"
)

func TestAccExampleResource(t *testing.T) {
func TestAccService(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
t.Cleanup(cancel)

Expand Down Expand Up @@ -54,7 +54,7 @@ func TestAccExampleResource(t *testing.T) {

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
ProtoV5ProviderFactories: testAccProtoV5ProviderFactories,
Steps: []resource.TestStep{
// Create and Read.
{
Expand Down
16 changes: 10 additions & 6 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ import (
//
//go:generate go run github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs -provider-name=terraform-provider-nobl9

// Version is the version of the provider.
// It is set at compile-time.
var Version string

func main() {
ctx := context.Background()
var debugMode bool
Expand All @@ -29,8 +33,8 @@ func main() {

muxServer, err := tf5muxserver.NewMuxServer(
ctx,
newSDKProvider(),
newFrameworkProvider(),
newSDKProvider(Version),
newFrameworkProvider(Version),
)
if err != nil {
log.Fatal(err)
Expand All @@ -52,13 +56,13 @@ func main() {
}
}

func newSDKProvider() func() tfprotov5.ProviderServer {
func newSDKProvider(version string) func() tfprotov5.ProviderServer {
return func() tfprotov5.ProviderServer {
return schema.NewGRPCProviderServer(nobl9.Provider())
return schema.NewGRPCProviderServer(nobl9.Provider(version))
}
}

func newFrameworkProvider() func() tfprotov5.ProviderServer {
provider := frameworkprovider.New("TODO")
func newFrameworkProvider(version string) func() tfprotov5.ProviderServer {
provider := frameworkprovider.New(version)
return providerserver.NewProtocol5(provider)
}
4 changes: 2 additions & 2 deletions nobl9/data_source_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"fmt"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
)

func TestAcc_Nobl9DataSource(t *testing.T) {
Expand All @@ -18,7 +18,7 @@ func TestAcc_Nobl9DataSource(t *testing.T) {
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
resource.Test(t, resource.TestCase{
ProviderFactories: ProviderFactory(),
ProtoV5ProviderFactories: testAccProtoV5ProviderFactories,
Steps: []resource.TestStep{
{
Config: tc.configFunc(tc.name),
Expand Down
6 changes: 5 additions & 1 deletion nobl9/doc.go
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
// Package nobl9 Terraform provider package
// Package nobl9 provides a [schema.Provider] for managing Nobl9 resources.
// It is built using the [terraform-plugin-sdk] version 2.
//
// [schema.Provider]: https://pkg.go.dev/github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema#Provider
// [terraform-plugin-sdk]: https://pkg.go.dev/github.com/hashicorp/terraform-plugin-sdk/v2
package nobl9
20 changes: 9 additions & 11 deletions nobl9/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,7 @@ import (
"github.com/nobl9/nobl9-go/sdk"
)

//nolint:gochecknoglobals,revive
var Version string

func Provider() *schema.Provider {
func Provider(version string) *schema.Provider {
return &schema.Provider{
Schema: map[string]*schema.Schema{
"ingest_url": {
Expand Down Expand Up @@ -114,7 +111,9 @@ func Provider() *schema.Provider {
"nobl9_report_system_health_review": resourceReportFactory(reportSystemHealthReview{}),
},

ConfigureContextFunc: providerConfigure,
ConfigureContextFunc: func(_ context.Context, data *schema.ResourceData) (interface{}, diag.Diagnostics) {
return getProviderConfig(data, version), nil
},
}
}

Expand All @@ -126,23 +125,22 @@ type ProviderConfig struct {
ClientSecret string
OktaOrgURL string
OktaAuthServer string
Version string
}

func providerConfigure(_ context.Context, data *schema.ResourceData) (interface{}, diag.Diagnostics) {
config := ProviderConfig{
func getProviderConfig(data *schema.ResourceData, version string) ProviderConfig {
return ProviderConfig{
IngestURL: data.Get("ingest_url").(string),
Organization: data.Get("organization").(string),
Project: data.Get("project").(string),
ClientID: data.Get("client_id").(string),
ClientSecret: data.Get("client_secret").(string),
OktaOrgURL: data.Get("okta_org_url").(string),
OktaAuthServer: data.Get("okta_auth_server").(string),
Version: version,
}

return config, nil
}

//nolint:gochecknoglobals
var (
sharedClient *sdk.Client
once sync.Once
Expand Down Expand Up @@ -186,7 +184,7 @@ func getClient(providerConfig ProviderConfig) (*sdk.Client, diag.Diagnostics) {
if err != nil {
panic(err)
}
sharedClient.SetUserAgent(fmt.Sprintf("terraform-%s", Version))
sharedClient.SetUserAgent(fmt.Sprintf("terraform-%s", providerConfig.Version))
})
return sharedClient, diags
}
66 changes: 35 additions & 31 deletions nobl9/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,22 @@ import (
"os"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-framework/providerserver"
"github.com/hashicorp/terraform-plugin-go/tfprotov5"
"github.com/hashicorp/terraform-plugin-mux/tf5muxserver"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/hashicorp/terraform-plugin-testing/terraform"

"github.com/nobl9/nobl9-go/manifest"
"github.com/nobl9/nobl9-go/sdk"
v1Objects "github.com/nobl9/nobl9-go/sdk/endpoints/objects/v1"
)

//nolint:gochecknoglobals
var (
testProvider *schema.Provider
testProject string
"github.com/nobl9/terraform-provider-nobl9/internal/frameworkprovider"
)

var testProject string

//nolint:gochecknoinits
func init() {
testProject = os.Getenv("NOBL9_PROJECT")
Expand All @@ -31,17 +32,35 @@ func init() {
}
}

func ProviderFactory() map[string]func() (*schema.Provider, error) {
testProvider = Provider()
return map[string]func() (*schema.Provider, error){
"nobl9": func() (*schema.Provider, error) {
return testProvider, nil
// testAccNewMux returns a new provider server which can multiplex
// between the SDK and framework provider implementations.
func testAccNewMux(ctx context.Context, version string) (tfprotov5.ProviderServer, error) {
mux, err := tf5muxserver.NewMuxServer(
ctx,
func() tfprotov5.ProviderServer {
provider := Provider(version)
return schema.NewGRPCProviderServer(provider)
},
providerserver.NewProtocol5(frameworkprovider.New(version)),
)
if err != nil {
return nil, err
}
return mux.ProviderServer(), nil
}

// testAccProtoV5ProviderFactories are used to instantiate a provider during
// acceptance testing. The factory function will be invoked for every Terraform
// CLI command executed to create a provider server to which the CLI can
// reattach.
var testAccProtoV5ProviderFactories = map[string]func() (tfprotov5.ProviderServer, error){
"nobl9": func() (tfprotov5.ProviderServer, error) {
return testAccNewMux(context.Background(), "test")
},
}

func TestProvider(t *testing.T) {
if err := Provider().InternalValidate(); err != nil {
if err := Provider("test").InternalValidate(); err != nil {
t.Fatalf("err: %s", err)
}
}
Expand All @@ -59,26 +78,11 @@ func CheckObjectCreated(name string) resource.TestCheckFunc {
}
}

func CheckStateContainData(key string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[key]
if !ok {
return fmt.Errorf("not found: %s", key)
}
if len(rs.String()) == 0 {
return fmt.Errorf("data not set")
}
return nil
}
}

func CheckDestroy(rsType string, kind manifest.Kind) func(s *terraform.State) error {
return func(s *terraform.State) error {
config, ok := testProvider.Meta().(ProviderConfig)
if !ok {
return fmt.Errorf("could not cast data to ProviderConfig, actual type: %T", testProvider.Meta())
}
client, ds := getClient(config)
// When CheckDestroy is called, the client is already created.
// There's no need to pass any config to this function at that point.
client, ds := getClient(ProviderConfig{})
if ds.HasError() {
return fmt.Errorf("unable create client when deleting objects")
}
Expand Down
6 changes: 3 additions & 3 deletions nobl9/resource_agent_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"fmt"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"

"github.com/nobl9/nobl9-go/manifest"
)
Expand Down Expand Up @@ -46,8 +46,8 @@ func TestAcc_Nobl9Agent(t *testing.T) {
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
resource.Test(t, resource.TestCase{
ProviderFactories: ProviderFactory(),
CheckDestroy: CheckDestroy("nobl9_agent", manifest.KindAgent),
ProtoV5ProviderFactories: testAccProtoV5ProviderFactories,
CheckDestroy: CheckDestroy("nobl9_agent", manifest.KindAgent),
Steps: []resource.TestStep{
{
Config: tc.configFunc(tc.name),
Expand Down
6 changes: 3 additions & 3 deletions nobl9/resource_alert_policy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import (
"strings"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/hashicorp/terraform-plugin-testing/terraform"

"github.com/nobl9/nobl9-go/manifest"
)
Expand Down Expand Up @@ -336,7 +336,7 @@ func TestAcc_Nobl9AlertPolicy(t *testing.T) {
res := alertPolicyConfig.Build(resourceName, alertPolicyName, testProject)

resource.Test(t, resource.TestCase{
ProviderFactories: ProviderFactory(),
ProtoV5ProviderFactories: testAccProtoV5ProviderFactories,
CheckDestroy: destroyMultiple(
[]string{"nobl9_alert_policy", "nobl9_alert_method_slack"},
[]manifest.Kind{manifest.KindAlertPolicy, manifest.KindAlertMethod},
Expand Down
14 changes: 0 additions & 14 deletions nobl9/resource_alertmethod_mock.go

This file was deleted.

Loading

0 comments on commit 3c74d0c

Please sign in to comment.