332 lines
		
	
	
		
			8.1 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			332 lines
		
	
	
		
			8.1 KiB
		
	
	
	
		
			Go
		
	
	
	
| /*
 | |
| Copyright 2020 The Crossplane Authors.
 | |
| 
 | |
| Licensed under the Apache License, Version 2.0 (the "License");
 | |
| you may not use this file except in compliance with the License.
 | |
| You may obtain a copy of the License at
 | |
| 
 | |
|     http://www.apache.org/licenses/LICENSE-2.0
 | |
| 
 | |
| Unless required by applicable law or agreed to in writing, software
 | |
| distributed under the License is distributed on an "AS IS" BASIS,
 | |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
| See the License for the specific language governing permissions and
 | |
| limitations under the License.
 | |
| */
 | |
| 
 | |
| package resource
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"testing"
 | |
| 
 | |
| 	"github.com/google/go-cmp/cmp"
 | |
| 	"github.com/spf13/afero"
 | |
| 	corev1 "k8s.io/api/core/v1"
 | |
| 	"sigs.k8s.io/controller-runtime/pkg/client"
 | |
| 
 | |
| 	xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
 | |
| 	"github.com/crossplane/crossplane-runtime/pkg/errors"
 | |
| 	"github.com/crossplane/crossplane-runtime/pkg/resource/fake"
 | |
| 	"github.com/crossplane/crossplane-runtime/pkg/test"
 | |
| )
 | |
| 
 | |
