Skip to content

Commit

Permalink
Add filter to associate variables with specific patches
Browse files Browse the repository at this point in the history
  • Loading branch information
killianmuldoon committed Feb 17, 2023
1 parent 042a2a6 commit ce5ab5f
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 40 deletions.
83 changes: 58 additions & 25 deletions internal/controllers/topology/cluster/patches/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,15 @@ func (e *engine) Apply(ctx context.Context, blueprint *scope.ClusterBlueprint, d
clusterClassPatch := blueprint.ClusterClass.Spec.Patches[i]
ctx, log := log.WithValues("patch", clusterClassPatch.Name).Into(ctx)

var patchName string
if clusterClassPatch.External != nil {
patchName = clusterClassPatch.Name
} else {
patchName = clusterv1.VariableDefinitionFromInline
}
if err := addVariablesForPatch(blueprint, desired, req, patchName); err != nil {
return err
}
log.V(5).Infof("Applying patch to templates")

// Create patch generator for the current patch.
Expand Down Expand Up @@ -139,23 +148,63 @@ func (e *engine) Apply(ctx context.Context, blueprint *scope.ClusterBlueprint, d
return nil
}

// addVariablesForPatch creates a GeneratePatchesRequest based on the ClusterBlueprint and the desired state.
func addVariablesForPatch(blueprint *scope.ClusterBlueprint, desired *scope.ClusterState, req *runtimehooksv1.GeneratePatchesRequest, patchName string) error {
// Calculate global variables.
globalVariables, err := variables.Global(blueprint.Topology, desired.Cluster, patchName)
if err != nil {
return errors.Wrapf(err, "failed to calculate global variables")
}
req.Variables = globalVariables

// Calculate controlPlane variables.
controlPlaneVariables, err := variables.ControlPlane(&blueprint.Topology.ControlPlane, desired.ControlPlane.Object, desired.ControlPlane.InfrastructureMachineTemplate)
if err != nil {
return errors.Wrapf(err, "failed to calculate ControlPlane variables")
}

mdStateIndex := map[string]*scope.MachineDeploymentState{}
for mdTopologyName, md := range desired.MachineDeployments {
mdStateIndex[mdTopologyName] = md
}
for i, item := range req.Items {
if item.HolderReference.FieldPath == "spec.controlPlaneRef" {
item.Variables = controlPlaneVariables
}
if blueprint.HasControlPlaneInfrastructureMachine() &&
item.HolderReference.FieldPath == strings.Join(contract.ControlPlane().MachineTemplate().InfrastructureRef().Path(), ".") {
item.Variables = controlPlaneVariables
}
if item.HolderReference.Kind == "MachineDeployment" {
mdTopology, err := lookupMDTopology(blueprint.Topology, item.HolderReference.Name)
if err != nil {
return err
}
md, ok := mdStateIndex[item.HolderReference.Name]
if !ok {
return errors.Wrapf(err, "failed to calculate variables for %s", tlog.KObj{Obj: md.Object})
}
// Calculate MachineDeployment variables.
mdVariables, err := variables.MachineDeployment(mdTopology, md.Object, md.BootstrapTemplate, md.InfrastructureMachineTemplate, patchName)
if err != nil {
return errors.Wrapf(err, "failed to calculate variables for %s", tlog.KObj{Obj: md.Object})
}
item.Variables = mdVariables
}
req.Items[i] = item
}
return nil
}

// createRequest creates a GeneratePatchesRequest based on the ClusterBlueprint and the desired state.
// ClusterBlueprint supplies the templates. Desired state is used to calculate variables which are later used
// as input for the patch generation.
// ClusterBlueprint supplies the templates.
// NOTE: GenerateRequestTemplates are created for the templates of each individual MachineDeployment in the desired
// state. This is necessary because some builtin variables are MachineDeployment specific. For example version and
// replicas of a MachineDeployment.
// NOTE: A single GeneratePatchesRequest object is used to carry templates state across subsequent Generate calls.
func createRequest(blueprint *scope.ClusterBlueprint, desired *scope.ClusterState) (*runtimehooksv1.GeneratePatchesRequest, error) {
req := &runtimehooksv1.GeneratePatchesRequest{}

// Calculate global variables.
globalVariables, err := variables.Global(blueprint.Topology, desired.Cluster)
if err != nil {
return nil, errors.Wrapf(err, "failed to calculate global variables")
}
req.Variables = globalVariables

// Add the InfrastructureClusterTemplate.
t, err := newRequestItemBuilder(blueprint.InfrastructureClusterTemplate).
WithHolder(desired.Cluster, "spec.infrastructureRef").
Expand All @@ -166,12 +215,6 @@ func createRequest(blueprint *scope.ClusterBlueprint, desired *scope.ClusterStat
}
req.Items = append(req.Items, *t)

// Calculate controlPlane variables.
controlPlaneVariables, err := variables.ControlPlane(&blueprint.Topology.ControlPlane, desired.ControlPlane.Object, desired.ControlPlane.InfrastructureMachineTemplate)
if err != nil {
return nil, errors.Wrapf(err, "failed to calculate ControlPlane variables")
}

// Add the ControlPlaneTemplate.
t, err = newRequestItemBuilder(blueprint.ControlPlane.Template).
WithHolder(desired.Cluster, "spec.controlPlaneRef").
Expand All @@ -180,7 +223,6 @@ func createRequest(blueprint *scope.ClusterBlueprint, desired *scope.ClusterStat
return nil, errors.Wrapf(err, "failed to prepare ControlPlane template %s for patching",
tlog.KObj{Obj: blueprint.ControlPlane.Template})
}
t.Variables = controlPlaneVariables
req.Items = append(req.Items, *t)

// If the clusterClass mandates the controlPlane has infrastructureMachines,
Expand All @@ -193,7 +235,6 @@ func createRequest(blueprint *scope.ClusterBlueprint, desired *scope.ClusterStat
return nil, errors.Wrapf(err, "failed to prepare ControlPlane's machine template %s for patching",
tlog.KObj{Obj: blueprint.ControlPlane.InfrastructureMachineTemplate})
}
t.Variables = controlPlaneVariables
req.Items = append(req.Items, *t)
}

Expand All @@ -216,12 +257,6 @@ func createRequest(blueprint *scope.ClusterBlueprint, desired *scope.ClusterStat
return nil, errors.Errorf("failed to lookup MachineDeployment class %q in ClusterClass", mdTopology.Class)
}

// Calculate MachineDeployment variables.
mdVariables, err := variables.MachineDeployment(mdTopology, md.Object, md.BootstrapTemplate, md.InfrastructureMachineTemplate)
if err != nil {
return nil, errors.Wrapf(err, "failed to calculate variables for %s", tlog.KObj{Obj: md.Object})
}

// Add the BootstrapTemplate.
t, err := newRequestItemBuilder(mdClass.BootstrapTemplate).
WithHolder(md.Object, "spec.template.spec.bootstrap.configRef").
Expand All @@ -230,7 +265,6 @@ func createRequest(blueprint *scope.ClusterBlueprint, desired *scope.ClusterStat
return nil, errors.Wrapf(err, "failed to prepare BootstrapConfig template %s for MachineDeployment topology %s for patching",
tlog.KObj{Obj: mdClass.BootstrapTemplate}, mdTopologyName)
}
t.Variables = mdVariables
req.Items = append(req.Items, *t)

// Add the InfrastructureMachineTemplate.
Expand All @@ -241,7 +275,6 @@ func createRequest(blueprint *scope.ClusterBlueprint, desired *scope.ClusterStat
return nil, errors.Wrapf(err, "failed to prepare InfrastructureMachine template %s for MachineDeployment topology %s for patching",
tlog.KObj{Obj: mdClass.InfrastructureMachineTemplate}, mdTopologyName)
}
t.Variables = mdVariables
req.Items = append(req.Items, *t)
}

