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

Do not do destructive updates during bootup #193

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
d457913
Bootstrapping admiral
aattuluri Sep 19, 2019
7b9a2ff
Add circle ci config
aattuluri Sep 19, 2019
c9e80a5
Fix the working directory for builds
aattuluri Sep 19, 2019
76b9561
Bootstrapping admiral
aattuluri Sep 19, 2019
85aeeaa
Add circle ci config
aattuluri Sep 19, 2019
fe313cc
Fix the working directory for builds
aattuluri Sep 19, 2019
306ad93
Merge branch 'master' of https://github.com/aattuluri/admiral
aattuluri Sep 19, 2019
a9fa409
Add build status badge.
aattuluri Sep 19, 2019
4ae717d
Revert "Add build status badge."
aattuluri Sep 19, 2019
ff09cfb
Merge remote-tracking branch 'upstream/master'
aattuluri Sep 19, 2019
eb51849
Merge remote-tracking branch 'upstream/master'
aattuluri Sep 20, 2019
928c0d2
Merge remote-tracking branch 'upstream/master'
aattuluri Sep 20, 2019
2fb3364
Merge remote-tracking branch 'upstream/master'
aattuluri Sep 23, 2019
ea3ed61
Merge remote-tracking branch 'upstream/master'
aattuluri Sep 23, 2019
15e7845
Merge remote-tracking branch 'upstream/master'
aattuluri Sep 24, 2019
3b34036
Merge remote-tracking branch 'upstream/master'
aattuluri Sep 29, 2019
656bf71
Merge remote-tracking branch 'upstream/master'
aattuluri Sep 29, 2019
0245314
Merge remote-tracking branch 'upstream/master'
aattuluri Sep 29, 2019
111c3de
Merge remote-tracking branch 'upstream/master'
aattuluri Oct 4, 2019
e39651c
Merge remote-tracking branch 'upstream/master'
aattuluri Oct 4, 2019
b280c0b
Merge remote-tracking branch 'upstream/master'
aattuluri Oct 6, 2019
4b74679
Merge remote-tracking branch 'upstream/master'
aattuluri Oct 16, 2019
09b8a5b
Merge remote-tracking branch 'upstream/master'
aattuluri Oct 30, 2019
6546170
Merge remote-tracking branch 'upstream/master'
aattuluri Dec 27, 2019
1b0dfeb
Merge remote-tracking branch 'upstream/master'
aattuluri Jan 2, 2020
9b0010b
Merge remote-tracking branch 'upstream/master'
aattuluri Jan 9, 2020
7b68379
Merge remote-tracking branch 'upstream/master'
aattuluri Jan 21, 2020
5ae2f4c
Merge remote-tracking branch 'upstream/master'
aattuluri Jan 24, 2020
6e297db
Publish images: i) latest from master, ii) TAG if its set and iii) co…
aattuluri Jan 24, 2020
d6674b9
Merge remote-tracking branch 'upstream/master'
aattuluri Feb 19, 2020
e45ef03
Merge remote-tracking branch 'upstream/master'
aattuluri Feb 26, 2020
9096f16
Merge remote-tracking branch 'upstream/master'
aattuluri Feb 27, 2020
1e7a3d5
Merge remote-tracking branch 'upstream/master'
aattuluri Mar 8, 2020
a9e55b3
Merge remote-tracking branch 'upstream/master'
aattuluri Mar 20, 2020
c40a8be
Merge remote-tracking branch 'upstream/master'
aattuluri Mar 27, 2020
798b06a
Merge remote-tracking branch 'upstream/master'
aattuluri Apr 6, 2020
f028e1e
Merge remote-tracking branch 'upstream/master'
aattuluri Apr 16, 2020
0956d03
Merge remote-tracking branch 'upstream/master'
aattuluri Apr 27, 2020
cecd2ca
Merge remote-tracking branch 'upstream/master'
aattuluri May 13, 2020
b46682c
Merge remote-tracking branch 'upstream/master'
aattuluri May 14, 2020
2d3460c
Merge remote-tracking branch 'upstream/master'
aattuluri May 26, 2020
77c46e0
Merge remote-tracking branch 'upstream/master'
aattuluri Jun 15, 2020
82a4223
Merge remote-tracking branch 'upstream/master'
aattuluri Jul 24, 2020
065b6d4
Merge remote-tracking branch 'upstream/master'
aattuluri Aug 26, 2020
933650b
Merge remote-tracking branch 'upstream/master'
aattuluri Sep 20, 2020
d2e76cb
Merge remote-tracking branch 'upstream/master'
aattuluri Nov 2, 2020
63b4c60
Merge remote-tracking branch 'upstream/master'
aattuluri Nov 6, 2020
7b6626f
Merge remote-tracking branch 'upstream/master'
aattuluri Nov 12, 2020
0631dce
Merge remote-tracking branch 'upstream/master'
aattuluri Nov 13, 2020
51a27da
Merge remote-tracking branch 'upstream/master'
aattuluri Jan 21, 2021
8683c4e
Merge remote-tracking branch 'upstream/master'
aattuluri Apr 23, 2021
e05e909
Merge remote-tracking branch 'upstream/master'
aattuluri Jun 3, 2021
85a586e
Merge remote-tracking branch 'upstream/master'
aattuluri Jan 30, 2022
5a3d36f
Merge remote-tracking branch 'upstream/master'
aattuluri Feb 4, 2022
0f387e1
Merge remote-tracking branch 'upstream/master'
aattuluri Feb 10, 2022
cf0996e
Do not do destructive updates during bootup time
aattuluri Feb 10, 2022
7eacc4d
Fix lint issues.
aattuluri Feb 10, 2022
806fac8
Fixes
aattuluri Feb 10, 2022
b561859
Another test fix
aattuluri Feb 10, 2022
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
70 changes: 68 additions & 2 deletions admiral/pkg/clusters/handler.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package clusters

