Skip to content

Commit

Permalink
feat: 3 ways merge base on json merge patch
Browse files Browse the repository at this point in the history
  • Loading branch information
howieyuen committed May 31, 2022
1 parent e643f6e commit 9eb311a
Showing 1 changed file with 23 additions and 35 deletions.
58 changes: 23 additions & 35 deletions pkg/engine/runtime/kubernetest_runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,21 @@ import (
"context"
"errors"

"kusionstack.io/kusion/pkg/engine/models"

k8serrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
k8syaml "k8s.io/apimachinery/pkg/runtime/serializer/yaml"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/jsonmergepatch"
"k8s.io/client-go/discovery"
memory "k8s.io/client-go/discovery/cached"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/restmapper"
"k8s.io/client-go/tools/clientcmd"

"kusionstack.io/kusion/pkg/engine/models"
"kusionstack.io/kusion/pkg/log"
"kusionstack.io/kusion/pkg/status"
"kusionstack.io/kusion/pkg/util/kube/config"
Expand Down Expand Up @@ -52,51 +52,39 @@ func (k *KubernetesRuntime) Apply(ctx context.Context, priorState, planState *mo
return nil, status.NewErrorStatus(errors.New("plan state is nil"))
}

// Get kubernetes resource from plan state
obj, resource, err := k.buildKubernetesResourceByState(planState)
// Get kubernetes resource interface from plan state
planObj, resource, err := k.buildKubernetesResourceByState(planState)
if err != nil {
return nil, status.NewErrorStatus(err)
}

// Create or Update resource
if priorState == nil {
// Create
if _, err = resource.Get(ctx, obj.GetName(), metav1.GetOptions{}); err != nil {
// Create the resource if not exists
if !k8serrors.IsNotFound(err) {
return nil, status.NewErrorStatus(err)
}

log.Infof("Create the resource %s/%s/%s because it doesn't exist", obj.GetKind(), obj.GetNamespace(), obj.GetName())
_, err = resource.Create(ctx, obj, metav1.CreateOptions{})
} else {
// Patch the resource because already exists
log.Infof("Patch the resource %s/%s/%s because it already exists", obj.GetKind(), obj.GetNamespace(), obj.GetName())

var objJSON []byte
objJSON, err = obj.MarshalJSON()
if err != nil {
return nil, status.NewErrorStatus(err)
}

_, err = resource.Patch(ctx, obj.GetName(), types.MergePatchType, objJSON, metav1.PatchOptions{
FieldManager: "kusion",
})
}
} else {
// Update
_, err = resource.Update(ctx, obj, metav1.UpdateOptions{
FieldManager: "kusion",
})
// original equals to last-applied from annotation, kusion store it in kusion_state.json
original := yaml.MergeToOneYAML(priorState.Attributes)
// modified equals input content
modified := yaml.MergeToOneYAML(planState.Attributes)
// live config
liveObj, err := resource.Get(ctx, planObj.GetName(), metav1.GetOptions{})
if err != nil {
return nil, status.NewErrorStatus(err)
}

current := yaml.MergeToOneYAML(liveObj.Object)
// 3-way json merge patch
patch, err := jsonmergepatch.CreateThreeWayJSONMergePatch([]byte(original), []byte(modified), []byte(current))
if err != nil {
return nil, status.NewErrorStatus(err)
}
// apply patch
patchedObj, err := resource.Patch(ctx, planObj.GetName(), types.MergePatchType, patch, metav1.PatchOptions{
FieldManager: "kusion",
})
if err != nil {
return nil, status.NewErrorStatus(err)
}

return &models.Resource{
ID: planState.ResourceKey(),
Attributes: obj.Object,
Attributes: patchedObj.Object,
DependsOn: planState.DependsOn,
}, nil
}
Expand Down

0 comments on commit 9eb311a

Please sign in to comment.