| func TestExtractEnv(t *testing.T) {
 | |
| 	credentials := []byte("supersecretcreds")
 | |
| 
 | |
| 	type args struct {
 | |
| 		e     EnvLookupFn
 | |
| 		creds xpv1.CommonCredentialSelectors
 | |
| 	}
 | |
| 
 | |
| 	type want struct {
 | |
| 		b   []byte
 | |
| 		err error
 | |
| 	}
 | |
| 
 | |
| 	cases := map[string]struct {
 | |
| 		reason string
 | |
| 		args   args
 | |
| 		want   want
 | |
| 	}{
 | |
| 		"EnvVarSuccess": {
 | |
| 			reason: "Successful extraction of credentials from environment variable",
 | |
| 			args: args{
 | |
| 				e: func(string) string { return string(credentials) },
 | |
| 				creds: xpv1.CommonCredentialSelectors{
 | |
| 					Env: &xpv1.EnvSelector{
 | |
| 						Name: "SECRET_CREDS",
 | |
| 					},
 | |
| 				},
 | |
| 			},
 | |
| 			want: want{
 | |
| 				b: credentials,
 | |
| 			},
 | |
| 		},
 | |
| 		"EnvVarFail": {
 | |
| 			reason: "Failed extraction of credentials from environment variable",
 | |
| 			args: args{
 | |
| 				e: func(string) string { return string(credentials) },
 | |
| 			},
 | |
| 			want: want{
 | |
| 				err: errors.New(errExtractEnv),
 | |
| 			},
 | |
| 		},
 | |
| 	}
 | |
| 	for name, tc := range cases {
 | |
| 		t.Run(name, func(t *testing.T) {
 | |
| 			got, err := ExtractEnv(context.TODO(), tc.args.e, tc.args.creds)
 | |
| 			if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" {
 | |
| 				t.Errorf("\n%s\npc.ExtractEnv(...): -want error, +got error:\n%s\n", tc.reason, diff)
 | |
| 			}
 | |
| 
 | |
| 			if diff := cmp.Diff(tc.want.b, got); diff != "" {
 | |
| 				t.Errorf("\n%s\npc.ExtractEnv(...): -want, +got:\n%s\n", tc.reason, diff)
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestExtractFs(t *testing.T) {
 | |
| 	credentials := []byte("supersecretcreds")
 | |
| 	mockFs := afero.NewMemMapFs()
 | |
| 	f, _ := mockFs.Create("credentials.txt")
 | |
| 	f.Write(credentials)
 | |
| 	f.Close()
 | |
| 
 | |
| 	type args struct {
 | |
| 		fs    afero.Fs
 | |
| 		creds xpv1.CommonCredentialSelectors
 | |
| 	}
 | |
| 
 | |
| 	type want struct {
 | |
| 		b   []byte
 | |
| 		err error
 | |
| 	}
 | |
| 
 | |
| 	cases := map[string]struct {
 | |
| 		reason string
 | |
| 		args   args
 | |
| 		want   want
 | |
| 	}{
 | |
| 		"FsSuccess": {
 | |
| 			reason: "Successful extraction of credentials from filesystem",
 | |
| 			args: args{
 | |
| 				fs: mockFs,
 | |
| 				creds: xpv1.CommonCredentialSelectors{
 | |
| 					Fs: &xpv1.FsSelector{
 | |
| 						Path: "credentials.txt",
 | |
| 					},
 | |
| 				},
 | |
| 			},
 | |
| 			want: want{
 | |
| 				b: credentials,
 | |
| 			},
 | |
| 		},
 | |
| 		"FsFailure": {
 | |
| 			reason: "Failed extraction of credentials from filesystem",
 | |
| 			args: args{
 | |
| 				fs: mockFs,
 | |
| 			},
 | |
| 			want: want{
 | |
| 				err: errors.New(errExtractFs),
 | |
| 			},
 | |
| 		},
 | |
| 	}
 | |
| 	for name, tc := range cases {
 | |
| 		t.Run(name, func(t *testing.T) {
 | |
| 			got, err := ExtractFs(context.TODO(), tc.args.fs, tc.args.creds)
 | |
| 			if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" {
 | |
| 				t.Errorf("\n%s\npc.ExtractFs(...): -want error, +got error:\n%s\n", tc.reason, diff)
 | |
| 			}
 | |
| 
 | |
| 			if diff := cmp.Diff(tc.want.b, got); diff != "" {
 | |
| 				t.Errorf("\n%s\npc.ExtractFs(...): -want, +got:\n%s\n", tc.reason, diff)
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestExtractSecret(t *testing.T) {
 | |
| 	errBoom := errors.New("boom")
 | |
| 	credentials := []byte("supersecretcreds")
 | |
| 
 | |
| 	type args struct {
 | |
| 		client client.Client
 | |
| 		creds  xpv1.CommonCredentialSelectors
 | |
| 	}
 | |
| 
 | |
| 	type want struct {
 | |
| 		b   []byte
 | |
| 		err error
 | |
| 	}
 | |
| 
 | |
| 	cases := map[string]struct {
 | |
| 		reason string
 | |
| 		args   args
 | |
| 		want   want
 | |
| 	}{
 | |
| 		"SecretSuccess": {
 | |
| 			reason: "Successful extraction of credentials from Secret",
 | |
| 			args: args{
 | |
| 				client: &test.MockClient{
 | |
| 					MockGet: test.NewMockGetFn(nil, func(o client.Object) error {
 | |
| 						s, _ := o.(*corev1.Secret)
 | |
| 						s.Data = map[string][]byte{
 | |
| 							"creds": credentials,
 | |
| 						}
 | |
| 						return nil
 | |
| 					}),
 | |
| 				},
 | |
| 				creds: xpv1.CommonCredentialSelectors{
 | |
| 					SecretRef: &xpv1.SecretKeySelector{
 | |
| 						SecretReference: xpv1.SecretReference{
 | |
| 							Name:      "super",
 | |
| 							Namespace: "secret",
 | |
| 						},
 | |
| 						Key: "creds",
 | |
| 					},
 | |
| 				},
 | |
| 			},
 | |
| 			want: want{
 | |
| 				b: credentials,
 | |
| 			},
 | |
| 		},
 | |
| 		"SecretFailureNotDefined": {
 | |
| 			reason: "Failed extraction of credentials from Secret when key not defined",
 | |
| 			args:   args{},
 | |
| 			want: want{
 | |
| 				err: errors.New(errExtractSecretKey),
 | |
| 			},
 | |
| 		},
 | |
| 		"SecretFailureGet": {
 | |
| 			reason: "Failed extraction of credentials from Secret when client fails",
 | |
| 			args: args{
 | |
| 				client: &test.MockClient{
 | |
| 					MockGet: test.NewMockGetFn(nil, func(client.Object) error {
 | |
| 						return errBoom
 | |
| 					}),
 | |
| 				},
 | |
| 				creds: xpv1.CommonCredentialSelectors{
 | |
| 					SecretRef: &xpv1.SecretKeySelector{
 | |
| 						SecretReference: xpv1.SecretReference{
 | |
| 							Name:      "super",
 | |
| 							Namespace: "secret",
 | |
| 						},
 | |
| 						Key: "creds",
 | |
| 					},
 | |
| 				},
 | |
| 			},
 | |
| 			want: want{
 | |
| 				err: errors.Wrap(errBoom, errGetCredentialsSecret),
 | |
| 			},
 | |
| 		},
 | |
| 	}
 | |
| 	for name, tc := range cases {
 | |
| 		t.Run(name, func(t *testing.T) {
 | |
| 			got, err := ExtractSecret(context.TODO(), tc.args.client, tc.args.creds)
 | |
| 			if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" {
 | |
| 				t.Errorf("\n%s\npc.ExtractSecret(...): -want error, +got error:\n%s\n", tc.reason, diff)
 | |
| 			}
 | |
| 
 | |
| 			if diff := cmp.Diff(tc.want.b, got); diff != "" {
 | |
| 				t.Errorf("\n%s\npc.ExtractSecret(...): -want, +got:\n%s\n", tc.reason, diff)
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestTrack(t *testing.T) {
 | |
| 	errBoom := errors.New("boom")
 | |
| 	name := "provisional"
 | |
| 
 | |
| 	type fields struct {
 | |
| 		c  Applicator
 | |
| 		of ProviderConfigUsage
 | |
| 	}
 | |
| 
 | |
| 	type args struct {
 | |
| 		ctx context.Context
 | |
| 		mg  Managed
 | |
| 	}
 | |
| 
 | |
| 	cases := map[string]struct {
 | |
| 		reason string
 | |
| 		fields fields
 | |
| 		args   args
 | |
| 		want   error
 | |
| 	}{
 | |
| 		"MissingRef": {
 | |
| 			reason: "An error that satisfies IsMissingReference should be returned if the managed resource has no provider config reference",
 | |
| 			fields: fields{
 | |
| 				of: &fake.ProviderConfigUsage{},
 | |
| 			},
 | |
| 			args: args{
 | |
| 				mg: &fake.Managed{},
 | |
| 			},
 | |
| 			want: missingRefError{errors.New(errMissingPCRef)},
 | |
| 		},
 | |
| 		"NopUpdate": {
 | |
| 			reason: "No error should be returned if the apply fails because it would be a no-op",
 | |
| 			fields: fields{
 | |
| 				c: ApplyFn(func(ctx context.Context, _ client.Object, ao ...ApplyOption) error {
 | |
| 					for _, fn := range ao {
 | |
| 						// Exercise the MustBeControllableBy and AllowUpdateIf
 | |
| 						// ApplyOptions. The former should pass because the
 | |
| 						// current object has no controller ref. The latter
 | |
| 						// should return an error that satisfies IsNotAllowed
 | |
| 						// because the current object has the same PC ref as the
 | |
| 						// new one we would apply.
 | |
| 						current := &fake.ProviderConfigUsage{
 | |
| 							RequiredProviderConfigReferencer: fake.RequiredProviderConfigReferencer{
 | |
| 								Ref: xpv1.Reference{Name: name},
 | |
| 							},
 | |
| 						}
 | |
| 						if err := fn(ctx, current, nil); err != nil {
 | |
| 							return err
 | |
| 						}
 | |
| 					}
 | |
| 					return errBoom
 | |
| 				}),
 | |
| 				of: &fake.ProviderConfigUsage{},
 | |
| 			},
 | |
| 			args: args{
 | |
| 				mg: &fake.Managed{
 | |
| 					ProviderConfigReferencer: fake.ProviderConfigReferencer{
 | |
| 						Ref: &xpv1.Reference{Name: name},
 | |
| 					},
 | |
| 				},
 | |
| 			},
 | |
| 			want: nil,
 | |
| 		},
 | |
| 		"ApplyError": {
 | |
| 			reason: "Errors applying the ProviderConfigUsage should be returned",
 | |
| 			fields: fields{
 | |
| 				c: ApplyFn(func(_ context.Context, _ client.Object, _ ...ApplyOption) error {
 | |
| 					return errBoom
 | |
| 				}),
 | |
| 				of: &fake.ProviderConfigUsage{},
 | |
| 			},
 | |
| 			args: args{
 | |
| 				mg: &fake.Managed{
 | |
| 					ProviderConfigReferencer: fake.ProviderConfigReferencer{
 | |
| 						Ref: &xpv1.Reference{Name: name},
 | |
| 					},
 | |
| 				},
 | |
| 			},
 | |
| 			want: errors.Wrap(errBoom, errApplyPCU),
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	for name, tc := range cases {
 | |
| 		t.Run(name, func(t *testing.T) {
 | |
| 			ut := &ProviderConfigUsageTracker{c: tc.fields.c, of: tc.fields.of}
 | |
| 
 | |
| 			got := ut.Track(tc.args.ctx, tc.args.mg)
 | |
| 			if diff := cmp.Diff(tc.want, got, test.EquateErrors()); diff != "" {
 | |
| 				t.Errorf("\n%s\nut.Track(...): -want error, +got error:\n%s\n", tc.reason, diff)
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 |