// checkAndUpdatePod updates existing, and: // * if ref makes a meaningful change, returns needUpdate=true // * if ref makes no meaningful change, but changes the pod status, returns needReconcile=true // * else return both false // Now, needUpdate and needReconcile should never be both true func checkAndUpdatePod(existing, ref *api.Pod) (needUpdate, needReconcile bool) { // TODO: it would be better to update the whole object and only preserve certain things // like the source annotation or the UID (to ensure safety) if !podsDifferSemantically(existing, ref) { // this is not an update // Only check reconcile when it is not an update, because if the pod is going to // be updated, an extra reconcile is unnecessary if !reflect.DeepEqual(existing.Status, ref.Status) { // Pod with changed pod status needs reconcile, because kubelet should // be the source of truth of pod status. existing.Status = ref.Status needReconcile = true } return } // this is an update // Overwrite the first-seen time with the existing one. This is our own // internal annotation, there is no need to update. ref.Annotations[kubetypes.ConfigFirstSeenAnnotationKey] = existing.Annotations[kubetypes.ConfigFirstSeenAnnotationKey] existing.Spec = ref.Spec existing.Labels = ref.Labels existing.DeletionTimestamp = ref.DeletionTimestamp existing.DeletionGracePeriodSeconds = ref.DeletionGracePeriodSeconds existing.Status = ref.Status updateAnnotations(existing, ref) needUpdate = true return }
// checkAndUpdatePod updates existing if ref makes a meaningful change and returns true, or // returns false if there was no update. func checkAndUpdatePod(existing, ref *api.Pod) bool { // TODO: it would be better to update the whole object and only preserve certain things // like the source annotation or the UID (to ensure safety) if !podsDifferSemantically(existing, ref) { return false } // this is an update // Overwrite the first-seen time with the existing one. This is our own // internal annotation, there is no need to update. ref.Annotations[kubeletTypes.ConfigFirstSeenAnnotationKey] = existing.Annotations[kubeletTypes.ConfigFirstSeenAnnotationKey] existing.Spec = ref.Spec existing.Labels = ref.Labels existing.DeletionTimestamp = ref.DeletionTimestamp existing.DeletionGracePeriodSeconds = ref.DeletionGracePeriodSeconds updateAnnotations(existing, ref) return true }
func TestConsistencyPodUpdateRemovePrev(t *testing.T) { env := new(TestFramework) env.SetUp("192.0.2.0/24") client := env.client config := env.config pod1 := &api.Pod{ ObjectMeta: api.ObjectMeta{ Name: "x-1", Namespace: "testns", UID: kubetypes.UID(uuid.New()), Labels: map[string]string{ "Name": "client", config.NetworkTag: "client", config.NetworkAccessTag: "red", }, }, } pod2 := &api.Pod{ ObjectMeta: api.ObjectMeta{ Name: "x-2", Namespace: "testns", UID: kubetypes.UID(uuid.New()), Labels: map[string]string{ "Name": "client", config.NetworkTag: "client", config.NetworkAccessTag: "red", }, }, } pod3 := &api.Pod{ ObjectMeta: api.ObjectMeta{ Name: "redPod", Namespace: "testns", UID: kubetypes.UID(uuid.New()), Labels: map[string]string{ "Name": "red", config.NetworkTag: "redPrivate", }, }, } pod4 := &api.Pod{ ObjectMeta: api.ObjectMeta{ Name: "bluePod", Namespace: "testns", UID: kubetypes.UID(uuid.New()), Labels: map[string]string{ "Name": "blue", config.NetworkTag: "bluePrivate", }, }, } redService := &api.Service{ ObjectMeta: api.ObjectMeta{ Name: "red", Namespace: "testns", Labels: map[string]string{ config.NetworkTag: "red", }, }, Spec: api.ServiceSpec{ Selector: map[string]string{ "Name": "red", }, ClusterIP: "10.254.42.42", }, } blueService := &api.Service{ ObjectMeta: api.ObjectMeta{ Name: "blue", Namespace: "testns", Labels: map[string]string{ config.NetworkTag: "blue", }, }, Spec: api.ServiceSpec{ Selector: map[string]string{ "Name": "blue", }, ClusterIP: "10.254.42.43", }, } netnsProject := new(types.Project) netnsProject.SetFQName("", []string{"default-domain", "testns"}) client.Create(netnsProject) env.Start() env.AddPod(pod1) env.AddService(redService, "red") env.AddPod(pod2) env.AddPod(pod3) env.AddPod(pod4) env.AddService(blueService, "blue") env.SyncBarrier() serviceConnections := getNetworkServiceConnections(t, client, config, "testns", "client") assert.EqualValues(t, []string{"default", "red"}, serviceConnections) assert.True(t, env.checker.Check()) policyName := makeServicePolicyName(config, "testns", "red") _, err := types.NetworkPolicyByName(client, strings.Join(policyName, ":")) assert.NoError(t, err) clonePodAndUpdateAccessTag := func(pod *api.Pod, color string) *api.Pod { newPod := new(api.Pod) *newPod = *pod newPod.Labels = make(map[string]string, 0) for k, v := range pod.Labels { newPod.Labels[k] = v } newPod.Labels[config.NetworkAccessTag] = color return newPod } // Update connections on pod1 nPod1 := clonePodAndUpdateAccessTag(pod1, "blue") env.UpdatePod(pod1, nPod1) env.SyncBarrier() serviceConnections = getNetworkServiceConnections(t, client, config, "testns", "client") assert.EqualValues(t, []string{"default", "red", "blue"}, serviceConnections) assert.True(t, env.checker.Check(), "red and blue present") // Update connections on pod2 // This will leave a stale connection to network red. nPod2 := clonePodAndUpdateAccessTag(pod2, "blue") env.UpdatePod(pod2, nPod2) env.SyncBarrier() serviceConnections = getNetworkServiceConnections(t, client, config, "testns", "client") assert.EqualValues(t, []string{"default", "red", "blue"}, serviceConnections) assert.False(t, env.checker.Check()) env.DeleteService(redService, "red") env.SyncBarrier() // The second pass will delete the connection to network red. assert.False(t, env.checker.Check()) serviceConnections = getNetworkServiceConnections(t, client, config, "testns", "client") assert.EqualValues(t, []string{"default", "blue"}, serviceConnections) assert.True(t, env.checker.Check()) env.Shutdown() _, err = types.NetworkPolicyByName(client, strings.Join(policyName, ":")) assert.Error(t, err) }