@@ -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
@@ -853,6 +854,70 @@ func (mm *ModuleManager) RunModule(ctx context.Context, moduleName string, logLa
853854 return oldValuesChecksum != newValuesChecksum , nil
854855}
855856
857+ // EnsureConversionWebhooks renders the module's ConversionWebhook resources and
858+ // applies them to the cluster before the main helm install. The subsequent
859+ // release adopts the already-present resources. It is a no-op for non-helm
860+ // modules and for modules without ConversionWebhook templates.
861+ func (mm * ModuleManager ) EnsureConversionWebhooks (moduleName string , logLabels map [string ]string ) error {
862+ bm := mm .GetModule (moduleName )
863+ if bm == nil {
864+ return fmt .Errorf ("module %q not found" , moduleName )
865+ }
866+
867+ schemaStorage := bm .GetValuesStorage ().GetSchemaStorage ()
868+ deps := & modules.HelmModuleDependencies {
869+ HelmClientFactory : mm .dependencies .Helm ,
870+ HelmResourceManager : mm .dependencies .HelmResourcesManager ,
871+ MetricsStorage : mm .dependencies .MetricStorage ,
872+ HelmValuesValidator : schemaStorage ,
873+ }
874+
875+ helmModule , err := modules .NewHelmModule (bm , mm .defaultNamespace , mm .TempDir , deps , schemaStorage , modules .WithLogger (mm .logger .Named ("helm-module" )))
876+ if err != nil {
877+ if errors .Is (err , modules .ErrModuleIsNotHelm ) {
878+ return nil
879+ }
880+
881+ return fmt .Errorf ("create helm module: %w" , err )
882+ }
883+
884+ webhooks , err := helmModule .RenderConversionWebhooks (mm .defaultNamespace , bm .GetMaintenanceState ())
885+ if err != nil {
886+ return fmt .Errorf ("render conversion webhooks: %w" , err )
887+ }
888+
889+ if len (webhooks ) == 0 {
890+ return nil
891+ }
892+
893+ ops := make ([]sdkpkg.PatchCollectorOperation , 0 , len (webhooks ))
894+ for _ , m := range webhooks {
895+ ops = append (ops , objectpatch .NewCreateOrUpdateOperation (map [string ]any (m )))
896+ }
897+
898+ if err := mm .dependencies .KubeObjectPatcher .ExecuteOperations (ops ); err != nil {
899+ return fmt .Errorf ("apply conversion webhooks: %w" , err )
900+ }
901+
902+ utils .EnrichLoggerWithLabels (mm .logger , logLabels ).Debug ("applied conversion webhooks" ,
903+ slog .String (pkg .LogKeyModule , moduleName ),
904+ slog .Int (pkg .LogKeyCount , len (webhooks )))
905+
906+ return nil
907+ }
908+
909+ // ModuleHasConversionWebhooks reports whether the module's templates contain at
910+ // least one ConversionWebhook resource. It is a cheap static check used to skip
911+ // rendering for modules that have no conversion webhooks.
912+ func (mm * ModuleManager ) ModuleHasConversionWebhooks (moduleName string ) bool {
913+ bm := mm .GetModule (moduleName )
914+ if bm == nil {
915+ return false
916+ }
917+
918+ return bm .ConversionWebhookExist ()
919+ }
920+
856921func (mm * ModuleManager ) RunGlobalHook (ctx context.Context , hookName string , binding BindingType , bindingContext []BindingContext , logLabels map [string ]string ) (string , string , error ) {
857922 return mm .global .RunHookByName (ctx , hookName , binding , bindingContext , logLabels )
858923}
0 commit comments