mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 19:56:01 +00:00
DaemonSets adds a Status subresource
This commit is contained in:
parent
6a04145362
commit
aead9d3291
@ -114,6 +114,23 @@ func ValidateDaemonSetUpdate(oldController, controller *experimental.DaemonSet)
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// validateDaemonSetStatus validates a DaemonSetStatus
|
||||
func validateDaemonSetStatus(status *experimental.DaemonSetStatus) errs.ValidationErrorList {
|
||||
allErrs := errs.ValidationErrorList{}
|
||||
allErrs = append(allErrs, apivalidation.ValidatePositiveField(int64(status.CurrentNumberScheduled), "currentNumberScheduled")...)
|
||||
allErrs = append(allErrs, apivalidation.ValidatePositiveField(int64(status.NumberMisscheduled), "numberMisscheduled")...)
|
||||
allErrs = append(allErrs, apivalidation.ValidatePositiveField(int64(status.DesiredNumberScheduled), "desiredNumberScheduled")...)
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// ValidateDaemonSetStatus validates tests if required fields in the DaemonSet Status section
|
||||
func ValidateDaemonSetStatusUpdate(controller, oldController *experimental.DaemonSet) errs.ValidationErrorList {
|
||||
allErrs := errs.ValidationErrorList{}
|
||||
allErrs = append(allErrs, apivalidation.ValidateObjectMetaUpdate(&controller.ObjectMeta, &oldController.ObjectMeta).Prefix("metadata")...)
|
||||
allErrs = append(allErrs, validateDaemonSetStatus(&controller.Status)...)
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// ValidateDaemonSetTemplateUpdate tests that certain fields in the daemon set's pod template are not updated.
|
||||
func ValidateDaemonSetTemplateUpdate(oldPodTemplate, podTemplate *api.PodTemplateSpec) errs.ValidationErrorList {
|
||||
allErrs := errs.ValidationErrorList{}
|
||||
@ -314,16 +331,9 @@ func ValidateJobSpec(spec *experimental.JobSpec) errs.ValidationErrorList {
|
||||
|
||||
func ValidateJobStatus(status *experimental.JobStatus) errs.ValidationErrorList {
|
||||
allErrs := errs.ValidationErrorList{}
|
||||
|
||||
if status.Active < 0 {
|
||||
allErrs = append(allErrs, errs.NewFieldInvalid("active", status.Active, isNegativeErrorMsg))
|
||||
}
|
||||
if status.Successful < 0 {
|
||||
allErrs = append(allErrs, errs.NewFieldInvalid("successful", status.Successful, isNegativeErrorMsg))
|
||||
}
|
||||
if status.Unsuccessful < 0 {
|
||||
allErrs = append(allErrs, errs.NewFieldInvalid("unsuccessful", status.Unsuccessful, isNegativeErrorMsg))
|
||||
}
|
||||
allErrs = append(allErrs, apivalidation.ValidatePositiveField(int64(status.Active), "active")...)
|
||||
allErrs = append(allErrs, apivalidation.ValidatePositiveField(int64(status.Successful), "successful")...)
|
||||
allErrs = append(allErrs, apivalidation.ValidatePositiveField(int64(status.Unsuccessful), "unsuccessful")...)
|
||||
return allErrs
|
||||
}
|
||||
|
||||
|
@ -130,6 +130,70 @@ func TestValidateHorizontalPodAutoscaler(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateDaemonSetStatusUpdate(t *testing.T) {
|
||||
type dsUpdateTest struct {
|
||||
old experimental.DaemonSet
|
||||
update experimental.DaemonSet
|
||||
}
|
||||
|
||||
successCases := []dsUpdateTest{
|
||||
{
|
||||
old: experimental.DaemonSet{
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
|
||||
Status: experimental.DaemonSetStatus{
|
||||
CurrentNumberScheduled: 1,
|
||||
NumberMisscheduled: 2,
|
||||
DesiredNumberScheduled: 3,
|
||||
},
|
||||
},
|
||||
update: experimental.DaemonSet{
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
|
||||
Status: experimental.DaemonSetStatus{
|
||||
CurrentNumberScheduled: 1,
|
||||
NumberMisscheduled: 1,
|
||||
DesiredNumberScheduled: 3,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, successCase := range successCases {
|
||||
successCase.old.ObjectMeta.ResourceVersion = "1"
|
||||
successCase.update.ObjectMeta.ResourceVersion = "1"
|
||||
if errs := ValidateDaemonSetStatusUpdate(&successCase.update, &successCase.old); len(errs) != 0 {
|
||||
t.Errorf("expected success: %v", errs)
|
||||
}
|
||||
}
|
||||
|
||||
errorCases := map[string]dsUpdateTest{
|
||||
"negative values": {
|
||||
old: experimental.DaemonSet{
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
|
||||
Status: experimental.DaemonSetStatus{
|
||||
CurrentNumberScheduled: 1,
|
||||
NumberMisscheduled: 2,
|
||||
DesiredNumberScheduled: 3,
|
||||
},
|
||||
},
|
||||
update: experimental.DaemonSet{
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
|
||||
Status: experimental.DaemonSetStatus{
|
||||
CurrentNumberScheduled: -1,
|
||||
NumberMisscheduled: -1,
|
||||
DesiredNumberScheduled: -3,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for testName, errorCase := range errorCases {
|
||||
if errs := ValidateDaemonSetStatusUpdate(&errorCase.old, &errorCase.update); len(errs) == 0 {
|
||||
t.Errorf("expected failure: %s", testName)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestValidateDaemonSetUpdate(t *testing.T) {
|
||||
validSelector := map[string]string{"a": "b"}
|
||||
validSelector2 := map[string]string{"c": "d"}
|
||||
|
@ -33,6 +33,7 @@ type DaemonSetInterface interface {
|
||||
Get(name string) (*experimental.DaemonSet, error)
|
||||
Create(ctrl *experimental.DaemonSet) (*experimental.DaemonSet, error)
|
||||
Update(ctrl *experimental.DaemonSet) (*experimental.DaemonSet, error)
|
||||
UpdateStatus(ctrl *experimental.DaemonSet) (*experimental.DaemonSet, error)
|
||||
Delete(name string) error
|
||||
Watch(label labels.Selector, field fields.Selector, resourceVersion string) (watch.Interface, error)
|
||||
}
|
||||
@ -77,6 +78,13 @@ func (c *daemonSets) Update(daemon *experimental.DaemonSet) (result *experimenta
|
||||
return
|
||||
}
|
||||
|
||||
// UpdateStatus updates an existing daemon set status
|
||||
func (c *daemonSets) UpdateStatus(daemon *experimental.DaemonSet) (result *experimental.DaemonSet, err error) {
|
||||
result = &experimental.DaemonSet{}
|
||||
err = c.r.Put().Namespace(c.ns).Resource("daemonsets").Name(daemon.Name).SubResource("status").Body(daemon).Do().Into(result)
|
||||
return
|
||||
}
|
||||
|
||||
// Delete deletes an existing daemon set.
|
||||
func (c *daemonSets) Delete(name string) error {
|
||||
return c.r.Delete().Namespace(c.ns).Resource("daemonsets").Name(name).Do().Error()
|
||||
|
@ -122,6 +122,34 @@ func TestUpdateDaemonSet(t *testing.T) {
|
||||
c.Validate(t, receivedDaemonSet, err)
|
||||
}
|
||||
|
||||
func TestUpdateDaemonSetUpdateStatus(t *testing.T) {
|
||||
ns := api.NamespaceDefault
|
||||
requestDaemonSet := &experimental.DaemonSet{
|
||||
ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "1"},
|
||||
}
|
||||
c := &testClient{
|
||||
Request: testRequest{Method: "PUT", Path: testapi.Experimental.ResourcePath(getDSResourceName(), ns, "foo") + "/status", Query: buildQueryValues(nil)},
|
||||
Response: Response{
|
||||
StatusCode: 200,
|
||||
Body: &experimental.DaemonSet{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "foo",
|
||||
Labels: map[string]string{
|
||||
"foo": "bar",
|
||||
"name": "baz",
|
||||
},
|
||||
},
|
||||
Spec: experimental.DaemonSetSpec{
|
||||
Template: &api.PodTemplateSpec{},
|
||||
},
|
||||
Status: experimental.DaemonSetStatus{},
|
||||
},
|
||||
},
|
||||
}
|
||||
receivedDaemonSet, err := c.Setup(t).Experimental().DaemonSets(ns).UpdateStatus(requestDaemonSet)
|
||||
c.Validate(t, receivedDaemonSet, err)
|
||||
}
|
||||
|
||||
func TestDeleteDaemon(t *testing.T) {
|
||||
ns := api.NamespaceDefault
|
||||
c := &testClient{
|
||||
|
@ -66,6 +66,14 @@ func (c *FakeDaemonSets) Update(daemon *experimental.DaemonSet) (*experimental.D
|
||||
return obj.(*experimental.DaemonSet), err
|
||||
}
|
||||
|
||||
func (c *FakeDaemonSets) UpdateStatus(daemon *experimental.DaemonSet) (*experimental.DaemonSet, error) {
|
||||
obj, err := c.Fake.Invokes(NewUpdateSubresourceAction("daemonsets", "status", c.Namespace, daemon), &experimental.DaemonSet{})
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*experimental.DaemonSet), err
|
||||
}
|
||||
|
||||
func (c *FakeDaemonSets) Delete(name string) error {
|
||||
_, err := c.Fake.Invokes(NewDeleteAction("daemonsets", c.Namespace, name), &experimental.DaemonSet{})
|
||||
return err
|
||||
|
@ -400,7 +400,7 @@ func storeDaemonSetStatus(dsClient client.DaemonSetInterface, ds *experimental.D
|
||||
ds.Status.DesiredNumberScheduled = desiredNumberScheduled
|
||||
ds.Status.CurrentNumberScheduled = currentNumberScheduled
|
||||
ds.Status.NumberMisscheduled = numberMisscheduled
|
||||
_, updateErr = dsClient.Update(ds)
|
||||
_, updateErr = dsClient.UpdateStatus(ds)
|
||||
if updateErr == nil {
|
||||
// successful update
|
||||
return nil
|
||||
|
@ -958,7 +958,7 @@ func (m *Master) experimental(c *Config) *apiserver.APIGroupVersion {
|
||||
controllerStorage := expcontrolleretcd.NewStorage(c.ExpDatabaseStorage)
|
||||
autoscalerStorage := horizontalpodautoscaleretcd.NewREST(c.ExpDatabaseStorage)
|
||||
thirdPartyResourceStorage := thirdpartyresourceetcd.NewREST(c.ExpDatabaseStorage)
|
||||
daemonSetStorage := daemonetcd.NewREST(c.ExpDatabaseStorage)
|
||||
daemonSetStorage, daemonSetStatusStorage := daemonetcd.NewREST(c.ExpDatabaseStorage)
|
||||
deploymentStorage := deploymentetcd.NewStorage(c.ExpDatabaseStorage)
|
||||
jobStorage, jobStatusStorage := jobetcd.NewREST(c.ExpDatabaseStorage)
|
||||
|
||||
@ -979,6 +979,7 @@ func (m *Master) experimental(c *Config) *apiserver.APIGroupVersion {
|
||||
strings.ToLower("horizontalpodautoscalers"): autoscalerStorage,
|
||||
strings.ToLower("thirdpartyresources"): thirdPartyResourceStorage,
|
||||
strings.ToLower("daemonsets"): daemonSetStorage,
|
||||
strings.ToLower("daemonsets/status"): daemonSetStatusStorage,
|
||||
strings.ToLower("deployments"): deploymentStorage.Deployment,
|
||||
strings.ToLower("deployments/scale"): deploymentStorage.Scale,
|
||||
strings.ToLower("jobs"): jobStorage,
|
||||
|
@ -37,7 +37,7 @@ type REST struct {
|
||||
var daemonPrefix = "/daemonsets"
|
||||
|
||||
// NewREST returns a RESTStorage object that will work against DaemonSets.
|
||||
func NewREST(s storage.Interface) *REST {
|
||||
func NewREST(s storage.Interface) (*REST, *StatusREST) {
|
||||
store := &etcdgeneric.Etcd{
|
||||
NewFunc: func() runtime.Object { return &experimental.DaemonSet{} },
|
||||
|
||||
@ -71,6 +71,22 @@ func NewREST(s storage.Interface) *REST {
|
||||
|
||||
Storage: s,
|
||||
}
|
||||
statusStore := *store
|
||||
statusStore.UpdateStrategy = daemonset.StatusStrategy
|
||||
|
||||
return &REST{store}
|
||||
return &REST{store}, &StatusREST{store: &statusStore}
|
||||
}
|
||||
|
||||
// StatusREST implements the REST endpoint for changing the status of a daemonset
|
||||
type StatusREST struct {
|
||||
store *etcdgeneric.Etcd
|
||||
}
|
||||
|
||||
func (r *StatusREST) New() runtime.Object {
|
||||
return &experimental.DaemonSet{}
|
||||
}
|
||||
|
||||
// Update alters the status subset of an object.
|
||||
func (r *StatusREST) Update(ctx api.Context, obj runtime.Object) (runtime.Object, bool, error) {
|
||||
return r.store.Update(ctx, obj)
|
||||
}
|
||||
|
@ -28,9 +28,10 @@ import (
|
||||
"k8s.io/kubernetes/pkg/tools"
|
||||
)
|
||||
|
||||
func newStorage(t *testing.T) (*REST, *tools.FakeEtcdClient) {
|
||||
func newStorage(t *testing.T) (*REST, *StatusREST, *tools.FakeEtcdClient) {
|
||||
etcdStorage, fakeClient := registrytest.NewEtcdStorage(t, "experimental")
|
||||
return NewREST(etcdStorage), fakeClient
|
||||
storage, statusStorage := NewREST(etcdStorage)
|
||||
return storage, statusStorage, fakeClient
|
||||
}
|
||||
|
||||
func newValidDaemonSet() *experimental.DaemonSet {
|
||||
@ -64,7 +65,7 @@ func newValidDaemonSet() *experimental.DaemonSet {
|
||||
var validDaemonSet = newValidDaemonSet()
|
||||
|
||||
func TestCreate(t *testing.T) {
|
||||
storage, fakeClient := newStorage(t)
|
||||
storage, _, fakeClient := newStorage(t)
|
||||
test := registrytest.New(t, fakeClient, storage.Etcd)
|
||||
ds := newValidDaemonSet()
|
||||
ds.ObjectMeta = api.ObjectMeta{}
|
||||
@ -82,7 +83,7 @@ func TestCreate(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestUpdate(t *testing.T) {
|
||||
storage, fakeClient := newStorage(t)
|
||||
storage, _, fakeClient := newStorage(t)
|
||||
test := registrytest.New(t, fakeClient, storage.Etcd)
|
||||
test.TestUpdate(
|
||||
// valid
|
||||
@ -118,25 +119,25 @@ func TestUpdate(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDelete(t *testing.T) {
|
||||
storage, fakeClient := newStorage(t)
|
||||
storage, _, fakeClient := newStorage(t)
|
||||
test := registrytest.New(t, fakeClient, storage.Etcd)
|
||||
test.TestDelete(newValidDaemonSet())
|
||||
}
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
storage, fakeClient := newStorage(t)
|
||||
storage, _, fakeClient := newStorage(t)
|
||||
test := registrytest.New(t, fakeClient, storage.Etcd)
|
||||
test.TestGet(newValidDaemonSet())
|
||||
}
|
||||
|
||||
func TestList(t *testing.T) {
|
||||
storage, fakeClient := newStorage(t)
|
||||
storage, _, fakeClient := newStorage(t)
|
||||
test := registrytest.New(t, fakeClient, storage.Etcd)
|
||||
test.TestList(newValidDaemonSet())
|
||||
}
|
||||
|
||||
func TestWatch(t *testing.T) {
|
||||
storage, fakeClient := newStorage(t)
|
||||
storage, _, fakeClient := newStorage(t)
|
||||
test := registrytest.New(t, fakeClient, storage.Etcd)
|
||||
test.TestWatch(
|
||||
validDaemonSet,
|
||||
@ -160,3 +161,5 @@ func TestWatch(t *testing.T) {
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// TODO TestUpdateStatus
|
||||
|
@ -57,6 +57,9 @@ func (daemonSetStrategy) PrepareForUpdate(obj, old runtime.Object) {
|
||||
newDaemonSet := obj.(*experimental.DaemonSet)
|
||||
oldDaemonSet := old.(*experimental.DaemonSet)
|
||||
|
||||
// update is not allowed to set status
|
||||
newDaemonSet.Status = oldDaemonSet.Status
|
||||
|
||||
// Any changes to the spec increment the generation number, any changes to the
|
||||
// status should reflect the generation number of the corresponding object. We push
|
||||
// the burden of managing the status onto the clients because we can't (in general)
|
||||
@ -120,3 +123,19 @@ func MatchDaemonSet(label labels.Selector, field fields.Selector) generic.Matche
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type daemonSetStatusStrategy struct {
|
||||
daemonSetStrategy
|
||||
}
|
||||
|
||||
var StatusStrategy = daemonSetStatusStrategy{Strategy}
|
||||
|
||||
func (daemonSetStatusStrategy) PrepareForUpdate(obj, old runtime.Object) {
|
||||
newDaemonSet := obj.(*experimental.DaemonSet)
|
||||
oldDaemonSet := old.(*experimental.DaemonSet)
|
||||
newDaemonSet.Spec = oldDaemonSet.Spec
|
||||
}
|
||||
|
||||
func (daemonSetStatusStrategy) ValidateUpdate(ctx api.Context, obj, old runtime.Object) fielderrors.ValidationErrorList {
|
||||
return validation.ValidateDaemonSetStatusUpdate(obj.(*experimental.DaemonSet), old.(*experimental.DaemonSet))
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user