| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960 |
- /*
- Copyright © The ESO 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
- https://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 mysterybox
- import (
- "context"
- b64 "encoding/base64"
- "encoding/json"
- "errors"
- "fmt"
- "sync"
- "sync/atomic"
- "testing"
- "github.com/google/uuid"
- lru "github.com/hashicorp/golang-lru"
- "github.com/nebius/gosdk/auth"
- tassert "github.com/stretchr/testify/assert"
- corev1 "k8s.io/api/core/v1"
- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- ctrl "sigs.k8s.io/controller-runtime"
- k8sclient "sigs.k8s.io/controller-runtime/pkg/client"
- clientfake "sigs.k8s.io/controller-runtime/pkg/client/fake"
- esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
- esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
- "github.com/external-secrets/external-secrets/providers/v1/nebius/common/sdk/mysterybox"
- "github.com/external-secrets/external-secrets/providers/v1/nebius/common/sdk/mysterybox/fake"
- )
- const (
- tokenSecretName = "tokenSecretName"
- tokenSecretKey = "tokenSecretKey"
- saCredsSecretName = "saCredsSecretName"
- saCredsSecretKey = "saCredsSecretKey"
- authRefName = "authRefSecretName"
- authRefKey = "authRefSecretKey"
- apiDomain = "api.public"
- tokenToBeIssued = "token-to-be-issued"
- )
- var (
- logger = ctrl.Log.WithName("provider").WithName("nebius").WithName("mysterybox")
- )
- func setupClientWithTokenAuth(t *testing.T, entries []mysterybox.Entry, tokenGetter TokenGetter) (context.Context, *SecretsClient, *fake.Secret, k8sclient.Client, *fake.MysteryboxService) {
- t.Helper()
- ctx := context.Background()
- namespace := uuid.NewString()
- mysteryboxService := fake.InitMysteryboxService()
- k8sClient := clientfake.NewClientBuilder().Build()
- secret := mysteryboxService.CreateSecret(entries)
- provider := newProvider(
- t,
- func(ctx context.Context, apiDomain string, caCertificate []byte) (mysterybox.Client, error) {
- return fake.NewFakeMysteryboxClient(mysteryboxService), nil
- },
- tokenGetter,
- )
- createK8sSecret(ctx, t, k8sClient, namespace, tokenSecretName, tokenSecretKey, []byte("token"))
- store := newNebiusMysteryboxSecretStoreWithAuthTokenKey(apiDomain, namespace, tokenSecretName, tokenSecretKey)
- client, err := provider.NewClient(ctx, store, k8sClient, namespace)
- tassert.NoError(t, err)
- mysteryboxSecretsClient, ok := client.(*SecretsClient)
- tassert.True(t, ok, "expected *SecretsClient, got %T", client)
- return ctx, mysteryboxSecretsClient, secret, k8sClient, mysteryboxService
- }
- func TestNewClient_GetTokenError(t *testing.T) {
- t.Parallel()
- tokenGetter := faketokenGetter{returnError: true}
- ctx := context.Background()
- namespace := uuid.NewString()
- mysteryboxService := fake.InitMysteryboxService()
- k8sClient := clientfake.NewClientBuilder().Build()
- createK8sSecret(ctx, t, k8sClient, namespace, saCredsSecretName, saCredsSecretKey, []byte("PRIVATE KEY"))
- provider := newProvider(
- t,
- func(ctx context.Context, apiDomain string, caCertificate []byte) (mysterybox.Client, error) {
- return fake.NewFakeMysteryboxClient(mysteryboxService), nil
- },
- &tokenGetter,
- )
- _, err := provider.NewClient(ctx, newNebiusMysteryboxSecretStoreWithServiceAccountCreds(apiDomain, namespace, saCredsSecretName, saCredsSecretKey), k8sClient, namespace)
- tassert.Error(t, err)
- tassert.ErrorContains(t, err, "failed to retrieve iam token by credentials")
- }
- func TestGetSecret(t *testing.T) {
- t.Parallel()
- entries := []mysterybox.Entry{
- {Key: "key1", StringValue: "string"},
- {Key: "key2", StringValue: "string2"},
- {Key: "key3", BinaryValue: []byte("binaryValue")},
- }
- tests := []struct {
- name string
- prepare func(ctx context.Context, client *SecretsClient, secret *fake.Secret, svc *fake.MysteryboxService) ([]byte, error)
- expectJSON map[string]string
- expectRaw []byte
- }{
- {
- name: "get all entries as JSON",
- prepare: func(ctx context.Context, client *SecretsClient, secret *fake.Secret, _ *fake.MysteryboxService) ([]byte, error) {
- return client.GetSecret(ctx, esv1.ExternalSecretDataRemoteRef{Key: secret.Id})
- },
- expectJSON: map[string]string{
- "key1": "string",
- "key2": "string2",
- "key3": b64.StdEncoding.EncodeToString([]byte("binaryValue")),
- },
- },
- {
- name: "string entry by key",
- prepare: func(ctx context.Context, client *SecretsClient, secret *fake.Secret, _ *fake.MysteryboxService) ([]byte, error) {
- return client.GetSecret(ctx, esv1.ExternalSecretDataRemoteRef{Key: secret.Id, Property: "key1"})
- },
- expectRaw: []byte("string"),
- },
- {
- name: "binary entry by key",
- prepare: func(ctx context.Context, client *SecretsClient, secret *fake.Secret, _ *fake.MysteryboxService) ([]byte, error) {
- return client.GetSecret(ctx, esv1.ExternalSecretDataRemoteRef{Key: secret.Id, Property: "key3"})
- },
- expectRaw: []byte("binaryValue"),
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- t.Parallel()
- ctx, client, secret, _, svc := setupClientWithTokenAuth(t, entries, nil)
- result, err := tt.prepare(ctx, client, secret, svc)
- tassert.NoError(t, err)
- if tt.expectJSON != nil {
- tassert.Equal(t, tt.expectJSON, unmarshalStringMap(t, result))
- } else {
- tassert.Equal(t, tt.expectRaw, result)
- }
- })
- }
- }
- func TestGetSecret_ByVersionId(t *testing.T) {
- t.Parallel()
- ctx, client, secret, _, mboxService := setupClientWithTokenAuth(t, []mysterybox.Entry{{Key: "key", StringValue: "string_value"}}, nil)
- _, err := mboxService.CreateNewSecretVersion(secret.Id, []mysterybox.Entry{
- {Key: "new_key", StringValue: "updated_string_value"},
- {Key: "new", StringValue: "new"},
- })
- tassert.NoError(t, err)
- result, err := client.GetSecret(ctx, esv1.ExternalSecretDataRemoteRef{Key: secret.Id, Property: "key", Version: secret.VersionId})
- tassert.NoError(t, err)
- tassert.Equal(t, []byte("string_value"), result)
- }
- func TestGetSecretMap(t *testing.T) {
- t.Parallel()
- allEntries := []mysterybox.Entry{
- {Key: "key1", StringValue: "string"},
- {Key: "key2", StringValue: "string2"},
- {Key: "key3", BinaryValue: []byte("binaryValue")},
- }
- tests := []struct {
- name string
- entries []mysterybox.Entry
- prepare func(ctx context.Context, client *SecretsClient, secret *fake.Secret, svc *fake.MysteryboxService) (map[string][]byte, error)
- expectMap map[string][]byte
- }{
- {
- name: "all entries",
- entries: allEntries,
- prepare: func(ctx context.Context, client *SecretsClient, secret *fake.Secret, _ *fake.MysteryboxService) (map[string][]byte, error) {
- return client.GetSecretMap(ctx, esv1.ExternalSecretDataRemoteRef{Key: secret.Id})
- },
- expectMap: map[string][]byte{
- "key1": []byte("string"),
- "key2": []byte("string2"),
- "key3": []byte("binaryValue"),
- },
- },
- {
- name: "by version id",
- entries: []mysterybox.Entry{{Key: "key", StringValue: "string_value"}},
- prepare: func(ctx context.Context, client *SecretsClient, secret *fake.Secret, svc *fake.MysteryboxService) (map[string][]byte, error) {
- _, err := svc.CreateNewSecretVersion(secret.Id, []mysterybox.Entry{{Key: "new_key", StringValue: "updated_string_value"}, {Key: "new", StringValue: "new"}})
- if err != nil {
- return nil, err
- }
- return client.GetSecretMap(ctx, esv1.ExternalSecretDataRemoteRef{Key: secret.Id, Version: secret.VersionId})
- },
- expectMap: map[string][]byte{
- "key": []byte("string_value"),
- },
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- t.Parallel()
- ctx, client, secret, _, svc := setupClientWithTokenAuth(t, tt.entries, nil)
- result, err := tt.prepare(ctx, client, secret, svc)
- tassert.NoError(t, err)
- tassert.Equal(t, tt.expectMap, result)
- })
- }
- }
- func TestNewClient_ValidationErrors(t *testing.T) {
- t.Parallel()
- ctx := context.Background()
- namespace := uuid.NewString()
- mysteryboxService := fake.InitMysteryboxService()
- k8sClient := clientfake.NewClientBuilder().Build()
- createK8sSecret(ctx, t, k8sClient, namespace, tokenSecretName, tokenSecretKey, []byte("token"))
- tokenToIssue := tokenToBeIssued
- notExistingSecretName := "not-existing-secret"
- notExistingSecretKey := "not-existing-secret-key"
- cache, err := lru.New(10)
- tassert.NoError(t, err)
- tokenGetter := &faketokenGetter{tokenToIssue: tokenToIssue}
- newProvider := func() *Provider {
- return &Provider{
- Logger: logger,
- NewMysteryboxClient: func(ctx context.Context, apiDomain string, caCertificate []byte) (mysterybox.Client, error) {
- return fake.NewFakeMysteryboxClient(mysteryboxService), nil
- },
- TokenGetter: tokenGetter,
- mysteryboxClientsCache: cache,
- }
- }
- tests := []struct {
- name string
- storeSpec func() *esv1.SecretStore
- expectErr string
- }{
- {
- name: "missing api domain",
- storeSpec: func() *esv1.SecretStore {
- return &esv1.SecretStore{
- ObjectMeta: metav1.ObjectMeta{Namespace: namespace},
- Spec: esv1.SecretStoreSpec{
- Provider: &esv1.SecretStoreProvider{
- NebiusMysterybox: &esv1.NebiusMysteryboxProvider{},
- },
- },
- }
- },
- expectErr: errMissingAPIDomain,
- },
- {
- name: "missing auth options",
- storeSpec: func() *esv1.SecretStore {
- return &esv1.SecretStore{
- ObjectMeta: metav1.ObjectMeta{Namespace: namespace},
- Spec: esv1.SecretStoreSpec{
- Provider: &esv1.SecretStoreProvider{
- NebiusMysterybox: &esv1.NebiusMysteryboxProvider{APIDomain: apiDomain},
- },
- },
- }
- },
- expectErr: errMissingAuthOptions,
- },
- {
- name: "specified token secret does not exist in kubernetes secrets",
- storeSpec: func() *esv1.SecretStore {
- return &esv1.SecretStore{
- ObjectMeta: metav1.ObjectMeta{Namespace: namespace},
- Spec: esv1.SecretStoreSpec{
- Provider: &esv1.SecretStoreProvider{
- NebiusMysterybox: &esv1.NebiusMysteryboxProvider{
- APIDomain: apiDomain,
- Auth: esv1.NebiusAuth{
- Token: esmeta.SecretKeySelector{
- Namespace: &namespace,
- Name: notExistingSecretName,
- Key: tokenSecretKey,
- },
- },
- },
- },
- },
- }
- },
- expectErr: fmt.Sprintf("read token secret %s/%s: cannot get Kubernetes secret", namespace, notExistingSecretName),
- },
- {
- name: "specified service account " +
- "creds secret does not exist in kubernetes secrets",
- storeSpec: func() *esv1.SecretStore {
- return &esv1.SecretStore{
- ObjectMeta: metav1.ObjectMeta{Namespace: namespace},
- Spec: esv1.SecretStoreSpec{
- Provider: &esv1.SecretStoreProvider{
- NebiusMysterybox: &esv1.NebiusMysteryboxProvider{
- APIDomain: apiDomain,
- Auth: esv1.NebiusAuth{
- ServiceAccountCreds: esmeta.SecretKeySelector{
- Namespace: &namespace,
- Name: notExistingSecretName,
- Key: "secretKey",
- },
- },
- },
- },
- },
- }
- },
- expectErr: fmt.Sprintf("read service account creds %s/%s: cannot get Kubernetes secret", namespace, notExistingSecretName),
- },
- {
- name: "specified token secret's key does not exist in the secret",
- storeSpec: func() *esv1.SecretStore {
- return &esv1.SecretStore{
- ObjectMeta: metav1.ObjectMeta{Namespace: namespace},
- Spec: esv1.SecretStoreSpec{
- Provider: &esv1.SecretStoreProvider{
- NebiusMysterybox: &esv1.NebiusMysteryboxProvider{
- APIDomain: apiDomain,
- Auth: esv1.NebiusAuth{
- Token: esmeta.SecretKeySelector{
- Namespace: &namespace,
- Name: tokenSecretName,
- Key: notExistingSecretKey,
- },
- },
- },
- },
- },
- }
- },
- expectErr: fmt.Sprintf("cannot find secret data for key: %q", notExistingSecretKey),
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- t.Parallel()
- p := newProvider()
- store := tt.storeSpec()
- _, err := p.NewClient(ctx, store, k8sClient, namespace)
- tassert.Error(t, err)
- tassert.ErrorContains(t, err, tt.expectErr)
- })
- }
- }
- func TestNewClient_AuthWithSecretAccountCreds(t *testing.T) {
- t.Parallel()
- ctx := context.Background()
- namespace := uuid.NewString()
- mysteryboxService := fake.InitMysteryboxService()
- k8sClient := clientfake.NewClientBuilder().Build()
- secret := mysteryboxService.CreateSecret([]mysterybox.Entry{{Key: "k", StringValue: "v"}})
- providedCreds, _ := json.Marshal(&auth.ServiceAccountCredentials{
- SubjectCredentials: auth.SubjectCredentials{
- PrivateKey: "-----BEGIN PRIVATE KEY-----\nTEST-KEY\n-----END PRIVATE KEY-----",
- KeyID: "keyId",
- Subject: "subjectId",
- Issuer: "subjectId",
- },
- })
- tokenToIssue := tokenToBeIssued
- tokenGetter := &faketokenGetter{
- tokenToIssue: tokenToIssue,
- }
- cache, err := lru.New(10)
- tassert.NoError(t, err)
- p := &Provider{
- Logger: logger,
- NewMysteryboxClient: func(ctx context.Context, apiDomain string, caCertificate []byte) (mysterybox.Client, error) {
- return fake.NewFakeMysteryboxClient(mysteryboxService), nil
- },
- mysteryboxClientsCache: cache,
- }
- settokenGetterWorkaround(tokenGetter, p)
- createK8sSecret(ctx, t, k8sClient, namespace, authRefName, authRefKey, providedCreds)
- store := newNebiusMysteryboxSecretStoreWithServiceAccountCreds(apiDomain, namespace, authRefName, authRefKey)
- client, err := p.NewClient(ctx, store, k8sClient, namespace)
- tassert.NoError(t, err)
- msc, ok := client.(*SecretsClient)
- tassert.True(t, ok, "expected *MysteryboxSecretsClient, got %T", client)
- tassert.Equal(t, tokenToIssue, msc.token, fmt.Sprintf("token mismatch: got %q want %q (issued by TokenGetter)", msc.token, tokenToIssue))
- // also ensure TokenGetter was exercised with the domain and creds we expect
- tassert.Equal(t, int32(1), tokenGetter.calls, "expected TokenGetter to be called once")
- tassert.Equal(t, apiDomain, tokenGetter.gotDomain, "expected TokenGetter to be called with the correct domain")
- tassert.Equal(t, string(providedCreds), tokenGetter.gotCreds, "expected TokenGetter to be called with the correct creds")
- tassert.Nil(t, tokenGetter.gotCACert, "expected TokenGetter to be called without CA cert")
- got, err := msc.GetSecret(ctx, esv1.ExternalSecretDataRemoteRef{Key: secret.Id, Property: "k"})
- tassert.NoError(t, err)
- tassert.Equal(t, []byte("v"), got)
- }
- func TestGetSecret_NotFound(t *testing.T) {
- t.Parallel()
- // Use table-driven tests to cover all NotFound scenarios in one place
- cases := []struct {
- name string
- entries []mysterybox.Entry
- prepare func(ctx context.Context, client *SecretsClient, secret *fake.Secret) ([]byte, error)
- expectErrEqual func(t *testing.T, err error, secret *fake.Secret)
- }{
- {
- name: "SecretID not found",
- entries: []mysterybox.Entry{{Key: "key1", StringValue: "string"}},
- prepare: func(ctx context.Context, client *SecretsClient, _ *fake.Secret) ([]byte, error) {
- desiredSecretId := "notexists"
- return client.GetSecret(ctx, esv1.ExternalSecretDataRemoteRef{Key: desiredSecretId})
- },
- expectErrEqual: func(t *testing.T, err error, _ *fake.Secret) {
- tassert.Error(t, err)
- tassert.ErrorIs(t, err, esv1.NoSecretErr)
- tassert.EqualError(t, err, fmt.Errorf(errSecretNotFound, "notexists", esv1.NoSecretErr).Error())
- },
- },
- {
- name: "Version not found",
- entries: []mysterybox.Entry{{Key: "key1", StringValue: "string"}},
- prepare: func(ctx context.Context, client *SecretsClient, secret *fake.Secret) ([]byte, error) {
- desiredVersion := "notexistversion"
- return client.GetSecret(ctx, esv1.ExternalSecretDataRemoteRef{Key: secret.Id, Version: desiredVersion})
- },
- expectErrEqual: func(t *testing.T, err error, secret *fake.Secret) {
- tassert.Error(t, err)
- tassert.ErrorIs(t, err, esv1.NoSecretErr)
- tassert.EqualError(t, err, fmt.Errorf(errSecretVersionNotFound, "notexistversion", secret.Id, esv1.NoSecretErr).Error())
- },
- },
- {
- name: "Property key not found in version",
- entries: []mysterybox.Entry{{Key: "key1", StringValue: "string"}},
- prepare: func(ctx context.Context, client *SecretsClient, secret *fake.Secret) ([]byte, error) {
- desiredKey := "key"
- return client.GetSecret(ctx, esv1.ExternalSecretDataRemoteRef{Key: secret.Id, Version: secret.VersionId, Property: desiredKey})
- },
- expectErrEqual: func(t *testing.T, err error, secret *fake.Secret) {
- tassert.Error(t, err)
- tassert.ErrorIs(t, err, esv1.NoSecretErr)
- tassert.EqualError(t, err, fmt.Errorf(errSecretVersionByKeyNotFound, secret.VersionId, secret.Id, "key", esv1.NoSecretErr).Error())
- },
- },
- {
- name: "Property key not found",
- entries: []mysterybox.Entry{{Key: "key1", StringValue: "string"}},
- prepare: func(ctx context.Context, client *SecretsClient, secret *fake.Secret) ([]byte, error) {
- desiredKey := "key"
- return client.GetSecret(ctx, esv1.ExternalSecretDataRemoteRef{Key: secret.Id, Property: desiredKey})
- },
- expectErrEqual: func(t *testing.T, err error, secret *fake.Secret) {
- tassert.Error(t, err)
- tassert.ErrorIs(t, err, esv1.NoSecretErr)
- tassert.EqualError(t, err, fmt.Errorf(errSecretByKeyNotFound, "key", secret.Id, esv1.NoSecretErr).Error())
- },
- },
- }
- for _, tc := range cases {
- t.Run(tc.name, func(t *testing.T) {
- t.Parallel()
- ctx, client, secret, _, _ := setupClientWithTokenAuth(t, tc.entries, nil)
- _, err := tc.prepare(ctx, client, secret)
- tc.expectErrEqual(t, err, secret)
- })
- }
- }
- func TestGetSecretMap_NotFound(t *testing.T) {
- t.Parallel()
- cases := []struct {
- name string
- entries []mysterybox.Entry
- prepare func(ctx context.Context, client *SecretsClient, secret *fake.Secret) (map[string][]byte, error)
- expectErrEqual func(t *testing.T, err error, secret *fake.Secret)
- }{
- {
- name: "SecretID not found",
- entries: []mysterybox.Entry{{Key: "key1", StringValue: "string"}},
- prepare: func(ctx context.Context, client *SecretsClient, _ *fake.Secret) (map[string][]byte, error) {
- desiredSecretId := "notexists"
- return client.GetSecretMap(ctx, esv1.ExternalSecretDataRemoteRef{Key: desiredSecretId})
- },
- expectErrEqual: func(t *testing.T, err error, _ *fake.Secret) {
- tassert.Error(t, err)
- tassert.ErrorIs(t, err, esv1.NoSecretErr)
- tassert.EqualError(t, err, fmt.Errorf(errSecretNotFound, "notexists", esv1.NoSecretErr).Error())
- },
- },
- {
- name: "Version not found",
- entries: []mysterybox.Entry{{Key: "key1", StringValue: "string"}},
- prepare: func(ctx context.Context, client *SecretsClient, secret *fake.Secret) (map[string][]byte, error) {
- desiredVersion := "notexistversion"
- return client.GetSecretMap(ctx, esv1.ExternalSecretDataRemoteRef{Key: secret.Id, Version: desiredVersion})
- },
- expectErrEqual: func(t *testing.T, err error, secret *fake.Secret) {
- tassert.Error(t, err)
- tassert.ErrorIs(t, err, esv1.NoSecretErr)
- tassert.EqualError(t, err, fmt.Errorf(errSecretVersionNotFound, "notexistversion", secret.Id, esv1.NoSecretErr).Error())
- },
- },
- }
- for _, tc := range cases {
- t.Run(tc.name, func(t *testing.T) {
- t.Parallel()
- ctx, client, secret, _, _ := setupClientWithTokenAuth(t, tc.entries, nil)
- _, err := tc.prepare(ctx, client, secret)
- tc.expectErrEqual(t, err, secret)
- })
- }
- }
- func TestCreateOrGetMysteryboxClient_CachesByKey(t *testing.T) {
- t.Parallel()
- ctx := context.Background()
- cache, err := lru.New(10)
- tassert.NoError(t, err)
- var factoryCalls int32
- p := &Provider{
- Logger: logger,
- NewMysteryboxClient: func(ctx context.Context, apiDomain string, caCertificate []byte) (mysterybox.Client, error) {
- atomic.AddInt32(&factoryCalls, 1)
- return fake.NewFakeMysteryboxClient(nil), nil
- },
- mysteryboxClientsCache: cache,
- }
- // same domain + same CA
- _, err = p.createOrGetMysteryboxClient(ctx, "api.nebius.example", []byte("CA1"))
- tassert.NoError(t, err)
- _, err = p.createOrGetMysteryboxClient(ctx, "api.nebius.example", []byte("CA1"))
- tassert.NoError(t, err)
- // different CA
- _, err = p.createOrGetMysteryboxClient(ctx, "api.nebius.example", []byte("CA2"))
- tassert.NoError(t, err)
- _, err = p.createOrGetMysteryboxClient(ctx, "other.nebius.example", []byte("CA1"))
- tassert.NoError(t, err)
- tassert.Equal(t, int32(3), atomic.LoadInt32(&factoryCalls), fmt.Sprintf("factory called %d times, want %d", atomic.LoadInt32(&factoryCalls), 3))
- }
- func TestCreateOrGetMysteryboxClient_EmptyCA_EqualsNil(t *testing.T) {
- t.Parallel()
- ctx := context.Background()
- cache, err := lru.New(10)
- tassert.NoError(t, err)
- var factoryCalls int32
- p := &Provider{
- Logger: logger,
- NewMysteryboxClient: func(ctx context.Context, apiDomain string, caCertificate []byte) (mysterybox.Client, error) {
- atomic.AddInt32(&factoryCalls, 1)
- return fake.NewFakeMysteryboxClient(nil), nil
- },
- mysteryboxClientsCache: cache,
- }
- _, err = p.createOrGetMysteryboxClient(ctx, "api.nebius.example", nil)
- tassert.NoError(t, err)
- _, err = p.createOrGetMysteryboxClient(ctx, "api.nebius.example", []byte{})
- tassert.NoError(t, err)
- tassert.Equal(t, int32(1), atomic.LoadInt32(&factoryCalls), fmt.Sprintf("factory called %d times, want %d when CA=nil and CA=empty should map to same key", factoryCalls, 1))
- }
- func TestMysteryboxClientsCache_EvictionClosesClient(t *testing.T) {
- t.Parallel()
- ctx := context.Background()
- var created []*fake.FakeMysteryboxClient
- p := &Provider{
- Logger: logger,
- NewMysteryboxClient: func(ctx context.Context, apiDomain string, caCertificate []byte) (mysterybox.Client, error) {
- c := &fake.FakeMysteryboxClient{}
- created = append(created, c)
- return c, nil
- },
- }
- setCacheSizeWorkaround(t, 1, p)
- _, err := p.createOrGetMysteryboxClient(ctx, "domain-a", nil)
- tassert.NoError(t, err)
- tassert.Len(t, created, 1, "expected 1 client created, got %d", len(created))
- _, err = p.createOrGetMysteryboxClient(ctx, "domain-b", nil)
- tassert.NoError(t, err)
- tassert.Len(t, created, 2, "expected 2 clients created, got %d", len(created))
- // clients are not closed because of the eviction policy for provider's mysterybox clients connections cache
- tassert.Equal(t, int32(0), atomic.LoadInt32(&created[0].Closed), "expected second client not to be closed")
- tassert.Equal(t, int32(0), atomic.LoadInt32(&created[1].Closed), "expected second client not to be closed")
- }
- // concurrent tests
- func TestCreateOrGetMysteryboxClient_Concurrent_SingleClient(t *testing.T) {
- t.Parallel()
- clientData := ClientData{domain: "api.nebius.example", ca: []byte("CA1")}
- ctx := context.Background()
- cache, err := lru.New(10)
- tassert.NoError(t, err)
- var factoryCalls int32
- p := &Provider{
- Logger: logger,
- NewMysteryboxClient: func(ctx context.Context, apiDomain string, caCertificate []byte) (mysterybox.Client, error) {
- atomic.AddInt32(&factoryCalls, 1)
- return fake.NewFakeMysteryboxClient(nil), nil
- },
- mysteryboxClientsCache: cache,
- }
- const goroutines = 8
- var wg sync.WaitGroup
- wg.Add(goroutines)
- start := make(chan struct{})
- errs := make([]error, goroutines)
- for i := range goroutines {
- go func(ix int) {
- defer wg.Done()
- <-start
- _, err := p.createOrGetMysteryboxClient(ctx, clientData.domain, clientData.ca)
- errs[ix] = err
- }(i)
- }
- close(start)
- wg.Wait()
- for i, err := range errs {
- if err != nil {
- tassert.NoError(t, err, "goroutine %d", i)
- }
- }
- tassert.Equal(t, int32(1), atomic.LoadInt32(&factoryCalls), fmt.Sprintf("factory called %d times, want %d for concurrent same-key requests", factoryCalls, 1))
- }
- func TestCreateOrGetMysteryboxClient_Concurrent_MultipleClients(t *testing.T) {
- clientRequests := []ClientData{
- {domain: "api.nebius.example1", ca: []byte("CA1")},
- {domain: "api.nebius.example1", ca: []byte("CA1")}, // duplicate
- {domain: "api.nebius.example1", ca: []byte("CA2")}, // the same domain, different CA
- {domain: "api.nebius.example2", ca: []byte("CA2")}, // different domain, the same CA
- {domain: "api.nebius.example1", ca: []byte{}}, // the same domain, empty CA
- }
- ctx := context.Background()
- cache, err := lru.New(10)
- tassert.NoError(t, err)
- var factoryCalls int32
- p := &Provider{
- Logger: logger,
- NewMysteryboxClient: func(ctx context.Context, apiDomain string, caCertificate []byte) (mysterybox.Client, error) {
- atomic.AddInt32(&factoryCalls, 1)
- return fake.NewFakeMysteryboxClient(nil), nil
- },
- mysteryboxClientsCache: cache,
- }
- var wg sync.WaitGroup
- wg.Add(len(clientRequests))
- start := make(chan struct{})
- errs := make([]error, len(clientRequests))
- for i, r := range clientRequests {
- go func(ix int, req ClientData) {
- defer wg.Done()
- <-start
- _, err := p.createOrGetMysteryboxClient(ctx, req.domain, req.ca)
- errs[ix] = err
- }(i, r)
- }
- close(start)
- wg.Wait()
- for i, err := range errs {
- if err != nil {
- tassert.NoError(t, err, "goroutine %d", i)
- }
- }
- tassert.Equal(t, int32(4), atomic.LoadInt32(&factoryCalls), fmt.Sprintf("factory called %d times, want %d", atomic.LoadInt32(&factoryCalls), 4))
- }
- func TestMysteryboxClientsCache_ConcurrentEviction_CloseOnce(t *testing.T) {
- ctx := context.Background()
- var created []*fake.FakeMysteryboxClient
- var mu sync.Mutex
- p := &Provider{
- Logger: logger,
- NewMysteryboxClient: func(ctx context.Context, apiDomain string, caCertificate []byte) (mysterybox.Client, error) {
- c := &fake.FakeMysteryboxClient{}
- mu.Lock()
- created = append(created, c)
- mu.Unlock()
- return c, nil
- },
- }
- setCacheSizeWorkaround(t, 1, p)
- var wg sync.WaitGroup
- wg.Add(3)
- start := make(chan struct{})
- go func() {
- defer wg.Done()
- <-start
- _, _ = p.createOrGetMysteryboxClient(ctx, "domain-a", nil)
- }()
- go func() {
- defer wg.Done()
- <-start
- _, _ = p.createOrGetMysteryboxClient(ctx, "domain-b", nil)
- }()
- go func() {
- defer wg.Done()
- <-start
- _, _ = p.createOrGetMysteryboxClient(ctx, "domain-c", nil)
- }()
- close(start)
- wg.Wait()
- tassert.Len(t, created, 3, "expected 3 clients created, got %d", len(created))
- // clients are not closed because of the eviction policy for provider's mysterybox clients connections cache
- tassert.Equal(t, int32(0), atomic.LoadInt32(&created[0].Closed), "expected first client not to be closed on eviction")
- tassert.Equal(t, int32(0), atomic.LoadInt32(&created[1].Closed), "expected first client not to be closed on eviction")
- tassert.Equal(t, int32(0), atomic.LoadInt32(&created[2].Closed), "expected first client not to be closed on eviction")
- }
- func TestNewClient_Concurrent_SameConfig_SingleClient_DifferentTokens(t *testing.T) {
- ctx := context.Background()
- namespace := uuid.NewString()
- mboxSvc := fake.InitMysteryboxService()
- k8sClient := clientfake.NewClientBuilder().Build()
- secret := mboxSvc.CreateSecret([]mysterybox.Entry{{Key: "k", StringValue: "v"}})
- var factoryCalls int32
- tokenToIssue := tokenToBeIssued
- tokenGetter := &faketokenGetter{tokenToIssue: tokenToIssue}
- p := &Provider{
- Logger: logger,
- NewMysteryboxClient: func(ctx context.Context, apiDomain string, caCertificate []byte) (mysterybox.Client, error) {
- atomic.AddInt32(&factoryCalls, 1)
- return fake.NewFakeMysteryboxClient(mboxSvc), nil
- },
- }
- settokenGetterWorkaround(tokenGetter, p)
- creds := []byte(`{"private_key":"KEY","key_id":"id","subject":"sub","issuer":"iss"}`)
- createK8sSecret(ctx, t, k8sClient, namespace, authRefName, authRefKey, creds)
- store := newNebiusMysteryboxSecretStoreWithServiceAccountCreds(apiDomain, namespace, authRefName, authRefKey)
- const goroutines = 10
- var wg sync.WaitGroup
- wg.Add(goroutines)
- start := make(chan struct{})
- clients := make([]esv1.SecretsClient, goroutines)
- errs := make([]error, goroutines)
- for i := range goroutines {
- go func(ix int) {
- defer wg.Done()
- <-start
- c, err := p.NewClient(ctx, store, k8sClient, namespace)
- clients[ix], errs[ix] = c, err
- }(i)
- }
- close(start)
- wg.Wait()
- for i := range goroutines {
- tassert.NoError(t, errs[i], "NewClient error: %w", errs[i])
- msc := clients[i].(*SecretsClient)
- got, err := msc.GetSecret(ctx, esv1.ExternalSecretDataRemoteRef{Key: secret.Id, Property: "k"})
- tassert.NoError(t, err)
- tassert.Equal(t, []byte("v"), got)
- }
- tassert.Equal(t, int32(goroutines), atomic.LoadInt32(&tokenGetter.calls), fmt.Sprintf("TokenGetter.GetToken called %d times, want %d", factoryCalls, goroutines))
- tassert.Equal(t, int32(1), atomic.LoadInt32(&factoryCalls), fmt.Sprintf("NewMysteryboxClient called %d times, want 1", factoryCalls))
- }
- // helpers
- func newNebiusMysteryboxSecretStoreWithAuthTokenKey(apiDomain, namespace, tokenSecretName, tokenSecretKey string) esv1.GenericStore {
- return &esv1.SecretStore{
- ObjectMeta: metav1.ObjectMeta{
- Namespace: namespace,
- },
- Spec: esv1.SecretStoreSpec{
- Provider: &esv1.SecretStoreProvider{
- NebiusMysterybox: &esv1.NebiusMysteryboxProvider{
- APIDomain: apiDomain,
- Auth: esv1.NebiusAuth{
- Token: esmeta.SecretKeySelector{
- Name: tokenSecretName,
- Key: tokenSecretKey,
- },
- },
- },
- },
- },
- }
- }
- func newNebiusMysteryboxSecretStoreWithServiceAccountCreds(apiDomain, namespace, keySecretName, keySecretKey string) esv1.GenericStore {
- return &esv1.SecretStore{
- ObjectMeta: metav1.ObjectMeta{
- Namespace: namespace,
- },
- Spec: esv1.SecretStoreSpec{
- Provider: &esv1.SecretStoreProvider{
- NebiusMysterybox: &esv1.NebiusMysteryboxProvider{
- APIDomain: apiDomain,
- Auth: esv1.NebiusAuth{
- ServiceAccountCreds: esmeta.SecretKeySelector{
- Name: keySecretName,
- Key: keySecretKey,
- },
- },
- },
- },
- },
- }
- }
- func createK8sSecret(ctx context.Context, t *testing.T, k8sClient k8sclient.Client, namespace, secretName, secretKey string, secretValue []byte) {
- err := k8sClient.Create(ctx, &corev1.Secret{
- ObjectMeta: metav1.ObjectMeta{
- Namespace: namespace,
- Name: secretName,
- },
- Data: map[string][]byte{secretKey: secretValue},
- })
- tassert.NoError(t, err)
- }
- func unmarshalStringMap(t *testing.T, data []byte) map[string]string {
- stringMap := make(map[string]string)
- err := json.Unmarshal(data, &stringMap)
- tassert.NoError(t, err)
- return stringMap
- }
- func newProvider(t *testing.T, newMysteryboxClientFunc NewMysteryboxClient, tokenGetter TokenGetter) *Provider {
- t.Helper()
- cache, err := lru.New(10)
- tassert.NoError(t, err)
- return &Provider{
- Logger: logger,
- NewMysteryboxClient: newMysteryboxClientFunc,
- mysteryboxClientsCache: cache,
- TokenGetter: tokenGetter,
- }
- }
- type faketokenGetter struct {
- calls int32
- returnError bool
- gotDomain string
- gotCreds string
- gotCACert []byte
- tokenToIssue string
- mu sync.Mutex
- }
- func (f *faketokenGetter) GetToken(_ context.Context, apiDomain, subjectCreds string, caCert []byte) (string, error) {
- atomic.AddInt32(&f.calls, 1)
- f.mu.Lock()
- defer f.mu.Unlock()
- f.gotDomain = apiDomain
- f.gotCreds = subjectCreds
- f.gotCACert = caCert
- if f.returnError {
- return "", errors.New("internal error")
- }
- return f.tokenToIssue, nil
- }
- func setCacheSizeWorkaround(t *testing.T, size int, p *Provider) {
- t.Helper()
- err := p.initMysteryboxClientsCache()
- tassert.NoError(t, err)
- p.mysteryboxClientsCache.Resize(size)
- }
- func settokenGetterWorkaround(tokenGetter TokenGetter, p *Provider) {
- p.tokenInitMutex.Lock()
- defer p.tokenInitMutex.Unlock()
- p.TokenGetter = tokenGetter
- }
- type ClientData struct {
- domain string
- ca []byte
- }
|