diff --git a/api/v1alpha2/linodecluster_webhook.go b/api/v1alpha2/linodecluster_webhook.go index 355782db..f3d64c9d 100644 --- a/api/v1alpha2/linodecluster_webhook.go +++ b/api/v1alpha2/linodecluster_webhook.go @@ -25,6 +25,8 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/validation/field" ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/config" logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/webhook" "sigs.k8s.io/controller-runtime/pkg/webhook/admission" @@ -53,7 +55,6 @@ func (r *LinodeCluster) ValidateCreate() (admission.Warnings, error) { ctx, cancel := context.WithTimeout(context.Background(), defaultWebhookTimeout) defer cancel() - return nil, r.validateLinodeCluster(ctx, &defaultLinodeClient) } @@ -73,11 +74,25 @@ func (r *LinodeCluster) ValidateDelete() (admission.Warnings, error) { return nil, nil } -func (r *LinodeCluster) validateLinodeCluster(ctx context.Context, client LinodeClient) error { +func (r *LinodeCluster) validateLinodeCluster(ctx context.Context, linodeclient LinodeClient) error { + cl, err := client.New(config.GetConfigOrDie(), client.Options{}) + if err != nil { + linodeclusterlog.Info("failed to configure runtime client", "name", r.Name) + return err + } + + if r.Spec.CredentialsRef != nil { + apiToken, err := getCredentialDataFromRef(ctx, cl, *r.Spec.CredentialsRef, r.GetNamespace(), "apiToken") + if err != nil { + linodeclusterlog.Info("credentials from secret ref error", "name", r.Name) + return err + } + linodeclient = linodeclient.SetToken(string(apiToken)) + } // TODO: instrument with tracing, might need refactor to preserve readibility var errs field.ErrorList - if err := r.validateLinodeClusterSpec(ctx, client); err != nil { + if err := r.validateLinodeClusterSpec(ctx, linodeclient); err != nil { errs = slices.Concat(errs, err) } @@ -89,11 +104,10 @@ func (r *LinodeCluster) validateLinodeCluster(ctx context.Context, client Linode r.Name, errs) } -func (r *LinodeCluster) validateLinodeClusterSpec(ctx context.Context, client LinodeClient) field.ErrorList { - // TODO: instrument with tracing, might need refactor to preserve readibility +func (r *LinodeCluster) validateLinodeClusterSpec(ctx context.Context, linodeclient LinodeClient) field.ErrorList { var errs field.ErrorList - if err := validateRegion(ctx, client, r.Spec.Region, field.NewPath("spec").Child("region")); err != nil { + if err := validateRegion(ctx, linodeclient, r.Spec.Region, field.NewPath("spec").Child("region")); err != nil { errs = append(errs, err) } diff --git a/api/v1alpha2/linodemachine_webhook.go b/api/v1alpha2/linodemachine_webhook.go index 0bd50e4f..708610ee 100644 --- a/api/v1alpha2/linodemachine_webhook.go +++ b/api/v1alpha2/linodemachine_webhook.go @@ -28,6 +28,8 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/validation/field" ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/config" logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/webhook" "sigs.k8s.io/controller-runtime/pkg/webhook/admission" @@ -93,11 +95,27 @@ func (r *LinodeMachine) ValidateDelete() (admission.Warnings, error) { return nil, nil } -func (r *LinodeMachine) validateLinodeMachine(ctx context.Context, client LinodeClient) error { +func (r *LinodeMachine) validateLinodeMachine(ctx context.Context, linodeclient LinodeClient) error { + + cl, err := client.New(config.GetConfigOrDie(), client.Options{}) + if err != nil { + linodeclusterlog.Info("failed to configure runtime client", "name", r.Name) + return err + } + + if r.Spec.CredentialsRef != nil { + apiToken, err := getCredentialDataFromRef(ctx, cl, *r.Spec.CredentialsRef, r.GetNamespace(), "apiToken") + if err != nil { + linodeclusterlog.Info("credentials from secret ref error", "name", r.Name) + return err + } + linodeclient = linodeclient.SetToken(string(apiToken)) + } + // TODO: instrument with tracing, might need refactor to preserve readibility var errs field.ErrorList - if err := r.validateLinodeMachineSpec(ctx, client); err != nil { + if err := r.validateLinodeMachineSpec(ctx, linodeclient); err != nil { errs = slices.Concat(errs, err) } @@ -109,14 +127,13 @@ func (r *LinodeMachine) validateLinodeMachine(ctx context.Context, client Linode r.Name, errs) } -func (r *LinodeMachine) validateLinodeMachineSpec(ctx context.Context, client LinodeClient) field.ErrorList { - // TODO: instrument with tracing, might need refactor to preserve readibility +func (r *LinodeMachine) validateLinodeMachineSpec(ctx context.Context, linodeclient LinodeClient) field.ErrorList { var errs field.ErrorList - if err := validateRegion(ctx, client, r.Spec.Region, field.NewPath("spec").Child("region")); err != nil { + if err := validateRegion(ctx, linodeclient, r.Spec.Region, field.NewPath("spec").Child("region")); err != nil { errs = append(errs, err) } - plan, err := validateLinodeType(ctx, client, r.Spec.Type, field.NewPath("spec").Child("type")) + plan, err := validateLinodeType(ctx, linodeclient, r.Spec.Type, field.NewPath("spec").Child("type")) if err != nil { errs = append(errs, err) } diff --git a/api/v1alpha2/linodevpc_webhook.go b/api/v1alpha2/linodevpc_webhook.go index 8a4066fe..79917f71 100644 --- a/api/v1alpha2/linodevpc_webhook.go +++ b/api/v1alpha2/linodevpc_webhook.go @@ -31,6 +31,8 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/validation/field" ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/config" logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/webhook" "sigs.k8s.io/controller-runtime/pkg/webhook/admission" @@ -112,11 +114,25 @@ func (r *LinodeVPC) ValidateDelete() (admission.Warnings, error) { return nil, nil } -func (r *LinodeVPC) validateLinodeVPC(ctx context.Context, client LinodeClient) error { +func (r *LinodeVPC) validateLinodeVPC(ctx context.Context, linodeclient LinodeClient) error { + cl, err := client.New(config.GetConfigOrDie(), client.Options{}) + if err != nil { + linodeclusterlog.Info("failed to configure runtime client", "name", r.Name) + return err + } + + if r.Spec.CredentialsRef != nil { + apiToken, err := getCredentialDataFromRef(ctx, cl, *r.Spec.CredentialsRef, r.GetNamespace(), "apiToken") + if err != nil { + linodeclusterlog.Info("credentials from secret ref error", "name", r.Name) + return err + } + linodeclient = linodeclient.SetToken(string(apiToken)) + } // TODO: instrument with tracing, might need refactor to preserve readibility var errs field.ErrorList - if err := r.validateLinodeVPCSpec(ctx, client); err != nil { + if err := r.validateLinodeVPCSpec(ctx, linodeclient); err != nil { errs = slices.Concat(errs, err) } @@ -128,11 +144,11 @@ func (r *LinodeVPC) validateLinodeVPC(ctx context.Context, client LinodeClient) r.Name, errs) } -func (r *LinodeVPC) validateLinodeVPCSpec(ctx context.Context, client LinodeClient) field.ErrorList { +func (r *LinodeVPC) validateLinodeVPCSpec(ctx context.Context, linodeclient LinodeClient) field.ErrorList { // TODO: instrument with tracing, might need refactor to preserve readibility var errs field.ErrorList - if err := validateRegion(ctx, client, r.Spec.Region, field.NewPath("spec").Child("region"), LinodeVPCCapability); err != nil { + if err := validateRegion(ctx, linodeclient, r.Spec.Region, field.NewPath("spec").Child("region"), LinodeVPCCapability); err != nil { errs = append(errs, err) } if err := r.validateLinodeVPCSubnets(); err != nil { diff --git a/api/v1alpha2/webhook_helpers.go b/api/v1alpha2/webhook_helpers.go index 37f09dd3..26fa505f 100644 --- a/api/v1alpha2/webhook_helpers.go +++ b/api/v1alpha2/webhook_helpers.go @@ -27,10 +27,11 @@ import ( "github.com/linode/linodego" "k8s.io/apimachinery/pkg/util/validation/field" "k8s.io/utils/ptr" - - "github.com/linode/cluster-api-provider-linode/observability/wrappers/linodeclient" + "sigs.k8s.io/controller-runtime/pkg/client" . "github.com/linode/cluster-api-provider-linode/clients" + "github.com/linode/cluster-api-provider-linode/observability/wrappers/linodeclient" + corev1 "k8s.io/api/core/v1" ) const ( @@ -100,3 +101,33 @@ func validateObjectStorageRegion(ctx context.Context, client LinodeClient, id st } return validateRegion(ctx, client, region, path, LinodeObjectStorageCapability) } + +func getCredentialDataFromRef(ctx context.Context, crClient K8sClient, credentialsRef corev1.SecretReference, defaultNamespace, key string) ([]byte, error) { + credSecret, err := getCredentials(ctx, crClient, credentialsRef, defaultNamespace) + if err != nil { + return nil, err + } + rawData, ok := credSecret.Data[key] + if !ok { + return nil, fmt.Errorf("no %s key in credentials secret %s/%s", key, credentialsRef.Namespace, credentialsRef.Name) + } + + return rawData, nil +} + +func getCredentials(ctx context.Context, crClient K8sClient, credentialsRef corev1.SecretReference, defaultNamespace string) (*corev1.Secret, error) { + secretRef := client.ObjectKey{ + Name: credentialsRef.Name, + Namespace: credentialsRef.Namespace, + } + if secretRef.Namespace == "" { + secretRef.Namespace = defaultNamespace + } + + var credSecret corev1.Secret + if err := crClient.Get(ctx, secretRef, &credSecret); err != nil { + return nil, fmt.Errorf("get credentials secret %s/%s: %w", secretRef.Namespace, secretRef.Name, err) + } + + return &credSecret, nil +}