Skip to content

test: add cassette system #617

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Oct 12, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ module github.com/terraform-providers/terraform-provider-scaleway

require (
github.com/aws/aws-sdk-go v1.34.32
github.com/dnaeon/go-vcr v1.0.1
github.com/google/go-cmp v0.5.2
github.com/hashicorp/go-retryablehttp v0.6.7
github.com/hashicorp/terraform-plugin-sdk/v2 v2.0.3
Expand Down
4 changes: 2 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ func main() {
if debugMode {
err := plugin.Debug(context.Background(), "registry.terraform.io/namespace/provider",
&plugin.ServeOpts{
ProviderFunc: scaleway.Provider(),
ProviderFunc: scaleway.Provider(scaleway.DefaultProviderConfig()),
})
if err != nil {
log.Println(err.Error())
}
} else {
plugin.Serve(&plugin.ServeOpts{
ProviderFunc: scaleway.Provider(),
ProviderFunc: scaleway.Provider(scaleway.DefaultProviderConfig()),
})
}
}
27 changes: 23 additions & 4 deletions scaleway/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package scaleway
import (
"context"
"fmt"
"net/http"

"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
Expand All @@ -11,8 +12,20 @@ import (
"github.com/scaleway/scaleway-sdk-go/validation"
)

// Provider config can be used to provide additional config when creating provider.
type ProviderConfig struct {
// Meta can be used to override Meta that will be used by the provider.
// This is useful for tests.
Meta *Meta
}

// DefaultProviderConfig return default ProviderConfig struct
func DefaultProviderConfig() *ProviderConfig {
return &ProviderConfig{}
}

// Provider returns a terraform.ResourceProvider.
func Provider() plugin.ProviderFunc {
func Provider(config *ProviderConfig) plugin.ProviderFunc {
return func() *schema.Provider {
p := &schema.Provider{
Schema: map[string]*schema.Schema{
Expand Down Expand Up @@ -102,9 +115,16 @@ func Provider() plugin.ProviderFunc {

p.ConfigureContextFunc = func(ctx context.Context, data *schema.ResourceData) (interface{}, diag.Diagnostics) {
terraformVersion := p.TerraformVersion

// If we provide meta in config use it. This is useful for tests
if config.Meta != nil {
return config.Meta, nil
}

meta, err := buildMeta(&MetaConfig{
providerSchema: data,
terraformVersion: terraformVersion,
httpClient: &http.Client{Transport: newRetryableTransport(http.DefaultTransport)},
})
if err != nil {
return nil, diag.FromErr(err)
Expand All @@ -128,12 +148,11 @@ type MetaConfig struct {
providerSchema *schema.ResourceData
terraformVersion string
forceZone scw.Zone
httpClient *http.Client
}

// providerConfigure creates the Meta object containing the SDK client.
func buildMeta(config *MetaConfig) (*Meta, error) {
httpClient := createRetryableHTTPClient(false)

////
// Load Profile
////
Expand All @@ -158,7 +177,7 @@ func buildMeta(config *MetaConfig) (*Meta, error) {
opts := []scw.ClientOption{
scw.WithUserAgent(fmt.Sprintf("terraform-provider/%s terraform/%s", version, config.terraformVersion)),
scw.WithProfile(profile),
scw.WithHTTPClient(httpClient),
scw.WithHTTPClient(config.httpClient),
}

scwClient, err := scw.NewClient(opts...)
Expand Down
104 changes: 101 additions & 3 deletions scaleway/provider_test.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,34 @@
package scaleway

import (
"flag"
"net/http"
"os"
"path/filepath"
"regexp"
"strings"
"testing"

"github.com/dnaeon/go-vcr/cassette"
"github.com/dnaeon/go-vcr/recorder"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/scaleway/scaleway-sdk-go/strcase"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

var testAccProviders map[string]*schema.Provider
var testAccProvider *schema.Provider
var (
// Deprecated
testAccProviders map[string]*schema.Provider
// Deprecated
testAccProvider *schema.Provider

// UpdateCassettes will update all cassettes of a given test
UpdateCassettes = flag.Bool("cassettes", os.Getenv("TF_UPDATE_CASSETTES") == "true", "Record Cassettes")
)

func init() {
p := Provider()()
p := Provider(DefaultProviderConfig())()
testAccProvider = p
version += "-tftest"
testAccProviders = map[string]*schema.Provider{
Expand All @@ -19,3 +37,83 @@ func init() {
}

func testAccPreCheck(_ *testing.T) {}

// getTestFilePath returns a valid filename path based on the go test name and suffix. (Take care of non fs friendly char)
func getTestFilePath(t *testing.T, suffix string) string {
specialChars := regexp.MustCompile(`[\\?%*:|"<>. ]`)

// Replace nested tests separators.
fileName := strings.Replace(t.Name(), "/", "-", -1)

fileName = strcase.ToBashArg(fileName)

// Replace special characters.
fileName = specialChars.ReplaceAllLiteralString(fileName, "") + suffix

// Remove prefix to simplify
fileName = strings.TrimPrefix(fileName, "test-acc-scaleway-")

return filepath.Join(".", "testdata", fileName)
}

// getHTTPRecoder creates a new httpClient that records all HTTP requests in a cassette.
// This cassette is then replayed whenever tests are executed again. This means that once the
// requests are recorded in the cassette, no more real HTTP requests must be made to run the tests.
//
// It is important to add a `defer cleanup()` so the given cassette files are correctly
// closed and saved after the requests.
func getHTTPRecoder(t *testing.T, update bool) (client *http.Client, cleanup func(), err error) {
recorderMode := recorder.ModeReplaying
if update {
recorderMode = recorder.ModeRecording
}

// Setup recorder and scw client
r, err := recorder.NewAsMode(getTestFilePath(t, ".cassette"), recorderMode, nil)
if err != nil {
return nil, nil, err
}

// Add a filter which removes Authorization headers from all requests:
r.AddFilter(func(i *cassette.Interaction) error {
delete(i.Request.Headers, "x-auth-token")
delete(i.Request.Headers, "X-Auth-Token")
return nil
})

return &http.Client{Transport: newRetryableTransport(r)}, func() {
assert.NoError(t, r.Stop()) // Make sure recorder is stopped once done with it
}, nil
}

type TestTools struct {
T *testing.T
Meta *Meta
ProviderFactories map[string]func() (*schema.Provider, error)
Cleanup func()
}

func NewTestTools(t *testing.T) *TestTools {
// Create an http client with recording capabilities
httpClient, cleanup, err := getHTTPRecoder(t, *UpdateCassettes)
require.NoError(t, err)

// Create meta that will be passed in the provider config
meta, err := buildMeta(&MetaConfig{
providerSchema: nil,
terraformVersion: "terraform-tests",
httpClient: httpClient,
})
require.NoError(t, err)

return &TestTools{
T: t,
Meta: meta,
ProviderFactories: map[string]func() (*schema.Provider, error){
"scaleway": func() (*schema.Provider, error) {
return Provider(&ProviderConfig{Meta: meta})(), nil
},
},
Cleanup: cleanup,
}
}
3 changes: 1 addition & 2 deletions scaleway/resource_baremetal_server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,8 @@ func init() {
}

func testSweepBaremetalServer(_ string) error {
return sweepZones([]scw.Zone{scw.ZoneFrPar2}, func(scwClient *scw.Client) error {
return sweepZones([]scw.Zone{scw.ZoneFrPar2}, func(scwClient *scw.Client, zone scw.Zone) error {
baremetalAPI := baremetal.NewAPI(scwClient)
zone, _ := scwClient.GetDefaultZone()
l.Debugf("sweeper: destroying the baremetal server in (%s)", zone)
listServers, err := baremetalAPI.ListServers(&baremetal.ListServersRequest{}, scw.WithAllPages())
if err != nil {
Expand Down
53 changes: 29 additions & 24 deletions scaleway/resource_instance_ip.go
Original file line number Diff line number Diff line change
@@ -1,22 +1,21 @@
package scaleway

import (
"context"

"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/scaleway/scaleway-sdk-go/api/instance/v1"
"github.com/scaleway/scaleway-sdk-go/scw"
)

func resourceScalewayInstanceIP() *schema.Resource {
return &schema.Resource{
Create: resourceScalewayInstanceIPCreate,
Read: resourceScalewayInstanceIPRead,
Delete: resourceScalewayInstanceIPDelete,

// Because of removed attribute server_id we must add an update func that does nothing. This could be removed on
// next major release.
Update: resourceScalewayInstanceIPRead,

CreateContext: resourceScalewayInstanceIPCreate,
ReadContext: resourceScalewayInstanceIPRead,
DeleteContext: resourceScalewayInstanceIPDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
StateContext: schema.ImportStatePassthroughContext,
},
SchemaVersion: 0,
Schema: map[string]*schema.Schema{
Expand All @@ -32,7 +31,7 @@ func resourceScalewayInstanceIP() *schema.Resource {
},
"server_id": {
Type: schema.TypeString,
Optional: true,
Computed: true,
Description: "The server associated with this IP",
},
"zone": zoneSchema(),
Expand All @@ -42,67 +41,73 @@ func resourceScalewayInstanceIP() *schema.Resource {
}
}

func resourceScalewayInstanceIPCreate(d *schema.ResourceData, m interface{}) error {
func resourceScalewayInstanceIPCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
instanceAPI, zone, err := instanceAPIWithZone(d, m)
if err != nil {
return err
return diag.FromErr(err)
}

res, err := instanceAPI.CreateIP(&instance.CreateIPRequest{
Zone: zone,
Organization: expandStringPtr(d.Get("organization_id")),
Project: expandStringPtr(d.Get("project_id")),
})
}, scw.WithContext(ctx))
if err != nil {
return err
return diag.FromErr(err)
}

d.SetId(newZonedIDString(zone, res.IP.ID))
return resourceScalewayInstanceIPRead(d, m)
return resourceScalewayInstanceIPRead(ctx, d, m)
}

func resourceScalewayInstanceIPRead(d *schema.ResourceData, m interface{}) error {
func resourceScalewayInstanceIPRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
instanceAPI, zone, ID, err := instanceAPIWithZoneAndID(m, d.Id())
if err != nil {
return err
return diag.FromErr(err)
}

res, err := instanceAPI.GetIP(&instance.GetIPRequest{
IP: ID,
Zone: zone,
})
}, scw.WithContext(ctx))

if err != nil {
// We check for 403 because instance API returns 403 for a deleted IP
if is404Error(err) || is403Error(err) {
d.SetId("")
return nil
}
return err
return diag.FromErr(err)
}

_ = d.Set("address", res.IP.Address.String())
_ = d.Set("zone", string(zone))
_ = d.Set("zone", zone)
_ = d.Set("organization_id", res.IP.Organization)
_ = d.Set("project_id", res.IP.Project)
_ = d.Set("reverse", res.IP.Reverse)

if res.IP.Server != nil {
_ = d.Set("server_id", newZonedIDString(res.IP.Zone, res.IP.Server.ID))
} else {
_ = d.Set("server_id", "")
}

return nil
}

func resourceScalewayInstanceIPDelete(d *schema.ResourceData, m interface{}) error {
func resourceScalewayInstanceIPDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
instanceAPI, zone, ID, err := instanceAPIWithZoneAndID(m, d.Id())
if err != nil {
return err
return diag.FromErr(err)
}

err = instanceAPI.DeleteIP(&instance.DeleteIPRequest{
IP: ID,
Zone: zone,
})
}, scw.WithContext(ctx))

if err != nil && !is404Error(err) {
return err
return diag.FromErr(err)
}

return nil
Expand Down
7 changes: 4 additions & 3 deletions scaleway/resource_instance_ip_reverse_dns_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ import (

func TestAccScalewayInstanceReverseDnsIP(t *testing.T) {
resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckScalewayInstanceIPDestroy,
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
// TODO enable back when using TestTools
//CheckDestroy: testAccCheckScalewayInstanceIPDestroy,
Steps: []resource.TestStep{
{
Config: `
Expand Down
Loading