| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428 |
- /*
- 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 chef
- import (
- "bytes"
- "context"
- "errors"
- "fmt"
- "strings"
- "testing"
- "time"
- "github.com/go-chef/chef"
- corev1 "k8s.io/api/core/v1"
- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- clientfake "sigs.k8s.io/controller-runtime/pkg/client/fake"
- esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
- v1 "github.com/external-secrets/external-secrets/apis/meta/v1"
- fake "github.com/external-secrets/external-secrets/pkg/provider/chef/fake"
- "github.com/external-secrets/external-secrets/pkg/utils"
- )
- const (
- name = "chef-demo-user"
- baseURL = "https://chef.cloudant.com/organizations/myorg/"
- noEndSlashInvalidBaseURL = "no end slash invalid base URL"
- baseInvalidURL = "invalid base URL/"
- authName = "chef-demo-auth-name"
- authKey = "chef-demo-auth-key"
- authNamespace = "chef-demo-auth-namespace"
- kind = "SecretStore"
- apiversion = "external-secrets.io/v1beta1"
- databagName = "databag01"
- )
- type chefTestCase struct {
- mockClient *fake.ChefMockClient
- databagName string
- databagItemName string
- property string
- ref *esv1beta1.ExternalSecretDataRemoteRef
- apiErr error
- expectError string
- expectedData map[string][]byte
- expectedByte []byte
- }
- type ValidateStoreTestCase struct {
- store *esv1beta1.SecretStore
- err error
- }
- // type storeModifier func(*esv1beta1.SecretStore) *esv1beta1.SecretStore
- func makeValidChefTestCase() *chefTestCase {
- smtc := chefTestCase{
- mockClient: &fake.ChefMockClient{},
- databagName: "databag01",
- databagItemName: "item01",
- property: "",
- apiErr: nil,
- expectError: "",
- expectedData: map[string][]byte{"item01": []byte(`"https://chef.com/organizations/dev/data/databag01/item01"`)},
- expectedByte: []byte(`{"item01":"{\"id\":\"databag01-item01\",\"some_key\":\"fe7f29ede349519a1\",\"some_password\":\"dolphin_123zc\",\"some_username\":\"testuser\"}"}`),
- }
- smtc.ref = makeValidRef(smtc.databagName, smtc.databagItemName, smtc.property)
- smtc.mockClient.WithListItems(smtc.databagName, smtc.apiErr)
- smtc.mockClient.WithItem(smtc.databagName, smtc.databagItemName, smtc.apiErr)
- return &smtc
- }
- func makeInValidChefTestCase() *chefTestCase {
- smtc := chefTestCase{
- mockClient: &fake.ChefMockClient{},
- databagName: "databag01",
- databagItemName: "item03",
- property: "",
- apiErr: errors.New("unable to convert databagItem into JSON"),
- expectError: "unable to convert databagItem into JSON",
- expectedData: nil,
- expectedByte: nil,
- }
- smtc.ref = makeValidRef(smtc.databagName, smtc.databagItemName, smtc.property)
- smtc.mockClient.WithListItems(smtc.databagName, smtc.apiErr)
- smtc.mockClient.WithItem(smtc.databagName, smtc.databagItemName, smtc.apiErr)
- return &smtc
- }
- func makeValidRef(databag, dataitem, property string) *esv1beta1.ExternalSecretDataRemoteRef {
- return &esv1beta1.ExternalSecretDataRemoteRef{
- Key: databag + "/" + dataitem,
- Property: property,
- }
- }
- func makeinValidRef() *esv1beta1.ExternalSecretDataRemoteRef {
- return &esv1beta1.ExternalSecretDataRemoteRef{
- Key: "",
- }
- }
- func makeValidRefForGetSecretMap(databag string) *esv1beta1.ExternalSecretDataRemoteRef {
- return &esv1beta1.ExternalSecretDataRemoteRef{
- Key: databag,
- }
- }
- func makeValidChefTestCaseCustom(tweaks ...func(smtc *chefTestCase)) *chefTestCase {
- smtc := makeValidChefTestCase()
- for _, fn := range tweaks {
- fn(smtc)
- }
- return smtc
- }
- func TestChefGetSecret(t *testing.T) {
- nilClient := func(smtc *chefTestCase) {
- smtc.mockClient = nil
- smtc.expectedByte = nil
- smtc.expectError = "chef provider is not initialized"
- }
- invalidDatabagName := func(smtc *chefTestCase) {
- smtc.databagName = "databag02"
- smtc.expectedByte = nil
- smtc.ref = makeinValidRef()
- smtc.expectError = "invalid key format in data section. Expected value 'databagName/databagItemName'"
- }
- invalidDatabagItemName := func(smtc *chefTestCase) {
- smtc.expectError = "data bag item item02 not found in data bag databag01"
- smtc.databagName = databagName
- smtc.databagItemName = "item02"
- smtc.expectedByte = nil
- smtc.ref = makeValidRef(smtc.databagName, smtc.databagItemName, "")
- }
- noProperty := func(smtc *chefTestCase) {
- smtc.expectError = "property findProperty not found in data bag item"
- smtc.databagName = databagName
- smtc.databagItemName = "item01"
- smtc.expectedByte = nil
- smtc.ref = makeValidRef(smtc.databagName, smtc.databagItemName, "findProperty")
- }
- withProperty := func(smtc *chefTestCase) {
- smtc.expectedByte = []byte("foundProperty")
- smtc.databagName = "databag03"
- smtc.databagItemName = "item03"
- smtc.ref = makeValidRef(smtc.databagName, smtc.databagItemName, "findProperty")
- }
- successCases := []*chefTestCase{
- makeValidChefTestCase(),
- makeValidChefTestCaseCustom(nilClient),
- makeValidChefTestCaseCustom(invalidDatabagName),
- makeValidChefTestCaseCustom(invalidDatabagItemName),
- makeValidChefTestCaseCustom(noProperty),
- makeValidChefTestCaseCustom(withProperty),
- makeInValidChefTestCase(),
- }
- sm := Providerchef{
- databagService: &chef.DataBagService{},
- }
- ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
- defer cancel()
- for k, v := range successCases {
- sm.databagService = v.mockClient
- out, err := sm.GetSecret(ctx, *v.ref)
- if err != nil && !utils.ErrorContains(err, v.expectError) {
- t.Errorf("[case %d] expected error: %v, got: %v", k, v.expectError, err)
- } else if v.expectError != "" && err == nil {
- t.Errorf("[case %d] expected error: %v, got: nil", k, v.expectError)
- }
- if !bytes.Equal(out, v.expectedByte) {
- t.Errorf("[case %d] expected secret: %s, got: %s", k, v.expectedByte, out)
- }
- }
- }
- func TestChefGetSecretMap(t *testing.T) {
- nilClient := func(smtc *chefTestCase) {
- smtc.mockClient = nil
- smtc.expectedByte = nil
- smtc.expectError = "chef provider is not initialized"
- }
- databagHasSlash := func(smtc *chefTestCase) {
- smtc.expectedByte = nil
- smtc.ref = makeinValidRef()
- smtc.ref.Key = "data/Bag02"
- smtc.expectError = "invalid key format in dataForm section. Expected only 'databagName'"
- }
- withProperty := func(smtc *chefTestCase) {
- smtc.expectedByte = []byte(`{"item01":"{\"id\":\"databag01-item01\",\"some_key\":\"fe7f29ede349519a1\",\"some_password\":\"dolphin_123zc\",\"some_username\":\"testuser\"}"}`)
- smtc.databagName = databagName
- smtc.ref = makeValidRefForGetSecretMap(smtc.databagName)
- }
- withProperty2 := func(smtc *chefTestCase) {
- smtc.expectError = "unable to list items in data bag 123, may be given data bag doesn't exists or it is empty"
- smtc.expectedByte = nil
- smtc.databagName = "123"
- smtc.ref = makeValidRefForGetSecretMap(smtc.databagName)
- }
- successCases := []*chefTestCase{
- makeValidChefTestCaseCustom(nilClient),
- makeValidChefTestCaseCustom(databagHasSlash),
- makeValidChefTestCaseCustom(withProperty),
- makeValidChefTestCaseCustom(withProperty2),
- }
- pc := Providerchef{
- databagService: &chef.DataBagService{},
- }
- ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
- defer cancel()
- for k, v := range successCases {
- pc.databagService = v.mockClient
- out, err := pc.GetSecretMap(ctx, *v.ref)
- if err != nil && !utils.ErrorContains(err, v.expectError) {
- t.Errorf("[case %d] expected error: %v, got: %v", k, v.expectError, err)
- } else if v.expectError != "" && err == nil {
- t.Errorf("[case %d] expected error: %v, got: nil", k, v.expectError)
- }
- if !bytes.Equal(out["item01"], v.expectedByte) {
- t.Errorf("[case %d] unexpected secret: expected %s, got %s", k, v.expectedByte, out)
- }
- }
- }
- func makeSecretStore(name, baseURL string, auth *esv1beta1.ChefAuth) *esv1beta1.SecretStore {
- store := &esv1beta1.SecretStore{
- Spec: esv1beta1.SecretStoreSpec{
- Provider: &esv1beta1.SecretStoreProvider{
- Chef: &esv1beta1.ChefProvider{
- UserName: name,
- ServerURL: baseURL,
- Auth: auth,
- },
- },
- },
- }
- return store
- }
- func makeAuth(name, namespace, key string) *esv1beta1.ChefAuth {
- return &esv1beta1.ChefAuth{
- SecretRef: esv1beta1.ChefAuthSecretRef{
- SecretKey: v1.SecretKeySelector{
- Name: name,
- Key: key,
- Namespace: &namespace,
- },
- },
- }
- }
- func TestValidateStore(t *testing.T) {
- testCases := []ValidateStoreTestCase{
- {
- store: makeSecretStore("", baseURL, makeAuth(authName, authNamespace, authKey)),
- err: fmt.Errorf("received invalid Chef SecretStore resource: missing username"),
- },
- {
- store: makeSecretStore(name, "", makeAuth(authName, authNamespace, authKey)),
- err: fmt.Errorf("received invalid Chef SecretStore resource: missing serverurl"),
- },
- {
- store: makeSecretStore(name, baseURL, nil),
- err: fmt.Errorf("received invalid Chef SecretStore resource: cannot initialize Chef Client: no valid authType was specified"),
- },
- {
- store: makeSecretStore(name, baseInvalidURL, makeAuth(authName, authNamespace, authKey)),
- err: fmt.Errorf("received invalid Chef SecretStore resource: invalid serverurl: parse \"invalid base URL/\": invalid URI for request"),
- },
- {
- store: makeSecretStore(name, noEndSlashInvalidBaseURL, makeAuth(authName, authNamespace, authKey)),
- err: fmt.Errorf("received invalid Chef SecretStore resource: serverurl does not end with slash(/)"),
- },
- {
- store: makeSecretStore(name, baseURL, makeAuth(authName, authNamespace, "")),
- err: fmt.Errorf("received invalid Chef SecretStore resource: missing Secret Key"),
- },
- {
- store: makeSecretStore(name, baseURL, makeAuth(authName, authNamespace, authKey)),
- err: fmt.Errorf("received invalid Chef SecretStore resource: namespace not allowed with namespaced SecretStore"),
- },
- {
- store: &esv1beta1.SecretStore{
- Spec: esv1beta1.SecretStoreSpec{
- Provider: nil,
- },
- },
- err: fmt.Errorf("received invalid Chef SecretStore resource: missing provider"),
- },
- {
- store: &esv1beta1.SecretStore{
- Spec: esv1beta1.SecretStoreSpec{
- Provider: &esv1beta1.SecretStoreProvider{
- Chef: nil,
- },
- },
- },
- err: fmt.Errorf("received invalid Chef SecretStore resource: missing chef provider"),
- },
- }
- pc := Providerchef{}
- for _, tc := range testCases {
- _, err := pc.ValidateStore(tc.store)
- if tc.err != nil && err != nil && err.Error() != tc.err.Error() {
- t.Errorf("test failed! want: %v, got: %v", tc.err, err)
- } else if tc.err == nil && err != nil {
- t.Errorf("want: nil got: err %v", err)
- } else if tc.err != nil && err == nil {
- t.Errorf("want: err %v got: nil", tc.err)
- }
- }
- }
- func TestNewClient(t *testing.T) {
- store := &esv1beta1.SecretStore{TypeMeta: metav1.TypeMeta{Kind: "ClusterSecretStore"},
- Spec: esv1beta1.SecretStoreSpec{
- Provider: &esv1beta1.SecretStoreProvider{
- Chef: &esv1beta1.ChefProvider{
- Auth: makeAuth(authName, authNamespace, authKey),
- UserName: name,
- ServerURL: baseURL,
- },
- },
- },
- }
- expected := fmt.Sprintf("could not fetch SecretKey Secret: secrets %q not found", authName)
- expectedMissingStore := "missing or invalid spec: missing store"
- ctx := context.TODO()
- kube := clientfake.NewClientBuilder().WithObjects(&corev1.Secret{
- ObjectMeta: metav1.ObjectMeta{
- Name: "creds",
- Namespace: "default",
- }, TypeMeta: metav1.TypeMeta{
- Kind: kind,
- APIVersion: apiversion,
- },
- }).Build()
- pc := Providerchef{databagService: &fake.ChefMockClient{}}
- _, errMissingStore := pc.NewClient(ctx, nil, kube, "default")
- if !ErrorContains(errMissingStore, expectedMissingStore) {
- t.Errorf("CheckNewClient unexpected error: %s, expected: '%s'", errMissingStore.Error(), expectedMissingStore)
- }
- _, err := pc.NewClient(ctx, store, kube, "default")
- if !ErrorContains(err, expected) {
- t.Errorf("CheckNewClient unexpected error: %s, expected: '%s'", err.Error(), expected)
- }
- }
- func ErrorContains(out error, want string) bool {
- if out == nil {
- return want == ""
- }
- if want == "" {
- return false
- }
- return strings.Contains(out.Error(), want)
- }
- func TestValidate(t *testing.T) {
- pc := Providerchef{}
- var mockClient *fake.ChefMockClient
- pc.userService = mockClient
- pc.clientName = "correctUser"
- _, err := pc.Validate()
- t.Log("Error: ", err)
- pc.clientName = "wrongUser"
- _, err = pc.Validate()
- t.Log("Error: ", err)
- }
- func TestCapabilities(t *testing.T) {
- pc := Providerchef{}
- capabilities := pc.Capabilities()
- t.Log(capabilities)
- }
- // Test Cases To be added when Close function is implemented.
- func TestClose(_ *testing.T) {
- pc := Providerchef{}
- pc.Close(context.Background())
- }
- // Test Cases To be added when GetAllSecrets function is implemented.
- func TestGetAllSecrets(_ *testing.T) {
- pc := Providerchef{}
- pc.GetAllSecrets(context.Background(), esv1beta1.ExternalSecretFind{})
- }
- // Test Cases To be implemented when DeleteSecret function is implemented.
- func TestDeleteSecret(_ *testing.T) {
- pc := Providerchef{}
- pc.DeleteSecret(context.Background(), nil)
- }
- // Test Cases To be implemented when PushSecret function is implemented.
- func TestPushSecret(_ *testing.T) {
- pc := Providerchef{}
- pc.PushSecret(context.Background(), &corev1.Secret{}, nil)
- }
|