Merge pull request #36399 from mwielgus/fedrc1

Automatic merge from submit-queue

Make desired objects in fed controller reconcilation function fully writable

When creating desired objects for sub-cluster in reconciliate functions we don't make full copies. This may result in unexpected race condition in the future when someone actually adds some object modifications (not needed in most cases but anyway - the cost of extra copy will be relatively small comparing to inter-cluster traffic). In case of ReplicaSet and Deployment the spec copy was a shallow one - ok for setting just replicas but also error-prone in the future.

cc: @quinton-hoole @nikhiljindal @madhusudancs
pull/6/head
Kubernetes Submit Queue 2016-11-21 00:00:26 -08:00 committed by GitHub
commit ab184e1b1f
10 changed files with 31 additions and 15 deletions

View File

@ -269,8 +269,9 @@ func (configmapcontroller *ConfigMapController) reconcileConfigMap(configmap typ
return
}
// Do not modify data.
desiredConfigMap := &api_v1.ConfigMap{
ObjectMeta: util.CopyObjectMeta(baseConfigMap.ObjectMeta),
ObjectMeta: util.DeepCopyRelevantObjectMeta(baseConfigMap.ObjectMeta),
Data: baseConfigMap.Data,
}

View File

@ -401,9 +401,10 @@ func (daemonsetcontroller *DaemonSetController) reconcileDaemonSet(namespace str
return
}
// Do not modify. Otherwise make a deep copy.
desiredDaemonSet := &extensionsv1.DaemonSet{
ObjectMeta: util.CopyObjectMeta(baseDaemonSet.ObjectMeta),
Spec: baseDaemonSet.Spec,
ObjectMeta: util.DeepCopyRelevantObjectMeta(baseDaemonSet.ObjectMeta),
Spec: util.DeepCopyApiTypeOrPanic(baseDaemonSet.Spec).(extensionsv1.DaemonSetSpec),
}
if !found {

View File

@ -561,9 +561,10 @@ func (fdc *DeploymentController) reconcileDeployment(key string) (reconciliation
return statusError, err
}
// The object can be modified.
ld := &extensionsv1.Deployment{
ObjectMeta: fedutil.CopyObjectMeta(fd.ObjectMeta),
Spec: fd.Spec,
ObjectMeta: fedutil.DeepCopyRelevantObjectMeta(fd.ObjectMeta),
Spec: fedutil.DeepCopyApiTypeOrPanic(fd.Spec).(extensionsv1.DeploymentSpec),
}
specReplicas := int32(replicas)
ld.Spec.Replicas = &specReplicas

View File

@ -735,7 +735,7 @@ func (ic *IngressController) reconcileIngress(ingress types.NamespacedName) {
if !clusterIngressFound {
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.
desiredIngress.ObjectMeta = util.DeepCopyObjectMeta(baseIngress.ObjectMeta)
desiredIngress.ObjectMeta = util.DeepCopyRelevantObjectMeta(baseIngress.ObjectMeta)
ic.eventRecorder.Eventf(baseIngress, api.EventTypeNormal, "CreateInCluster",
"Creating ingress in cluster %s", cluster.Name)

View File

@ -391,9 +391,10 @@ func (nc *NamespaceController) reconcileNamespace(namespace string) {
nc.deliverNamespace(namespace, 0, true)
return
}
// The object should not be modified.
desiredNamespace := &api_v1.Namespace{
ObjectMeta: util.CopyObjectMeta(baseNamespace.ObjectMeta),
Spec: baseNamespace.Spec,
ObjectMeta: util.DeepCopyRelevantObjectMeta(baseNamespace.ObjectMeta),
Spec: util.DeepCopyApiTypeOrPanic(baseNamespace.Spec).(api_v1.NamespaceSpec),
}
glog.V(5).Infof("Desired namespace in underlying clusters: %+v", desiredNamespace)

View File

@ -562,9 +562,10 @@ func (frsc *ReplicaSetController) reconcileReplicaSet(key string) (reconciliatio
return statusError, err
}
// The object can be modified.
lrs := &extensionsv1.ReplicaSet{
ObjectMeta: fedutil.CopyObjectMeta(frs.ObjectMeta),
Spec: frs.Spec,
ObjectMeta: fedutil.DeepCopyRelevantObjectMeta(frs.ObjectMeta),
Spec: fedutil.DeepCopyApiTypeOrPanic(frs.Spec).(extensionsv1.ReplicaSetSpec),
}
specReplicas := int32(replicas)
lrs.Spec.Replicas = &specReplicas

View File

@ -366,8 +366,9 @@ func (secretcontroller *SecretController) reconcileSecret(secret types.Namespace
return
}
// The data should not be modified.
desiredSecret := &api_v1.Secret{
ObjectMeta: util.CopyObjectMeta(baseSecret.ObjectMeta),
ObjectMeta: util.DeepCopyRelevantObjectMeta(baseSecret.ObjectMeta),
Data: baseSecret.Data,
Type: baseSecret.Type,
}

View File

@ -36,6 +36,7 @@ go_library(
"//pkg/client/restclient:go_default_library",
"//pkg/client/unversioned/clientcmd:go_default_library",
"//pkg/client/unversioned/clientcmd/api:go_default_library",
"//pkg/conversion:go_default_library",
"//pkg/runtime:go_default_library",
"//pkg/util/flowcontrol:go_default_library",
"//pkg/util/net:go_default_library",

View File

@ -20,13 +20,14 @@ import (
"reflect"
api_v1 "k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/conversion"
"k8s.io/kubernetes/pkg/runtime"
)
// Copies cluster-independent, user provided data from the given ObjectMeta struct. If in
// the future the ObjectMeta structure is expanded then any field that is not populated
// by the api server should be included here.
func CopyObjectMeta(obj api_v1.ObjectMeta) api_v1.ObjectMeta {
func copyObjectMeta(obj api_v1.ObjectMeta) api_v1.ObjectMeta {
return api_v1.ObjectMeta{
Name: obj.Name,
Namespace: obj.Namespace,
@ -38,8 +39,8 @@ func CopyObjectMeta(obj api_v1.ObjectMeta) api_v1.ObjectMeta {
// Deep copies cluster-independent, user provided data from the given ObjectMeta struct. If in
// the future the ObjectMeta structure is expanded then any field that is not populated
// by the api server should be included here.
func DeepCopyObjectMeta(obj api_v1.ObjectMeta) api_v1.ObjectMeta {
copyMeta := CopyObjectMeta(obj)
func DeepCopyRelevantObjectMeta(obj api_v1.ObjectMeta) api_v1.ObjectMeta {
copyMeta := copyObjectMeta(obj)
if obj.Labels != nil {
copyMeta.Labels = make(map[string]string)
for key, val := range obj.Labels {
@ -83,3 +84,11 @@ func ObjectMetaAndSpecEquivalent(a, b runtime.Object) bool {
specB := reflect.ValueOf(b).Elem().FieldByName("Spec").Interface()
return ObjectMetaEquivalent(objectMetaA, objectMetaB) && reflect.DeepEqual(specA, specB)
}
func DeepCopyApiTypeOrPanic(item interface{}) interface{} {
result, err := conversion.NewCloner().DeepCopy(item)
if err != nil {
panic(err)
}
return result
}

View File

@ -31,7 +31,7 @@ func TestObjectMeta(t *testing.T) {
UID: "1231231412",
ResourceVersion: "999",
}
o2 := CopyObjectMeta(o1)
o2 := copyObjectMeta(o1)
o3 := api_v1.ObjectMeta{
Namespace: "ns1",
Name: "s1",