func (configmapcontroller *ConfigMapController) reconcileConfigMap(configmap types.NamespacedName) { if !configmapcontroller.isSynced() { glog.V(4).Infof("Configmap controller not synced") configmapcontroller.deliverConfigMap(configmap, configmapcontroller.clusterAvailableDelay, false) return } key := configmap.String() baseConfigMapObj, exist, err := configmapcontroller.configmapInformerStore.GetByKey(key) if err != nil { glog.Errorf("Failed to query main configmap store for %v: %v", key, err) configmapcontroller.deliverConfigMap(configmap, 0, true) return } if !exist { // Not federated configmap, ignoring. glog.V(8).Infof("Skipping not federated config map: %s", key) return } baseConfigMap := baseConfigMapObj.(*apiv1.ConfigMap) clusters, err := configmapcontroller.configmapFederatedInformer.GetReadyClusters() if err != nil { glog.Errorf("Failed to get cluster list: %v, retrying shortly", err) configmapcontroller.deliverConfigMap(configmap, configmapcontroller.clusterAvailableDelay, false) return } operations := make([]util.FederatedOperation, 0) for _, cluster := range clusters { clusterConfigMapObj, found, err := configmapcontroller.configmapFederatedInformer.GetTargetStore().GetByKey(cluster.Name, key) if err != nil { glog.Errorf("Failed to get %s from %s: %v, retrying shortly", key, cluster.Name, err) configmapcontroller.deliverConfigMap(configmap, 0, true) return } // Do not modify data. desiredConfigMap := &apiv1.ConfigMap{ ObjectMeta: util.DeepCopyRelevantObjectMeta(baseConfigMap.ObjectMeta), Data: baseConfigMap.Data, } if !found { configmapcontroller.eventRecorder.Eventf(baseConfigMap, api.EventTypeNormal, "CreateInCluster", "Creating configmap in cluster %s", cluster.Name) operations = append(operations, util.FederatedOperation{ Type: util.OperationTypeAdd, Obj: desiredConfigMap, ClusterName: cluster.Name, }) } else { clusterConfigMap := clusterConfigMapObj.(*apiv1.ConfigMap) // Update existing configmap, if needed. if !util.ConfigMapEquivalent(desiredConfigMap, clusterConfigMap) { configmapcontroller.eventRecorder.Eventf(baseConfigMap, api.EventTypeNormal, "UpdateInCluster", "Updating configmap in cluster %s", cluster.Name) operations = append(operations, util.FederatedOperation{ Type: util.OperationTypeUpdate, Obj: desiredConfigMap, ClusterName: cluster.Name, }) } } } if len(operations) == 0 { // Everything is in order glog.V(8).Infof("No operations needed for %s", key) return } err = configmapcontroller.federatedUpdater.UpdateWithOnError(operations, configmapcontroller.updateTimeout, func(op util.FederatedOperation, operror error) { configmapcontroller.eventRecorder.Eventf(baseConfigMap, api.EventTypeNormal, "UpdateInClusterFailed", "ConfigMap update in cluster %s failed: %v", op.ClusterName, operror) }) if err != nil { glog.Errorf("Failed to execute updates for %s: %v, retrying shortly", key, err) configmapcontroller.deliverConfigMap(configmap, 0, true) return } }
func TestConfigMapController(t *testing.T) { cluster1 := NewCluster("cluster1", apiv1.ConditionTrue) cluster2 := NewCluster("cluster2", apiv1.ConditionTrue) fakeClient := &fakefedclientset.Clientset{} RegisterFakeList("clusters", &fakeClient.Fake, &federationapi.ClusterList{Items: []federationapi.Cluster{*cluster1}}) RegisterFakeList("configmaps", &fakeClient.Fake, &apiv1.ConfigMapList{Items: []apiv1.ConfigMap{}}) configmapWatch := RegisterFakeWatch("configmaps", &fakeClient.Fake) clusterWatch := RegisterFakeWatch("clusters", &fakeClient.Fake) cluster1Client := &fakekubeclientset.Clientset{} cluster1Watch := RegisterFakeWatch("configmaps", &cluster1Client.Fake) RegisterFakeList("configmaps", &cluster1Client.Fake, &apiv1.ConfigMapList{Items: []apiv1.ConfigMap{}}) cluster1CreateChan := RegisterFakeCopyOnCreate("configmaps", &cluster1Client.Fake, cluster1Watch) cluster1UpdateChan := RegisterFakeCopyOnUpdate("configmaps", &cluster1Client.Fake, cluster1Watch) cluster2Client := &fakekubeclientset.Clientset{} cluster2Watch := RegisterFakeWatch("configmaps", &cluster2Client.Fake) RegisterFakeList("configmaps", &cluster2Client.Fake, &apiv1.ConfigMapList{Items: []apiv1.ConfigMap{}}) cluster2CreateChan := RegisterFakeCopyOnCreate("configmaps", &cluster2Client.Fake, cluster2Watch) configmapController := NewConfigMapController(fakeClient) informer := ToFederatedInformerForTestOnly(configmapController.configmapFederatedInformer) informer.SetClientFactory(func(cluster *federationapi.Cluster) (kubeclientset.Interface, error) { switch cluster.Name { case cluster1.Name: return cluster1Client, nil case cluster2.Name: return cluster2Client, nil default: return nil, fmt.Errorf("Unknown cluster") } }) configmapController.clusterAvailableDelay = time.Second configmapController.configmapReviewDelay = 50 * time.Millisecond configmapController.smallDelay = 20 * time.Millisecond configmapController.updateTimeout = 5 * time.Second stop := make(chan struct{}) configmapController.Run(stop) configmap1 := &apiv1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: "test-configmap", Namespace: "ns", SelfLink: "/api/v1/namespaces/ns/configmaps/test-configmap", }, Data: map[string]string{ "A": "ala ma kota", "B": "quick brown fox", }, } // Test add federated configmap. configmapWatch.Add(configmap1) createdConfigMap := GetConfigMapFromChan(cluster1CreateChan) assert.NotNil(t, createdConfigMap) assert.Equal(t, configmap1.Namespace, createdConfigMap.Namespace) assert.Equal(t, configmap1.Name, createdConfigMap.Name) assert.True(t, util.ConfigMapEquivalent(configmap1, createdConfigMap)) // Wait for the configmap to appear in the informer store err := WaitForStoreUpdate( configmapController.configmapFederatedInformer.GetTargetStore(), cluster1.Name, types.NamespacedName{Namespace: configmap1.Namespace, Name: configmap1.Name}.String(), wait.ForeverTestTimeout) assert.Nil(t, err, "configmap should have appeared in the informer store") // Test update federated configmap. configmap1.Annotations = map[string]string{ "A": "B", } configmapWatch.Modify(configmap1) updatedConfigMap := GetConfigMapFromChan(cluster1UpdateChan) assert.NotNil(t, updatedConfigMap) assert.Equal(t, configmap1.Name, updatedConfigMap.Name) assert.Equal(t, configmap1.Namespace, updatedConfigMap.Namespace) assert.True(t, util.ConfigMapEquivalent(configmap1, updatedConfigMap)) // Test update federated configmap. configmap1.Data = map[string]string{ "config": "myconfigurationfile", } configmapWatch.Modify(configmap1) updatedConfigMap2 := GetConfigMapFromChan(cluster1UpdateChan) assert.NotNil(t, updatedConfigMap) assert.Equal(t, configmap1.Name, updatedConfigMap.Name) assert.Equal(t, configmap1.Namespace, updatedConfigMap.Namespace) assert.True(t, util.ConfigMapEquivalent(configmap1, updatedConfigMap2)) // Test add cluster clusterWatch.Add(cluster2) createdConfigMap2 := GetConfigMapFromChan(cluster2CreateChan) assert.NotNil(t, createdConfigMap2) assert.Equal(t, configmap1.Name, createdConfigMap2.Name) assert.Equal(t, configmap1.Namespace, createdConfigMap2.Namespace) assert.True(t, util.ConfigMapEquivalent(configmap1, createdConfigMap2)) close(stop) }