Change:
Resource quota update does not take effect until create/delete another ResourceQuota. I am not sure it is an issue or design.
I am 99% sure that the issue is the update is changing the Spec and the Kubectl client is showing the Status. The Status will update at the next quota sync interval to align with the desired Spec. Can you describe what KUBERNETES environment you are using so I can verify? In particular, how is kube-controller-manager started? You can confirm this by doing kubectl get quota -o json And you should see the spec update occurred and if you wait 1 minute you will see the status take effect. A few options to improve user experience: Improve latency of updating status with spec. This has some potential side effects that I need to think through with how the current controller is structured. Change kubectl output to show spec if it differs from status so it's clear that the system will ultimately drive to its desired state. This seems like a good thing to do in general, will experiment with some options to present this information, but I am open to your input. I think even a single line noting that the enforced usage limits are different than the desired state but will reconcile soon. Sent from my iPhone
Sure to append config of kube-controller-manager: Tried the cmd, the spec updated as you said, however, the status refuse to take effect after more than 5 mins. I am not sure if it is caused by my version v0.13.2.
I see the issue, will send a PR. It appears the quota status was only getting marked as dirty for update if the usage changed, and not the underlying limit itself. So I suspect if you removed a pod (assuming you were tracking total number of pods), you would see the status kept updated to the desired spec.
The status changed correctly after resizing a rc and wait a while (8s).
I want to update the Kubectl output to show the desired value if it differs from the status value. I will look to do that in the next few days. I think that would eliminate all confusion. Sent from my iPhone

Discussion:
// syncResourceQuota runs a complete sync of current status func (rm *ResourceQuotaManager) syncResourceQuota(quota api.ResourceQuota) (err error) { <ins> // quota is dirty if any part of spec hard limits differs from the status hard limits dirty := !api.Semantic.DeepEqual(quota.Spec.Hard, quota.Status.Hard) </ins> // dirty tracks if the usage status differs from the previous sync, // if so, we send a new usage with latest status // if this is our first sync, it will be dirty by default, since we need track usage <del> dirty := quota.Status.Hard == nil || quota.Status.Used == nil </del> <ins> dirty = dirty || (quota.Status.Hard == nil || quota.Status.Used == nil) </ins> // Create a usage object that is based on the quota resource version usage := api.ResourceQuota{

} } <ins> func TestSyncResourceQuotaSpecChange(t *testing.T) { quota := api.ResourceQuota{ Spec: api.ResourceQuotaSpec{ Hard: api.ResourceList{ api.ResourceCPU: resource.MustParse("4"), }, }, Status: api.ResourceQuotaStatus{ Hard: api.ResourceList{ api.ResourceCPU: resource.MustParse("3"), }, Used: api.ResourceList{ api.ResourceCPU: resource.MustParse("0"), }, }, } expectedUsage := api.ResourceQuota{ Status: api.ResourceQuotaStatus{ Hard: api.ResourceList{ api.ResourceCPU: resource.MustParse("4"), }, Used: api.ResourceList{ api.ResourceCPU: resource.MustParse("0"), }, }, } kubeClient := testclient.NewSimpleFake(&quota) resourceQuotaManager := NewResourceQuotaManager(kubeClient) err := resourceQuotaManager.syncResourceQuota(quota) if err != nil { t.Fatalf("Unexpected error %v", err) } usage := kubeClient.Actions[1].Value.(*api.ResourceQuota) // ensure hard and used limits are what we expected for k, v := range expectedUsage.Status.Hard { actual := usage.Status.Hard[k] actualValue := actual.String() expectedValue := v.String() if expectedValue != actualValue { t.Errorf("Usage Hard: Key: %v, Expected: %v, Actual: %v", k, expectedValue, actualValue) } } for k, v := range expectedUsage.Status.Used { actual := usage.Status.Used[k] actualValue := actual.String() expectedValue := v.String() if expectedValue != actualValue { t.Errorf("Usage Used: Key: %v, Expected: %v, Actual: %v", k, expectedValue, actualValue) } } } func TestSyncResourceQuotaNoChange(t *testing.T) { quota := api.ResourceQuota{ Spec: api.ResourceQuotaSpec{ Hard: api.ResourceList{ api.ResourceCPU: resource.MustParse("4"), }, }, Status: api.ResourceQuotaStatus{ Hard: api.ResourceList{ api.ResourceCPU: resource.MustParse("4"), }, Used: api.ResourceList{ api.ResourceCPU: resource.MustParse("0"), }, }, } kubeClient := testclient.NewSimpleFake(&api.PodList{}, &quota) resourceQuotaManager := NewResourceQuotaManager(kubeClient) err := resourceQuotaManager.syncResourceQuota(quota) if err != nil { t.Fatalf("Unexpected error %v", err) } if len(kubeClient.Actions) != 1 && kubeClient.Actions[0].Action != "list-pods" { t.Errorf("SyncResourceQuota made an unexpected client action when state was not dirty: %v", kubeClient.Actions) } } </ins>