From d6a5fa868a98c8df93b3816980a2f55c65d08894 Mon Sep 17 00:00:00 2001 From: faiq Date: Tue, 9 Apr 2024 14:22:48 -0600 Subject: [PATCH] feat: adds a validation hook --- api/variables/variables.go | 29 +++++++ cmd/main.go | 3 + pkg/handlers/generic/validation/handlers.go | 20 +++++ .../generic/validation/helm/handler.go | 78 +++++++++++++++++++ 4 files changed, 130 insertions(+) create mode 100644 api/variables/variables.go create mode 100644 pkg/handlers/generic/validation/handlers.go create mode 100644 pkg/handlers/generic/validation/helm/handler.go diff --git a/api/variables/variables.go b/api/variables/variables.go new file mode 100644 index 000000000..0a769a3ed --- /dev/null +++ b/api/variables/variables.go @@ -0,0 +1,29 @@ +package variables + +import ( + "encoding/json" + "fmt" + + runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1" +) + +func UnmarshalRuntimeVariable[T any](runtimeVariable *runtimehooksv1.Variable, obj *T) error { + err := json.Unmarshal(runtimeVariable.Value.Raw, obj) + if err != nil { + return fmt.Errorf("error unmarshalling variable: %w", err) + } + + return nil +} + +func GetRuntimhookVariableByName( + name string, + variables []runtimehooksv1.Variable, +) (*runtimehooksv1.Variable, int) { + for i, runtimevar := range variables { + if runtimevar.Name == name { + return &runtimevar, i + } + } + return nil, -1 +} diff --git a/cmd/main.go b/cmd/main.go index f43f3ff7e..bf46db546 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -34,6 +34,7 @@ import ( dockermutation "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/docker/mutation" dockerworkerconfig "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/docker/workerconfig" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/lifecycle" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/validation" nutanixclusterconfig "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/nutanix/clusterconfig" nutanixmutation "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/nutanix/mutation" nutanixworkerconfig "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/nutanix/workerconfig" @@ -84,6 +85,7 @@ func main() { genericLifecycleHandlers := lifecycle.New(globalOptions) + validationHandlers := validation.New() // Initialize and parse command line flags. logs.AddFlags(pflag.CommandLine, logs.SkipLoggingConfigurationFlags()) logsv1.AddFlags(logOptions, pflag.CommandLine) @@ -142,6 +144,7 @@ func main() { } var allHandlers []handlers.Named + allHandlers = append(allHandlers, validationHandlers.AllHandlers(mgr)...) allHandlers = append(allHandlers, genericLifecycleHandlers.AllHandlers(mgr)...) allHandlers = append(allHandlers, awsMetaHandlers...) allHandlers = append(allHandlers, dockerMetaHandlers...) diff --git a/pkg/handlers/generic/validation/handlers.go b/pkg/handlers/generic/validation/handlers.go new file mode 100644 index 000000000..be422ff7f --- /dev/null +++ b/pkg/handlers/generic/validation/handlers.go @@ -0,0 +1,20 @@ +package validation + +import ( + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/handlers" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/validation/helm" + "sigs.k8s.io/controller-runtime/pkg/manager" +) + +type Handlers struct{} + +func New() *Handlers { + return &Handlers{} +} + +func (h *Handlers) AllHandlers(mgr manager.Manager) []handlers.Named { + validationHandler := helm.New(mgr.GetClient()) + return []handlers.Named{ + validationHandler, + } +} diff --git a/pkg/handlers/generic/validation/helm/handler.go b/pkg/handlers/generic/validation/helm/handler.go new file mode 100644 index 000000000..640d3d5af --- /dev/null +++ b/pkg/handlers/generic/validation/helm/handler.go @@ -0,0 +1,78 @@ +package helm + +import ( + "context" + "crypto/tls" + "fmt" + "net/http" + + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/api/v1alpha1" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/api/variables" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/clusterconfig" + runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1" + ctrl "sigs.k8s.io/controller-runtime" + ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" +) + +type HelmRegistryValidator struct { + client ctrlclient.Client + variableName string +} + +func New( + c ctrlclient.Client, +) *HelmRegistryValidator { + return &HelmRegistryValidator{ + client: c, + variableName: clusterconfig.MetaVariableName, + } +} + +func (h *HelmRegistryValidator) Name() string { + return "HelmRegistryValidator" +} + +func (h *HelmRegistryValidator) ValidateTopology( + ctx context.Context, + req *runtimehooksv1.ValidateTopologyRequest, + res *runtimehooksv1.ValidateTopologyResponse, +) { + log := ctrl.LoggerFrom(ctx) + clusterVar, ind := variables.GetRuntimhookVariableByName(h.variableName, req.Variables) + if ind == -1 { + log.V(5).Info(fmt.Sprintf("did not find variable %s in %v", h.variableName, req.Variables)) + return + } + var cluster v1alpha1.ClusterConfig + if err := variables.UnmarshalRuntimeVariable[v1alpha1.ClusterConfig](clusterVar, &cluster); err != nil { + failString := fmt.Sprintf("failed to unmarshal variable %v to clusterConfig", clusterVar) + log.Error(err, failString) + res.SetStatus(runtimehooksv1.ResponseStatusFailure) + res.SetMessage(failString) + return + } + helmChartRepo := cluster.Spec.Addons.HelmChartRepository + cl := &http.Client{ + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + }, + } + resp, err := cl.Get(fmt.Sprintf("%s/v2", *helmChartRepo)) + if err != nil { + failString := fmt.Sprintf("failed to ping provided helm registry %s", *helmChartRepo) + log.Error(err, failString) + res.SetStatus(runtimehooksv1.ResponseStatusFailure) + res.SetMessage(failString) + return + } + defer resp.Body.Close() + if resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusUnauthorized { + res.SetStatus(runtimehooksv1.ResponseStatusSuccess) + return + } + failString := fmt.Sprintf("failed to get 401 or 200 response from hitting registry: %s got status: %d", *helmChartRepo, resp.StatusCode) + log.Error(err, failString) + res.SetStatus(runtimehooksv1.ResponseStatusFailure) + res.SetMessage(failString) + return +}