Repository: Azure/aad-pod-identity
Pull request #385 (2019-09-19 00:10:19)

Add unit tests for forcenamespaced
<!-- Thank you for helping AAD Pod Identity with a pull request! -->

**Reason for Change**:
<!-- What does this PR improve or fix in AAD Pod Identity? Why is it needed? -->
- Add unit tests for forcenamespaced testing
- Log kubernetes server version
- Clean up logs
- Redact client id

**Issue Fixed**:
<!-- If this PR fixes GitHub issue 1234, add "Fixes #1234" to the next line. -->

**Notes for Reviewers**:



Patch:
diff --git a/pkg/cloudprovider/cloudprovider.go b/pkg/cloudprovider/cloudprovider.go
index 6138f29d..2a379b8d 100644
--- a/pkg/cloudprovider/cloudprovider.go
+++ b/pkg/cloudprovider/cloudprovider.go
@@ -11,6 +11,7 @@ import (
 	"time"
 
 	config "github.com/Azure/aad-pod-identity/pkg/config"
+	"github.com/Azure/aad-pod-identity/pkg/utils"
 	"github.com/Azure/aad-pod-identity/version"
 	"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2018-04-01/compute"
 	"github.com/Azure/go-autorest/autorest"
@@ -97,7 +98,7 @@ func NewCloudProvider(configFile string) (c *Client, e error) {
 				return nil, err
 			}
 		} else { // User assigned identity usage.
-			glog.Infof("MIC using user assigned identity: %s for authentication.", azureConfig.UserAssignedIdentityID)
+			glog.Infof("MIC using user assigned identity: %s for authentication.", utils.RedactClientID(azureConfig.UserAssignedIdentityID))
 			spt, err = adal.NewServicePrincipalTokenFromMSIWithUserAssignedID(msiEndpoint, azureEnv.ResourceManagerEndpoint, azureConfig.UserAssignedIdentityID)
 			if err != nil {
 				glog.Errorf("Get token from user assigned MSI error: %+v", err)
diff --git a/pkg/mic/utils.go b/pkg/mic/log.go
similarity index 100%
rename from pkg/mic/utils.go
rename to pkg/mic/log.go
diff --git a/pkg/mic/mic.go b/pkg/mic/mic.go
index 028f5cf7..b22a51b4 100644
--- a/pkg/mic/mic.go
+++ b/pkg/mic/mic.go
@@ -84,6 +84,12 @@ func NewMICClient(cloudconfig string, config *rest.Config, isNamespaced bool, sy
 	glog.Infof("Starting to create the pod identity client. Version: %v. Build date: %v", version.MICVersion, version.BuildDate)
 
 	clientSet := kubernetes.NewForConfigOrDie(config)
+
+	k8sVersion, err := clientSet.ServerVersion()
+	if err == nil {
+		glog.Infof("Kubernetes server version: %s", k8sVersion.String())
+	}
+
 	informer := informers.NewSharedInformerFactory(clientSet, 30*time.Second)
 
 	cloudClient, err := cloudprovider.NewCloudProvider(cloudconfig)
@@ -393,19 +399,18 @@ func (c *Client) createDesiredAssignedIdentityList(
 		}
 
 		for _, binding := range matchedBindings {
-			glog.V(5).Infof("Looking up id map: %v", binding.Spec.AzureIdentity)
+			glog.V(5).Infof("Looking up id map: %s/%s", binding.Namespace, binding.Spec.AzureIdentity)
 			if azureID, idPresent := idMap[getIDKey(binding.Namespace, binding.Spec.AzureIdentity)]; idPresent {
 				// working in Namespaced mode or this specific identity is namespaced
 				if c.IsNamespaced || aadpodid.IsNamespacedIdentity(&azureID) {
 					// They have to match all
 					if !(azureID.Namespace == binding.Namespace && binding.Namespace == pod.Namespace) {
 						glog.V(5).Infof("identity %s/%s was matched via binding %s/%s to %s/%s but namespaced identity is enforced, so it will be ignored",
-							azureID.Namespace, azureID.Name, binding.Namespace, binding.Name, pod.Name, pod.Namespace)
+							azureID.Namespace, azureID.Name, binding.Namespace, binding.Name, pod.Namespace, pod.Name)
 						continue
 					}
 				}
 				glog.V(5).Infof("identity %s/%s assigned to %s/%s via %s/%s", azureID.Namespace, azureID.Name, pod.Namespace, pod.Name, binding.Namespace, binding.Name)
-
 				assignedID, err := c.makeAssignedIDs(azureID, binding, pod.Name, pod.Namespace, pod.Spec.NodeName)
 
 				if err != nil {
@@ -418,7 +423,7 @@ func (c *Client) createDesiredAssignedIdentityList(
 				// In such a case, we will skip it from matching binding.
 				// This will ensure that the new assigned ids created will not have the
 				// one associated with this azure identity.
-				glog.V(5).Infof("%s identity not found when using %s binding", binding.Spec.AzureIdentity, binding.Name)
+				glog.V(5).Infof("%s identity not found when using %s/%s binding", binding.Spec.AzureIdentity, binding.Namespace, binding.Name)
 			}
 		}
 	}
@@ -591,7 +596,7 @@ func (c *Client) makeAssignedIDs(azID aadpodid.AzureIdentity, azBinding aadpodid
 		},
 	}
 	// if we are in namespaced mode (or az identity is namespaced)
-	if c.IsNamespaced || aadpodid.IsNamespacedIdentity(&azID) {
+	if c.IsNamespaced || aadpodid.IsNamespacedIdentity(&id) {
 		assignedID.Namespace = azID.Namespace
 	} else {
 		// eventually this should be identity namespace
@@ -883,7 +888,7 @@ func (c *Client) updateUserMSI(newAssignedIDs []aadpodid.AzureAssignedIdentity,
 // cleanUpAllAssignedIdentitiesOnNode deletes all assigned identities associated with a the node
 func (c *Client) cleanUpAllAssignedIdentitiesOnNode(node string, nodeTrackList trackUserAssignedMSIIds, wg *sync.WaitGroup) {
 	defer wg.Done()
-	glog.Infof("deleting all assigned identites for node %s", node)
+	glog.Infof("deleting all assigned identites for %s as node not found", node)
 	for _, deleteID := range nodeTrackList.assignedIDsToDelete {
 		binding := deleteID.Spec.AzureBindingRef
 
diff --git a/pkg/nmi/server/utils.go b/pkg/nmi/server/log.go
similarity index 71%
rename from pkg/nmi/server/utils.go
rename to pkg/nmi/server/log.go
index 9155851f..cc938e58 100644
--- a/pkg/nmi/server/utils.go
+++ b/pkg/nmi/server/log.go
@@ -1,8 +1,6 @@
 package server
 
 import (
-	"regexp"
-
 	log "github.com/sirupsen/logrus"
 )
 
@@ -28,13 +26,4 @@ func (l Log) Error(err error) {
 // Errorf - log the messages to be error messages and formatted.
 func (l Log) Errorf(format string, args ...interface{}) {
 	log.Errorf(format, &args)
-}
-
-func redactClientID(clientID string) string {
-	return redact(clientID, "$1##### REDACTED #####$3")
-}
-
-func redact(src, repl string) string {
-	r, _ := regexp.Compile("^(\\S{4})(\\S|\\s)*(\\S{4})$")
-	return r.ReplaceAllString(src, repl)
-}
+}
\ No newline at end of file
diff --git a/pkg/nmi/server/server.go b/pkg/nmi/server/server.go
index 2bc33f8d..12869e4b 100644
--- a/pkg/nmi/server/server.go
+++ b/pkg/nmi/server/server.go
@@ -23,6 +23,7 @@ import (
 	iptables "github.com/Azure/aad-pod-identity/pkg/nmi/iptables"
 	"github.com/Azure/aad-pod-identity/pkg/pod"
 	"github.com/Azure/go-autorest/autorest/adal"
+	utils "github.com/Azure/aad-pod-identity/pkg/utils"
 
 	log "github.com/sirupsen/logrus"
 )
@@ -367,12 +368,12 @@ func getTokenForMatchingID(kubeClient k8s.Client, logger *log.Entry, rqClientID
 		idType := v.Spec.Type
 		switch idType {
 		case aadpodid.UserAssignedMSI:
-			logger.Infof("matched identityType:%v clientid:%s resource:%s", idType, redactClientID(clientID), rqResource)
+			logger.Infof("matched identityType:%v clientid:%s resource:%s", idType, utils.RedactClientID(clientID), rqResource)
 			token, err := auth.GetServicePrincipalTokenFromMSIWithUserAssignedID(clientID, rqResource)
 			return token, clientID, err
 		case aadpodid.ServicePrincipal:
 			tenantid := v.Spec.TenantID
-			logger.Infof("matched identityType:%v tenantid:%s clientid:%s resource:%s", idType, tenantid, redactClientID(clientID), rqResource)
+			logger.Infof("matched identityType:%v tenantid:%s clientid:%s resource:%s", idType, tenantid, utils.RedactClientID(clientID), rqResource)
 			secret, err := kubeClient.GetSecret(&v.Spec.ClientPassword)
 			if err != nil {
 				return nil, clientID, err
diff --git a/pkg/probes/probes.go b/pkg/probes/probes.go
index 56f41f1e..b10f38f0 100644
--- a/pkg/probes/probes.go
+++ b/pkg/probes/probes.go
@@ -39,7 +39,7 @@ func Start(port string, log log.Logger) {
 // InitAndStart - Initialize the default probes and starts the http listening port.
 func InitAndStart(port string, condition *bool, log log.Logger) {
 	InitHealthProbe(condition)
-	log.Info("Initialized health probe")
+	log.Infof("Initialized health probe on port %s", port)
 	// start the probe.
 	Start(port, log)
 	log.Info("Started health probe")
diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go
new file mode 100644
index 00000000..a510f038
--- /dev/null
+++ b/pkg/utils/utils.go
@@ -0,0 +1,13 @@
+package utils
+
+import "regexp"
+
+// RedactClientID redacts client id
+func RedactClientID(clientID string) string {
+	return redact(clientID, "$1##### REDACTED #####$3")
+}
+
+func redact(src, repl string) string {
+	r, _ := regexp.Compile("^(\\S{4})(\\S|\\s)*(\\S{4})$")
+	return r.ReplaceAllString(src, repl)
+}


Test patch:
diff --git a/pkg/mic/mic_test.go b/pkg/mic/mic_test.go
index f08c8387..9c69388f 100644
--- a/pkg/mic/mic_test.go
+++ b/pkg/mic/mic_test.go
@@ -383,10 +383,12 @@ func (c *TestCrdClient) UpdateAzureAssignedIdentityStatus(assignedIdentity *aadp
 	return nil
 }
 
-func (c *TestCrdClient) CreateBinding(bindingName string, idName string, selector string) {
+func (c *TestCrdClient) CreateBinding(name, ns, idName, selector, resourceVersion string) {
 	binding := &aadpodid.AzureIdentityBinding{
 		ObjectMeta: v1.ObjectMeta{
-			Name: bindingName,
+			Name:            name,
+			Namespace:       ns,
+			ResourceVersion: resourceVersion,
 		},
 		Spec: aadpodid.AzureIdentityBindingSpec{
 			AzureIdentity: idName,
@@ -394,27 +396,28 @@ func (c *TestCrdClient) CreateBinding(bindingName string, idName string, selecto
 		},
 	}
 	c.mu.Lock()
-	c.bindingMap[bindingName] = binding
+	c.bindingMap[getIDKey(ns, name)] = binding
 	c.mu.Unlock()
 }
 
-func (c *TestCrdClient) CreateID(idName string, t aadpodid.IdentityType, rID string, cID string, cp *api.SecretReference, tID string, adRID string, adEpt string) {
+func (c *TestCrdClient) CreateID(idName, ns string, t aadpodid.IdentityType, rID, cID string, cp *api.SecretReference, tID, adRID, adEpt, resourceVersion string) {
 	id := &aadpodid.AzureIdentity{
 		ObjectMeta: v1.ObjectMeta{
-			Name: idName,
+			Name:            idName,
+			Namespace:       ns,
+			ResourceVersion: resourceVersion,
 		},
 		Spec: aadpodid.AzureIdentitySpec{
-			Type:       t,
-			ResourceID: rID,
-			ClientID:   cID,
-			//ClientPassword: *cp,
+			Type:         t,
+			ResourceID:   rID,
+			ClientID:     cID,
 			TenantID:     tID,
 			ADResourceID: adRID,
 			ADEndpoint:   adEpt,
 		},
 	}
 	c.mu.Lock()
-	c.idMap[idName] = id
+	c.idMap[getIDKey(ns, idName)] = id
 	c.mu.Unlock()
 }
 
@@ -569,7 +572,12 @@ func (c *TestEventRecorder) AnnotatedEventf(object runtime.Object, annotations m
 }
 
 /************************ MIC MOC *************************************/
-func NewMICTestClient(eventCh chan aadpodid.EventType, cpClient *TestCloudClient, crdClient *TestCrdClient, podClient *TestPodClient, nodeClient *TestNodeClient, eventRecorder *TestEventRecorder) *TestMICClient {
+func NewMICTestClient(eventCh chan aadpodid.EventType,
+	cpClient *TestCloudClient,
+	crdClient *TestCrdClient,
+	podClient *TestPodClient,
+	nodeClient *TestNodeClient,
+	eventRecorder *TestEventRecorder, isNamespaced bool) *TestMICClient {
 
 	realMICClient := &Client{
 		CloudClient:       cpClient,
@@ -579,6 +587,7 @@ func NewMICTestClient(eventCh chan aadpodid.EventType, cpClient *TestCloudClient
 		EventChannel:      eventCh,
 		NodeClient:        nodeClient,
 		syncRetryInterval: 120 * time.Second,
+		IsNamespaced:      isNamespaced,
 	}
 
 	return &TestMICClient{
@@ -593,6 +602,7 @@ type TestMICClient struct {
 /************************ UNIT TEST *************************************/
 
 func TestMapMICClient(t *testing.T) {
+	defaultNS := "default"
 	micClient := &TestMICClient{}
 
 	idList := make([]aadpodid.AzureIdentity, 0)
@@ -600,6 +610,7 @@ func TestMapMICClient(t *testing.T) {
 	id := new(aadpodid.AzureIdentity)
 	id.Namespace = "default"
 	id.Name = "test-azure-identity"
+	id.Namespace = defaultNS
 
 	idList = append(idList, *id)
 
@@ -641,7 +652,7 @@ func TestMapMICClient(t *testing.T) {
 		count = count - 1
 	}
 	if count != 0 {
-		t.Fatalf("Test count mismatch - count %d", count)
+		t.Fatalf("Test count mismatch. Expected %d, actual %d", 0, count)
 	}
 }
 
@@ -683,10 +694,10 @@ func TestSimpleMICClient(t *testing.T) {
 	evtRecorder.lastEvent = new(LastEvent)
 	evtRecorder.eventChannel = make(chan bool, 100)
 
-	micClient := NewMICTestClient(eventCh, cloudClient, crdClient, podClient, nodeClient, &evtRecorder)
+	micClient := NewMICTestClient(eventCh, cloudClient, crdClient, podClient, nodeClient, &evtRecorder, false)
 
-	crdClient.CreateID("test-id", aadpodid.UserAssignedMSI, "test-user-msi-resourceid", "test-user-msi-clientid", nil, "", "", "")
-	crdClient.CreateBinding("testbinding", "test-id", "test-select")
+	crdClient.CreateID("test-id", "default", aadpodid.UserAssignedMSI, "test-user-msi-resourceid", "test-user-msi-clientid", nil, "", "", "", "")
+	crdClient.CreateBinding("testbinding", "default", "test-id", "test-select", "")
 
 	nodeClient.AddNode("test-node")
 	podClient.AddPod("test-pod", "default", "test-node", "test-select")
@@ -709,13 +720,6 @@ func TestSimpleMICClient(t *testing.T) {
 			if assignedID.Spec.Pod == "test-pod" && assignedID.Spec.PodNamespace == "default" && assignedID.Spec.NodeName == "test-node" &&
 				assignedID.Spec.AzureBindingRef.Name == "testbinding" && assignedID.Spec.AzureIdentityRef.Name == "test-id" {
 				testPass = true
-				/*
-					testPass = evtRecorder.Validate(&LastEvent{Type: "Normal", Reason: "binding applied",
-						Message: "Binding testbinding applied on node test-node for pod test-pod-default-test-id"})
-					if !testPass {
-						t.Errorf("event mismatch")
-					}
-				*/
 				break
 			}
 		}
@@ -811,19 +815,19 @@ func TestAddDelMICClient(t *testing.T) {
 	evtRecorder.lastEvent = new(LastEvent)
 	evtRecorder.eventChannel = make(chan bool, 100)
 
-	micClient := NewMICTestClient(eventCh, cloudClient, crdClient, podClient, nodeClient, &evtRecorder)
+	micClient := NewMICTestClient(eventCh, cloudClient, crdClient, podClient, nodeClient, &evtRecorder, false)
 
 	// Test to add and delete at the same time.
 	// Add a pod, identity and binding.
-	crdClient.CreateID("test-id2", aadpodid.UserAssignedMSI, "test-user-msi-resourceid", "test-user-msi-clientid", nil, "", "", "")
-	crdClient.CreateBinding("testbinding2", "test-id2", "test-select2")
+	crdClient.CreateID("test-id2", "default", aadpodid.UserAssignedMSI, "test-user-msi-resourceid", "test-user-msi-clientid", nil, "", "", "", "")
+	crdClient.CreateBinding("testbinding2", "default", "test-id2", "test-select2", "")
 
 	nodeClient.AddNode("test-node2")
 	podClient.AddPod("test-pod2", "default", "test-node2", "test-select2")
 	podClient.GetPods()
 
-	crdClient.CreateID("test-id4", aadpodid.UserAssignedMSI, "test-user-msi-resourceid", "test-user-msi-clientid", nil, "", "", "")
-	crdClient.CreateBinding("testbinding4", "test-id4", "test-select4")
+	crdClient.CreateID("test-id4", "default", aadpodid.UserAssignedMSI, "test-user-msi-resourceid", "test-user-msi-clientid", nil, "", "", "", "")
+	crdClient.CreateBinding("testbinding4", "default", "test-id4", "test-select4", "")
 	podClient.AddPod("test-pod4", "default", "test-node2", "test-select4")
 	podClient.GetPods()
 
@@ -855,8 +859,8 @@ func TestAddDelMICClient(t *testing.T) {
 	podClient.DeletePod("test-pod4", "default")
 
 	//Add a new pod, with different id and binding on the same node.
-	crdClient.CreateID("test-id3", aadpodid.UserAssignedMSI, "test-user-msi-resourceid", "test-user-msi-clientid", nil, "", "", "")
-	crdClient.CreateBinding("testbinding3", "test-id3", "test-select3")
+	crdClient.CreateID("test-id3", "default", aadpodid.UserAssignedMSI, "test-user-msi-resourceid", "test-user-msi-clientid", nil, "", "", "", "")
+	crdClient.CreateBinding("testbinding3", "default", "test-id3", "test-select3", "")
 	podClient.AddPod("test-pod3", "default", "test-node2", "test-select3")
 	podClient.GetPods()
 
@@ -903,12 +907,12 @@ func TestMicAddDelVMSS(t *testing.T) {
 	evtRecorder.lastEvent = new(LastEvent)
 	evtRecorder.eventChannel = make(chan bool, 100)
 
-	micClient := NewMICTestClient(eventCh, cloudClient, crdClient, podClient, nodeClient, &evtRecorder)
+	micClient := NewMICTestClient(eventCh, cloudClient, crdClient, podClient, nodeClient, &evtRecorder, false)
 
 	// Test to add and delete at the same time.
 	// Add a pod, identity and binding.
-	crdClient.CreateID("test-id1", aadpodid.UserAssignedMSI, "test-user-msi-resourceid", "test-user-msi-clientid", nil, "", "", "")
-	crdClient.CreateBinding("testbinding1", "test-id1", "test-select1")
+	crdClient.CreateID("test-id1", "default", aadpodid.UserAssignedMSI, "test-user-msi-resourceid", "test-user-msi-clientid", nil, "", "", "", "")
+	crdClient.CreateBinding("testbinding1", "default", "test-id1", "test-select1", "")
 
 	nodeClient.AddNode("test-node1", func(n *corev1.Node) {
 		n.Spec.ProviderID = "azure:///subscriptions/fakeSub/resourceGroups/fakeGroup/providers/Microsoft.Compute/virtualMachineScaleSets/testvmss1/virtualMachines/0"
@@ -1006,11 +1010,11 @@ func TestMICStateFlow(t *testing.T) {
 	evtRecorder.lastEvent = new(LastEvent)
 	evtRecorder.eventChannel = make(chan bool, 100)
 
-	micClient := NewMICTestClient(eventCh, cloudClient, crdClient, podClient, nodeClient, &evtRecorder)
+	micClient := NewMICTestClient(eventCh, cloudClient, crdClient, podClient, nodeClient, &evtRecorder, false)
 
 	// Add a pod, identity and binding.
-	crdClient.CreateID("test-id1", aadpodid.UserAssignedMSI, "test-user-msi-resourceid", "test-user-msi-clientid", nil, "", "", "")
-	crdClient.CreateBinding("testbinding1", "test-id1", "test-select1")
+	crdClient.CreateID("test-id1", "default", aadpodid.UserAssignedMSI, "test-user-msi-resourceid", "test-user-msi-clientid", nil, "", "", "", "")
+	crdClient.CreateBinding("testbinding1", "default", "test-id1", "test-select1", "")
 
 	nodeClient.AddNode("test-node1")
 	podClient.AddPod("test-pod1", "default", "test-node1", "test-select1")
@@ -1062,8 +1066,8 @@ func TestMICStateFlow(t *testing.T) {
 
 	// add new pod, this time the old assigned identity which is in Assigned state should be tried to delete
 	// simulate failure on kube api call to delete crd
-	crdClient.CreateID("test-id2", aadpodid.UserAssignedMSI, "test-user-msi-resourceid2", "test-user-msi-clientid2", nil, "", "", "")
-	crdClient.CreateBinding("testbinding2", "test-id2", "test-select2")
+	crdClient.CreateID("test-id2", "default", aadpodid.UserAssignedMSI, "test-user-msi-resourceid2", "test-user-msi-clientid2", nil, "", "", "", "")
+	crdClient.CreateBinding("testbinding2", "default", "test-id2", "test-select2", "")
 
 	nodeClient.AddNode("test-node2")
 	podClient.AddPod("test-pod2", "default", "test-node2", "test-select2")
@@ -1110,6 +1114,70 @@ func TestMICStateFlow(t *testing.T) {
 	}
 }
 
+func TestForceNamespaced(t *testing.T) {
+	eventCh := make(chan aadpodid.EventType, 100)
+	cloudClient := NewTestCloudClient(config.AzureConfig{})
+	crdClient := NewTestCrdClient(nil)
+	podClient := NewTestPodClient()
+	nodeClient := NewTestNodeClient()
+	var evtRecorder TestEventRecorder
+	evtRecorder.lastEvent = new(LastEvent)
+	evtRecorder.eventChannel = make(chan bool, 100)
+
+	micClient := NewMICTestClient(eventCh, cloudClient, crdClient, podClient, nodeClient, &evtRecorder, true)
+
+	crdClient.CreateID("test-id1", "default", aadpodid.UserAssignedMSI, "test-user-msi-resourceid", "test-user-msi-clientid", nil, "", "", "", "idrv1")
+	crdClient.CreateBinding("testbinding1", "default", "test-id1", "test-select1", "bindingrv1")
+
+	nodeClient.AddNode("test-node1")
+	podClient.AddPod("test-pod1", "default", "test-node1", "test-select1")
+
+	eventCh <- aadpodid.PodCreated
+	defer micClient.testRunSync()(t)
+
+	if !evtRecorder.WaitForEvents(1) {
+		t.Fatalf("Timeout waiting for mic sync cycles")
+	}
+	listAssignedIDs, err := crdClient.ListAssignedIDs()
+	if err != nil {
+		glog.Error(err)
+		t.Errorf("list assigned failed")
+	}
+	if !(len(*listAssignedIDs) == 1) {
+		t.Fatalf("expected assigned identities len: %d, got: %d", 1, len(*listAssignedIDs))
+	}
+	if !((*listAssignedIDs)[0].Status.Status == aadpodid.AssignedIDAssigned) {
+		t.Fatalf("expected status to be %s, got: %s", aadpodid.AssignedIDAssigned, (*listAssignedIDs)[0].Status.Status)
+	}
+
+	crdClient.CreateID("test-id1", "default2", aadpodid.UserAssignedMSI, "test-user-msi-resourceid1", "test-user-msi-clientid", nil, "", "", "", "idrv2")
+	crdClient.CreateBinding("testbinding1", "default2", "test-id1", "test-select1", "bindingrv2")
+	podClient.AddPod("test-pod2", "default2", "test-node1", "test-select1")
+
+	eventCh <- aadpodid.IdentityCreated
+	eventCh <- aadpodid.BindingCreated
+	eventCh <- aadpodid.PodCreated
+
+	if !evtRecorder.WaitForEvents(1) {
+		t.Fatalf("Timeout waiting for mic sync cycles")
+	}
+
+	listAssignedIDs, err = crdClient.ListAssignedIDs()
+	if err != nil {
+		glog.Error(err)
+		t.Errorf("list assigned failed")
+	}
+	if !(len(*listAssignedIDs) == 2) {
+		t.Fatalf("expected assigned identities len: %d, got: %d", 1, len(*listAssignedIDs))
+	}
+
+	for _, assignedID := range *listAssignedIDs {
+		if !(assignedID.Status.Status == aadpodid.AssignedIDAssigned) {
+			t.Fatalf("expected status to be %s, got: %s", aadpodid.AssignedIDAssigned, (*listAssignedIDs)[0].Status.Status)
+		}
+	}
+}
+
 func TestSyncRetryLoop(t *testing.T) {
 	eventCh := make(chan aadpodid.EventType, 100)
 	cloudClient := NewTestCloudClient(config.AzureConfig{})
@@ -1120,7 +1188,7 @@ func TestSyncRetryLoop(t *testing.T) {
 	evtRecorder.lastEvent = new(LastEvent)
 	evtRecorder.eventChannel = make(chan bool, 100)
 
-	micClient := NewMICTestClient(eventCh, cloudClient, crdClient, podClient, nodeClient, &evtRecorder)
+	micClient := NewMICTestClient(eventCh, cloudClient, crdClient, podClient, nodeClient, &evtRecorder, false)
 	syncRetryInterval, err := time.ParseDuration("10s")
 	if err != nil {
 		t.Errorf("error parsing duration: %v", err)
@@ -1128,8 +1196,8 @@ func TestSyncRetryLoop(t *testing.T) {
 	micClient.syncRetryInterval = syncRetryInterval
 
 	// Add a pod, identity and binding.
-	crdClient.CreateID("test-id1", aadpodid.UserAssignedMSI, "test-user-msi-resourceid", "test-user-msi-clientid", nil, "", "", "")
-	crdClient.CreateBinding("testbinding1", "test-id1", "test-select1")
+	crdClient.CreateID("test-id1", "default", aadpodid.UserAssignedMSI, "test-user-msi-resourceid", "test-user-msi-clientid", nil, "", "", "", "")
+	crdClient.CreateBinding("testbinding1", "default", "test-id1", "test-select1", "")
 
 	nodeClient.AddNode("test-node1")
 	podClient.AddPod("test-pod1", "default", "test-node1", "test-select1")
@@ -1199,11 +1267,11 @@ func TestSyncNodeNotFound(t *testing.T) {
 	evtRecorder.lastEvent = new(LastEvent)
 	evtRecorder.eventChannel = make(chan bool, 100)
 
-	micClient := NewMICTestClient(eventCh, cloudClient, crdClient, podClient, nodeClient, &evtRecorder)
+	micClient := NewMICTestClient(eventCh, cloudClient, crdClient, podClient, nodeClient, &evtRecorder, false)
 
 	// Add a pod, identity and binding.
-	crdClient.CreateID("test-id1", aadpodid.UserAssignedMSI, "test-user-msi-resourceid", "test-user-msi-clientid", nil, "", "", "")
-	crdClient.CreateBinding("testbinding1", "test-id1", "test-select1")
+	crdClient.CreateID("test-id1", "default", aadpodid.UserAssignedMSI, "test-user-msi-resourceid", "test-user-msi-clientid", nil, "", "", "", "")
+	crdClient.CreateBinding("testbinding1", "default", "test-id1", "test-select1", "")
 
 	for i := 0; i < 10; i++ {
 		nodeClient.AddNode(fmt.Sprintf("test-node%d", i))
@@ -1271,7 +1339,7 @@ func TestSyncExit(t *testing.T) {
 	evtRecorder.lastEvent = new(LastEvent)
 	evtRecorder.eventChannel = make(chan bool)
 
-	micClient := NewMICTestClient(eventCh, cloudClient, crdClient, podClient, nodeClient, &evtRecorder)
+	micClient := NewMICTestClient(eventCh, cloudClient, crdClient, podClient, nodeClient, &evtRecorder, false)
 
 	micClient.testRunSync()(t)
 }
diff --git a/pkg/nmi/server/utils_test.go b/pkg/utils/utils_test.go
similarity index 88%
rename from pkg/nmi/server/utils_test.go
rename to pkg/utils/utils_test.go
index 151d4d0b..3099d02e 100644
--- a/pkg/nmi/server/utils_test.go
+++ b/pkg/utils/utils_test.go
@@ -1,4 +1,4 @@
-package server
+package utils
 
 import (
 	"testing"
@@ -18,7 +18,7 @@ func TestRedactClientID(t *testing.T) {
 	}
 
 	for _, test := range tests {
-		actual := redactClientID(test.clientID)
+		actual := RedactClientID(test.clientID)
 		if actual != test.expected {
 			t.Fatalf("expected: %s, got %s", test.expected, actual)
 		}