Merge pull request #45364 from marun/fed-record-events-in-updater

Automatic merge from submit-queue

[Federation] Record events in federated updater

Controllers for federated types were previously recording events when generating the list of operations.  This change delegates responsibility for recording events to the federated updater so that events are recorded when the operations are actually executed, and ensures consistency across recording of both operation initiation and failure.  

The deletion helper was similarly updated to rely on the federated updater for event recording.  To support this change to the deletion helper, controllers have been updated to provide a namespace qualified name via the objNameFunc function to ensure that the updater can record events for deletions with the same detail as for add and update operations.

cc: @kubernetes/sig-federation-pr-reviews @perotinus
This commit is contained in:
Kubernetes Submit Queue 2017-05-04 16:27:15 -07:00 committed by GitHub
commit b3beeff9c2
11 changed files with 83 additions and 101 deletions

View File

@ -188,7 +188,7 @@ func NewDeploymentController(federationClient fedclientset.Interface) *Deploymen
), ),
) )
fdc.fedUpdater = fedutil.NewFederatedUpdater(fdc.fedDeploymentInformer, fdc.fedUpdater = fedutil.NewFederatedUpdater(fdc.fedDeploymentInformer, "deployment", fdc.eventRecorder,
func(client kubeclientset.Interface, obj runtime.Object) error { func(client kubeclientset.Interface, obj runtime.Object) error {
rs := obj.(*extensionsv1.Deployment) rs := obj.(*extensionsv1.Deployment)
_, err := client.Extensions().Deployments(rs.Namespace).Create(rs) _, err := client.Extensions().Deployments(rs.Namespace).Create(rs)
@ -211,10 +211,9 @@ func NewDeploymentController(federationClient fedclientset.Interface) *Deploymen
// objNameFunc // objNameFunc
func(obj runtime.Object) string { func(obj runtime.Object) string {
deployment := obj.(*extensionsv1.Deployment) deployment := obj.(*extensionsv1.Deployment)
return deployment.Name return fmt.Sprintf("%s/%s", deployment.Namespace, deployment.Name)
}, },
updateTimeout, updateTimeout,
fdc.eventRecorder,
fdc.fedDeploymentInformer, fdc.fedDeploymentInformer,
fdc.fedUpdater, fdc.fedUpdater,
) )
@ -526,13 +525,11 @@ func (fdc *DeploymentController) reconcileDeployment(key string) (reconciliation
if !exists { if !exists {
if replicas > 0 { if replicas > 0 {
fdc.eventRecorder.Eventf(fd, api.EventTypeNormal, "CreateInCluster",
"Creating deployment in cluster %s", clusterName)
operations = append(operations, fedutil.FederatedOperation{ operations = append(operations, fedutil.FederatedOperation{
Type: fedutil.OperationTypeAdd, Type: fedutil.OperationTypeAdd,
Obj: ld, Obj: ld,
ClusterName: clusterName, ClusterName: clusterName,
Key: key,
}) })
} }
} else { } else {
@ -541,13 +538,11 @@ func (fdc *DeploymentController) reconcileDeployment(key string) (reconciliation
currentLd := ldObj.(*extensionsv1.Deployment) currentLd := ldObj.(*extensionsv1.Deployment)
// Update existing replica set, if needed. // Update existing replica set, if needed.
if !fedutil.DeploymentEquivalent(ld, currentLd) { if !fedutil.DeploymentEquivalent(ld, currentLd) {
fdc.eventRecorder.Eventf(fd, api.EventTypeNormal, "UpdateInCluster",
"Updating deployment in cluster %s", clusterName)
operations = append(operations, fedutil.FederatedOperation{ operations = append(operations, fedutil.FederatedOperation{
Type: fedutil.OperationTypeUpdate, Type: fedutil.OperationTypeUpdate,
Obj: ld, Obj: ld,
ClusterName: clusterName, ClusterName: clusterName,
Key: key,
}) })
glog.Infof("Updating %s in %s", currentLd.Name, clusterName) glog.Infof("Updating %s in %s", currentLd.Name, clusterName)
} }
@ -572,10 +567,7 @@ func (fdc *DeploymentController) reconcileDeployment(key string) (reconciliation
// Everything is in order // Everything is in order
return statusAllOk, nil return statusAllOk, nil
} }
err = fdc.fedUpdater.UpdateWithOnError(operations, updateTimeout, func(op fedutil.FederatedOperation, operror error) { err = fdc.fedUpdater.Update(operations, updateTimeout)
fdc.eventRecorder.Eventf(fd, api.EventTypeWarning, "FailedUpdateInCluster",
"Deployment update in cluster %s failed: %v", op.ClusterName, operror)
})
if err != nil { if err != nil {
glog.Errorf("Failed to execute updates for %s: %v", key, err) glog.Errorf("Failed to execute updates for %s: %v", key, err)
return statusError, err return statusError, err

View File

@ -229,7 +229,7 @@ func NewIngressController(client federationclientset.Interface) *IngressControll
) )
// Federated ingress updater along with Create/Update/Delete operations. // Federated ingress updater along with Create/Update/Delete operations.
ic.federatedIngressUpdater = util.NewFederatedUpdater(ic.ingressFederatedInformer, ic.federatedIngressUpdater = util.NewFederatedUpdater(ic.ingressFederatedInformer, "ingress", ic.eventRecorder,
func(client kubeclientset.Interface, obj pkgruntime.Object) error { func(client kubeclientset.Interface, obj pkgruntime.Object) error {
ingress := obj.(*extensionsv1beta1.Ingress) ingress := obj.(*extensionsv1beta1.Ingress)
glog.V(4).Infof("Attempting to create Ingress: %v", ingress) glog.V(4).Infof("Attempting to create Ingress: %v", ingress)
@ -261,7 +261,7 @@ func NewIngressController(client federationclientset.Interface) *IngressControll
}) })
// Federated configmap updater along with Create/Update/Delete operations. Only Update should ever be called. // Federated configmap updater along with Create/Update/Delete operations. Only Update should ever be called.
ic.federatedConfigMapUpdater = util.NewFederatedUpdater(ic.configMapFederatedInformer, ic.federatedConfigMapUpdater = util.NewFederatedUpdater(ic.configMapFederatedInformer, "configmap", ic.eventRecorder,
func(client kubeclientset.Interface, obj pkgruntime.Object) error { func(client kubeclientset.Interface, obj pkgruntime.Object) error {
configMap := obj.(*v1.ConfigMap) configMap := obj.(*v1.ConfigMap)
configMapName := types.NamespacedName{Name: configMap.Name, Namespace: configMap.Namespace} configMapName := types.NamespacedName{Name: configMap.Name, Namespace: configMap.Namespace}
@ -294,10 +294,9 @@ func NewIngressController(client federationclientset.Interface) *IngressControll
// objNameFunc // objNameFunc
func(obj pkgruntime.Object) string { func(obj pkgruntime.Object) string {
ingress := obj.(*extensionsv1beta1.Ingress) ingress := obj.(*extensionsv1beta1.Ingress)
return ingress.Name return fmt.Sprintf("%s/%s", ingress.Namespace, ingress.Name)
}, },
ic.updateTimeout, ic.updateTimeout,
ic.eventRecorder,
ic.ingressFederatedInformer, ic.ingressFederatedInformer,
ic.federatedIngressUpdater, ic.federatedIngressUpdater,
) )
@ -566,12 +565,12 @@ func (ic *IngressController) reconcileConfigMap(cluster *federationapi.Cluster,
Type: util.OperationTypeUpdate, Type: util.OperationTypeUpdate,
Obj: configMap, Obj: configMap,
ClusterName: cluster.Name, ClusterName: cluster.Name,
Key: configMapNsName.String(),
}} }}
glog.V(4).Infof("Calling federatedConfigMapUpdater.Update() - operations: %v", operations) glog.V(4).Infof("Calling federatedConfigMapUpdater.Update() - operations: %v", operations)
err := ic.federatedConfigMapUpdater.Update(operations, ic.updateTimeout) err := ic.federatedConfigMapUpdater.Update(operations, ic.updateTimeout)
if err != nil { if err != nil {
configMapName := types.NamespacedName{Name: configMap.Name, Namespace: configMap.Namespace} glog.Errorf("Failed to execute update of ConfigMap %q on cluster %q: %v", configMapNsName, cluster.Name, err)
glog.Errorf("Failed to execute update of ConfigMap %q on cluster %q: %v", configMapName, cluster.Name, err)
ic.configMapDeliverer.DeliverAfter(cluster.Name, nil, ic.configMapReviewDelay) ic.configMapDeliverer.DeliverAfter(cluster.Name, nil, ic.configMapReviewDelay)
} }
} }
@ -770,8 +769,6 @@ func (ic *IngressController) reconcileIngress(ingress types.NamespacedName) {
glog.V(4).Infof("No existing Ingress %s in cluster %s - checking if appropriate to queue a create operation", ingress, cluster.Name) glog.V(4).Infof("No existing Ingress %s in cluster %s - checking if appropriate to queue a create operation", ingress, cluster.Name)
// We can't supply server-created fields when creating a new object. // We can't supply server-created fields when creating a new object.
desiredIngress.ObjectMeta = util.DeepCopyRelevantObjectMeta(baseIngress.ObjectMeta) desiredIngress.ObjectMeta = util.DeepCopyRelevantObjectMeta(baseIngress.ObjectMeta)
ic.eventRecorder.Eventf(baseIngress, api.EventTypeNormal, "CreateInCluster",
"Creating ingress in cluster %s", cluster.Name)
// We always first create an ingress in the first available cluster. Once that ingress // We always first create an ingress in the first available cluster. Once that ingress
// has been created and allocated a global IP (visible via an annotation), // has been created and allocated a global IP (visible via an annotation),
@ -797,6 +794,7 @@ func (ic *IngressController) reconcileIngress(ingress types.NamespacedName) {
Type: util.OperationTypeAdd, Type: util.OperationTypeAdd,
Obj: desiredIngress, Obj: desiredIngress,
ClusterName: cluster.Name, ClusterName: cluster.Name,
Key: key,
}) })
} else { } else {
glog.V(4).Infof("No annotation %q exists on ingress %q in federation and waiting for ingress in cluster %s. Not queueing create operation for ingress until annotation exists", staticIPNameKeyWritable, ingress, firstClusterName) glog.V(4).Infof("No annotation %q exists on ingress %q in federation and waiting for ingress in cluster %s. Not queueing create operation for ingress until annotation exists", staticIPNameKeyWritable, ingress, firstClusterName)
@ -867,13 +865,12 @@ func (ic *IngressController) reconcileIngress(ingress types.NamespacedName) {
for key, val := range baseIngress.ObjectMeta.Labels { for key, val := range baseIngress.ObjectMeta.Labels {
desiredIngress.ObjectMeta.Labels[key] = val desiredIngress.ObjectMeta.Labels[key] = val
} }
ic.eventRecorder.Eventf(baseIngress, api.EventTypeNormal, "UpdateInCluster",
"Updating ingress in cluster %s", cluster.Name)
operations = append(operations, util.FederatedOperation{ operations = append(operations, util.FederatedOperation{
Type: util.OperationTypeUpdate, Type: util.OperationTypeUpdate,
Obj: desiredIngress, Obj: desiredIngress,
ClusterName: cluster.Name, ClusterName: cluster.Name,
Key: key,
}) })
// TODO: Transfer any readonly (target-proxy, url-map etc) annotations from the master cluster to the federation, if this is the master cluster. // TODO: Transfer any readonly (target-proxy, url-map etc) annotations from the master cluster to the federation, if this is the master cluster.
// This is only for consistency, so that the federation ingress metadata matches the underlying clusters. It's not actually required } // This is only for consistency, so that the federation ingress metadata matches the underlying clusters. It's not actually required }
@ -887,10 +884,7 @@ func (ic *IngressController) reconcileIngress(ingress types.NamespacedName) {
return return
} }
glog.V(4).Infof("Calling federatedUpdater.Update() - operations: %v", operations) glog.V(4).Infof("Calling federatedUpdater.Update() - operations: %v", operations)
err = ic.federatedIngressUpdater.UpdateWithOnError(operations, ic.updateTimeout, func(op util.FederatedOperation, operror error) { err = ic.federatedIngressUpdater.Update(operations, ic.updateTimeout)
ic.eventRecorder.Eventf(baseIngress, api.EventTypeWarning, "FailedClusterUpdate",
"Ingress update in cluster %s failed: %v", op.ClusterName, operror)
})
if err != nil { if err != nil {
glog.Errorf("Failed to execute updates for %s: %v", ingress, err) glog.Errorf("Failed to execute updates for %s: %v", ingress, err)
ic.deliverIngress(ingress, ic.ingressReviewDelay, true) ic.deliverIngress(ingress, ic.ingressReviewDelay, true)

View File

@ -156,7 +156,7 @@ func NewNamespaceController(client federationclientset.Interface, dynamicClientP
) )
// Federated updater along with Create/Update/Delete operations. // Federated updater along with Create/Update/Delete operations.
nc.federatedUpdater = util.NewFederatedUpdater(nc.namespaceFederatedInformer, nc.federatedUpdater = util.NewFederatedUpdater(nc.namespaceFederatedInformer, "namespace", nc.eventRecorder,
func(client kubeclientset.Interface, obj runtime.Object) error { func(client kubeclientset.Interface, obj runtime.Object) error {
namespace := obj.(*apiv1.Namespace) namespace := obj.(*apiv1.Namespace)
_, err := client.Core().Namespaces().Create(namespace) _, err := client.Core().Namespaces().Create(namespace)
@ -183,10 +183,9 @@ func NewNamespaceController(client federationclientset.Interface, dynamicClientP
// objNameFunc // objNameFunc
func(obj runtime.Object) string { func(obj runtime.Object) string {
namespace := obj.(*apiv1.Namespace) namespace := obj.(*apiv1.Namespace)
return namespace.Name return fmt.Sprintf("%s/%s", namespace.Namespace, namespace.Name)
}, },
nc.updateTimeout, nc.updateTimeout,
nc.eventRecorder,
nc.namespaceFederatedInformer, nc.namespaceFederatedInformer,
nc.federatedUpdater, nc.federatedUpdater,
) )
@ -370,26 +369,22 @@ func (nc *NamespaceController) reconcileNamespace(namespace string) {
glog.V(5).Infof("Desired namespace in underlying clusters: %+v", desiredNamespace) glog.V(5).Infof("Desired namespace in underlying clusters: %+v", desiredNamespace)
if !found { if !found {
nc.eventRecorder.Eventf(baseNamespace, api.EventTypeNormal, "CreateInCluster",
"Creating namespace in cluster %s", cluster.Name)
operations = append(operations, util.FederatedOperation{ operations = append(operations, util.FederatedOperation{
Type: util.OperationTypeAdd, Type: util.OperationTypeAdd,
Obj: desiredNamespace, Obj: desiredNamespace,
ClusterName: cluster.Name, ClusterName: cluster.Name,
Key: namespace,
}) })
} else { } else {
clusterNamespace := clusterNamespaceObj.(*apiv1.Namespace) clusterNamespace := clusterNamespaceObj.(*apiv1.Namespace)
// Update existing namespace, if needed. // Update existing namespace, if needed.
if !util.ObjectMetaAndSpecEquivalent(desiredNamespace, clusterNamespace) { if !util.ObjectMetaAndSpecEquivalent(desiredNamespace, clusterNamespace) {
nc.eventRecorder.Eventf(baseNamespace, api.EventTypeNormal, "UpdateInCluster",
"Updating namespace in cluster %s. Desired: %+v\n Actual: %+v\n", cluster.Name, desiredNamespace, clusterNamespace)
operations = append(operations, util.FederatedOperation{ operations = append(operations, util.FederatedOperation{
Type: util.OperationTypeUpdate, Type: util.OperationTypeUpdate,
Obj: desiredNamespace, Obj: desiredNamespace,
ClusterName: cluster.Name, ClusterName: cluster.Name,
Key: namespace,
}) })
} }
} }
@ -401,10 +396,7 @@ func (nc *NamespaceController) reconcileNamespace(namespace string) {
} }
glog.V(2).Infof("Updating namespace %s in underlying clusters. Operations: %d", baseNamespace.Name, len(operations)) glog.V(2).Infof("Updating namespace %s in underlying clusters. Operations: %d", baseNamespace.Name, len(operations))
err = nc.federatedUpdater.UpdateWithOnError(operations, nc.updateTimeout, func(op util.FederatedOperation, operror error) { err = nc.federatedUpdater.Update(operations, nc.updateTimeout)
nc.eventRecorder.Eventf(baseNamespace, api.EventTypeWarning, "UpdateInClusterFailed",
"Namespace update in cluster %s failed: %v", op.ClusterName, operror)
})
if err != nil { if err != nil {
glog.Errorf("Failed to execute updates for %s: %v", namespace, err) glog.Errorf("Failed to execute updates for %s: %v", namespace, err)
nc.deliverNamespace(namespace, 0, true) nc.deliverNamespace(namespace, 0, true)

View File

@ -196,7 +196,7 @@ func NewReplicaSetController(federationClient fedclientset.Interface) *ReplicaSe
) )
frsc.replicaSetLister = extensionslisters.NewReplicaSetLister(replicaSetIndexer) frsc.replicaSetLister = extensionslisters.NewReplicaSetLister(replicaSetIndexer)
frsc.fedUpdater = fedutil.NewFederatedUpdater(frsc.fedReplicaSetInformer, frsc.fedUpdater = fedutil.NewFederatedUpdater(frsc.fedReplicaSetInformer, "replicaset", frsc.eventRecorder,
func(client kubeclientset.Interface, obj runtime.Object) error { func(client kubeclientset.Interface, obj runtime.Object) error {
rs := obj.(*extensionsv1.ReplicaSet) rs := obj.(*extensionsv1.ReplicaSet)
_, err := client.Extensions().ReplicaSets(rs.Namespace).Create(rs) _, err := client.Extensions().ReplicaSets(rs.Namespace).Create(rs)
@ -219,10 +219,9 @@ func NewReplicaSetController(federationClient fedclientset.Interface) *ReplicaSe
// objNameFunc // objNameFunc
func(obj runtime.Object) string { func(obj runtime.Object) string {
replicaset := obj.(*extensionsv1.ReplicaSet) replicaset := obj.(*extensionsv1.ReplicaSet)
return replicaset.Name return fmt.Sprintf("%s/%s", replicaset.Namespace, replicaset.Name)
}, },
updateTimeout, updateTimeout,
frsc.eventRecorder,
frsc.fedReplicaSetInformer, frsc.fedReplicaSetInformer,
frsc.fedUpdater, frsc.fedUpdater,
) )
@ -543,26 +542,22 @@ func (frsc *ReplicaSetController) reconcileReplicaSet(key string) (reconciliatio
if !exists { if !exists {
if replicas > 0 { if replicas > 0 {
frsc.eventRecorder.Eventf(frs, api.EventTypeNormal, "CreateInCluster",
"Creating replicaset in cluster %s", clusterName)
operations = append(operations, fedutil.FederatedOperation{ operations = append(operations, fedutil.FederatedOperation{
Type: fedutil.OperationTypeAdd, Type: fedutil.OperationTypeAdd,
Obj: lrs, Obj: lrs,
ClusterName: clusterName, ClusterName: clusterName,
Key: key,
}) })
} }
} else { } else {
currentLrs := lrsObj.(*extensionsv1.ReplicaSet) currentLrs := lrsObj.(*extensionsv1.ReplicaSet)
// Update existing replica set, if needed. // Update existing replica set, if needed.
if !fedutil.ObjectMetaAndSpecEquivalent(lrs, currentLrs) { if !fedutil.ObjectMetaAndSpecEquivalent(lrs, currentLrs) {
frsc.eventRecorder.Eventf(frs, api.EventTypeNormal, "UpdateInCluster",
"Updating replicaset in cluster %s", clusterName)
operations = append(operations, fedutil.FederatedOperation{ operations = append(operations, fedutil.FederatedOperation{
Type: fedutil.OperationTypeUpdate, Type: fedutil.OperationTypeUpdate,
Obj: lrs, Obj: lrs,
ClusterName: clusterName, ClusterName: clusterName,
Key: key,
}) })
} }
fedStatus.Replicas += currentLrs.Status.Replicas fedStatus.Replicas += currentLrs.Status.Replicas
@ -584,10 +579,7 @@ func (frsc *ReplicaSetController) reconcileReplicaSet(key string) (reconciliatio
// Everything is in order // Everything is in order
return statusAllOk, nil return statusAllOk, nil
} }
err = frsc.fedUpdater.UpdateWithOnError(operations, updateTimeout, func(op fedutil.FederatedOperation, operror error) { err = frsc.fedUpdater.Update(operations, updateTimeout)
frsc.eventRecorder.Eventf(frs, api.EventTypeWarning, "FailedUpdateInCluster",
"Replicaset update in cluster %s failed: %v", op.ClusterName, operror)
})
if err != nil { if err != nil {
glog.Errorf("Failed to execute updates for %s: %v", key, err) glog.Errorf("Failed to execute updates for %s: %v", key, err)
return statusError, err return statusError, err

View File

@ -185,7 +185,7 @@ func New(federationClient fedclientset.Interface, dns dnsprovider.Interface,
s.federatedInformer = fedutil.NewFederatedInformer(federationClient, fedInformerFactory, &clusterLifecycle) s.federatedInformer = fedutil.NewFederatedInformer(federationClient, fedInformerFactory, &clusterLifecycle)
s.federatedUpdater = fedutil.NewFederatedUpdater(s.federatedInformer, s.federatedUpdater = fedutil.NewFederatedUpdater(s.federatedInformer, "service", s.eventRecorder,
func(client kubeclientset.Interface, obj pkgruntime.Object) error { func(client kubeclientset.Interface, obj pkgruntime.Object) error {
svc := obj.(*v1.Service) svc := obj.(*v1.Service)
_, err := client.Core().Services(svc.Namespace).Create(svc) _, err := client.Core().Services(svc.Namespace).Create(svc)
@ -240,10 +240,9 @@ func New(federationClient fedclientset.Interface, dns dnsprovider.Interface,
// objNameFunc // objNameFunc
func(obj pkgruntime.Object) string { func(obj pkgruntime.Object) string {
service := obj.(*v1.Service) service := obj.(*v1.Service)
return service.Name return fmt.Sprintf("%s/%s", service.Namespace, service.Name)
}, },
updateTimeout, updateTimeout,
s.eventRecorder,
s.federatedInformer, s.federatedInformer,
s.federatedUpdater, s.federatedUpdater,
) )
@ -601,11 +600,7 @@ func (s *ServiceController) reconcileService(key string) reconciliationStatus {
} }
if len(operations) != 0 { if len(operations) != 0 {
err = s.federatedUpdater.UpdateWithOnError(operations, s.updateTimeout, err = s.federatedUpdater.Update(operations, s.updateTimeout)
func(op fedutil.FederatedOperation, operror error) {
runtime.HandleError(fmt.Errorf("Service update in cluster %s failed: %v", op.ClusterName, operror))
s.eventRecorder.Eventf(fedService, api.EventTypeWarning, "UpdateInClusterFailed", "Service update in cluster %s failed: %v", op.ClusterName, operror)
})
if err != nil { if err != nil {
if !errors.IsAlreadyExists(err) { if !errors.IsAlreadyExists(err) {
runtime.HandleError(fmt.Errorf("Failed to execute updates for %s: %v", key, err)) runtime.HandleError(fmt.Errorf("Failed to execute updates for %s: %v", key, err))
@ -642,12 +637,12 @@ func (s *ServiceController) getOperationsToPerformOnCluster(cluster *v1beta1.Clu
desiredService.ResourceVersion = "" desiredService.ResourceVersion = ""
glog.V(4).Infof("Creating service in underlying cluster %s: %+v", cluster.Name, desiredService) glog.V(4).Infof("Creating service in underlying cluster %s: %+v", cluster.Name, desiredService)
s.eventRecorder.Eventf(fedService, api.EventTypeNormal, "CreateInCluster", "Creating service in cluster %s", cluster.Name)
operation = &fedutil.FederatedOperation{ operation = &fedutil.FederatedOperation{
Type: fedutil.OperationTypeAdd, Type: fedutil.OperationTypeAdd,
Obj: desiredService, Obj: desiredService,
ClusterName: cluster.Name, ClusterName: cluster.Name,
Key: key,
} }
} else { } else {
clusterService, ok := clusterServiceObj.(*v1.Service) clusterService, ok := clusterServiceObj.(*v1.Service)
@ -674,7 +669,6 @@ func (s *ServiceController) getOperationsToPerformOnCluster(cluster *v1beta1.Clu
// Update existing service, if needed. // Update existing service, if needed.
if !Equivalent(desiredService, clusterService) { if !Equivalent(desiredService, clusterService) {
glog.V(4).Infof("Service in underlying cluster %s does not match, Desired: %+v, Existing: %+v", cluster.Name, desiredService, clusterService) glog.V(4).Infof("Service in underlying cluster %s does not match, Desired: %+v, Existing: %+v", cluster.Name, desiredService, clusterService)
s.eventRecorder.Eventf(fedService, api.EventTypeNormal, "UpdateInCluster", "Updating service in cluster %s. Desired: %+v\n Actual: %+v\n", cluster.Name, desiredService, clusterService)
// ResourceVersion of cluster service can be different from federated service, // ResourceVersion of cluster service can be different from federated service,
// so do not update ResourceVersion while updating cluster service // so do not update ResourceVersion while updating cluster service
@ -684,6 +678,7 @@ func (s *ServiceController) getOperationsToPerformOnCluster(cluster *v1beta1.Clu
Type: fedutil.OperationTypeUpdate, Type: fedutil.OperationTypeUpdate,
Obj: desiredService, Obj: desiredService,
ClusterName: cluster.Name, ClusterName: cluster.Name,
Key: key,
} }
} else { } else {
glog.V(5).Infof("Service in underlying cluster %s is up to date: %+v", cluster.Name, desiredService) glog.V(5).Infof("Service in underlying cluster %s is up to date: %+v", cluster.Name, desiredService)

View File

@ -166,7 +166,7 @@ func newFederationSyncController(client federationclientset.Interface, adapter f
) )
// Federated updeater along with Create/Update/Delete operations. // Federated updeater along with Create/Update/Delete operations.
s.updater = util.NewFederatedUpdater(s.informer, s.updater = util.NewFederatedUpdater(s.informer, adapter.Kind(), s.eventRecorder,
func(client kubeclientset.Interface, obj pkgruntime.Object) error { func(client kubeclientset.Interface, obj pkgruntime.Object) error {
_, err := adapter.ClusterCreate(client, obj) _, err := adapter.ClusterCreate(client, obj)
return err return err
@ -186,10 +186,9 @@ func newFederationSyncController(client federationclientset.Interface, adapter f
s.updateObject, s.updateObject,
// objNameFunc // objNameFunc
func(obj pkgruntime.Object) string { func(obj pkgruntime.Object) string {
return adapter.ObjectMeta(obj).Name return adapter.NamespacedName(obj).String()
}, },
s.updateTimeout, s.updateTimeout,
s.eventRecorder,
s.informer, s.informer,
s.updater, s.updater,
) )
@ -352,25 +351,22 @@ func (s *FederationSyncController) reconcile(namespacedName types.NamespacedName
desiredObj := s.adapter.Copy(obj) desiredObj := s.adapter.Copy(obj)
if !found { if !found {
s.eventRecorder.Eventf(obj, api.EventTypeNormal, "CreateInCluster",
"Creating %s in cluster %s", kind, cluster.Name)
operations = append(operations, util.FederatedOperation{ operations = append(operations, util.FederatedOperation{
Type: util.OperationTypeAdd, Type: util.OperationTypeAdd,
Obj: desiredObj, Obj: desiredObj,
ClusterName: cluster.Name, ClusterName: cluster.Name,
Key: key,
}) })
} else { } else {
clusterObj := clusterObj.(pkgruntime.Object) clusterObj := clusterObj.(pkgruntime.Object)
// Update existing resource, if needed. // Update existing resource, if needed.
if !s.adapter.Equivalent(desiredObj, clusterObj) { if !s.adapter.Equivalent(desiredObj, clusterObj) {
s.eventRecorder.Eventf(obj, api.EventTypeNormal, "UpdateInCluster",
"Updating %s in cluster %s", kind, cluster.Name)
operations = append(operations, util.FederatedOperation{ operations = append(operations, util.FederatedOperation{
Type: util.OperationTypeUpdate, Type: util.OperationTypeUpdate,
Obj: desiredObj, Obj: desiredObj,
ClusterName: cluster.Name, ClusterName: cluster.Name,
Key: key,
}) })
} }
} }
@ -380,11 +376,7 @@ func (s *FederationSyncController) reconcile(namespacedName types.NamespacedName
// Everything is in order // Everything is in order
return return
} }
err = s.updater.UpdateWithOnError(operations, s.updateTimeout, err = s.updater.Update(operations, s.updateTimeout)
func(op util.FederatedOperation, operror error) {
s.eventRecorder.Eventf(obj, api.EventTypeWarning, "UpdateInClusterFailed",
"%s update in cluster %s failed: %v", strings.ToTitle(kind), op.ClusterName, operror)
})
if err != nil { if err != nil {
glog.Errorf("Failed to execute updates for %s: %v", key, err) glog.Errorf("Failed to execute updates for %s: %v", key, err)

View File

@ -43,6 +43,7 @@ go_library(
"//vendor/k8s.io/client-go/tools/cache:go_default_library", "//vendor/k8s.io/client-go/tools/cache:go_default_library",
"//vendor/k8s.io/client-go/tools/clientcmd:go_default_library", "//vendor/k8s.io/client-go/tools/clientcmd:go_default_library",
"//vendor/k8s.io/client-go/tools/clientcmd/api:go_default_library", "//vendor/k8s.io/client-go/tools/clientcmd/api:go_default_library",
"//vendor/k8s.io/client-go/tools/record:go_default_library",
"//vendor/k8s.io/client-go/util/flowcontrol:go_default_library", "//vendor/k8s.io/client-go/util/flowcontrol:go_default_library",
], ],
) )

View File

@ -14,12 +14,10 @@ go_library(
deps = [ deps = [
"//federation/pkg/federation-controller/util:go_default_library", "//federation/pkg/federation-controller/util:go_default_library",
"//federation/pkg/federation-controller/util/finalizers:go_default_library", "//federation/pkg/federation-controller/util/finalizers:go_default_library",
"//pkg/api:go_default_library",
"//vendor/github.com/golang/glog:go_default_library", "//vendor/github.com/golang/glog:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//vendor/k8s.io/client-go/tools/record:go_default_library",
], ],
) )

View File

@ -27,10 +27,8 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/sets"
"k8s.io/client-go/tools/record"
"k8s.io/kubernetes/federation/pkg/federation-controller/util" "k8s.io/kubernetes/federation/pkg/federation-controller/util"
finalizersutil "k8s.io/kubernetes/federation/pkg/federation-controller/util/finalizers" finalizersutil "k8s.io/kubernetes/federation/pkg/federation-controller/util/finalizers"
"k8s.io/kubernetes/pkg/api"
"github.com/golang/glog" "github.com/golang/glog"
) )
@ -53,20 +51,17 @@ type DeletionHelper struct {
updateObjFunc UpdateObjFunc updateObjFunc UpdateObjFunc
objNameFunc ObjNameFunc objNameFunc ObjNameFunc
updateTimeout time.Duration updateTimeout time.Duration
eventRecorder record.EventRecorder
informer util.FederatedInformer informer util.FederatedInformer
updater util.FederatedUpdater updater util.FederatedUpdater
} }
func NewDeletionHelper( func NewDeletionHelper(
updateObjFunc UpdateObjFunc, objNameFunc ObjNameFunc, updateObjFunc UpdateObjFunc, objNameFunc ObjNameFunc, updateTimeout time.Duration,
updateTimeout time.Duration, eventRecorder record.EventRecorder,
informer util.FederatedInformer, updater util.FederatedUpdater) *DeletionHelper { informer util.FederatedInformer, updater util.FederatedUpdater) *DeletionHelper {
return &DeletionHelper{ return &DeletionHelper{
updateObjFunc: updateObjFunc, updateObjFunc: updateObjFunc,
objNameFunc: objNameFunc, objNameFunc: objNameFunc,
updateTimeout: updateTimeout, updateTimeout: updateTimeout,
eventRecorder: eventRecorder,
informer: informer, informer: informer,
updater: updater, updater: updater,
} }
@ -157,13 +152,10 @@ func (dh *DeletionHelper) HandleObjectInUnderlyingClusters(obj runtime.Object) (
Type: util.OperationTypeDelete, Type: util.OperationTypeDelete,
ClusterName: clusterNsObj.ClusterName, ClusterName: clusterNsObj.ClusterName,
Obj: clusterNsObj.Object.(runtime.Object), Obj: clusterNsObj.Object.(runtime.Object),
Key: objName,
}) })
} }
err = dh.updater.UpdateWithOnError(operations, dh.updateTimeout, func(op util.FederatedOperation, operror error) { err = dh.updater.Update(operations, dh.updateTimeout)
objName := dh.objNameFunc(op.Obj)
dh.eventRecorder.Eventf(obj, api.EventTypeWarning, "DeleteInClusterFailed",
"Failed to delete obj %s in cluster %s: %v", objName, op.ClusterName, operror)
})
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to execute updates for obj %s: %v", objName, err) return nil, fmt.Errorf("failed to execute updates for obj %s: %v", objName, err)
} }

View File

@ -18,10 +18,14 @@ package util
import ( import (
"fmt" "fmt"
"strings"
"time" "time"
"k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
pkgruntime "k8s.io/apimachinery/pkg/runtime" pkgruntime "k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/tools/record"
"k8s.io/kubernetes/pkg/api"
kubeclientset "k8s.io/kubernetes/pkg/client/clientset_generated/clientset" kubeclientset "k8s.io/kubernetes/pkg/client/clientset_generated/clientset"
) )
@ -39,6 +43,7 @@ type FederatedOperation struct {
Type FederatedOperationType Type FederatedOperationType
ClusterName string ClusterName string
Obj pkgruntime.Object Obj pkgruntime.Object
Key string
} }
// A helper that executes the given set of updates on federation, in parallel. // A helper that executes the given set of updates on federation, in parallel.
@ -48,8 +53,6 @@ type FederatedUpdater interface {
// stopped when it is reached. However the function will return after the timeout // stopped when it is reached. However the function will return after the timeout
// with a non-nil error. // with a non-nil error.
Update([]FederatedOperation, time.Duration) error Update([]FederatedOperation, time.Duration) error
UpdateWithOnError([]FederatedOperation, time.Duration, func(FederatedOperation, error)) error
} }
// A function that executes some operation using the passed client and object. // A function that executes some operation using the passed client and object.
@ -58,25 +61,32 @@ type FederatedOperationHandler func(kubeclientset.Interface, pkgruntime.Object)
type federatedUpdaterImpl struct { type federatedUpdaterImpl struct {
federation FederationView federation FederationView
kind string
eventRecorder record.EventRecorder
addFunction FederatedOperationHandler addFunction FederatedOperationHandler
updateFunction FederatedOperationHandler updateFunction FederatedOperationHandler
deleteFunction FederatedOperationHandler deleteFunction FederatedOperationHandler
} }
func NewFederatedUpdater(federation FederationView, add, update, del FederatedOperationHandler) FederatedUpdater { func NewFederatedUpdater(federation FederationView, kind string, recorder record.EventRecorder, add, update, del FederatedOperationHandler) FederatedUpdater {
return &federatedUpdaterImpl{ return &federatedUpdaterImpl{
federation: federation, federation: federation,
kind: kind,
eventRecorder: recorder,
addFunction: add, addFunction: add,
updateFunction: update, updateFunction: update,
deleteFunction: del, deleteFunction: del,
} }
} }
func (fu *federatedUpdaterImpl) Update(ops []FederatedOperation, timeout time.Duration) error { func (fu *federatedUpdaterImpl) recordEvent(obj runtime.Object, eventType, eventVerb string, args ...interface{}) {
return fu.UpdateWithOnError(ops, timeout, nil) messageFmt := eventVerb + " %s %q in cluster %s"
fu.eventRecorder.Eventf(obj, api.EventTypeNormal, eventType, messageFmt, args...)
} }
func (fu *federatedUpdaterImpl) UpdateWithOnError(ops []FederatedOperation, timeout time.Duration, onError func(FederatedOperation, error)) error { func (fu *federatedUpdaterImpl) Update(ops []FederatedOperation, timeout time.Duration) error {
done := make(chan error, len(ops)) done := make(chan error, len(ops))
for _, op := range ops { for _, op := range ops {
go func(op FederatedOperation) { go func(op FederatedOperation) {
@ -89,21 +99,37 @@ func (fu *federatedUpdaterImpl) UpdateWithOnError(ops []FederatedOperation, time
return return
} }
eventArgs := []interface{}{fu.kind, op.Key, clusterName}
baseEventType := fmt.Sprintf("%s", op.Type)
eventType := fmt.Sprintf("%sInCluster", strings.Title(baseEventType))
switch op.Type { switch op.Type {
case OperationTypeAdd: case OperationTypeAdd:
// TODO s+OperationTypeAdd+OperationTypeCreate+
baseEventType = "create"
eventType := "CreateInCluster"
fu.recordEvent(op.Obj, eventType, "Creating", eventArgs...)
err = fu.addFunction(clientset, op.Obj) err = fu.addFunction(clientset, op.Obj)
case OperationTypeUpdate: case OperationTypeUpdate:
fu.recordEvent(op.Obj, eventType, "Updating", eventArgs...)
err = fu.updateFunction(clientset, op.Obj) err = fu.updateFunction(clientset, op.Obj)
case OperationTypeDelete: case OperationTypeDelete:
fu.recordEvent(op.Obj, eventType, "Deleting", eventArgs...)
err = fu.deleteFunction(clientset, op.Obj) err = fu.deleteFunction(clientset, op.Obj)
// IsNotFound error is fine since that means the object is deleted already. // IsNotFound error is fine since that means the object is deleted already.
if errors.IsNotFound(err) { if err != nil && !errors.IsNotFound(err) {
err = nil err = nil
} }
} }
if err != nil && onError != nil {
onError(op, err) if err != nil {
eventType := eventType + "Failed"
messageFmt := "Failed to " + baseEventType + " %s %q in cluster %s: %v"
eventArgs = append(eventArgs, err)
fu.eventRecorder.Eventf(op.Obj, api.EventTypeWarning, eventType, messageFmt, eventArgs...)
} }
done <- err done <- err
}(op) }(op)
} }

View File

@ -58,11 +58,19 @@ func (f *fakeFederationView) ClustersSynced() bool {
return true return true
} }
type fakeEventRecorder struct{}
func (f *fakeEventRecorder) Event(object pkgruntime.Object, eventtype, reason, message string) {}
func (f *fakeEventRecorder) Eventf(object pkgruntime.Object, eventtype, reason, messageFmt string, args ...interface{}) {
}
func (f *fakeEventRecorder) PastEventf(object pkgruntime.Object, timestamp metav1.Time, eventtype, reason, messageFmt string, args ...interface{}) {
}
func TestFederatedUpdaterOK(t *testing.T) { func TestFederatedUpdaterOK(t *testing.T) {
addChan := make(chan string, 5) addChan := make(chan string, 5)
updateChan := make(chan string, 5) updateChan := make(chan string, 5)
updater := NewFederatedUpdater(&fakeFederationView{}, updater := NewFederatedUpdater(&fakeFederationView{}, "foo", &fakeEventRecorder{},
func(_ kubeclientset.Interface, obj pkgruntime.Object) error { func(_ kubeclientset.Interface, obj pkgruntime.Object) error {
service := obj.(*apiv1.Service) service := obj.(*apiv1.Service)
addChan <- service.Name addChan <- service.Name
@ -93,7 +101,7 @@ func TestFederatedUpdaterOK(t *testing.T) {
} }
func TestFederatedUpdaterError(t *testing.T) { func TestFederatedUpdaterError(t *testing.T) {
updater := NewFederatedUpdater(&fakeFederationView{}, updater := NewFederatedUpdater(&fakeFederationView{}, "foo", &fakeEventRecorder{},
func(_ kubeclientset.Interface, obj pkgruntime.Object) error { func(_ kubeclientset.Interface, obj pkgruntime.Object) error {
return fmt.Errorf("boom") return fmt.Errorf("boom")
}, noop, noop) }, noop, noop)
@ -113,7 +121,7 @@ func TestFederatedUpdaterError(t *testing.T) {
func TestFederatedUpdaterTimeout(t *testing.T) { func TestFederatedUpdaterTimeout(t *testing.T) {
start := time.Now() start := time.Now()
updater := NewFederatedUpdater(&fakeFederationView{}, updater := NewFederatedUpdater(&fakeFederationView{}, "foo", &fakeEventRecorder{},
func(_ kubeclientset.Interface, obj pkgruntime.Object) error { func(_ kubeclientset.Interface, obj pkgruntime.Object) error {
time.Sleep(time.Minute) time.Sleep(time.Minute)
return nil return nil