Skip to content

Commit

Permalink
selective sync (argoproj#213)
Browse files Browse the repository at this point in the history
Signed-off-by: kshamajain99 <kshamajain99@gmail.com>
  • Loading branch information
kshamajain99 authored Jan 29, 2021
1 parent 814d79d commit c5b7114
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
.vscode
.idea
coverage.out
vendor/
44 changes: 44 additions & 0 deletions pkg/sync/sync_context.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package sync

import (
"context"
"encoding/json"
"fmt"
"sort"
"strconv"
Expand All @@ -23,6 +24,7 @@ import (
"k8s.io/klog/v2/klogr"
cmdutil "k8s.io/kubectl/pkg/cmd/util"

"github.com/argoproj/gitops-engine/pkg/diff"
"github.com/argoproj/gitops-engine/pkg/health"
"github.com/argoproj/gitops-engine/pkg/sync/common"
"github.com/argoproj/gitops-engine/pkg/sync/hook"
Expand Down Expand Up @@ -129,6 +131,18 @@ func WithPruneLast(enabled bool) SyncOpt {
}
}

// WithResourceModificationChecker sets resource modification result
func WithResourceModificationChecker(enabled bool, diffResults *diff.DiffResultList) SyncOpt {
return func(ctx *syncContext) {
ctx.applyOutOfSyncOnly = enabled
if enabled {
ctx.modificationResult = groupDiffResults(diffResults)
} else {
ctx.modificationResult = nil
}
}
}

// WithNamespaceCreation will create non-exist namespace
func WithNamespaceCreation(createNamespace bool, namespaceModifier func(*unstructured.Unstructured) bool) SyncOpt {
return func(ctx *syncContext) {
Expand Down Expand Up @@ -217,6 +231,25 @@ func groupResources(reconciliationResult ReconciliationResult) map[kubeutil.Reso
return resources
}

// generates a map of resource and its modification result based on diffResultList
func groupDiffResults(diffResultList *diff.DiffResultList) map[kubeutil.ResourceKey]bool {
modifiedResources := make(map[kube.ResourceKey]bool)
for _, res := range diffResultList.Diffs {
var obj unstructured.Unstructured
var err error
if string(res.NormalizedLive) != "null" {
err = json.Unmarshal(res.NormalizedLive, &obj)
} else {
err = json.Unmarshal(res.PredictedLive, &obj)
}
if err != nil {
continue
}
modifiedResources[kube.GetResourceKey(&obj)] = res.Modified
}
return modifiedResources
}

const (
crdReadinessTimeout = time.Duration(3) * time.Second
)
Expand Down Expand Up @@ -281,6 +314,10 @@ type syncContext struct {
namespaceModifier func(*unstructured.Unstructured) bool

syncWaveHook common.SyncWaveHook

applyOutOfSyncOnly bool
// stores whether the resource is modified or not
modificationResult map[kube.ResourceKey]bool
}

func (sc *syncContext) setRunningPhase(tasks []*syncTask, isPendingDeletion bool) {
Expand Down Expand Up @@ -516,6 +553,13 @@ func (sc *syncContext) getSyncTasks() (_ syncTasks, successful bool) {
continue
}

if sc.applyOutOfSyncOnly {
if modified, ok := sc.modificationResult[k]; !modified && ok {
sc.log.WithValues("resource key", k).V(1).Info("Skipping as resource was not modified")
continue
}
}

for _, phase := range syncPhases(obj) {
resourceTasks = append(resourceTasks, &syncTask{phase: phase, targetObj: resource.Target, liveObj: resource.Live})
}
Expand Down
77 changes: 77 additions & 0 deletions pkg/sync/sync_context_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package sync

import (
"encoding/json"
"errors"
"fmt"
"reflect"
Expand All @@ -18,6 +19,7 @@ import (
testcore "k8s.io/client-go/testing"
"k8s.io/klog/v2/klogr"

"github.com/argoproj/gitops-engine/pkg/diff"
"github.com/argoproj/gitops-engine/pkg/health"
synccommon "github.com/argoproj/gitops-engine/pkg/sync/common"
"github.com/argoproj/gitops-engine/pkg/utils/kube"
Expand Down Expand Up @@ -1056,3 +1058,78 @@ func TestPruneLast(t *testing.T) {
assert.Equal(t, 3, tasks.lastWave())
})
}

func diffResultList() *diff.DiffResultList {
pod1 := NewPod()
pod1.SetName("pod-1")
pod2 := NewPod()
pod2.SetName("pod-2")
pod3 := NewPod()
pod3.SetName("pod-3")

diffResultList := diff.DiffResultList{
Modified: true,
Diffs: []diff.DiffResult{},
}

podBytes, _ := json.Marshal(pod1)
diffResultList.Diffs = append(diffResultList.Diffs, diff.DiffResult{NormalizedLive: []byte("null"), PredictedLive: podBytes, Modified: true})

podBytes, _ = json.Marshal(pod2)
diffResultList.Diffs = append(diffResultList.Diffs, diff.DiffResult{NormalizedLive: podBytes, PredictedLive: []byte("null"), Modified: true})

podBytes, _ = json.Marshal(pod3)
diffResultList.Diffs = append(diffResultList.Diffs, diff.DiffResult{NormalizedLive: podBytes, PredictedLive: podBytes, Modified: false})

return &diffResultList
}

func TestApplyOutOfSyncOnly(t *testing.T) {
pod1 := NewPod()
pod1.SetName("pod-1")
pod2 := NewPod()
pod2.SetName("pod-2")
pod3 := NewPod()
pod3.SetName("pod-3")
syncCtx := newTestSyncCtx()

t.Run("applyOutOfSyncOnly=false", func(t *testing.T) {
syncCtx.applyOutOfSyncOnly = true
syncCtx.modificationResult = nil
syncCtx.resources = groupResources(ReconciliationResult{
Live: []*unstructured.Unstructured{nil, pod2, pod3},
Target: []*unstructured.Unstructured{pod1, nil, pod3},
})
tasks, successful := syncCtx.getSyncTasks()

assert.True(t, successful)
assert.Len(t, tasks, 3)
})

syncCtx = newTestSyncCtx(WithResourceModificationChecker(true, diffResultList()))
t.Run("applyOutOfSyncOnly=true", func(t *testing.T) {
syncCtx.applyOutOfSyncOnly = true
syncCtx.resources = groupResources(ReconciliationResult{
Live: []*unstructured.Unstructured{nil, pod2, pod3},
Target: []*unstructured.Unstructured{pod1, nil, pod3},
})
tasks, successful := syncCtx.getSyncTasks()

assert.True(t, successful)
assert.Len(t, tasks, 2)
})

pod4 := NewPod()
pod4.SetName("pod-4")
t.Run("applyOutOfSyncOnly=true and missing resource key", func(t *testing.T) {
syncCtx.applyOutOfSyncOnly = true
syncCtx.resources = groupResources(ReconciliationResult{
Live: []*unstructured.Unstructured{nil, pod2, pod3, pod4},
Target: []*unstructured.Unstructured{pod1, nil, pod3, pod4},
})
tasks, successful := syncCtx.getSyncTasks()

assert.True(t, successful)
assert.Len(t, tasks, 3)
})
}

0 comments on commit c5b7114

Please sign in to comment.