import (
"bytes"
"fmt"
argo "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1"
"github.com/gogo/protobuf/types"
Expand All @@ -15,8 +16,10 @@ import (
k8sAppsV1 "k8s.io/api/apps/v1"
k8sV1 "k8s.io/api/core/v1"
v12 "k8s.io/apimachinery/pkg/apis/meta/v1"
"reflect"
"sort"
"strings"
"time"
)

const ROLLOUT_POD_HASH_LABEL string = "rollouts-pod-template-hash"
Expand Down Expand Up @@ -540,12 +543,23 @@ func addUpdateServiceEntry(obj *v1alpha3.ServiceEntry, exist *v1alpha3.ServiceEn
obj.ResourceVersion = ""
_, err = rc.ServiceEntryController.IstioClient.NetworkingV1alpha3().ServiceEntries(namespace).Create(obj)
op = "Add"
log.Infof(LogFormat + " SE=%s", op, "ServiceEntry", obj.Name, rc.ClusterID, "New SE", obj.Spec.String())
} else {
exist.Labels = obj.Labels
exist.Annotations = obj.Annotations
exist.Spec = obj.Spec
op = "Update"
_, err = rc.ServiceEntryController.IstioClient.NetworkingV1alpha3().ServiceEntries(namespace).Update(exist)
skipUpdate, diff := skipDestructiveUpdate(rc, obj, exist)
if diff != "" {
log.Infof(LogFormat + " diff=%s", op, "ServiceEntry", obj.Name, rc.ClusterID, "Diff in update", diff)
}
if skipUpdate {
log.Infof(LogFormat, op, "ServiceEntry", obj.Name, rc.ClusterID, "Update skipped as it was destructive during Admiral's bootup phase")
return
} else {
exist.Spec = obj.Spec
_, err = rc.ServiceEntryController.IstioClient.NetworkingV1alpha3().ServiceEntries(namespace).Update(exist)
}

}

if err != nil {
Expand All @@ -555,6 +569,58 @@ func addUpdateServiceEntry(obj *v1alpha3.ServiceEntry, exist *v1alpha3.ServiceEn
}
}

func skipDestructiveUpdate(rc *RemoteController, new *v1alpha3.ServiceEntry, old *v1alpha3.ServiceEntry) (skipDestructive bool, diff string) {
skipDestructive = false
destructive, diff := getServiceEntryDiff(new, old)
//do not update SEs during bootup phase if they are destructive
if time.Since(rc.StartTime) < (2 * common.GetAdmiralParams().CacheRefreshDuration) && destructive {
skipDestructive = true
}

return skipDestructive, diff
}

//Diffs only endpoints
func getServiceEntryDiff(new *v1alpha3.ServiceEntry, old *v1alpha3.ServiceEntry) (destructive bool, diff string) {

//we diff only if both objects exist
if old == nil || new == nil {
return false, ""
}
destructive = false
format := "%s %s before: %v, after: %v;"
var buffer bytes.Buffer
seNew := new.Spec
seOld := old.Spec

oldEndpointMap := make(map[string]*v1alpha32.ServiceEntry_Endpoint)
found := make(map[string]string)
for _, oEndpoint := range seOld.Endpoints {
oldEndpointMap[oEndpoint.Address] = oEndpoint
}
for _, nEndpoint := range seNew.Endpoints {
if val, ok := oldEndpointMap[nEndpoint.Address]; ok {
found[nEndpoint.Address] = "1"
if !reflect.DeepEqual(val, nEndpoint) {
destructive = true
buffer.WriteString(fmt.Sprintf(format, "endpoint", "Update", val.String(), nEndpoint.String()))
}
} else {
buffer.WriteString(fmt.Sprintf(format, "endpoint", "Add", "", nEndpoint.String()))
}
}

for key := range oldEndpointMap {
if _, ok := found[key]; !ok {
destructive = true
buffer.WriteString(fmt.Sprintf(format, "endpoint", "Delete", oldEndpointMap[key].String(), ""))
}
}

diff = buffer.String()
return destructive, diff
}

func deleteServiceEntry(exist *v1alpha3.ServiceEntry, namespace string, rc *RemoteController) {
if exist != nil {
err := rc.ServiceEntryController.IstioClient.NetworkingV1alpha3().ServiceEntries(namespace).Delete(exist.Name, &v12.DeleteOptions{})
Expand Down
262 changes: 262 additions & 0 deletions admiral/pkg/clusters/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
k8sv1 "k8s.io/apimachinery/pkg/apis/meta/v1"
v12 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/rest"
"strings"
"testing"
"time"
)
Expand Down Expand Up @@ -993,4 +994,265 @@ func TestGetServiceForRolloutBlueGreen(t *testing.T) {
}
})
}
}

