1
0
Fork 0
mirror of https://github.com/external-secrets/external-secrets.git synced 2024-12-14 11:57:59 +00:00
external-secrets/pkg/provider/chef/chef_test.go
Gergely Brautigam a5ddd97c21
chore: update go version of the project to 1.23 (#3829)
* chore: update go version of the project to 1.23

Signed-off-by: Gergely Brautigam <182850+Skarlso@users.noreply.github.com>

* fixed an absurd amount of linter issues

Signed-off-by: Gergely Brautigam <182850+Skarlso@users.noreply.github.com>

---------

Signed-off-by: Gergely Brautigam <182850+Skarlso@users.noreply.github.com>
2024-08-26 11:10:58 +02:00

428 lines
14 KiB
Go

/*
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: errors.New("received invalid Chef SecretStore resource: missing username"),
},
{
store: makeSecretStore(name, "", makeAuth(authName, authNamespace, authKey)),
err: errors.New("received invalid Chef SecretStore resource: missing serverurl"),
},
{
store: makeSecretStore(name, baseURL, nil),
err: errors.New("received invalid Chef SecretStore resource: cannot initialize Chef Client: no valid authType was specified"),
},
{
store: makeSecretStore(name, baseInvalidURL, makeAuth(authName, authNamespace, authKey)),
err: errors.New("received invalid Chef SecretStore resource: invalid serverurl: parse \"invalid base URL/\": invalid URI for request"),
},
{
store: makeSecretStore(name, noEndSlashInvalidBaseURL, makeAuth(authName, authNamespace, authKey)),
err: errors.New("received invalid Chef SecretStore resource: serverurl does not end with slash(/)"),
},
{
store: makeSecretStore(name, baseURL, makeAuth(authName, authNamespace, "")),
err: errors.New("received invalid Chef SecretStore resource: missing Secret Key"),
},
{
store: makeSecretStore(name, baseURL, makeAuth(authName, authNamespace, authKey)),
err: errors.New("received invalid Chef SecretStore resource: namespace should either be empty or match the namespace of the SecretStore for a namespaced SecretStore"),
},
{
store: &esv1beta1.SecretStore{
Spec: esv1beta1.SecretStoreSpec{
Provider: nil,
},
},
err: errors.New("received invalid Chef SecretStore resource: missing provider"),
},
{
store: &esv1beta1.SecretStore{
Spec: esv1beta1.SecretStoreSpec{
Provider: &esv1beta1.SecretStoreProvider{
Chef: nil,
},
},
},
err: errors.New("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)
}