@@ -13,6 +13,7 @@ import (
1313
1414 "github.com/deckhouse/deckhouse/pkg/log"
1515 metricsstorage "github.com/deckhouse/deckhouse/pkg/metrics-storage"
16+ sdkpkg "github.com/deckhouse/module-sdk/pkg"
1617 "github.com/hashicorp/go-multierror"
1718 "go.opentelemetry.io/otel"
1819
@@ -867,6 +868,70 @@ func (mm *ModuleManager) RunModule(ctx context.Context, moduleName string, logLa
867868 return oldValuesChecksum != newValuesChecksum , nil
868869}
869870
871+ // EnsureConversionWebhooks renders the module's ConversionWebhook resources and
872+ // applies them to the cluster before the main helm install. The subsequent
873+ // release adopts the already-present resources. It is a no-op for non-helm
874+ // modules and for modules without ConversionWebhook templates.
875+ func (mm * ModuleManager ) EnsureConversionWebhooks (moduleName string , logLabels map [string ]string ) error {
876+ bm := mm .GetModule (moduleName )
877+ if bm == nil {
878+ return fmt .Errorf ("module %q not found" , moduleName )
879+ }
880+
881+ schemaStorage := bm .GetValuesStorage ().GetSchemaStorage ()
882+ deps := & modules.HelmModuleDependencies {
883+ HelmClientFactory : mm .dependencies .Helm ,
884+ HelmResourceManager : mm .dependencies .HelmResourcesManager ,
885+ MetricsStorage : mm .dependencies .MetricStorage ,
886+ HelmValuesValidator : schemaStorage ,
887+ }
888+
889+ helmModule , err := modules .NewHelmModule (bm , mm .defaultNamespace , mm .TempDir , deps , schemaStorage , modules .WithLogger (mm .logger .Named ("helm-module" )))
890+ if err != nil {
891+ if errors .Is (err , modules .ErrModuleIsNotHelm ) {
892+ return nil
893+ }
894+
895+ return fmt .Errorf ("create helm module: %w" , err )
896+ }
897+
898+ webhooks , err := helmModule .RenderConversionWebhooks (mm .defaultNamespace , bm .GetMaintenanceState ())
899+ if err != nil {
900+ return fmt .Errorf ("render conversion webhooks: %w" , err )
901+ }
902+
903+ if len (webhooks ) == 0 {
904+ return nil
905+ }
906+
907+ ops := make ([]sdkpkg.PatchCollectorOperation , 0 , len (webhooks ))
908+ for _ , m := range webhooks {
909+ ops = append (ops , objectpatch .NewCreateOrUpdateOperation (map [string ]any (m )))
910+ }
911+
912+ if err := mm .dependencies .KubeObjectPatcher .ExecuteOperations (ops ); err != nil {
913+ return fmt .Errorf ("apply conversion webhooks: %w" , err )
914+ }
915+
916+ utils .EnrichLoggerWithLabels (mm .logger , logLabels ).Debug ("applied conversion webhooks" ,
917+ slog .String (pkg .LogKeyModule , moduleName ),
918+ slog .Int (pkg .LogKeyCount , len (webhooks )))
919+
920+ return nil
921+ }
922+
923+ // ModuleHasConversionWebhooks reports whether the module's templates contain at
924+ // least one ConversionWebhook resource. It is a cheap static check used to skip
925+ // rendering for modules that have no conversion webhooks.
926+ func (mm * ModuleManager ) ModuleHasConversionWebhooks (moduleName string ) bool {
927+ bm := mm .GetModule (moduleName )
928+ if bm == nil {
929+ return false
930+ }
931+
932+ return bm .ConversionWebhookExist ()
933+ }
934+
870935func (mm * ModuleManager ) RunGlobalHook (ctx context.Context , hookName string , binding BindingType , bindingContext []BindingContext , logLabels map [string ]string ) (string , string , error ) {
871936 return mm .global .RunHookByName (ctx , hookName , binding , bindingContext , logLabels )
872937}
0 commit comments