func TestSkipDestructiveUpdate(t *testing.T) {

twoEndpointSe := v1alpha3.ServiceEntry{
Hosts: []string{"e2e.my-first-service.mesh"},
Addresses: []string{"240.10.1.1"},
Ports: []*v1alpha3.Port{{Number: uint32(common.DefaultServiceEntryPort),
Name: "http", Protocol: "http"}},
Location: v1alpha3.ServiceEntry_MESH_INTERNAL,
Resolution: v1alpha3.ServiceEntry_DNS,
SubjectAltNames: []string{"spiffe://prefix/my-first-service"},
Endpoints: []*v1alpha3.ServiceEntry_Endpoint{
{Address: "dummy.admiral.global-west", Ports: map[string]uint32{"http": 0}, Locality: "us-west-2"},
{Address: "dummy.admiral.global-east", Ports: map[string]uint32{"http": 0}, Locality: "us-east-2"},
},
}

twoEndpointSeUpdated := v1alpha3.ServiceEntry{
Hosts: []string{"e2e.my-first-service.mesh"},
Addresses: []string{"240.10.1.1"},
Ports: []*v1alpha3.Port{{Number: uint32(common.DefaultServiceEntryPort),
Name: "http", Protocol: "http"}},
Location: v1alpha3.ServiceEntry_MESH_INTERNAL,
Resolution: v1alpha3.ServiceEntry_DNS,
SubjectAltNames: []string{"spiffe://prefix/my-first-service"},
Endpoints: []*v1alpha3.ServiceEntry_Endpoint{
{Address: "dummy.admiral.global-west", Ports: map[string]uint32{"http": 90}, Locality: "us-west-2"},
{Address: "dummy.admiral.global-east", Ports: map[string]uint32{"http": 0}, Locality: "us-east-2"},
},
}

oneEndpointSe := v1alpha3.ServiceEntry{
Hosts: []string{"e2e.my-first-service.mesh"},
Addresses: []string{"240.10.1.1"},
Ports: []*v1alpha3.Port{{Number: uint32(common.DefaultServiceEntryPort),
Name: "http", Protocol: "http"}},
Location: v1alpha3.ServiceEntry_MESH_INTERNAL,
Resolution: v1alpha3.ServiceEntry_DNS,
SubjectAltNames: []string{"spiffe://prefix/my-first-service"},
Endpoints: []*v1alpha3.ServiceEntry_Endpoint{
{Address: "dummy.admiral.global-west", Ports: map[string]uint32{"http": 0}, Locality: "us-west-2"},
},
}

newSeTwoEndpoints := &v1alpha32.ServiceEntry{
ObjectMeta: v12.ObjectMeta{Name: "se1", Namespace: "random"},
Spec: twoEndpointSe,
}

newSeTwoEndpointsUpdated := &v1alpha32.ServiceEntry{
ObjectMeta: v12.ObjectMeta{Name: "se1", Namespace: "random"},
Spec: twoEndpointSeUpdated,
}

newSeOneEndpoint := &v1alpha32.ServiceEntry{
ObjectMeta: v12.ObjectMeta{Name: "se1", Namespace: "random"},
Spec: oneEndpointSe,
}

oldSeTwoEndpoints := &v1alpha32.ServiceEntry{
ObjectMeta: v12.ObjectMeta{Name: "se1", Namespace: "random"},
Spec: twoEndpointSe,
}

oldSeOneEndpoint := &v1alpha32.ServiceEntry{
ObjectMeta: v12.ObjectMeta{Name: "se1", Namespace: "random"},
Spec: oneEndpointSe,
}


rcWarmupPhase := &RemoteController{
StartTime: time.Now(),
}

rcNotinWarmupPhase := &RemoteController{
StartTime: time.Now().Add(time.Duration(-21) * time.Minute),
}

//Struct of test case info. Name is required.
testCases := []struct {
name string
rc *RemoteController
newSe *v1alpha32.ServiceEntry
oldSe *v1alpha32.ServiceEntry
skipDestructive bool
diff string
}{
{
name: "Should return false when in warm up phase but not destructive",
rc: rcWarmupPhase,
newSe: newSeOneEndpoint,
oldSe: oldSeOneEndpoint,
skipDestructive: false,
diff: "",
},
{
name: "Should return true when in warm up phase but is destructive",
rc: rcWarmupPhase,
newSe: newSeOneEndpoint,
oldSe: oldSeTwoEndpoints,
skipDestructive: true,
diff: "Delete",
},
{
name: "Should return false when not in warm up phase but is destructive",
rc: rcNotinWarmupPhase,
newSe: newSeOneEndpoint,
oldSe: oldSeTwoEndpoints,
skipDestructive: false,
diff: "Delete",
},
{
name: "Should return false when in warm up phase but is constructive",
rc: rcWarmupPhase,
newSe: newSeTwoEndpoints,
oldSe: oldSeOneEndpoint,
skipDestructive: false,
diff: "Add",
},
{
name: "Should return false when not in warm up phase but endpoints updated",
rc: rcNotinWarmupPhase,
newSe: newSeTwoEndpointsUpdated,
oldSe: oldSeTwoEndpoints,
skipDestructive: false,
diff: "Update",
},
{
name: "Should return true when in warm up phase but endpoints are updated (destructive)",
rc: rcWarmupPhase,
newSe: newSeTwoEndpointsUpdated,
oldSe: oldSeTwoEndpoints,
skipDestructive: true,
diff: "Update",
},

}

//Run the test for every provided case
for _, c := range testCases {
t.Run(c.name, func(t *testing.T) {
skipDestructive, diff := skipDestructiveUpdate(c.rc, c.newSe, c.oldSe)
if skipDestructive == c.skipDestructive {
//perfect
} else {
t.Errorf("Result Failed. Got %v, expected %v", skipDestructive, c.skipDestructive)
}
if c.diff == "" || (c.diff != "" && strings.Contains(diff, c.diff)) {
//perfect
} else {
t.Errorf("Diff Failed. Got %v, expected %v", diff, c.diff)
}
})
}
}

