@@ -26,6 +26,7 @@ import (
2626 jsonpatch "github.com/evanphx/json-patch/v5"
2727 "github.com/pkg/errors"
2828 kerrors "k8s.io/apimachinery/pkg/util/errors"
29+ "k8s.io/klog/v2"
2930
3031 clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
3132 runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1"
@@ -83,6 +84,14 @@ func (e *engine) Apply(ctx context.Context, blueprint *scope.ClusterBlueprint, d
8384 clusterClassPatch := blueprint .ClusterClass .Spec .Patches [i ]
8485 ctx , log := log .WithValues ("patch" , clusterClassPatch .Name ).Into (ctx )
8586
87+ definitionFrom := clusterClassPatch .Name
88+ // If this isn't an external patch, use the inline patch name.
89+ if clusterClassPatch .External == nil {
90+ definitionFrom = clusterv1 .VariableDefinitionFromInline
91+ }
92+ if err := addVariablesForPatch (blueprint , desired , req , definitionFrom ); err != nil {
93+ return errors .Wrapf (err , "failed to calculate variables for patch %q" , clusterClassPatch .Name )
94+ }
8695 log .V (5 ).Infof ("Applying patch to templates" )
8796
8897 // Create patch generator for the current patch.
@@ -139,23 +148,86 @@ func (e *engine) Apply(ctx context.Context, blueprint *scope.ClusterBlueprint, d
139148 return nil
140149}
141150
151+ // addVariablesForPatch adds variables for a given ClusterClassPatch to the items in the PatchRequest.
152+ func addVariablesForPatch (blueprint * scope.ClusterBlueprint , desired * scope.ClusterState , req * runtimehooksv1.GeneratePatchesRequest , definitionFrom string ) error {
153+ // If there is no definitionFrom return an error.
154+ if definitionFrom == "" {
155+ return errors .New ("failed to calculate variables: no patch name provided" )
156+ }
157+
158+ patchVariableDefinitions := definitionsForPatch (blueprint , definitionFrom )
159+ // Calculate global variables.
160+ globalVariables , err := variables .Global (blueprint .Topology , desired .Cluster , definitionFrom , patchVariableDefinitions )
161+ if err != nil {
162+ return errors .Wrapf (err , "failed to calculate global variables" )
163+ }
164+ req .Variables = globalVariables
165+
166+ // Calculate the Control Plane variables.
167+ controlPlaneVariables , err := variables .ControlPlane (& blueprint .Topology .ControlPlane , desired .ControlPlane .Object , desired .ControlPlane .InfrastructureMachineTemplate )
168+ if err != nil {
169+ return errors .Wrapf (err , "failed to calculate ControlPlane variables" )
170+ }
171+
172+ mdStateIndex := map [string ]* scope.MachineDeploymentState {}
173+ for _ , md := range desired .MachineDeployments {
174+ mdStateIndex [md .Object .Name ] = md
175+ }
176+ for i , item := range req .Items {
177+ // If the item is a Control Plane add the Control Plane variables.
178+ if item .HolderReference .FieldPath == "spec.controlPlaneRef" {
179+ item .Variables = controlPlaneVariables
180+ }
181+ // If the item holder reference is a Control Plane machine add the Control Plane variables.
182+ if blueprint .HasControlPlaneInfrastructureMachine () &&
183+ item .HolderReference .FieldPath == strings .Join (contract .ControlPlane ().MachineTemplate ().InfrastructureRef ().Path (), "." ) {
184+ item .Variables = controlPlaneVariables
185+ }
186+ // If the item holder reference is a MachineDeployment calculate the variables for each MachineDeploymentTopology
187+ // and add them to the variables for the MachineDeployment.
188+ if item .HolderReference .Kind == "MachineDeployment" {
189+ md , ok := mdStateIndex [item .HolderReference .Name ]
190+ if ! ok {
191+ return errors .Errorf ("could not find desired state for MachineDeployment %s" , klog .KObj (md .Object ))
192+ }
193+ mdTopology , err := getMDTopologyFromMD (blueprint , md .Object )
194+ if err != nil {
195+ return err
196+ }
197+
198+ // Calculate MachineDeployment variables.
199+ mdVariables , err := variables .MachineDeployment (mdTopology , md .Object , md .BootstrapTemplate , md .InfrastructureMachineTemplate , definitionFrom , patchVariableDefinitions )
200+ if err != nil {
201+ return errors .Wrapf (err , "failed to calculate variables for %s" , klog .KObj (md .Object ))
202+ }
203+ item .Variables = mdVariables
204+ }
205+ req .Items [i ] = item
206+ }
207+ return nil
208+ }
209+
210+ func getMDTopologyFromMD (blueprint * scope.ClusterBlueprint , md * clusterv1.MachineDeployment ) (* clusterv1.MachineDeploymentTopology , error ) {
211+ topologyName , ok := md .Labels [clusterv1 .ClusterTopologyMachineDeploymentNameLabel ]
212+ if ! ok {
213+ return nil , errors .Errorf ("failed to get topology name for %s" , klog .KObj (md ))
214+ }
215+ mdTopology , err := lookupMDTopology (blueprint .Topology , topologyName )
216+ if err != nil {
217+ return nil , err
218+ }
219+ return mdTopology , nil
220+ }
221+
142222// createRequest creates a GeneratePatchesRequest based on the ClusterBlueprint and the desired state.
143- // ClusterBlueprint supplies the templates. Desired state is used to calculate variables which are later used
144- // as input for the patch generation.
145223// NOTE: GenerateRequestTemplates are created for the templates of each individual MachineDeployment in the desired
146224// state. This is necessary because some builtin variables are MachineDeployment specific. For example version and
147225// replicas of a MachineDeployment.
148226// NOTE: A single GeneratePatchesRequest object is used to carry templates state across subsequent Generate calls.
227+ // NOTE: This function does not add variables to items for the request, as the variables depend on the specific patch.
149228func createRequest (blueprint * scope.ClusterBlueprint , desired * scope.ClusterState ) (* runtimehooksv1.GeneratePatchesRequest , error ) {
150229 req := & runtimehooksv1.GeneratePatchesRequest {}
151230
152- // Calculate global variables.
153- globalVariables , err := variables .Global (blueprint .Topology , desired .Cluster )
154- if err != nil {
155- return nil , errors .Wrapf (err , "failed to calculate global variables" )
156- }
157- req .Variables = globalVariables
158-
159231 // Add the InfrastructureClusterTemplate.
160232 t , err := newRequestItemBuilder (blueprint .InfrastructureClusterTemplate ).
161233 WithHolder (desired .Cluster , "spec.infrastructureRef" ).
@@ -166,12 +238,6 @@ func createRequest(blueprint *scope.ClusterBlueprint, desired *scope.ClusterStat
166238 }
167239 req .Items = append (req .Items , * t )
168240
169- // Calculate controlPlane variables.
170- controlPlaneVariables , err := variables .ControlPlane (& blueprint .Topology .ControlPlane , desired .ControlPlane .Object , desired .ControlPlane .InfrastructureMachineTemplate )
171- if err != nil {
172- return nil , errors .Wrapf (err , "failed to calculate ControlPlane variables" )
173- }
174-
175241 // Add the ControlPlaneTemplate.
176242 t , err = newRequestItemBuilder (blueprint .ControlPlane .Template ).
177243 WithHolder (desired .Cluster , "spec.controlPlaneRef" ).
@@ -180,7 +246,6 @@ func createRequest(blueprint *scope.ClusterBlueprint, desired *scope.ClusterStat
180246 return nil , errors .Wrapf (err , "failed to prepare ControlPlane template %s for patching" ,
181247 tlog.KObj {Obj : blueprint .ControlPlane .Template })
182248 }
183- t .Variables = controlPlaneVariables
184249 req .Items = append (req .Items , * t )
185250
186251 // If the clusterClass mandates the controlPlane has infrastructureMachines,
@@ -193,7 +258,6 @@ func createRequest(blueprint *scope.ClusterBlueprint, desired *scope.ClusterStat
193258 return nil , errors .Wrapf (err , "failed to prepare ControlPlane's machine template %s for patching" ,
194259 tlog.KObj {Obj : blueprint .ControlPlane .InfrastructureMachineTemplate })
195260 }
196- t .Variables = controlPlaneVariables
197261 req .Items = append (req .Items , * t )
198262 }
199263
@@ -216,12 +280,6 @@ func createRequest(blueprint *scope.ClusterBlueprint, desired *scope.ClusterStat
216280 return nil , errors .Errorf ("failed to lookup MachineDeployment class %q in ClusterClass" , mdTopology .Class )
217281 }
218282
219- // Calculate MachineDeployment variables.
220- mdVariables , err := variables .MachineDeployment (mdTopology , md .Object , md .BootstrapTemplate , md .InfrastructureMachineTemplate )
221- if err != nil {
222- return nil , errors .Wrapf (err , "failed to calculate variables for %s" , tlog.KObj {Obj : md .Object })
223- }
224-
225283 // Add the BootstrapTemplate.
226284 t , err := newRequestItemBuilder (mdClass .BootstrapTemplate ).
227285 WithHolder (md .Object , "spec.template.spec.bootstrap.configRef" ).
@@ -230,7 +288,6 @@ func createRequest(blueprint *scope.ClusterBlueprint, desired *scope.ClusterStat
230288 return nil , errors .Wrapf (err , "failed to prepare BootstrapConfig template %s for MachineDeployment topology %s for patching" ,
231289 tlog.KObj {Obj : mdClass .BootstrapTemplate }, mdTopologyName )
232290 }
233- t .Variables = mdVariables
234291 req .Items = append (req .Items , * t )
235292
236293 // Add the InfrastructureMachineTemplate.
@@ -241,7 +298,6 @@ func createRequest(blueprint *scope.ClusterBlueprint, desired *scope.ClusterStat
241298 return nil , errors .Wrapf (err , "failed to prepare InfrastructureMachine template %s for MachineDeployment topology %s for patching" ,
242299 tlog.KObj {Obj : mdClass .InfrastructureMachineTemplate }, mdTopologyName )
243300 }
244- t .Variables = mdVariables
245301 req .Items = append (req .Items , * t )
246302 }
247303
@@ -431,3 +487,16 @@ func updateDesiredState(ctx context.Context, req *runtimehooksv1.GeneratePatches
431487
432488 return nil
433489}
490+
491+ // definitionsForPatch returns a set which of variables in a ClusterClass defined by "definitionFrom".
492+ func definitionsForPatch (blueprint * scope.ClusterBlueprint , definitionFrom string ) map [string ]bool {
493+ variableDefinitionsForPatch := make (map [string ]bool )
494+ for _ , definitionsWithName := range blueprint .ClusterClass .Status .Variables {
495+ for _ , definition := range definitionsWithName .Definitions {
496+ if definition .From == definitionFrom {
497+ variableDefinitionsForPatch [definitionsWithName .Name ] = true
498+ }
499+ }
500+ }
501+ return variableDefinitionsForPatch
502+ }
0 commit comments