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/utils/resolvers/generator.go
Mathew Wicks 0656bf33c5
fix: issues with generators (#4163)
* fix: issues with generators

Signed-off-by: Mathew Wicks <5735406+thesuperzapper@users.noreply.github.com>

* fix generator RBAC permissions for controller

Signed-off-by: Mathew Wicks <5735406+thesuperzapper@users.noreply.github.com>

* fix docs for UUID generator

Signed-off-by: Mathew Wicks <5735406+thesuperzapper@users.noreply.github.com>

* update tilt to 0.33.10

Signed-off-by: Mathew Wicks <5735406+thesuperzapper@users.noreply.github.com>

* explicitly specify generator RBAC roles

Signed-off-by: Mathew Wicks <5735406+thesuperzapper@users.noreply.github.com>

* update helm test with new entries

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

---------

Signed-off-by: Mathew Wicks <5735406+thesuperzapper@users.noreply.github.com>
Signed-off-by: Gergely Brautigam <182850+Skarlso@users.noreply.github.com>
Co-authored-by: Gergely Brautigam <182850+Skarlso@users.noreply.github.com>
2024-12-01 08:52:39 +01:00

214 lines
8.1 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 resolvers
import (
"context"
"fmt"
"reflect"
apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
genv1alpha1 "github.com/external-secrets/external-secrets/apis/generators/v1alpha1"
)
// these errors are explicitly defined so we can detect them with `errors.Is()`.
var (
// ErrUnableToGetGenerator is returned when a generator reference cannot be resolved.
ErrUnableToGetGenerator = fmt.Errorf("unable to get generator")
)
// GeneratorRef resolves a generator reference to a generator implementation.
func GeneratorRef(ctx context.Context, cl client.Client, scheme *runtime.Scheme, namespace string, generatorRef *esv1beta1.GeneratorRef) (genv1alpha1.Generator, *apiextensions.JSON, error) {
generator, jsonObj, err := getGenerator(ctx, cl, scheme, namespace, generatorRef)
if err != nil {
return nil, nil, fmt.Errorf("%w: %w", ErrUnableToGetGenerator, err)
}
return generator, jsonObj, nil
}
func getGenerator(ctx context.Context, cl client.Client, scheme *runtime.Scheme, namespace string, generatorRef *esv1beta1.GeneratorRef) (genv1alpha1.Generator, *apiextensions.JSON, error) {
// get a GVK from the generatorRef
gv, err := schema.ParseGroupVersion(generatorRef.APIVersion)
if err != nil {
return nil, nil, reconcile.TerminalError(fmt.Errorf("generatorRef has invalid APIVersion: %w", err))
}
gvk := schema.GroupVersionKind{
Group: gv.Group,
Version: gv.Version,
Kind: generatorRef.Kind,
}
// fail if the GVK does not use the generator group
if gvk.Group != genv1alpha1.Group {
return nil, nil, reconcile.TerminalError(fmt.Errorf("generatorRef may only reference the generators group, but got %s", gvk.Group))
}
// get a client Object from the GVK
t, exists := scheme.AllKnownTypes()[gvk]
if !exists {
return nil, nil, reconcile.TerminalError(fmt.Errorf("generatorRef references unknown GVK %s", gvk))
}
obj := reflect.New(t).Interface().(client.Object)
// this interface provides the Generate() method used by the controller
// NOTE: all instances of a generator kind use the same instance of this interface
var generator genv1alpha1.Generator
// ClusterGenerator is a special case because it's a cluster-scoped resource
// to use it, we create a "virtual" namespaced generator for the current namespace, as if one existed in the API
if gvk.Kind == genv1alpha1.ClusterGeneratorKind {
clusterGenerator := obj.(*genv1alpha1.ClusterGenerator)
// get the cluster generator resource from the API
// NOTE: it's important that we use the structured client so we use the cache
err = cl.Get(ctx, client.ObjectKey{Name: generatorRef.Name}, clusterGenerator)
if err != nil {
return nil, nil, err
}
// convert the cluster generator to a virtual namespaced generator object
obj, err = clusterGeneratorToVirtual(clusterGenerator)
if err != nil {
return nil, nil, reconcile.TerminalError(fmt.Errorf("invalid ClusterGenerator: %w", err))
}
// get the generator interface
var ok bool
generator, ok = genv1alpha1.GetGeneratorByName(string(clusterGenerator.Spec.Kind))
if !ok {
return nil, nil, reconcile.TerminalError(fmt.Errorf("ClusterGenerator has unknown kind %s", clusterGenerator.Spec.Kind))
}
} else {
// get the generator resource from the API
// NOTE: it's important that we use the structured client so we use the cache
err = cl.Get(ctx, types.NamespacedName{
Name: generatorRef.Name,
Namespace: namespace,
}, obj)
if err != nil {
return nil, nil, err
}
// get the generator interface
var ok bool
generator, ok = genv1alpha1.GetGeneratorByName(gvk.Kind)
if !ok {
return nil, nil, reconcile.TerminalError(fmt.Errorf("generatorRef has unknown kind %s", gvk.Kind))
}
}
// convert the generator to unstructured object
u := &unstructured.Unstructured{}
u.Object, err = runtime.DefaultUnstructuredConverter.ToUnstructured(obj)
if err != nil {
return nil, nil, err
}
// convert the unstructured object to JSON
// NOTE: we do this for backwards compatibility with how this API works, not because it's a good idea
// we should refactor the generator API to use the normal typed objects
jsonObj, err := u.MarshalJSON()
if err != nil {
return nil, nil, err
}
return generator, &apiextensions.JSON{Raw: jsonObj}, nil
}
// clusterGeneratorToVirtual converts a ClusterGenerator to a "virtual" namespaced generator that doesn't actually exist in the API.
func clusterGeneratorToVirtual(gen *genv1alpha1.ClusterGenerator) (client.Object, error) {
switch gen.Spec.Kind {
case genv1alpha1.GeneratorKindACRAccessToken:
if gen.Spec.Generator.ACRAccessTokenSpec == nil {
return nil, fmt.Errorf("when kind is %s, ACRAccessTokenSpec must be set", gen.Spec.Kind)
}
return &genv1alpha1.ACRAccessToken{
Spec: *gen.Spec.Generator.ACRAccessTokenSpec,
}, nil
case genv1alpha1.GeneratorKindECRAuthorizationToken:
if gen.Spec.Generator.ECRAuthorizationTokenSpec == nil {
return nil, fmt.Errorf("when kind is %s, ECRAuthorizationTokenSpec must be set", gen.Spec.Kind)
}
return &genv1alpha1.ECRAuthorizationToken{
Spec: *gen.Spec.Generator.ECRAuthorizationTokenSpec,
}, nil
case genv1alpha1.GeneratorKindFake:
if gen.Spec.Generator.FakeSpec == nil {
return nil, fmt.Errorf("when kind is %s, FakeSpec must be set", gen.Spec.Kind)
}
return &genv1alpha1.Fake{
Spec: *gen.Spec.Generator.FakeSpec,
}, nil
case genv1alpha1.GeneratorKindGCRAccessToken:
if gen.Spec.Generator.GCRAccessTokenSpec == nil {
return nil, fmt.Errorf("when kind is %s, GCRAccessTokenSpec must be set", gen.Spec.Kind)
}
return &genv1alpha1.GCRAccessToken{
Spec: *gen.Spec.Generator.GCRAccessTokenSpec,
}, nil
case genv1alpha1.GeneratorKindGithubAccessToken:
if gen.Spec.Generator.GithubAccessTokenSpec == nil {
return nil, fmt.Errorf("when kind is %s, GithubAccessTokenSpec must be set", gen.Spec.Kind)
}
return &genv1alpha1.GithubAccessToken{
Spec: *gen.Spec.Generator.GithubAccessTokenSpec,
}, nil
case genv1alpha1.GeneratorKindPassword:
if gen.Spec.Generator.PasswordSpec == nil {
return nil, fmt.Errorf("when kind is %s, PasswordSpec must be set", gen.Spec.Kind)
}
return &genv1alpha1.Password{
Spec: *gen.Spec.Generator.PasswordSpec,
}, nil
case genv1alpha1.GeneratorKindSTSSessionToken:
if gen.Spec.Generator.STSSessionTokenSpec == nil {
return nil, fmt.Errorf("when kind is %s, STSSessionTokenSpec must be set", gen.Spec.Kind)
}
return &genv1alpha1.STSSessionToken{
Spec: *gen.Spec.Generator.STSSessionTokenSpec,
}, nil
case genv1alpha1.GeneratorKindUUID:
if gen.Spec.Generator.UUIDSpec == nil {
return nil, fmt.Errorf("when kind is %s, UUIDSpec must be set", gen.Spec.Kind)
}
return &genv1alpha1.UUID{
Spec: *gen.Spec.Generator.UUIDSpec,
}, nil
case genv1alpha1.GeneratorKindVaultDynamicSecret:
if gen.Spec.Generator.VaultDynamicSecretSpec == nil {
return nil, fmt.Errorf("when kind is %s, VaultDynamicSecretSpec must be set", gen.Spec.Kind)
}
return &genv1alpha1.VaultDynamicSecret{
Spec: *gen.Spec.Generator.VaultDynamicSecretSpec,
}, nil
case genv1alpha1.GeneratorKindWebhook:
if gen.Spec.Generator.WebhookSpec == nil {
return nil, fmt.Errorf("when kind is %s, WebhookSpec must be set", gen.Spec.Kind)
}
return &genv1alpha1.Webhook{
Spec: *gen.Spec.Generator.WebhookSpec,
}, nil
default:
return nil, fmt.Errorf("unknown kind %s", gen.Spec.Kind)
}
}