Skip to content

Commit

Permalink
Add ClusterClass patch engine
Browse files Browse the repository at this point in the history
Signed-off-by: Stefan Büringer [email protected]

Co-authored-by: fabriziopandini <[email protected]>
  • Loading branch information
sbueringer and fabriziopandini committed Nov 4, 2021
1 parent de11b77 commit bf6dccd
Show file tree
Hide file tree
Showing 12 changed files with 2,159 additions and 11 deletions.
27 changes: 18 additions & 9 deletions controllers/topology/desired_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,17 @@ import (
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
"sigs.k8s.io/cluster-api/controllers/external"
"sigs.k8s.io/cluster-api/controllers/topology/internal/contract"
"sigs.k8s.io/cluster-api/controllers/topology/internal/extensions/patches"
tlog "sigs.k8s.io/cluster-api/controllers/topology/internal/log"
"sigs.k8s.io/cluster-api/controllers/topology/internal/scope"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
)

// computeDesiredState computes the desired state of the cluster topology.
// NOTE: We are assuming all the required objects are provided as input; also, in case of any error,
// the entire compute operation operation will fail. This might be improved in the future if support for reconciling
// the entire compute operation will fail. This might be improved in the future if support for reconciling
// subset of a topology will be implemented.
// NOTE: We have to make sure all spec fields we explicitly set in desired objects are preserved during patching.
func (r *ClusterReconciler) computeDesiredState(ctx context.Context, s *scope.Scope) (*scope.ClusterState, error) {
var err error
desiredState := &scope.ClusterState{
Expand Down Expand Up @@ -66,16 +68,23 @@ func (r *ClusterReconciler) computeDesiredState(ctx context.Context, s *scope.Sc
// InfrastructureCluster and the ControlPlane objects generated by the previous step.
desiredState.Cluster = computeCluster(ctx, s, desiredState.InfrastructureCluster, desiredState.ControlPlane.Object)

// If required by the blueprint, compute the desired state of the MachineDeployment objects for the worker nodes, if any.
if !s.Blueprint.HasMachineDeployments() {
return desiredState, nil
// If required, compute the desired state of the MachineDeployments from the list of MachineDeploymentTopologies
// defined in the cluster.
if s.Blueprint.HasMachineDeployments() {
desiredState.MachineDeployments, err = computeMachineDeployments(ctx, s, desiredState.ControlPlane)
if err != nil {
return nil, err
}
}

// Compute the desired state of the MachineDeployments from the list of MachineDeploymentTopologies
// defined in the cluster.
desiredState.MachineDeployments, err = computeMachineDeployments(ctx, s, desiredState.ControlPlane)
if err != nil {
return nil, err
// Apply patches the desired state according to the patches from the ClusterClass, variables from the Cluster
// and builtin variables.
// NOTE: We have to make sure all spec fields that were explicitly set in desired objects during the computation above
// are preserved during patching. When desired objects are computed their spec is copied from a template, in some cases
// further modifications to the spec are made afterwards. In those cases we have to make sure those fields are not overwritten
// in apply patches. Some examples are .spec.machineTemplate and .spec.version in control planes.
if err := patches.Apply(ctx, s.Blueprint, desiredState); err != nil {
return nil, errors.Wrap(err, "failed to apply patches")
}

return desiredState, nil
Expand Down
149 changes: 149 additions & 0 deletions controllers/topology/internal/extensions/patches/api/interface.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
/*
Copyright 2021 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// Package api contains the API definition for the patch engine.
package api

import (
"context"

apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
)

// Generator defines a component that can generate patches for ClusterClass templates.
// NOTE: We are introducing this interface as a decoupling layer between the patch engine and the concrete components
// responsible for generating patches, because we aim to provide support for external patches in a future iteration.
// We also assume that this interface and all the related types will be moved in a separated (versioned) package thus
// providing a versioned contract between Cluster API and the components implementing external patch extensions.
type Generator interface {
// Generate generates patches for templates.
// GenerateRequest contains templates and the corresponding variables.
// GenerateResponse contains the patches which should be applied to the templates of the GenerateRequest.
Generate(ctx context.Context, request *GenerateRequest) (*GenerateResponse, error)
}

// GenerateRequest defines the input for a Generate request.
type GenerateRequest struct {
// Variables is a name/value map containing variables.
Variables map[string]apiextensionsv1.JSON

// Items contains the list of templates to generate patches for.
Items []*GenerateRequestTemplate
}

// GenerateRequestTemplate defines one of the ClusterClass templates to generate patches for.
type GenerateRequestTemplate struct {
// TemplateID identifies a template to generate patches for;
// the same TemplateID must be used when specifying to which template a generated patch should be applied to.
TemplateID TemplateID

// Variables is a name/value map containing variables specifically for the current template.
// For example some builtin variables like MachineDeployment replicas and version are context-sensitive
// and thus are only added to templates for MachineDeployments and with values which correspond to the
// current MachineDeployment.
Variables map[string]apiextensionsv1.JSON

// Template contains the template.
Template apiextensionsv1.JSON
}

// TemplateID identifies one of the ClusterClass templates to generate patches for;
// the same TemplateID must be used when specifying where a generated patch should apply to.
type TemplateID struct {
// APIVersion of the current template.
APIVersion string

// Kind of the current template.
Kind string

// TargetType defines where the template is used.
TargetType TargetType

// MachineDeployment specifies the MachineDeployment in which the template is used.
// This field is only set if the template is used in the context of a MachineDeployment.
MachineDeployment MachineDeploymentID
}

// MachineDeploymentID specifies the MachineDeployment in which the template is used.
type MachineDeploymentID struct {
// TopologyName is the name of the MachineDeploymentTopology.
TopologyName string

// Class is the name of the MachineDeploymentClass.
Class string
}

// TargetType define the type for target types enum.
type TargetType int32

const (
// InfrastructureClusterTemplateTargetType identifies a template for the InfrastructureCluster object.
InfrastructureClusterTemplateTargetType TargetType = iota

// ControlPlaneTemplateTargetType identifies a template for the ControlPlane object.
ControlPlaneTemplateTargetType

// ControlPlaneInfrastructureMachineTemplateTargetType identifies a template for the InfrastructureMachines to be used for the ControlPlane object.
ControlPlaneInfrastructureMachineTemplateTargetType

// MachineDeploymentBootstrapConfigTemplateTargetType identifies a template for the BootstrapConfig to be used for a MachineDeployment object.
MachineDeploymentBootstrapConfigTemplateTargetType

// MachineDeploymentInfrastructureMachineTemplateTargetType identifies a template for the InfrastructureMachines to be used for a MachineDeployment object.
MachineDeploymentInfrastructureMachineTemplateTargetType
)

var (
// TargetTypeToName maps from a TargetType to its string representation.
TargetTypeToName = map[TargetType]string{
InfrastructureClusterTemplateTargetType: "InfrastructureClusterTemplate",
ControlPlaneTemplateTargetType: "ControlPlaneTemplate",
ControlPlaneInfrastructureMachineTemplateTargetType: "ControlPlane/InfrastructureMachineTemplate",
MachineDeploymentBootstrapConfigTemplateTargetType: "MachineDeployment/BootstrapConfigTemplate",
MachineDeploymentInfrastructureMachineTemplateTargetType: "MachineDeployment/InfrastructureMachineTemplate",
}
)

// PatchType define the type for patch types enum.
type PatchType int32

const (
// JSONPatchType identifies a https://datatracker.ietf.org/doc/html/rfc6902 json patch.
JSONPatchType PatchType = 0

// JSONMergePatchType identifies a https://datatracker.ietf.org/doc/html/rfc7386 json merge patch.
JSONMergePatchType PatchType = 1
)

// GenerateResponse defines the response of a Generate request.
// NOTE: Patches defined in GenerateResponse will be applied to the original GenerateRequest object, thus
// adding changes on templates across all the subsequent Generate calls.
type GenerateResponse struct {
// Items contains the list of generated patches.
Items []GenerateResponsePatch
}

// GenerateResponsePatch defines a Patch targeting a specific GenerateRequestTemplate.
type GenerateResponsePatch struct {
// TemplateID identifies the template the patch should apply to.
TemplateID TemplateID

// Patch contains the patch.
Patch apiextensionsv1.JSON

// Patch defines the type of the JSON patch.
PatchType PatchType
}
Loading

0 comments on commit bf6dccd

Please sign in to comment.