Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support patch type in Stage #1017

Merged
merged 1 commit into from
May 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ require (
github.com/containernetworking/plugins v1.4.0
github.com/creack/pty v1.1.21
github.com/emicklei/go-restful/v3 v3.11.2
github.com/evanphx/json-patch/v5 v5.9.0
github.com/fsnotify/fsnotify v1.7.0
github.com/google/cel-go v0.17.7
github.com/google/go-cmp v0.6.0
Expand Down Expand Up @@ -63,7 +64,6 @@ require (
github.com/docker/docker v24.0.9+incompatible // indirect
github.com/docker/docker-credential-helpers v0.7.0 // indirect
github.com/evanphx/json-patch v5.6.0+incompatible // indirect
github.com/evanphx/json-patch/v5 v5.8.0 // indirect
github.com/fatih/color v1.16.0 // indirect
github.com/go-errors/errors v1.4.2 // indirect
github.com/go-logr/logr v1.4.1 // indirect
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ github.com/emicklei/go-restful/v3 v3.11.2 h1:1onLa9DcsMYO9P+CXaL0dStDqQ2EHHXLiz+
github.com/emicklei/go-restful/v3 v3.11.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U=
github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/evanphx/json-patch/v5 v5.8.0 h1:lRj6N9Nci7MvzrXuX6HFzU8XjmhPiXPlsKEy1u0KQro=
github.com/evanphx/json-patch/v5 v5.8.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ=
github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg=
github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ=
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
Expand Down
7 changes: 7 additions & 0 deletions kustomize/crd/bases/kwok.x-k8s.io_stages.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,13 @@ spec:
description: Template indicates the template for modifying
the resource in the next.
type: string
type:
description: Type indicates the type of the patch.
enum:
- json
- merge
- strategic
type: string
type: object
type: array
statusPatchAs:
Expand Down
14 changes: 14 additions & 0 deletions pkg/apis/internalversion/stage_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,13 +93,27 @@ type StagePatch struct {
Root string
// Template indicates the template for modifying the resource in the next.
Template string
// Type indicates the type of the patch.
Type *StagePatchType
// Impersonation indicates the impersonating configuration for client when patching status.
// In most cases this will be empty, in which case the default client service account will be used.
// When this is not empty, a corresponding rbac change is required to grant `impersonate` privilege.
// The support for this field is not available in Pod and Node resources.
Impersonation *ImpersonationConfig
}

// StagePatchType is the type of the patch.
type StagePatchType string

const (
// StagePatchTypeJSONPatch is the JSON patch type.
StagePatchTypeJSONPatch StagePatchType = "json"
// StagePatchTypeMergePatch is the merge patch type.
StagePatchTypeMergePatch StagePatchType = "merge"
// StagePatchTypeStrategicMergePatch is the strategic merge patch type.
StagePatchTypeStrategicMergePatch StagePatchType = "strategic"
)

// ImpersonationConfig describes the configuration for impersonating clients
type ImpersonationConfig struct {
// Username the target username for the client to impersonate
Expand Down
2 changes: 2 additions & 0 deletions pkg/apis/internalversion/zz_generated.conversion.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions pkg/apis/internalversion/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions pkg/apis/v1alpha1/stage_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,13 +147,28 @@ type StagePatch struct {
Root string `json:"root,omitempty"`
// Template indicates the template for modifying the resource in the next.
Template string `json:"template,omitempty"`
// Type indicates the type of the patch.
// +kubebuilder:validation:Enum=json;merge;strategic
Type *StagePatchType `json:"type,omitempty"`
// Impersonation indicates the impersonating configuration for client when patching status.
// In most cases this will be empty, in which case the default client service account will be used.
// When this is not empty, a corresponding rbac change is required to grant `impersonate` privilege.
// The support for this field is not available in Pod and Node resources.
Impersonation *ImpersonationConfig `json:"impersonation,omitempty"`
}

// StagePatchType is the type of the patch.
type StagePatchType string

const (
// StagePatchTypeJSONPatch is the JSON patch type.
StagePatchTypeJSONPatch StagePatchType = "json"
// StagePatchTypeMergePatch is the merge patch type.
StagePatchTypeMergePatch StagePatchType = "merge"
// StagePatchTypeStrategicMergePatch is the strategic merge patch type.
StagePatchTypeStrategicMergePatch StagePatchType = "strategic"
)

// ImpersonationConfig describes the configuration for impersonating clients
type ImpersonationConfig struct {
// Username the target username for the client to impersonate
Expand Down
5 changes: 5 additions & 0 deletions pkg/apis/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

105 changes: 96 additions & 9 deletions pkg/kwok/controllers/node_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import (
"sigs.k8s.io/kwok/pkg/config/resources"
"sigs.k8s.io/kwok/pkg/log"
"sigs.k8s.io/kwok/pkg/utils/expression"
"sigs.k8s.io/kwok/pkg/utils/format"
"sigs.k8s.io/kwok/pkg/utils/gotpl"
"sigs.k8s.io/kwok/pkg/utils/informer"
"sigs.k8s.io/kwok/pkg/utils/maps"
Expand Down Expand Up @@ -455,7 +456,7 @@ func (c *NodeController) playStage(ctx context.Context, node *corev1.Node, stage
result = nil
} else if len(next.Patches) != 0 {
for _, patch := range next.Patches {
patchData, err := c.computeMergePatch(node, patch.Root, patch.Template)
patchData, patchType, err := c.computePatch(node, patch)
if err != nil {
return shouldRetry(err), fmt.Errorf("failed to compute the node %s: %w", node.Name, err)
}
Expand All @@ -464,7 +465,7 @@ func (c *NodeController) playStage(ctx context.Context, node *corev1.Node, stage
"reason", "do not need to modify",
)
} else {
result, err = c.patchResource(ctx, node, patchData, patch)
result, err = c.patchResource(ctx, node, patchData, patchType, patch)
if err != nil {
return shouldRetry(err), fmt.Errorf("failed to patch node %s: %w", node.Name, err)
}
Expand All @@ -488,7 +489,7 @@ func (c *NodeController) readOnly(nodeName string) bool {
}

// patchResource patches the resource
func (c *NodeController) patchResource(ctx context.Context, node *corev1.Node, patchData []byte, patch internalversion.StagePatch) (*corev1.Node, error) {
func (c *NodeController) patchResource(ctx context.Context, node *corev1.Node, patchData []byte, patchType types.PatchType, patch internalversion.StagePatch) (*corev1.Node, error) {
logger := log.FromContext(ctx)
logger = logger.With(
"node", node.Name,
Expand All @@ -501,14 +502,78 @@ func (c *NodeController) patchResource(ctx context.Context, node *corev1.Node, p
)
subresource = []string{patch.Subresource}
}
result, err := c.typedClient.CoreV1().Nodes().Patch(ctx, node.Name, types.StrategicMergePatchType, patchData, metav1.PatchOptions{}, subresource...)
result, err := c.typedClient.CoreV1().Nodes().Patch(ctx, node.Name, patchType, patchData, metav1.PatchOptions{}, subresource...)
if err != nil {
return nil, err
}
logger.Info("Patch node")
return result, nil
}

func (c *NodeController) computePatch(node *corev1.Node, patch internalversion.StagePatch) ([]byte, types.PatchType, error) {
switch format.ElemOrDefault(patch.Type) {
case internalversion.StagePatchTypeJSONPatch:
patchData, err := c.computeJSONPatch(node, patch.Root, patch.Template)
if err != nil {
return nil, "", err
}
return patchData, types.JSONPatchType, nil
case internalversion.StagePatchTypeStrategicMergePatch, "":
patchData, err := c.computeStrategicMergePatch(node, patch.Root, patch.Template)
if err != nil {
return nil, "", err
}
return patchData, types.StrategicMergePatchType, nil
case internalversion.StagePatchTypeMergePatch:
patchData, err := c.computeMergePatch(node, patch.Root, patch.Template)
if err != nil {
return nil, "", err
}
return patchData, types.MergePatchType, nil
}

return nil, "", fmt.Errorf("unknown patch type %s", *patch.Type)
}

func (c *NodeController) computeStrategicMergePatch(node *corev1.Node, root, tpl string) ([]byte, error) {
patchData, err := c.renderer.ToJSON(tpl, node)
if err != nil {
return nil, err
}

var hasChange bool
switch root {
default:
return nil, fmt.Errorf("root %q is not supported", root)
case "":
hasChange, err = checkNeedStrategicMergePatch(*node, patchData)
if err != nil {
return nil, err
}
case "metadata":
hasChange, err = checkNeedStrategicMergePatch(node.ObjectMeta, patchData)
if err != nil {
return nil, err
}
case "spec":
hasChange, err = checkNeedStrategicMergePatch(node.Spec, patchData)
if err != nil {
return nil, err
}
case "status":
hasChange, err = checkNeedStrategicMergePatch(node.Status, patchData)
if err != nil {
return nil, err
}
}

if !hasChange {
return nil, nil
}

return wrapMergePatchData(root, patchData)
}

func (c *NodeController) computeMergePatch(node *corev1.Node, root, tpl string) ([]byte, error) {
patchData, err := c.renderer.ToJSON(tpl, node)
if err != nil {
Expand All @@ -520,22 +585,22 @@ func (c *NodeController) computeMergePatch(node *corev1.Node, root, tpl string)
default:
return nil, fmt.Errorf("root %q is not supported", root)
case "":
hasChange, err = checkNeedPatch(*node, patchData)
hasChange, err = checkNeedMergePatch(*node, patchData)
if err != nil {
return nil, err
}
case "metadata":
hasChange, err = checkNeedPatch(node.ObjectMeta, patchData)
hasChange, err = checkNeedMergePatch(node.ObjectMeta, patchData)
if err != nil {
return nil, err
}
case "spec":
hasChange, err = checkNeedPatch(node.Spec, patchData)
hasChange, err = checkNeedMergePatch(node.Spec, patchData)
if err != nil {
return nil, err
}
case "status":
hasChange, err = checkNeedPatch(node.Status, patchData)
hasChange, err = checkNeedMergePatch(node.Status, patchData)
if err != nil {
return nil, err
}
Expand All @@ -545,7 +610,29 @@ func (c *NodeController) computeMergePatch(node *corev1.Node, root, tpl string)
return nil, nil
}

return wrapPatchData(root, patchData), nil
return wrapMergePatchData(root, patchData)
}

func (c *NodeController) computeJSONPatch(node *corev1.Node, root, tpl string) ([]byte, error) {
patchData, err := c.renderer.ToJSON(tpl, node)
if err != nil {
return nil, err
}

patchData, err = wrapJSONPatchData(root, patchData)
if err != nil {
return nil, err
}

hasChange, err := checkNeedJSONPatch(*node, patchData)
if err != nil {
return nil, err
}
if !hasChange {
return nil, nil
}

return patchData, nil
}

// putNodeInfo puts node info
Expand Down
Loading
Loading