Skip to content

Commit

Permalink
Add support for credentials binding
Browse files Browse the repository at this point in the history
  • Loading branch information
dimityrmirchev committed Oct 23, 2024
1 parent 9f29786 commit 60e3a1e
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 28 deletions.
16 changes: 16 additions & 0 deletions internal/client/garden/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
gardencorev1beta1 "github.com/gardener/gardener/pkg/apis/core/v1beta1"
corev1beta1constants "github.com/gardener/gardener/pkg/apis/core/v1beta1/constants"
operationsv1alpha1 "github.com/gardener/gardener/pkg/apis/operations/v1alpha1"
gardensecurityv1alpha1 "github.com/gardener/gardener/pkg/apis/security/v1alpha1"
seedmanagementv1alpha1 "github.com/gardener/gardener/pkg/apis/seedmanagement/v1alpha1"
authenticationv1 "k8s.io/api/authentication/v1"
corev1 "k8s.io/api/core/v1"
Expand Down Expand Up @@ -79,6 +80,9 @@ type Client interface {
// GetSecretBinding returns a Gardener secretbinding resource
GetSecretBinding(ctx context.Context, namespace, name string) (*gardencorev1beta1.SecretBinding, error)

// GetCredentialsBinding returns a Gardener credentialsbinding resource
GetCredentialsBinding(ctx context.Context, namespace, name string) (*gardensecurityv1alpha1.CredentialsBinding, error)

// GetCloudProfile returns a CloudProfileUnion resource which encapsulates the result of fetching a CloudProfile or NamespacedCloudProfile, depending on the given cloud profile reference
GetCloudProfile(ctx context.Context, ref gardencorev1beta1.CloudProfileReference) (*CloudProfileUnion, error)

Expand Down Expand Up @@ -262,6 +266,18 @@ func (g *clientImpl) GetSecretBinding(ctx context.Context, namespace, name strin
return secretBinding, nil
}

// GetCredentialsBinding returns a Gardener credentialsbinding resource.
func (g *clientImpl) GetCredentialsBinding(ctx context.Context, namespace, name string) (*gardensecurityv1alpha1.CredentialsBinding, error) {
credentialsBinding := &gardensecurityv1alpha1.CredentialsBinding{}
key := types.NamespacedName{Namespace: namespace, Name: name}

if err := g.c.Get(ctx, key, credentialsBinding); err != nil {
return nil, fmt.Errorf("failed to get credentialsbinding %v: %w", key, err)
}

return credentialsBinding, nil
}

// GetSecret returns a Kubernetes secret resource.
func (g *clientImpl) GetSecret(ctx context.Context, namespace, name string) (*corev1.Secret, error) {
secret := &corev1.Secret{}
Expand Down
22 changes: 19 additions & 3 deletions internal/client/garden/mocks/mock_client.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

32 changes: 26 additions & 6 deletions pkg/cmd/providerenv/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,16 +166,36 @@ func (o *options) Run(f util.Factory) error {
return err
}

if shoot.Spec.SecretBindingName == nil || *shoot.Spec.SecretBindingName == "" {
return fmt.Errorf("shoot %q is not bound to a cloud provider secret", o.Target.ShootName())
if (shoot.Spec.SecretBindingName == nil || *shoot.Spec.SecretBindingName == "") &&
(shoot.Spec.CredentialsBindingName == nil || *shoot.Spec.CredentialsBindingName == "") {
return fmt.Errorf("shoot %q is not bound to a cloud provider credential", o.Target.ShootName())
}

secretBinding, err := client.GetSecretBinding(ctx, shoot.Namespace, *shoot.Spec.SecretBindingName)
if err != nil {
return err
var (
secretName string
secretNamespace string
)

if shoot.Spec.SecretBindingName != nil && *shoot.Spec.SecretBindingName != "" {
secretBinding, err := client.GetSecretBinding(ctx, shoot.Namespace, *shoot.Spec.SecretBindingName)
if err != nil {
return err
}

secretName = secretBinding.SecretRef.Name
secretNamespace = secretBinding.SecretRef.Namespace
} else {
// TODO: This code should eventually support credentials of type workload identity
credentialsBinding, err := client.GetCredentialsBinding(ctx, shoot.Namespace, *shoot.Spec.CredentialsBindingName)
if err != nil {
return err
}

secretName = credentialsBinding.CredentialsRef.Name
secretNamespace = credentialsBinding.CredentialsRef.Namespace
}

secret, err := client.GetSecret(ctx, secretBinding.SecretRef.Namespace, secretBinding.SecretRef.Name)
secret, err := client.GetSecret(ctx, secretNamespace, secretName)
if err != nil {
return err
}
Expand Down
74 changes: 55 additions & 19 deletions pkg/cmd/providerenv/options_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
openstackv1alpha1 "github.com/gardener/gardener-extension-provider-openstack/pkg/apis/openstack/v1alpha1"
gardencorev1beta1 "github.com/gardener/gardener/pkg/apis/core/v1beta1"
corev1beta1constants "github.com/gardener/gardener/pkg/apis/core/v1beta1/constants"
gardensecurityv1alpha1 "github.com/gardener/gardener/pkg/apis/security/v1alpha1"
"github.com/golang/mock/gomock"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
Expand Down Expand Up @@ -192,28 +193,32 @@ var _ = Describe("Env Commands - Options", func() {

Describe("running the provider-env command with the given options", func() {
var (
ctx context.Context
manager *targetmocks.MockManager
client *gardenclientmocks.MockClient
t target.Target
secretBindingName string
cloudProfileName string
region string
provider *gardencorev1beta1.Provider
secretRef *corev1.SecretReference
cloudProfileRef *gardencorev1beta1.CloudProfileReference
shoot *gardencorev1beta1.Shoot
secretBinding *gardencorev1beta1.SecretBinding
cloudProfile *clientgarden.CloudProfileUnion
providerConfig *openstackv1alpha1.CloudProfileConfig
secret *corev1.Secret
ctx context.Context
manager *targetmocks.MockManager
client *gardenclientmocks.MockClient
t target.Target
secretBindingName string
credentialsBindingName string
cloudProfileName string
region string
provider *gardencorev1beta1.Provider
secretRef *corev1.SecretReference
// secretRef *corev1.SecretReference
cloudProfileRef *gardencorev1beta1.CloudProfileReference
shoot *gardencorev1beta1.Shoot
secretBinding *gardencorev1beta1.SecretBinding
credentialsBinding *gardensecurityv1alpha1.CredentialsBinding
cloudProfile *clientgarden.CloudProfileUnion
providerConfig *openstackv1alpha1.CloudProfileConfig
secret *corev1.Secret
)

BeforeEach(func() {
manager = targetmocks.NewMockManager(ctrl)
client = gardenclientmocks.NewMockClient(ctrl)
t = target.NewTarget("test", "project", "seed", "shoot")
secretBindingName = "secret-binding"
credentialsBindingName = "credentials-binding"
cloudProfileName = "cloud-profile"
region = "europe"
provider = &gardencorev1beta1.Provider{
Expand Down Expand Up @@ -255,6 +260,18 @@ var _ = Describe("Env Commands - Options", func() {
},
SecretRef: *secretRef.DeepCopy(),
}
credentialsBinding = &gardensecurityv1alpha1.CredentialsBinding{
ObjectMeta: metav1.ObjectMeta{
Name: credentialsBindingName,
Namespace: shoot.Namespace,
},
CredentialsRef: corev1.ObjectReference{
Kind: "Secret",
APIVersion: corev1.SchemeGroupVersion.String(),
Namespace: secretRef.Namespace,
Name: secretRef.Name,
},
}
secret = &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Namespace: secretRef.Namespace,
Expand Down Expand Up @@ -287,13 +304,13 @@ var _ = Describe("Env Commands - Options", func() {
})

JustBeforeEach(func() {
client.EXPECT().GetSecretBinding(ctx, shoot.Namespace, *shoot.Spec.SecretBindingName).Return(secretBinding, nil)
client.EXPECT().GetSecret(ctx, secretBinding.SecretRef.Namespace, secretBinding.SecretRef.Name).Return(secret, nil)
client.EXPECT().GetCloudProfile(ctx, *shoot.Spec.CloudProfile).Return(cloudProfile, nil)
})

Context("and the shoot is targeted via project", func() {
JustBeforeEach(func() {
client.EXPECT().GetSecretBinding(ctx, shoot.Namespace, *shoot.Spec.SecretBindingName).Return(secretBinding, nil)
currentTarget := t.WithSeedName("")
manager.EXPECT().CurrentTarget().Return(currentTarget, nil)
client.EXPECT().FindShoot(ctx, currentTarget.AsListOption()).Return(shoot, nil)
Expand Down Expand Up @@ -321,9 +338,28 @@ var _ = Describe("Env Commands - Options", func() {
manager.EXPECT().Configuration().Return(cfg)
})

It("does the work when the shoot is targeted via seed", func() {
Expect(options.Run(factory)).To(Succeed())
Expect(options.String()).To(Equal(fmt.Sprintf(readTestFile("gcp/export.seed.bash"), filepath.Join(sessionDir, ".config", "gcloud"))))
Context("and the shoot uses secret binding", func() {
JustBeforeEach(func() {
client.EXPECT().GetSecretBinding(ctx, shoot.Namespace, *shoot.Spec.SecretBindingName).Return(secretBinding, nil)
})

It("does the work when the shoot is targeted via seed", func() {
Expect(options.Run(factory)).To(Succeed())
Expect(options.String()).To(Equal(fmt.Sprintf(readTestFile("gcp/export.seed.bash"), filepath.Join(sessionDir, ".config", "gcloud"))))
})
})

Context("and the shoot uses credentials binding", func() {
JustBeforeEach(func() {
shoot.Spec.SecretBindingName = nil
shoot.Spec.CredentialsBindingName = &credentialsBindingName
client.EXPECT().GetCredentialsBinding(ctx, shoot.Namespace, *shoot.Spec.CredentialsBindingName).Return(credentialsBinding, nil)
})

It("does the work when the shoot is targeted via seed", func() {
Expect(options.Run(factory)).To(Succeed())
Expect(options.String()).To(Equal(fmt.Sprintf(readTestFile("gcp/export.seed.bash"), filepath.Join(sessionDir, ".config", "gcloud"))))
})
})
})
})
Expand Down

0 comments on commit 60e3a1e

Please sign in to comment.