func TestAddUpdateServiceEntry(t *testing.T) {


fakeIstioClient := istiofake.NewSimpleClientset()

seCtrl := &istio.ServiceEntryController{
IstioClient: fakeIstioClient,
}

twoEndpointSe := v1alpha3.ServiceEntry{
Hosts: []string{"e2e.my-first-service.mesh"},
Addresses: []string{"240.10.1.1"},
Ports: []*v1alpha3.Port{{Number: uint32(common.DefaultServiceEntryPort),
Name: "http", Protocol: "http"}},
Location: v1alpha3.ServiceEntry_MESH_INTERNAL,
Resolution: v1alpha3.ServiceEntry_DNS,
SubjectAltNames: []string{"spiffe://prefix/my-first-service"},
Endpoints: []*v1alpha3.ServiceEntry_Endpoint{
{Address: "dummy.admiral.global-west", Ports: map[string]uint32{"http": 0}, Locality: "us-west-2"},
{Address: "dummy.admiral.global-east", Ports: map[string]uint32{"http": 0}, Locality: "us-east-2"},
},
}

oneEndpointSe := v1alpha3.ServiceEntry{
Hosts: []string{"e2e.my-first-service.mesh"},
Addresses: []string{"240.10.1.1"},
Ports: []*v1alpha3.Port{{Number: uint32(common.DefaultServiceEntryPort),
Name: "http", Protocol: "http"}},
Location: v1alpha3.ServiceEntry_MESH_INTERNAL,
Resolution: v1alpha3.ServiceEntry_DNS,
SubjectAltNames: []string{"spiffe://prefix/my-first-service"},
Endpoints: []*v1alpha3.ServiceEntry_Endpoint{
{Address: "dummy.admiral.global-west", Ports: map[string]uint32{"http": 0}, Locality: "us-west-2"},
},
}

newSeOneEndpoint := &v1alpha32.ServiceEntry{
ObjectMeta: v12.ObjectMeta{Name: "se1", Namespace: "namespace"},
Spec: oneEndpointSe,
}

oldSeTwoEndpoints := &v1alpha32.ServiceEntry{
ObjectMeta: v12.ObjectMeta{Name: "se2", Namespace: "namespace"},
Spec: twoEndpointSe,
}

seCtrl.IstioClient.NetworkingV1alpha3().ServiceEntries("namespace").Create(oldSeTwoEndpoints)

rcWarmupPhase := &RemoteController{
ServiceEntryController: seCtrl,
StartTime: time.Now(),
}

rcNotinWarmupPhase := &RemoteController{
ServiceEntryController: seCtrl,
StartTime: time.Now().Add(time.Duration(-21) * time.Minute),
}

//Struct of test case info. Name is required.
testCases := []struct {
name string
rc *RemoteController
newSe *v1alpha32.ServiceEntry
oldSe *v1alpha32.ServiceEntry
skipDestructive bool
}{
{
name: "Should add a new SE",
rc: rcWarmupPhase,
newSe: newSeOneEndpoint,
oldSe: nil,
skipDestructive: false,
},
{
name: "Should not update SE when in warm up mode and the update is destructive",
rc: rcWarmupPhase,
newSe: newSeOneEndpoint,
oldSe: oldSeTwoEndpoints,
skipDestructive: true,
},
{
name: "Should update an SE",
rc: rcNotinWarmupPhase,
newSe: newSeOneEndpoint,
oldSe: oldSeTwoEndpoints,
skipDestructive: false,
},

}

//Run the test for every provided case
for _, c := range testCases {
t.Run(c.name, func(t *testing.T) {
addUpdateServiceEntry(c.newSe, c.oldSe, "namespace", c.rc)
if c.skipDestructive {
//verify the update did not go through
se, _ := c.rc.ServiceEntryController.IstioClient.NetworkingV1alpha3().ServiceEntries("namespace").Get(c.oldSe.Name, v12.GetOptions{})
_, diff := getServiceEntryDiff(c.oldSe, se)
if diff != "" {
t.Errorf("Failed. Got %v, expected %v", se.Spec.String(), c.oldSe.Spec.String())
}
}
})
}
}
1 change: 1 addition & 0 deletions admiral/pkg/clusters/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ func (r *RemoteRegistry) createCacheController(clientConfig *rest.Config, cluste
stop: stop,
ClusterID: clusterID,
ApiServer: clientConfig.Host,
StartTime: time.Now(),
}

var err error
Expand Down
Loading