Skip to content

Commit fbecbb8

Browse files
authored
feat: sync-options annotation with Force=true (argoproj#414) (argoproj#560)
Signed-off-by: kkk777-7 <[email protected]>
1 parent 1ade3a1 commit fbecbb8

File tree

4 files changed

+118
-54
lines changed

4 files changed

+118
-54
lines changed

pkg/sync/common/types.go

+2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ const (
2727
SyncOptionPruneLast = "PruneLast=true"
2828
// Sync option that enables use of replace or create command instead of apply
2929
SyncOptionReplace = "Replace=true"
30+
// Sync option that enables use of --force flag, delete and re-create
31+
SyncOptionForce = "Force=true"
3032
// Sync option that enables use of --server-side flag instead of client-side
3133
SyncOptionServerSideApply = "ServerSideApply=true"
3234
// Sync option that disables resource deletion

pkg/sync/sync_context.go

+20-19
Original file line numberDiff line numberDiff line change
@@ -459,8 +459,8 @@ func (sc *syncContext) Sync() {
459459
// if pruned tasks pending deletion, then wait...
460460
prunedTasksPendingDelete := tasks.Filter(func(t *syncTask) bool {
461461
if t.pruned() && t.liveObj != nil {
462-
return t.liveObj.GetDeletionTimestamp() != nil
463-
}
462+
return t.liveObj.GetDeletionTimestamp() != nil
463+
}
464464
return false
465465
})
466466
if prunedTasksPendingDelete.Len() > 0 {
@@ -761,31 +761,31 @@ func (sc *syncContext) getSyncTasks() (_ syncTasks, successful bool) {
761761
// for prune tasks, modify the waves for proper cleanup i.e reverse of sync wave (creation order)
762762
pruneTasks := make(map[int][]*syncTask)
763763
for _, task := range tasks {
764-
if task.isPrune() {
765-
pruneTasks[task.wave()] = append(pruneTasks[task.wave()], task)
766-
}
764+
if task.isPrune() {
765+
pruneTasks[task.wave()] = append(pruneTasks[task.wave()], task)
766+
}
767767
}
768-
768+
769769
var uniquePruneWaves []int
770770
for k := range pruneTasks {
771-
uniquePruneWaves = append(uniquePruneWaves, k)
771+
uniquePruneWaves = append(uniquePruneWaves, k)
772772
}
773773
sort.Ints(uniquePruneWaves)
774-
774+
775775
// reorder waves for pruning tasks using symmetric swap on prune waves
776776
n := len(uniquePruneWaves)
777777
for i := 0; i < n/2; i++ {
778-
// waves to swap
779-
startWave := uniquePruneWaves[i]
780-
endWave := uniquePruneWaves[n-1-i]
781-
782-
for _, task := range pruneTasks[startWave] {
778+
// waves to swap
779+
startWave := uniquePruneWaves[i]
780+
endWave := uniquePruneWaves[n-1-i]
781+
782+
for _, task := range pruneTasks[startWave] {
783783
task.waveOverride = &endWave
784-
}
785-
786-
for _, task := range pruneTasks[endWave] {
784+
}
785+
786+
for _, task := range pruneTasks[endWave] {
787787
task.waveOverride = &startWave
788-
}
788+
}
789789
}
790790

791791
// for pruneLast tasks, modify the wave to sync phase last wave of tasks + 1
@@ -940,7 +940,7 @@ func (sc *syncContext) ensureCRDReady(name string) error {
940940
})
941941
}
942942

943-
func (sc *syncContext) applyObject(t *syncTask, dryRun, force, validate bool) (common.ResultCode, string) {
943+
func (sc *syncContext) applyObject(t *syncTask, dryRun, validate bool) (common.ResultCode, string) {
944944
dryRunStrategy := cmdutil.DryRunNone
945945
if dryRun {
946946
// irrespective of the dry run mode set in the sync context, always run
@@ -954,6 +954,7 @@ func (sc *syncContext) applyObject(t *syncTask, dryRun, force, validate bool) (c
954954
var err error
955955
var message string
956956
shouldReplace := sc.replace || resourceutil.HasAnnotationOption(t.targetObj, common.AnnotationSyncOptions, common.SyncOptionReplace)
957+
force := sc.force || resourceutil.HasAnnotationOption(t.targetObj, common.AnnotationSyncOptions, common.SyncOptionForce)
957958
// if it is a dry run, disable server side apply, as the goal is to validate only the
958959
// yaml correctness of the rendered manifests.
959960
// running dry-run in server mode breaks the auto create namespace feature
@@ -1233,7 +1234,7 @@ func (sc *syncContext) processCreateTasks(state runState, tasks syncTasks, dryRu
12331234
logCtx := sc.log.WithValues("dryRun", dryRun, "task", t)
12341235
logCtx.V(1).Info("Applying")
12351236
validate := sc.validate && !resourceutil.HasAnnotationOption(t.targetObj, common.AnnotationSyncOptions, common.SyncOptionsDisableValidation)
1236-
result, message := sc.applyObject(t, dryRun, sc.force, validate)
1237+
result, message := sc.applyObject(t, dryRun, validate)
12371238
if result == common.ResultCodeSyncFailed {
12381239
logCtx.WithValues("message", message).Info("Apply failed")
12391240
state = failed

pkg/sync/sync_context_test.go

+80-34
Original file line numberDiff line numberDiff line change
@@ -840,6 +840,52 @@ func TestSync_ServerSideApply(t *testing.T) {
840840
}
841841
}
842842

843+
func withForceAnnotation(un *unstructured.Unstructured) *unstructured.Unstructured {
844+
un.SetAnnotations(map[string]string{synccommon.AnnotationSyncOptions: synccommon.SyncOptionForce})
845+
return un
846+
}
847+
848+
func withForceAndReplaceAnnotations(un *unstructured.Unstructured) *unstructured.Unstructured {
849+
un.SetAnnotations(map[string]string{synccommon.AnnotationSyncOptions: "Force=true,Replace=true"})
850+
return un
851+
}
852+
853+
func TestSync_Force(t *testing.T) {
854+
testCases := []struct {
855+
name string
856+
target *unstructured.Unstructured
857+
live *unstructured.Unstructured
858+
commandUsed string
859+
force bool
860+
}{
861+
{"NoAnnotation", NewPod(), NewPod(), "apply", false},
862+
{"ForceApplyAnnotationIsSet", withForceAnnotation(NewPod()), NewPod(), "apply", true},
863+
{"ForceReplaceAnnotationIsSet", withForceAndReplaceAnnotations(NewPod()), NewPod(), "replace", true},
864+
{"LiveObjectMissing", withReplaceAnnotation(NewPod()), nil, "create", false},
865+
}
866+
867+
for _, tc := range testCases {
868+
t.Run(tc.name, func(t *testing.T) {
869+
syncCtx := newTestSyncCtx(nil)
870+
871+
tc.target.SetNamespace(FakeArgoCDNamespace)
872+
if tc.live != nil {
873+
tc.live.SetNamespace(FakeArgoCDNamespace)
874+
}
875+
syncCtx.resources = groupResources(ReconciliationResult{
876+
Live: []*unstructured.Unstructured{tc.live},
877+
Target: []*unstructured.Unstructured{tc.target},
878+
})
879+
880+
syncCtx.Sync()
881+
882+
resourceOps, _ := syncCtx.resourceOps.(*kubetest.MockResourceOps)
883+
assert.Equal(t, tc.commandUsed, resourceOps.GetLastResourceCommand(kube.GetResourceKey(tc.target)))
884+
assert.Equal(t, tc.force, resourceOps.GetLastForce())
885+
})
886+
}
887+
}
888+
843889
func TestSelectiveSyncOnly(t *testing.T) {
844890
pod1 := NewPod()
845891
pod1.SetName("pod-1")
@@ -1771,11 +1817,11 @@ func TestWaveReorderingOfPruneTasks(t *testing.T) {
17711817
// no change in wave order
17721818
expectedWaveOrder: map[string]int{
17731819
// new wave // original wave
1774-
ns.GetName(): 0, // 0
1775-
pod1.GetName(): 1, // 1
1776-
pod2.GetName(): 2, // 2
1777-
pod3.GetName(): 3, // 3
1778-
pod4.GetName(): 4, // 4
1820+
ns.GetName(): 0, // 0
1821+
pod1.GetName(): 1, // 1
1822+
pod2.GetName(): 2, // 2
1823+
pod3.GetName(): 3, // 3
1824+
pod4.GetName(): 4, // 4
17791825
},
17801826
},
17811827
{
@@ -1785,11 +1831,11 @@ func TestWaveReorderingOfPruneTasks(t *testing.T) {
17851831
// change in prune wave order
17861832
expectedWaveOrder: map[string]int{
17871833
// new wave // original wave
1788-
ns.GetName(): 4, // 0
1789-
pod1.GetName(): 3, // 1
1790-
pod2.GetName(): 2, // 2
1791-
pod3.GetName(): 1, // 3
1792-
pod4.GetName(): 0, // 4
1834+
ns.GetName(): 4, // 0
1835+
pod1.GetName(): 3, // 1
1836+
pod2.GetName(): 2, // 2
1837+
pod3.GetName(): 1, // 3
1838+
pod4.GetName(): 0, // 4
17931839
},
17941840
},
17951841
{
@@ -1799,13 +1845,13 @@ func TestWaveReorderingOfPruneTasks(t *testing.T) {
17991845
// change in prune wave order
18001846
expectedWaveOrder: map[string]int{
18011847
// new wave // original wave
1802-
pod1.GetName(): 4, // 1
1803-
pod3.GetName(): 3, // 3
1804-
pod4.GetName(): 1, // 4
1848+
pod1.GetName(): 4, // 1
1849+
pod3.GetName(): 3, // 3
1850+
pod4.GetName(): 1, // 4
18051851

18061852
// no change since non prune tasks
1807-
ns.GetName(): 0, // 0
1808-
pod2.GetName(): 2, // 2
1853+
ns.GetName(): 0, // 0
1854+
pod2.GetName(): 2, // 2
18091855
},
18101856
},
18111857
}
@@ -1830,13 +1876,13 @@ func TestWaveReorderingOfPruneTasks(t *testing.T) {
18301876
// change in prune wave order
18311877
expectedWaveOrder: map[string]int{
18321878
// new wave // original wave
1833-
pod1.GetName(): 5, // 1
1834-
pod2.GetName(): 5, // 2
1835-
pod3.GetName(): 5, // 3
1836-
pod4.GetName(): 5, // 4
1879+
pod1.GetName(): 5, // 1
1880+
pod2.GetName(): 5, // 2
1881+
pod3.GetName(): 5, // 3
1882+
pod4.GetName(): 5, // 4
18371883

18381884
// no change since non prune tasks
1839-
ns.GetName(): 0, // 0
1885+
ns.GetName(): 0, // 0
18401886
},
18411887
},
18421888
{
@@ -1847,13 +1893,13 @@ func TestWaveReorderingOfPruneTasks(t *testing.T) {
18471893
// change in wave order
18481894
expectedWaveOrder: map[string]int{
18491895
// new wave // original wave
1850-
pod1.GetName(): 4, // 1
1851-
pod2.GetName(): 5, // 2
1852-
pod3.GetName(): 2, // 3
1853-
pod4.GetName(): 1, // 4
1896+
pod1.GetName(): 4, // 1
1897+
pod2.GetName(): 5, // 2
1898+
pod3.GetName(): 2, // 3
1899+
pod4.GetName(): 1, // 4
18541900

18551901
// no change since non prune tasks
1856-
ns.GetName(): 0, // 0
1902+
ns.GetName(): 0, // 0
18571903
},
18581904
},
18591905
}
@@ -1877,11 +1923,11 @@ func TestWaveReorderingOfPruneTasks(t *testing.T) {
18771923
// change in prune wave order
18781924
expectedWaveOrder: map[string]int{
18791925
// new wave // original wave
1880-
pod1.GetName(): 5, // 1
1881-
pod3.GetName(): 4, // 3
1882-
pod4.GetName(): 4, // 3
1883-
pod5.GetName(): 3, // 4
1884-
pod7.GetName(): 1, // 5
1926+
pod1.GetName(): 5, // 1
1927+
pod3.GetName(): 4, // 3
1928+
pod4.GetName(): 4, // 3
1929+
pod5.GetName(): 3, // 4
1930+
pod7.GetName(): 1, // 5
18851931

18861932
// no change since non prune tasks
18871933
ns.GetName(): -1, // -1
@@ -1941,8 +1987,8 @@ func TestWaitForCleanUpBeforeNextWave(t *testing.T) {
19411987

19421988
// simulate successful delete of pod3
19431989
syncCtx.resources = groupResources(ReconciliationResult{
1944-
Target: []*unstructured.Unstructured{nil, nil, },
1945-
Live: []*unstructured.Unstructured{pod1, pod2, },
1990+
Target: []*unstructured.Unstructured{nil, nil},
1991+
Live: []*unstructured.Unstructured{pod1, pod2},
19461992
})
19471993

19481994
// next sync should prune only pod2
@@ -1966,8 +2012,8 @@ func TestWaitForCleanUpBeforeNextWave(t *testing.T) {
19662012

19672013
// simulate successful delete of pod2
19682014
syncCtx.resources = groupResources(ReconciliationResult{
1969-
Target: []*unstructured.Unstructured{nil, },
1970-
Live: []*unstructured.Unstructured{pod1, },
2015+
Target: []*unstructured.Unstructured{nil},
2016+
Live: []*unstructured.Unstructured{pod1},
19712017
})
19722018

19732019
// next sync should proceed with next wave

pkg/utils/kube/kubetest/mock_resource_operations.go

+16-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ type MockResourceOps struct {
2222
lastValidate bool
2323
serverSideApply bool
2424
serverSideApplyManager string
25+
lastForce bool
2526

2627
recordLock sync.RWMutex
2728

@@ -73,6 +74,19 @@ func (r *MockResourceOps) SetLastServerSideApplyManager(manager string) {
7374
r.recordLock.Unlock()
7475
}
7576

77+
func (r *MockResourceOps) SetLastForce(force bool) {
78+
r.recordLock.Lock()
79+
r.lastForce = force
80+
r.recordLock.Unlock()
81+
}
82+
83+
func (r *MockResourceOps) GetLastForce() bool {
84+
r.recordLock.RLock()
85+
force := r.lastForce
86+
r.recordLock.RUnlock()
87+
return force
88+
}
89+
7690
func (r *MockResourceOps) SetLastResourceCommand(key kube.ResourceKey, cmd string) {
7791
r.recordLock.Lock()
7892
if r.lastCommandPerResource == nil {
@@ -95,6 +109,7 @@ func (r *MockResourceOps) ApplyResource(ctx context.Context, obj *unstructured.U
95109
r.SetLastValidate(validate)
96110
r.SetLastServerSideApply(serverSideApply)
97111
r.SetLastServerSideApplyManager(manager)
112+
r.SetLastForce(force)
98113
r.SetLastResourceCommand(kube.GetResourceKey(obj), "apply")
99114
command, ok := r.Commands[obj.GetName()]
100115
if !ok {
@@ -105,9 +120,9 @@ func (r *MockResourceOps) ApplyResource(ctx context.Context, obj *unstructured.U
105120
}
106121

107122
func (r *MockResourceOps) ReplaceResource(ctx context.Context, obj *unstructured.Unstructured, dryRunStrategy cmdutil.DryRunStrategy, force bool) (string, error) {
123+
r.SetLastForce(force)
108124
command, ok := r.Commands[obj.GetName()]
109125
r.SetLastResourceCommand(kube.GetResourceKey(obj), "replace")
110-
111126
if !ok {
112127
return "", nil
113128
}

0 commit comments

Comments
 (0)