@@ -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
@@ -820,6 +821,70 @@ func (mm *ModuleManager) RunModule(ctx context.Context, moduleName string, logLa
820821 return oldValuesChecksum != newValuesChecksum , nil
821822}
822823
824+ // EnsureConversionWebhooks renders the module's ConversionWebhook resources and
825+ // applies them to the cluster before the main helm install. The subsequent
826+ // release adopts the already-present resources. It is a no-op for non-helm
827+ // modules and for modules without ConversionWebhook templates.
828+ func (mm * ModuleManager ) EnsureConversionWebhooks (moduleName string , logLabels map [string ]string ) error {
829+ bm := mm .GetModule (moduleName )
830+ if bm == nil {
831+ return fmt .Errorf ("module %q not found" , moduleName )
832+ }
833+
834+ schemaStorage := bm .GetValuesStorage ().GetSchemaStorage ()
835+ deps := & modules.HelmModuleDependencies {
836+ HelmClientFactory : mm .dependencies .Helm ,
837+ HelmResourceManager : mm .dependencies .HelmResourcesManager ,
838+ MetricsStorage : mm .dependencies .MetricStorage ,
839+ HelmValuesValidator : schemaStorage ,
840+ }
841+
842+ helmModule , err := modules .NewHelmModule (bm , mm .defaultNamespace , mm .TempDir , deps , schemaStorage , modules .WithLogger (mm .logger .Named ("helm-module" )))
843+ if err != nil {
844+ if errors .Is (err , modules .ErrModuleIsNotHelm ) {
845+ return nil
846+ }
847+
848+ return fmt .Errorf ("create helm module: %w" , err )
849+ }
850+
851+ webhooks , err := helmModule .RenderConversionWebhooks (mm .defaultNamespace , bm .GetMaintenanceState ())
852+ if err != nil {
853+ return fmt .Errorf ("render conversion webhooks: %w" , err )
854+ }
855+
856+ if len (webhooks ) == 0 {
857+ return nil
858+ }
859+
860+ ops := make ([]sdkpkg.PatchCollectorOperation , 0 , len (webhooks ))
861+ for _ , m := range webhooks {
862+ ops = append (ops , objectpatch .NewCreateOrUpdateOperation (map [string ]any (m )))
863+ }
864+
865+ if err := mm .dependencies .KubeObjectPatcher .ExecuteOperations (ops ); err != nil {
866+ return fmt .Errorf ("apply conversion webhooks: %w" , err )
867+ }
868+
869+ utils .EnrichLoggerWithLabels (mm .logger , logLabels ).Debug ("applied conversion webhooks" ,
870+ slog .String (pkg .LogKeyModule , moduleName ),
871+ slog .Int (pkg .LogKeyCount , len (webhooks )))
872+
873+ return nil
874+ }
875+
876+ // ModuleHasConversionWebhooks reports whether the module's templates contain at
877+ // least one ConversionWebhook resource. It is a cheap static check used to skip
878+ // rendering for modules that have no conversion webhooks.
879+ func (mm * ModuleManager ) ModuleHasConversionWebhooks (moduleName string ) bool {
880+ bm := mm .GetModule (moduleName )
881+ if bm == nil {
882+ return false
883+ }
884+
885+ return bm .ConversionWebhookExist ()
886+ }
887+
823888func (mm * ModuleManager ) RunGlobalHook (ctx context.Context , hookName string , binding BindingType , bindingContext []BindingContext , logLabels map [string ]string ) (string , string , error ) {
824889 return mm .global .RunHookByName (ctx , hookName , binding , bindingContext , logLabels )
825890}
0 commit comments