Skip to content

Commit

Permalink
Merge pull request #870 from justinsb/export-tests
Browse files Browse the repository at this point in the history
Add tests for exporting resources
  • Loading branch information
google-oss-prow[bot] authored Oct 23, 2023
2 parents 22db576 + dfefd58 commit 370df59
Show file tree
Hide file tree
Showing 19 changed files with 195 additions and 59 deletions.
80 changes: 77 additions & 3 deletions config/tests/samples/create/harness.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@ import (
"net/http"
"os"
"path/filepath"
"strings"
"sync"
"testing"
"time"

exportparameters "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/cli/cmd/export/parameters"
"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/controller/dynamic"
"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/controller/kccmanager"
"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/controller/kccmanager/nocache"
Expand All @@ -36,6 +38,7 @@ import (
cnrmwebhook "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/webhook"
"golang.org/x/oauth2/google"

"github.com/google/go-cmp/cmp"
transport_tpg "github.com/hashicorp/terraform-provider-google-beta/google-beta/transport"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
Expand All @@ -58,6 +61,9 @@ type Harness struct {

client client.Client
restConfig *rest.Config

// gcpAccessToken is set to the oauth2 token to use for GCP, primarily when GCP is mocked.
gcpAccessToken string
}

// ForSubtest returns a harness scoped to a subtest (created by t.Run).
Expand Down Expand Up @@ -195,7 +201,8 @@ func NewHarness(t *testing.T, ctx context.Context) *Harness {

kccConfig.HTTPClient = &http.Client{Transport: roundTripper}

kccConfig.AccessToken = "dummytoken"
h.gcpAccessToken = "dummytoken"
kccConfig.GCPAccessToken = h.gcpAccessToken
} else if targetGCP := os.Getenv("E2E_GCP_TARGET"); targetGCP == "real" {
t.Logf("targeting real GCP")
} else {
Expand Down Expand Up @@ -283,6 +290,13 @@ func NewHarness(t *testing.T, ctx context.Context) *Harness {
return h
}

// ExportParams returns the default parameters.Parameters to use for an export
func (h *Harness) ExportParams() exportparameters.Parameters {
var exportParams exportparameters.Parameters
exportParams.GCPAccessToken = h.gcpAccessToken
return exportParams
}

func (h *Harness) GetClient() client.Client {
return h.client
}
Expand Down Expand Up @@ -344,15 +358,75 @@ func (t *Harness) waitForCRDReady(obj client.Object) {
// CRDs do not have observedGeneration
for _, condition := range objectStatus.Conditions {
if condition.Type == "Established" && condition.Status == "True" {
logger.Info("crd is ready", "kind", kind, "id", id)
logger.V(2).Info("crd is ready", "kind", kind, "id", id)
return true, nil
}
}
// This resource is not completely ready. Let's keep polling.
logger.Info("CRD is not ready", "kind", kind, "id", id, "conditions", objectStatus.Conditions)
logger.V(2).Info("CRD is not ready", "kind", kind, "id", id, "conditions", objectStatus.Conditions)
return false, nil
}); err != nil {
t.Errorf("error while polling for ready on %v %v: %v", kind, id, err)
return
}
}

func (h *Harness) CompareGoldenFile(p string, got string, normalizers ...func(s string) string) {
if os.Getenv("WRITE_GOLDEN_OUTPUT") != "" {
// Short-circuit when the output is correct
b, err := os.ReadFile(p)
if err == nil {
want := string(b)
for _, normalizer := range normalizers {
got = normalizer(got)
want = normalizer(want)
}
if want == got {
return
}
}

if err := os.WriteFile(p, []byte(got), 0644); err != nil {
h.Fatalf("failed to write golden output %s: %v", p, err)
}
h.Errorf("wrote output to %s", p)
} else {
want := string(h.MustReadFile(p))

for _, normalizer := range normalizers {
got = normalizer(got)
want = normalizer(want)
}

if diff := cmp.Diff(want, got); diff != "" {
h.Errorf("unexpected diff in %s: %s", p, diff)
}
}
}

func (h *Harness) MustReadFile(p string) []byte {
b, err := os.ReadFile(p)
if err != nil {
h.Fatalf("error from ReadFile(%q): %v", p, err)
}
return b
}

// IgnoreComments is a normalization function that strips comments.
func (h *Harness) IgnoreComments(s string) string {
lines := strings.Split(s, "\n")
for i, line := range lines {
if strings.HasPrefix(line, "#") {
lines[i] = ""
}
}
s = strings.Join(lines, "\n")
return strings.TrimSpace(s)
}

// ReplaceString is a normalization function that replaces a string, useful for e.g. project IDs.
func (h *Harness) ReplaceString(from, to string) func(string) string {
return func(s string) string {
return strings.ReplaceAll(s, from, to)
}
}
4 changes: 2 additions & 2 deletions config/tests/samples/create/samples.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ func RunCreateDeleteTest(t *Harness, unstructs []*unstructured.Unstructured, cle
waitForReady(t, unstructs)
// Clean up resources on success or if cleanupResources flag is true
if cleanupResources {
cleanup(t, unstructs)
DeleteResources(t, unstructs)
}
}

Expand Down Expand Up @@ -175,7 +175,7 @@ func waitForReadySingleResource(t *Harness, wg *sync.WaitGroup, u *unstructured.
t.Errorf("%v, final status: %+v", baseMsg, objectStatus)
}

func cleanup(t *Harness, unstructs []*unstructured.Unstructured) {
func DeleteResources(t *Harness, unstructs []*unstructured.Unstructured) {
logger := log.FromContext(t.Ctx)

for _, u := range unstructs {
Expand Down
3 changes: 2 additions & 1 deletion pkg/cli/cmd/apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,12 @@ var (
Short: "Apply a KRM resource configuration file to Google Cloud Platform backend",
Long: `Apply a KRM resource configuration file to Google Cloud Platform backend`,
RunE: func(cmd *cobra.Command, args []string) error {
ctx := cmd.Context()
if err := parameters.Validate(&applyParams, os.Stdin); err != nil {
return err
}
rootCmd.SilenceUsage = true
return apply.Execute(&applyParams, os.Stdout)
return apply.Execute(ctx, &applyParams, os.Stdout)
},
Args: cobra.NoArgs,
}
Expand Down
5 changes: 3 additions & 2 deletions pkg/cli/cmd/apply/execute.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package apply

import (
"context"
"fmt"
"io"

Expand All @@ -25,8 +26,8 @@ import (
"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/servicemapping/servicemappingloader"
)

func Execute(params *parameters.Parameters, output io.Writer) error {
tfProvider, err := tf.NewProvider(params.OAuth2Token)
func Execute(ctx context.Context, params *parameters.Parameters, output io.Writer) error {
tfProvider, err := tf.NewProvider(ctx, params.OAuth2Token)
if err != nil {
return err
}
Expand Down
4 changes: 2 additions & 2 deletions pkg/cli/cmd/bulkexport/execute.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ func Execute(ctx context.Context, params *parameters.Parameters) error {
if err != nil {
return err
}
tfProvider, err := tf.NewProvider(params.OAuth2Token)
tfProvider, err := tf.NewProvider(ctx, params.OAuth2Token)
if err != nil {
return err
}
Expand All @@ -46,7 +46,7 @@ func Execute(ctx context.Context, params *parameters.Parameters) error {
return err
}
defer assetStream.Close()
yamlStream, err := outputstream.NewResourceByteStream(params, assetStream)
yamlStream, err := outputstream.NewResourceByteStream(tfProvider, params, assetStream)
if err != nil {
return err
}
Expand Down
11 changes: 3 additions & 8 deletions pkg/cli/cmd/bulkexport/outputstream/outputstream.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,26 +25,21 @@ import (
"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/cli/outputsink"
"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/cli/serviceclient"
"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/cli/stream"
"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/cli/tf"
"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/servicemapping/servicemappingloader"

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

func NewResourceByteStream(params *parameters.Parameters, assetStream stream.AssetStream) (stream.ByteStream, error) {
provider, err := tf.NewProvider(params.OAuth2Token)
if err != nil {
return nil, err
}
func NewResourceByteStream(tfProvider *schema.Provider, params *parameters.Parameters, assetStream stream.AssetStream) (stream.ByteStream, error) {
smLoader, err := servicemappingloader.New()
if err != nil {
return nil, fmt.Errorf("error creating service mapping loader: %v", err)
}
unstructuredStream, err := NewUnstructuredStream(params, assetStream, provider, smLoader)
unstructuredStream, err := NewUnstructuredStream(params, assetStream, tfProvider, smLoader)
if err != nil {
return nil, err
}
return stream.NewByteStream(outputsink.ResourceFormat(params.ResourceFormat), unstructuredStream, smLoader, provider)
return stream.NewByteStream(outputsink.ResourceFormat(params.ResourceFormat), unstructuredStream, smLoader, tfProvider)
}

func NewUnstructuredStream(params *parameters.Parameters, assetStream stream.AssetStream, provider *schema.Provider, smLoader *servicemappingloader.ServiceMappingLoader) (stream.UnstructuredStream, error) {
Expand Down
2 changes: 1 addition & 1 deletion pkg/cli/cmd/export.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ var (
)

func init() {
commonparams.AddOAuth2TokenParam(exportCmd, &exportParams.OAuth2Token)
commonparams.AddOAuth2TokenParam(exportCmd, &exportParams.GCPAccessToken)
commonparams.AddIAMFormatParam(exportCmd, &exportParams.IAMFormat)
commonparams.AddFilterDeletedIAMMembersParam(exportCmd, &exportParams.FilterDeletedIAMMembers)
commonparams.AddOutputParam(exportCmd, &exportParams.Output)
Expand Down
8 changes: 5 additions & 3 deletions pkg/cli/cmd/export/execute.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,17 @@ import (
)

func Execute(ctx context.Context, params *parameters.Parameters) error {
byteStream, err := outputstream.NewResourceByteStream(params)
tfProvider, err := tf.NewProvider(ctx, params.GCPAccessToken)
if err != nil {
return err
}
recoverableStream := stream.NewRecoverableByteStream(byteStream)
tfProvider, err := tf.NewProvider(params.OAuth2Token)

byteStream, err := outputstream.NewResourceByteStream(tfProvider, params)
if err != nil {
return err
}
recoverableStream := stream.NewRecoverableByteStream(byteStream)

outputSink, err := outputsink.New(tfProvider, params.Output, outputsink.ResourceFormat(params.ResourceFormat))
if err != nil {
return err
Expand Down
11 changes: 3 additions & 8 deletions pkg/cli/cmd/export/outputstream/outputstream.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,26 +23,21 @@ import (
"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/cli/gcpclient"
"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/cli/outputsink"
"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/cli/stream"
"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/cli/tf"
"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/servicemapping/servicemappingloader"

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

func NewResourceByteStream(params *parameters.Parameters) (stream.ByteStream, error) {
provider, err := tf.NewProvider(params.OAuth2Token)
if err != nil {
return nil, err
}
func NewResourceByteStream(tfProvider *schema.Provider, params *parameters.Parameters) (stream.ByteStream, error) {
smLoader, err := servicemappingloader.New()
if err != nil {
return nil, fmt.Errorf("error creating service mapping loader: %v", err)
}
unstructuredStream, err := NewUnstructuredStream(params, provider, smLoader)
unstructuredStream, err := NewUnstructuredStream(params, tfProvider, smLoader)
if err != nil {
return nil, err
}
return stream.NewByteStream(outputsink.ResourceFormat(params.ResourceFormat), unstructuredStream, smLoader, provider)
return stream.NewByteStream(outputsink.ResourceFormat(params.ResourceFormat), unstructuredStream, smLoader, tfProvider)
}

func NewUnstructuredStream(params *parameters.Parameters, provider *schema.Provider, smLoader *servicemappingloader.ServiceMappingLoader) (stream.UnstructuredStream, error) {
Expand Down
4 changes: 2 additions & 2 deletions pkg/cli/cmd/export/outputstream/outputstream_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ func TestNewUnstructuredStreamWithPolicyMemberIAMOption(t *testing.T) {

func testNewUnstructuredStreamWithIAMOption(t *testing.T, iamFormatOption string, instanceOfExpectedType interface{}) {
params := parameters.Parameters{
IAMFormat: iamFormatOption,
OAuth2Token: "dummyToken",
IAMFormat: iamFormatOption,
GCPAccessToken: "dummyToken",
}
unstructuredStream, err := outputstream.NewUnstructuredStream(&params, tfprovider.NewOrLogFatal(tfprovider.UnitTestConfig()),
testservicemappingloader.New(t))
Expand Down
13 changes: 8 additions & 5 deletions pkg/cli/cmd/export/parameters/parameters.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,14 @@ import (
type Parameters struct {
IAMFormat string
FilterDeletedIAMMembers bool
OAuth2Token string
Output string
ResourceFormat string
URI string
Verbose bool

// GCPAccessToken is the (optional) static authentication token to use for GCP authentication.
GCPAccessToken string

Output string
ResourceFormat string
URI string
Verbose bool
}

func Validate(p *Parameters) error {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package resourcedescription

import (
"context"
"fmt"
"sort"

Expand All @@ -36,14 +37,16 @@ type ResourceDescription struct {
}

func GetAll() ([]ResourceDescription, error) {
ctx := context.TODO()

smLoader, err := servicemappingloader.New()
if err != nil {
return nil, fmt.Errorf("error creating service mapping loader: %w", err)
}
// tfprovider.New(...) configures the provider which requires valid access credentials. This is unnecessary
// for the purposes of getting the schema information. To get around this but not create a new codepath for creating
// a google provider, pass in an invalid, placeholder oauth2 token.
tfProvider, err := tf.NewProvider("invalid token")
tfProvider, err := tf.NewProvider(ctx, "invalid token")
if err != nil {
return nil, fmt.Errorf("error creating tf provider: %w", err)
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/cli/gcpclient/quick_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ func newDependencies(t *testing.T) (gcpclient.Client, *servicemappingloader.Serv
return nil, nil, nil, nil, fmt.Errorf("error loading service mappings: %v", err)
}
config := tfprovider.Config{
AccessToken: "", // <- insert a valid oauth2 token here
GCPAccessToken: "", // <- insert a valid oauth2 token here
}
tfProvider, err := tfprovider.New(ctx, config)
if err != nil {
Expand Down
6 changes: 2 additions & 4 deletions pkg/cli/tf/tf.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,9 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

func NewProvider(oauth2Token string) (*schema.Provider, error) {
ctx := context.TODO()

func NewProvider(ctx context.Context, gcpAccessToken string) (*schema.Provider, error) {
config := tfprovider.Config{
AccessToken: oauth2Token,
GCPAccessToken: gcpAccessToken,
}
p, err := tfprovider.New(ctx, config)
if err != nil {
Expand Down
6 changes: 3 additions & 3 deletions pkg/controller/kccmanager/kccmanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,9 @@ type Config struct {
// Currently only used in tests.
HTTPClient *http.Client

// AccessToken allows configuration of a static access token.
// GCPAccessToken allows configuration of a static access token for accessing GCP.
// Currently only used in tests.
AccessToken string
GCPAccessToken string
}

// Creates a new controller-runtime manager.Manager and starts all of the KCC controllers pointed at the
Expand Down Expand Up @@ -88,7 +88,7 @@ func New(ctx context.Context, restConfig *rest.Config, config Config) (manager.M
tfCfg := tfprovider.NewConfig()
tfCfg.UserProjectOverride = config.UserProjectOverride
tfCfg.BillingProject = config.BillingProject
tfCfg.AccessToken = config.AccessToken
tfCfg.GCPAccessToken = config.GCPAccessToken

provider, err := tfprovider.New(ctx, tfCfg)
if err != nil {
Expand Down
Loading

0 comments on commit 370df59

Please sign in to comment.