Expand Down
4 changes: 2 additions & 2 deletions internal/controllers/topology/cluster/patches/engine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -696,15 +696,15 @@ func setupTestObjects() (*scope.ClusterBlueprint, *scope.ClusterState) {
},
MachineDeployments: map[string]*scope.MachineDeploymentState{
"default-worker-topo1": {
Object: builder.MachineDeployment(metav1.NamespaceDefault, "md1").
Object: builder.MachineDeployment(metav1.NamespaceDefault, "default-worker-topo1").
WithVersion("v1.21.2").
Build(),
// Make sure we're using an independent instance of the template.
InfrastructureMachineTemplate: workerInfrastructureMachineTemplate.DeepCopy(),
BootstrapTemplate: workerBootstrapTemplate.DeepCopy(),
},
"default-worker-topo2": {
Object: builder.MachineDeployment(metav1.NamespaceDefault, "md2").
Object: builder.MachineDeployment(metav1.NamespaceDefault, "default-worker-topo2").
WithVersion("v1.20.6").
WithReplicas(5).
Build(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ type MachineDeploymentInfrastructureRefBuiltins struct {

// Global returns variables that apply to all the templates, including user provided variables
// and builtin variables for the Cluster object.
func Global(clusterTopology *clusterv1.Topology, cluster *clusterv1.Cluster) ([]runtimehooksv1.Variable, error) {
func Global(clusterTopology *clusterv1.Topology, cluster *clusterv1.Cluster, patchName string) ([]runtimehooksv1.Variable, error) {
variables := []runtimehooksv1.Variable{}

// Add user defined variables from Cluster.spec.topology.variables.
Expand All @@ -180,11 +180,13 @@ func Global(clusterTopology *clusterv1.Topology, cluster *clusterv1.Cluster) ([]
if variable.Name == BuiltinsName {
continue
}

variables = append(variables, runtimehooksv1.Variable{
Name: variable.Name,
Value: variable.Value,
})
// Add the variable only if it is defined for the current patch, or it is defined for all the patches.
if variable.DefinitionFrom == patchName || variable.DefinitionFrom == allPatchesDefinitionFrom {
variables = append(variables, runtimehooksv1.Variable{
Name: variable.Name,
Value: variable.Value,
})
}
}

// Construct builtin variable.
Expand Down Expand Up @@ -271,16 +273,19 @@ func ControlPlane(cpTopology *clusterv1.ControlPlaneTopology, cp, cpInfrastructu
}

// MachineDeployment returns variables that apply to templates belonging to a MachineDeployment.
func MachineDeployment(mdTopology *clusterv1.MachineDeploymentTopology, md *clusterv1.MachineDeployment, mdBootstrapTemplate, mdInfrastructureMachineTemplate *unstructured.Unstructured) ([]runtimehooksv1.Variable, error) {
func MachineDeployment(mdTopology *clusterv1.MachineDeploymentTopology, md *clusterv1.MachineDeployment, mdBootstrapTemplate, mdInfrastructureMachineTemplate *unstructured.Unstructured, patchName string) ([]runtimehooksv1.Variable, error) {
variables := []runtimehooksv1.Variable{}

// Add variables overrides for the MachineDeployment.
if mdTopology.Variables != nil {
for _, variable := range mdTopology.Variables.Overrides {
variables = append(variables, runtimehooksv1.Variable{
Name: variable.Name,
Value: variable.Value,
})
// Add the variable only if it is defined for the current patch, or it is defined for all the patches.
if variable.DefinitionFrom == patchName || variable.DefinitionFrom == allPatchesDefinitionFrom {
variables = append(variables, runtimehooksv1.Variable{
Name: variable.Name,
Value: variable.Value,
})
}
}
}

Expand Down Expand Up @@ -354,3 +359,7 @@ func ToMap(variables []runtimehooksv1.Variable) map[string]apiextensionsv1.JSON
}
return variablesMap
}

var (
allPatchesDefinitionFrom = ""
)
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ func TestGlobal(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
g := NewWithT(t)

got, err := Global(tt.clusterTopology, tt.cluster)
got, err := Global(tt.clusterTopology, tt.cluster, "")
g.Expect(err).ToNot(HaveOccurred())
g.Expect(got).To(Equal(tt.want))
})
Expand Down Expand Up @@ -676,7 +676,7 @@ func TestMachineDeployment(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
g := NewWithT(t)

got, err := MachineDeployment(tt.mdTopology, tt.md, tt.mdBootstrapTemplate, tt.mdInfrastructureMachineTemplate)
got, err := MachineDeployment(tt.mdTopology, tt.md, tt.mdBootstrapTemplate, tt.mdInfrastructureMachineTemplate, "")
g.Expect(err).ToNot(HaveOccurred())
g.Expect(got).To(Equal(tt.want))
})
Expand Down

0 comments on commit ce5ab5f

Please sign in to comment.