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

Added secret update function #55

Merged
merged 52 commits into from
Apr 27, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
91243aa
Added secret update function
Jan 13, 2020
af43510
finished first draft for recognizing admiral ignore annotation, ready…
Mengying-intuit Apr 14, 2020
a06c1e7
Merge branch 'master' of github.com:istio-ecosystem/admiral
Mengying-intuit Apr 16, 2020
7c65dfc
merged master
Mengying-intuit Apr 16, 2020
bcf6f84
changed test
Mengying-intuit Apr 20, 2020
2e05ff1
local test passed, need to double check in cluyster
Mengying-intuit Apr 20, 2020
56553a8
finish adding call back function for update
Mengying-intuit Apr 20, 2020
de7cef0
Tests for creating controllers (#52)
josephpeacock Jan 4, 2020
4d7e468
Add Istio blog post link for Admiral blog post
aattuluri Jan 7, 2020
9a2d73c
Added secret update function
Jan 13, 2020
25ebf4c
Add slack channel
aattuluri Jan 21, 2020
638bcef
Add slack channel (updates)
aattuluri Jan 21, 2020
264da18
Fix local SE generation. Fix related bugs. (#59)
aattuluri Jan 22, 2020
af1a953
Add multicluster example and bug fixes (#54)
aattuluri Jan 24, 2020
935301f
Fix docker image publish (#61)
aattuluri Jan 24, 2020
f146041
Update slack channel
aattuluri Jan 27, 2020
902768f
Fix configmap roles (#67)
josephpeacock Feb 12, 2020
98d10ba
Formalizing the behavior to fall back to annotation if label isn't pr…
josephpeacock Feb 19, 2020
f94f614
Revert latest commit (#69)
aattuluri Feb 19, 2020
5d47585
Fix init (#70)
josephpeacock Feb 19, 2020
5716874
Add locality lb settings support on destination rules (#41)
aattuluri Feb 26, 2020
b0e4662
Support long identities (#71)
josephpeacock Feb 26, 2020
9248d8c
Stop publishing tags for PRs (#74)
aattuluri Feb 26, 2020
540ac87
Stability fixes (#75)
josephpeacock Feb 29, 2020
ee1ccb3
Skip service instances that were not matched with deployments (#73)
Mar 2, 2020
1f72175
Fixing demo to use admiral-sync for sync namespace (#77)
josephpeacock Mar 2, 2020
d5367b9
fixed bug in adding to cnameidentitycache and variably assignment bug…
josephpeacock Mar 3, 2020
8c496cd
Fix default. Add sync interval. (#78)
aattuluri Mar 3, 2020
cb58c6f
Linking GTPs with Deployments (#80)
josephpeacock Mar 10, 2020
1cb8c13
Organize docs into GitHub pages (#82)
aattuluri Mar 12, 2020
4ff5405
Set theme jekyll-theme-leap-day
aattuluri Mar 12, 2020
6ddb790
Rename Index.md to index.md
aattuluri Mar 12, 2020
e2f2918
Rename index.md to Index.md
aattuluri Mar 12, 2020
e96577b
Update Compatibility.md
aattuluri Mar 12, 2020
37e3740
Use exportTo field to filter resources for syncing across clusters (#81)
aattuluri Mar 13, 2020
dac2450
Fix helm commands for newer versions of helm.
aattuluri Mar 24, 2020
35a8404
Update sidecar resource in workload namespace (#84)
vrushalijoshi Mar 25, 2020
0e70426
updating the sidecar controller (#86)
vrushalijoshi Mar 25, 2020
30b245c
Handle dependency update event, optimize sidecar egress update (#88)
aattuluri Mar 27, 2020
a5f69b1
Adding more unit tests (#89)
josephpeacock Apr 1, 2020
f2ced63
Update CONTRIBUTING.md
aattuluri Apr 10, 2020
a73acd1
finished first draft for recognizing admiral ignore annotation, ready…
Mengying-intuit Apr 14, 2020
6f9f8ec
Add ignore annotation to skip deployments/namespaces (#93)
Mengying-Li Apr 15, 2020
54d23f9
Traffic at ingressgateway should always be routed to cluster local se…
aattuluri Apr 15, 2020
b521a99
changed test
Mengying-intuit Apr 20, 2020
6d93dee
local test passed, need to double check in cluyster
Mengying-intuit Apr 20, 2020
c3f534b
finish adding call back function for update
Mengying-intuit Apr 20, 2020
a1e007e
trying to solve merge conflict during rebase
Mengying-intuit Apr 20, 2020
38ec69f
trying to solve merge conflict during rebase
Mengying-intuit Apr 20, 2020
76d0b2c
Merge branch 'MESH-376' of github.com:istio-ecosystem/admiral into ME…
Mengying-intuit Apr 21, 2020
177b5c1
finish adding call back function for update
Mengying-intuit Apr 20, 2020
1eb4d7c
Merge branch 'MESH-376' of github.com:istio-ecosystem/admiral into ME…
Mengying-intuit Apr 21, 2020
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
8 changes: 8 additions & 0 deletions admiral/pkg/clusters/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ func createSecretController(ctx context.Context, w *RemoteRegistry) error {

err = secret.StartSecretController(w.secretClient,
w.createCacheController,
w.updateCacheController,
w.deleteCacheController,
common.GetClusterRegistriesNamespace(),
ctx, common.GetSecretResolver())
Expand Down Expand Up @@ -182,6 +183,13 @@ func (r *RemoteRegistry) createCacheController(clientConfig *rest.Config, cluste
return nil
}

func (r *RemoteRegistry) updateCacheController(clientConfig *rest.Config, clusterID string, resyncPeriod time.Duration) error {
if err := r.deleteCacheController(clusterID); err != nil {
return err
}
return r.createCacheController(clientConfig, clusterID, resyncPeriod)
}

func (r *RemoteRegistry) deleteCacheController(clusterID string) error {

controller, ok := r.remoteControllers[clusterID]
Expand Down
117 changes: 88 additions & 29 deletions admiral/pkg/controller/secret/secretcontroller.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ var LoadKubeConfig = clientcmd.Load
// addSecretCallback prototype for the add secret callback function.
type addSecretCallback func(config *rest.Config, dataKey string, resyncPeriod time.Duration) error

// updateSecretCallback prototype for the update secret callback function.
type updateSecretCallback func(config *rest.Config, dataKey string, resyncPeriod time.Duration) error

// removeSecretCallback prototype for the remove secret callback function.
type removeSecretCallback func(dataKey string) error

Expand All @@ -58,6 +61,7 @@ type Controller struct {
queue workqueue.RateLimitingInterface
informer cache.SharedIndexInformer
addCallback addSecretCallback
updateCallback updateSecretCallback
removeCallback removeSecretCallback
secretResolver resolver.SecretResolver
}
Expand Down Expand Up @@ -86,6 +90,7 @@ func NewController(
namespace string,
cs *ClusterStore,
addCallback addSecretCallback,
updateCallback updateSecretCallback,
removeCallback removeSecretCallback,
secretResolverType string) *Controller {

Expand Down Expand Up @@ -126,6 +131,7 @@ func NewController(
informer: secretsInformer,
queue: queue,
addCallback: addCallback,
updateCallback: updateCallback,
removeCallback: removeCallback,
secretResolver: secretResolver,
}
Expand All @@ -139,6 +145,13 @@ func NewController(
queue.Add(key)
}
},
UpdateFunc: func(oldObj interface{}, newObj interface{}) {
key, err := cache.MetaNamespaceKeyFunc(newObj)
log.Infof("Processing update: %s", key)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change the log line to "Processing secret update for cluster: %s" for better readability.

if err == nil {
queue.Add(key)
}
},
DeleteFunc: func(obj interface{}) {
key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj)
log.Infof("Processing delete: %s", key)
Expand Down Expand Up @@ -172,10 +185,17 @@ func (c *Controller) Run(stopCh <-chan struct{}) {
}

// StartSecretController creates the secret controller.
func StartSecretController(k8s kubernetes.Interface, addCallback addSecretCallback, removeCallback removeSecretCallback, namespace string, ctx context.Context, secretResolverType string) error {
func StartSecretController(
k8s kubernetes.Interface,
addCallback addSecretCallback,
updateCallback updateSecretCallback,
removeCallback removeSecretCallback,
namespace string,
ctx context.Context,
secretResolverType string) error {

clusterStore := newClustersStore()
controller := NewController(k8s, namespace, clusterStore, addCallback, removeCallback, secretResolverType)
controller := NewController(k8s, namespace, clusterStore, addCallback, updateCallback, removeCallback, secretResolverType)

go controller.Run(ctx.Done())

Expand Down Expand Up @@ -227,52 +247,91 @@ func (c *Controller) processItem(secretName string) error {
return nil
}

func (c *Controller) createRemoteCluster(kubeConfig []byte, secretName string, clusterID string, namespace string) (*RemoteCluster, *rest.Config, error) {
if len(kubeConfig) == 0 {
log.Infof("Data '%s' in the secret %s in namespace %s is empty, and disregarded ",
clusterID, secretName, namespace)
return nil, nil, errors.New("kubeconfig is empty")
}

kubeConfig, err := c.secretResolver.FetchKubeConfig(clusterID, kubeConfig)

if err != nil {
log.Errorf("Failed to fetch kubeconfig for cluster '%s' using secret resolver: %v, err: %v",
clusterID, c.secretResolver, err)
return nil, nil, errors.New("kubeconfig cannot be fetched")
}

clusterConfig, err := LoadKubeConfig(kubeConfig)

if err != nil {
log.Infof("Data '%s' in the secret %s in namespace %s is not a kubeconfig: %v",
clusterID, secretName, namespace, err)
log.Infof("KubeConfig: '%s'", string(kubeConfig))
return nil, nil, errors.New("clusterConfig cannot be loaded")
}

clientConfig := clientcmd.NewDefaultClientConfig(*clusterConfig, &clientcmd.ConfigOverrides{})

var restConfig *rest.Config
restConfig, err = clientConfig.ClientConfig()

if err != nil {
log.Errorf("error during conversion of secret to client config: %v", err)
return nil, nil, errors.New("restConfig cannot be built")
}

return &RemoteCluster{
secretName: secretName,
}, restConfig, nil
}

func (c *Controller) addMemberCluster(secretName string, s *corev1.Secret) {
for clusterID, kubeConfig := range s.Data {
// clusterID must be unique even across multiple secrets
if _, ok := c.cs.remoteClusters[clusterID]; !ok {
if len(kubeConfig) == 0 {
log.Infof("Data '%s' in the secret %s in namespace %s is empty, and disregarded ",
clusterID, secretName, s.ObjectMeta.Namespace)
continue
}
if prev, ok := c.cs.remoteClusters[clusterID]; !ok {
log.Infof("Adding cluster_id=%v from secret=%v", clusterID, secretName)

kubeConfig, err := c.secretResolver.FetchKubeConfig(clusterID, kubeConfig)
remoteCluster, restConfig, err := c.createRemoteCluster(kubeConfig, secretName, clusterID, s.ObjectMeta.Namespace)

if err != nil {
log.Errorf("Failed to fetch kubeconfig for cluster '%s' using secret resolver: %v, err: %v",
clusterID, c.secretResolver, err)
log.Errorf("Failed to add remote cluster from secret=%v for cluster_id=%v: %v",
secretName, clusterID, err)
continue
}

clusterConfig, err := LoadKubeConfig(kubeConfig)
if err != nil {
log.Infof("Data '%s' in the secret %s in namespace %s is not a kubeconfig: %v",
clusterID, secretName, s.ObjectMeta.Namespace, err)
log.Infof("KubeConfig: '%s'", string(kubeConfig))
c.cs.remoteClusters[clusterID] = remoteCluster

if err := c.addCallback(restConfig, clusterID, 2 * time.Minute); err != nil {
log.Errorf("error during secret loading for clusterID: %s %v", clusterID, err)
continue
}

log.Infof("Adding new cluster member: %s", clusterID)
c.cs.remoteClusters[clusterID] = &RemoteCluster{}
c.cs.remoteClusters[clusterID].secretName = secretName
clientConfig := clientcmd.NewDefaultClientConfig(*clusterConfig, &clientcmd.ConfigOverrides{})
log.Infof("Secret loaded for cluster %s in the secret %s in namespace %s.",clusterID,c.cs.remoteClusters[clusterID].secretName, s.ObjectMeta.Namespace)

} else {
if prev.secretName != secretName {
log.Errorf("ClusterID reused in two different secrets: %v and %v. ClusterID "+
"must be unique across all secrets", prev.secretName, secretName)
continue
}

var restConfig *rest.Config
restConfig, err = clientConfig.ClientConfig()
log.Infof("Updating cluster %v from secret %v", clusterID, secretName)

remoteCluster, restConfig, err := c.createRemoteCluster(kubeConfig, secretName, clusterID, s.ObjectMeta.Namespace)
if err != nil {
log.Errorf("error during conversion of secret to client config: %v", err)
log.Errorf("Error updating cluster_id=%v from secret=%v: %v",
clusterID, secretName, err)
continue
}

err = c.addCallback(restConfig, clusterID, 2 * time.Minute)
if err != nil {
log.Errorf("error during create of clusterID: %s %v", clusterID, err)
c.cs.remoteClusters[clusterID] = remoteCluster
if err := c.updateCallback(restConfig, clusterID, 2 * time.Minute); err != nil {
log.Errorf("Error updating cluster_id from secret=%v: %s %v",
clusterID, secretName, err)
}
} else {
log.Infof("Cluster %s in the secret %s in namespace %s already exists",
clusterID, c.cs.remoteClusters[clusterID].secretName, s.ObjectMeta.Namespace)
}

}
log.Infof("Number of remote clusters: %d", len(c.cs.remoteClusters))
}
Expand Down
150 changes: 149 additions & 1 deletion admiral/pkg/controller/secret/secretcontroller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,13 @@ package secret

import (
"context"
"fmt"
"k8s.io/client-go/rest"
"sync"
"testing"
"time"

. "github.com/onsi/gomega"
"k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes/fake"
Expand All @@ -34,6 +37,57 @@ const secretNameSpace string = "istio-system"
var testCreateControllerCalled bool
var testDeleteControllerCalled bool


func makeSecret(secret, clusterID string, kubeconfig []byte) *v1.Secret {
return &v1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: secret,
Namespace: secretNameSpace,
Labels: map[string]string{
filterLabel: "true",
},
},
Data: map[string][]byte{
clusterID: kubeconfig,
},
}
}

var (
mu sync.Mutex
added string
updated string
deleted string
)

func addCallback(config *rest.Config, id string, resyncPeriod time.Duration) error {
mu.Lock()
defer mu.Unlock()
added = id
return nil
}

func updateCallback(config *rest.Config, id string, resyncPeriod time.Duration) error {
mu.Lock()
defer mu.Unlock()
updated = id
return nil
}

func deleteCallback(id string) error {
mu.Lock()
defer mu.Unlock()
deleted = id
return nil
}

func resetCallbackData() {
added = ""
updated = ""
deleted = ""
}


func testCreateController(clientConfig *rest.Config, clusterID string, resyncPeriod time.Duration) error {
testCreateControllerCalled = true
return nil
Expand Down Expand Up @@ -71,7 +125,15 @@ func deleteMultiClusterSecret(k8s *fake.Clientset) error {
}

func mockLoadKubeConfig(kubeconfig []byte) (*clientcmdapi.Config, error) {
return &clientcmdapi.Config{}, nil
config := clientcmdapi.NewConfig()
config.Clusters["clean"] = &clientcmdapi.Cluster{
Server: "https://anything.com:8080",
}
config.Contexts["clean"] = &clientcmdapi.Context{
Cluster: "clean",
}
config.CurrentContext = "clean"
return config, nil
}

func verifyControllerDeleted(t *testing.T, timeoutName string) {
Expand All @@ -86,6 +148,7 @@ func verifyControllerCreated(t *testing.T, timeoutName string) {
})
}

/*
func Test_SecretController(t *testing.T) {
LoadKubeConfig = mockLoadKubeConfig

Expand Down Expand Up @@ -127,4 +190,89 @@ func Test_SecretController(t *testing.T) {
if testCreateControllerCalled != false {
t.Fatalf("Test failed on delete secret, create callback function called")
}
}*/


func Test_SecretController(t *testing.T) {
g := NewWithT(t)

LoadKubeConfig = mockLoadKubeConfig

clientset := fake.NewSimpleClientset()

var (
secret0 = makeSecret("s0", "c0", []byte("kubeconfig0-0"))
secret0UpdateKubeconfigChanged = makeSecret("s0", "c0", []byte("kubeconfig0-1"))
secret1 = makeSecret("s1", "c1", []byte("kubeconfig1-0"))
)

steps := []struct {
// only set one of these per step. The others should be nil.
add *v1.Secret
update *v1.Secret
delete *v1.Secret

// only set one of these per step. The others should be empty.
wantAdded string
wantUpdated string
wantDeleted string
}{
{add: secret0, wantAdded: "c0"},
{update: secret0UpdateKubeconfigChanged, wantUpdated: "c0"},
{add: secret1, wantAdded: "c1"},
{delete: secret0, wantDeleted: "c0"},
{delete: secret1, wantDeleted: "c1"},
}

// Start the secret controller and sleep to allow secret process to start.
g.Expect(
StartSecretController(clientset, addCallback, updateCallback, deleteCallback, secretNameSpace, context.TODO(), "")).
Should(Succeed())

for i, step := range steps {
resetCallbackData()

t.Run(fmt.Sprintf("[%v]", i), func(t *testing.T) {
g := NewWithT(t)

switch {
case step.add != nil:
_, err := clientset.CoreV1().Secrets(secretNameSpace).Create(step.add)
g.Expect(err).Should(BeNil())
case step.update != nil:
_, err := clientset.CoreV1().Secrets(secretNameSpace).Update(step.update)
g.Expect(err).Should(BeNil())
case step.delete != nil:
g.Expect(clientset.CoreV1().Secrets(secretNameSpace).Delete(step.delete.Name, &metav1.DeleteOptions{})).
Should(Succeed())
}

switch {
case step.wantAdded != "":
g.Eventually(func() string {
mu.Lock()
defer mu.Unlock()
return added
}, 10*time.Second).Should(Equal(step.wantAdded))
case step.wantUpdated != "":
g.Eventually(func() string {
mu.Lock()
defer mu.Unlock()
return updated
}, 10*time.Second).Should(Equal(step.wantUpdated))
case step.wantDeleted != "":
g.Eventually(func() string {
mu.Lock()
defer mu.Unlock()
return deleted
}, 10*time.Second).Should(Equal(step.wantDeleted))
default:
g.Consistently(func() bool {
mu.Lock()
defer mu.Unlock()
return added == "" && updated == "" && deleted == ""
}).Should(Equal(true))
}
})
}
}
Loading