mirror of
https://github.com/external-secrets/external-secrets.git
synced 2024-12-14 11:57:59 +00:00
Add base for SecretStore controller
This commit is contained in:
parent
5e3c3e8d3f
commit
1845b77c0c
2 changed files with 137 additions and 5 deletions
|
@ -16,13 +16,26 @@ package controllers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/go-logr/logr"
|
"github.com/go-logr/logr"
|
||||||
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"k8s.io/apimachinery/pkg/types"
|
||||||
ctrl "sigs.k8s.io/controller-runtime"
|
ctrl "sigs.k8s.io/controller-runtime"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||||
|
|
||||||
externalsecretsv1alpha1 "github.com/external-secrets/external-secrets/api/v1alpha1"
|
esv1alpha1 "github.com/external-secrets/external-secrets/api/v1alpha1"
|
||||||
|
"github.com/external-secrets/external-secrets/pkg/provider"
|
||||||
|
_ "github.com/external-secrets/external-secrets/pkg/provider/register"
|
||||||
|
"github.com/external-secrets/external-secrets/pkg/provider/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
requeueAfter = time.Second * 30
|
||||||
)
|
)
|
||||||
|
|
||||||
// ExternalSecretReconciler reconciles a ExternalSecret object.
|
// ExternalSecretReconciler reconciles a ExternalSecret object.
|
||||||
|
@ -36,16 +49,113 @@ type ExternalSecretReconciler struct {
|
||||||
// +kubebuilder:rbac:groups=external-secrets.io,resources=externalsecrets/status,verbs=get;update;patch
|
// +kubebuilder:rbac:groups=external-secrets.io,resources=externalsecrets/status,verbs=get;update;patch
|
||||||
|
|
||||||
func (r *ExternalSecretReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
|
func (r *ExternalSecretReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
|
||||||
_ = context.Background()
|
ctx := context.Background()
|
||||||
_ = r.Log.WithValues("externalsecret", req.NamespacedName)
|
log := r.Log.WithValues("external-secrets", req.NamespacedName)
|
||||||
|
|
||||||
// your logic here
|
var externalSecret esv1alpha1.ExternalSecret
|
||||||
|
|
||||||
|
err := r.Get(ctx, req.NamespacedName, &externalSecret)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err, "could not get ExternalSecret")
|
||||||
|
return ctrl.Result{}, client.IgnoreNotFound(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
secret := &corev1.Secret{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: externalSecret.Name,
|
||||||
|
Namespace: externalSecret.Namespace,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = ctrl.CreateOrUpdate(ctx, r.Client, secret, func() error {
|
||||||
|
store, err := r.getStore(ctx, &externalSecret)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not get store reference from ExternalSecret %q: %w", externalSecret.Name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
storeProvider, err := schema.GetProvider(store)
|
||||||
|
if err != nil {
|
||||||
|
// TODO: add SecretStore name to the log message
|
||||||
|
return fmt.Errorf("could not get store provider: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Does the * work?
|
||||||
|
// If not, should I change the Provider interface?
|
||||||
|
providerClient, err := storeProvider.New(ctx, *store.GetProvider(), r.Client, req.Namespace)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not get provider client: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = controllerutil.SetControllerReference(&externalSecret, &secret.ObjectMeta, r.Scheme)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not set ExternalSecret controller reference: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
secret.Labels = externalSecret.Labels
|
||||||
|
secret.Annotations = externalSecret.Annotations
|
||||||
|
|
||||||
|
// TODO: Pass reference to the client (not a copy)?
|
||||||
|
secret.Data, err = r.getProviderSecretData(ctx, providerClient, &externalSecret)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not get secret data from provider: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err, "could not reconcile ExternalSecret")
|
||||||
|
|
||||||
|
// TODO: Set ExternalSecret.Status.Conditions
|
||||||
|
return ctrl.Result{RequeueAfter: requeueAfter}, nil
|
||||||
|
}
|
||||||
|
|
||||||
return ctrl.Result{}, nil
|
return ctrl.Result{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *ExternalSecretReconciler) getStore(ctx context.Context, externalSecret *esv1alpha1.ExternalSecret) (esv1alpha1.GenericStore, error) {
|
||||||
|
// TODO: Implement getting ClusterSecretStore
|
||||||
|
var secretStore esv1alpha1.SecretStore
|
||||||
|
|
||||||
|
ref := types.NamespacedName{
|
||||||
|
Name: externalSecret.Spec.SecretStoreRef.Name,
|
||||||
|
Namespace: externalSecret.Namespace,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := r.Get(ctx, ref, &secretStore)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not get SecretStore %q, %w", ref.Name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &secretStore, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *ExternalSecretReconciler) getProviderSecretData(ctx context.Context, providerClient provider.Provider, externalSecret *esv1alpha1.ExternalSecret) (map[string][]byte, error) {
|
||||||
|
providerData := make(map[string][]byte)
|
||||||
|
|
||||||
|
for _, remoteRef := range externalSecret.Spec.DataFrom {
|
||||||
|
secretMap, err := providerClient.GetSecretMap(ctx, remoteRef)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("key %q from ExternalSecret %q: %w", remoteRef.Key, externalSecret.Name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
providerData = Merge(providerData, secretMap)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, secretRef := range externalSecret.Spec.Data {
|
||||||
|
secretData, err := providerClient.GetSecret(ctx, secretRef.RemoteRef)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("key %q from ExternalSecret %q: %w", secretRef.RemoteRef.Key, externalSecret.Name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
providerData[secretRef.SecretKey] = secretData
|
||||||
|
}
|
||||||
|
|
||||||
|
return providerData, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (r *ExternalSecretReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
func (r *ExternalSecretReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||||
return ctrl.NewControllerManagedBy(mgr).
|
return ctrl.NewControllerManagedBy(mgr).
|
||||||
For(&externalsecretsv1alpha1.ExternalSecret{}).
|
For(&esv1alpha1.ExternalSecret{}).
|
||||||
Complete(r)
|
Complete(r)
|
||||||
}
|
}
|
||||||
|
|
22
controllers/utils.go
Normal file
22
controllers/utils.go
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
/*
|
||||||
|
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 controllers
|
||||||
|
|
||||||
|
func Merge(src, dst map[string][]byte) map[string][]byte {
|
||||||
|
for k, v := range dst {
|
||||||
|
src[k] = v
|
||||||
|
}
|
||||||
|
return src
|
||||||
|
}
|
Loading…
Reference in a new issue