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 21, 2023
1 parent 329753c commit 2da2063
Show file tree
Hide file tree
Showing 4 changed files with 230 additions and 39 deletions.
99 changes: 74 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,14 @@ 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)

patchName := clusterClassPatch.Name
// If this isn't an external patch, use the inline patch name.
if clusterClassPatch.External == nil {
patchName = clusterv1.VariableDefinitionFromInline
}
if err := addVariablesForPatch(blueprint, desired, req, patchName); err != nil {
return errors.Wrapf(err, "failed to calculate variables for patch %q", clusterClassPatch.Name)
}
log.V(5).Infof("Applying patch to templates")

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

// addVariablesForPatch adds variables for a given ClusterClassPatch to the items in the PatchRequest.
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 the Control Plane 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 _, md := range desired.MachineDeployments {
mdStateIndex[md.Object.Name] = md
}
for i, item := range req.Items {
// If the item is a Control Plane add the Control Plane variables.
if item.HolderReference.FieldPath == "spec.controlPlaneRef" {
item.Variables = controlPlaneVariables
}
// If the item holder reference is a Control Plane machine add the Control Plane variables.
if blueprint.HasControlPlaneInfrastructureMachine() &&
item.HolderReference.FieldPath == strings.Join(contract.ControlPlane().MachineTemplate().InfrastructureRef().Path(), ".") {
item.Variables = controlPlaneVariables
}
// If the item holder reference is a MachineDeployment calculate the variables for each MachineDeploymentTopology
// and add them to the variables for the MachineDeployment.
if item.HolderReference.Kind == "MachineDeployment" {
md, ok := mdStateIndex[item.HolderReference.Name]
if !ok {
return errors.Errorf("could not find desired state for MachineDeployment %s", tlog.KObj{Obj: md.Object})
}
mdTopology, err := getMDTopologyFromMD(blueprint, md.Object)
if err != nil {
return err
}

// 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
}

func getMDTopologyFromMD(blueprint *scope.ClusterBlueprint, md *clusterv1.MachineDeployment) (*clusterv1.MachineDeploymentTopology, error) {
topologyName, ok := md.Labels[clusterv1.ClusterTopologyMachineDeploymentNameLabel]
if !ok {
return nil, errors.Errorf("failed to get topology name for %s", tlog.KObj{Obj: md})
}
mdTopology, err := lookupMDTopology(blueprint.Topology, topologyName)
if err != nil {
return nil, err
}
return mdTopology, 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.
// 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.
// NOTE: This function does not add variables to items for the request.
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 +231,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 +239,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 +251,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 +273,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 +281,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 +291,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
2 changes: 2 additions & 0 deletions internal/controllers/topology/cluster/patches/engine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -697,6 +697,7 @@ func setupTestObjects() (*scope.ClusterBlueprint, *scope.ClusterState) {
MachineDeployments: map[string]*scope.MachineDeploymentState{
"default-worker-topo1": {
Object: builder.MachineDeployment(metav1.NamespaceDefault, "md1").
WithLabels(map[string]string{clusterv1.ClusterTopologyMachineDeploymentNameLabel: "default-worker-topo1"}).
WithVersion("v1.21.2").
Build(),
// Make sure we're using an independent instance of the template.
Expand All @@ -705,6 +706,7 @@ func setupTestObjects() (*scope.ClusterBlueprint, *scope.ClusterState) {
},
"default-worker-topo2": {
Object: builder.MachineDeployment(metav1.NamespaceDefault, "md2").
WithLabels(map[string]string{clusterv1.ClusterTopologyMachineDeploymentNameLabel: "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 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 = ""
)
Loading

0 comments on commit 2da2063

Please sign in to comment.