mirror of https://github.com/k3s-io/k3s
Merge pull request #14672 from derekwaynecarr/rc_status
Add replication controller status subresourcepull/6/head
commit
6217869085
|
@ -8024,6 +8024,65 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/api/v1/namespaces/{namespace}/replicationcontrollers/{name}/status",
|
||||
"description": "API at /api/v1",
|
||||
"operations": [
|
||||
{
|
||||
"type": "v1.ReplicationController",
|
||||
"method": "PUT",
|
||||
"summary": "replace status of the specified ReplicationController",
|
||||
"nickname": "replaceNamespacedReplicationControllerStatus",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"paramType": "query",
|
||||
"name": "pretty",
|
||||
"description": "If 'true', then the output is pretty printed.",
|
||||
"required": false,
|
||||
"allowMultiple": false
|
||||
},
|
||||
{
|
||||
"type": "v1.ReplicationController",
|
||||
"paramType": "body",
|
||||
"name": "body",
|
||||
"description": "",
|
||||
"required": true,
|
||||
"allowMultiple": false
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"paramType": "path",
|
||||
"name": "namespace",
|
||||
"description": "object name and auth scope, such as for teams and projects",
|
||||
"required": true,
|
||||
"allowMultiple": false
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"paramType": "path",
|
||||
"name": "name",
|
||||
"description": "name of the ReplicationController",
|
||||
"required": true,
|
||||
"allowMultiple": false
|
||||
}
|
||||
],
|
||||
"responseMessages": [
|
||||
{
|
||||
"code": 200,
|
||||
"message": "OK",
|
||||
"responseModel": "v1.ReplicationController"
|
||||
}
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"consumes": [
|
||||
"*/*"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/api/v1/namespaces/{namespace}/resourcequotas",
|
||||
"description": "API at /api/v1",
|
||||
|
|
|
@ -1352,6 +1352,15 @@ func ValidateReplicationControllerUpdate(oldController, controller *api.Replicat
|
|||
return allErrs
|
||||
}
|
||||
|
||||
// ValidateReplicationControllerStatusUpdate tests if required fields in the replication controller are set.
|
||||
func ValidateReplicationControllerStatusUpdate(oldController, controller *api.ReplicationController) errs.ValidationErrorList {
|
||||
allErrs := errs.ValidationErrorList{}
|
||||
allErrs = append(allErrs, ValidateObjectMetaUpdate(&controller.ObjectMeta, &oldController.ObjectMeta).Prefix("metadata")...)
|
||||
allErrs = append(allErrs, ValidatePositiveField(int64(controller.Status.Replicas), "status.replicas")...)
|
||||
allErrs = append(allErrs, ValidatePositiveField(int64(controller.Status.ObservedGeneration), "status.observedGeneration")...)
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// Validates that the given selector is non-empty.
|
||||
func ValidateNonEmptySelector(selectorMap map[string]string, fieldName string) errs.ValidationErrorList {
|
||||
allErrs := errs.ValidationErrorList{}
|
||||
|
|
|
@ -2101,6 +2101,89 @@ func TestValidateService(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestValidateReplicationControllerStatusUpdate(t *testing.T) {
|
||||
validSelector := map[string]string{"a": "b"}
|
||||
validPodTemplate := api.PodTemplate{
|
||||
Template: api.PodTemplateSpec{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Labels: validSelector,
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicyAlways,
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}},
|
||||
},
|
||||
},
|
||||
}
|
||||
type rcUpdateTest struct {
|
||||
old api.ReplicationController
|
||||
update api.ReplicationController
|
||||
}
|
||||
successCases := []rcUpdateTest{
|
||||
{
|
||||
old: api.ReplicationController{
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
|
||||
Spec: api.ReplicationControllerSpec{
|
||||
Selector: validSelector,
|
||||
Template: &validPodTemplate.Template,
|
||||
},
|
||||
Status: api.ReplicationControllerStatus{
|
||||
Replicas: 2,
|
||||
},
|
||||
},
|
||||
update: api.ReplicationController{
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
|
||||
Spec: api.ReplicationControllerSpec{
|
||||
Replicas: 3,
|
||||
Selector: validSelector,
|
||||
Template: &validPodTemplate.Template,
|
||||
},
|
||||
Status: api.ReplicationControllerStatus{
|
||||
Replicas: 4,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, successCase := range successCases {
|
||||
successCase.old.ObjectMeta.ResourceVersion = "1"
|
||||
successCase.update.ObjectMeta.ResourceVersion = "1"
|
||||
if errs := ValidateReplicationControllerStatusUpdate(&successCase.old, &successCase.update); len(errs) != 0 {
|
||||
t.Errorf("expected success: %v", errs)
|
||||
}
|
||||
}
|
||||
errorCases := map[string]rcUpdateTest{
|
||||
"negative replicas": {
|
||||
old: api.ReplicationController{
|
||||
ObjectMeta: api.ObjectMeta{Name: "", Namespace: api.NamespaceDefault},
|
||||
Spec: api.ReplicationControllerSpec{
|
||||
Selector: validSelector,
|
||||
Template: &validPodTemplate.Template,
|
||||
},
|
||||
Status: api.ReplicationControllerStatus{
|
||||
Replicas: 3,
|
||||
},
|
||||
},
|
||||
update: api.ReplicationController{
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
|
||||
Spec: api.ReplicationControllerSpec{
|
||||
Replicas: 2,
|
||||
Selector: validSelector,
|
||||
Template: &validPodTemplate.Template,
|
||||
},
|
||||
Status: api.ReplicationControllerStatus{
|
||||
Replicas: -3,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for testName, errorCase := range errorCases {
|
||||
if errs := ValidateReplicationControllerStatusUpdate(&errorCase.old, &errorCase.update); len(errs) == 0 {
|
||||
t.Errorf("expected failure: %s", testName)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestValidateReplicationControllerUpdate(t *testing.T) {
|
||||
validSelector := map[string]string{"a": "b"}
|
||||
validPodTemplate := api.PodTemplate{
|
||||
|
@ -2475,7 +2558,8 @@ func TestValidateReplicationController(t *testing.T) {
|
|||
field != "spec.replicas" &&
|
||||
field != "spec.template.labels" &&
|
||||
field != "metadata.annotations" &&
|
||||
field != "metadata.labels" {
|
||||
field != "metadata.labels" &&
|
||||
field != "status.replicas" {
|
||||
t.Errorf("%s: missing prefix for: %v", k, errs[i])
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@ type ReplicationControllerInterface interface {
|
|||
Get(name string) (*api.ReplicationController, error)
|
||||
Create(ctrl *api.ReplicationController) (*api.ReplicationController, error)
|
||||
Update(ctrl *api.ReplicationController) (*api.ReplicationController, error)
|
||||
UpdateStatus(ctrl *api.ReplicationController) (*api.ReplicationController, error)
|
||||
Delete(name string) error
|
||||
Watch(label labels.Selector, field fields.Selector, resourceVersion string) (watch.Interface, error)
|
||||
}
|
||||
|
@ -77,6 +78,13 @@ func (c *replicationControllers) Update(controller *api.ReplicationController) (
|
|||
return
|
||||
}
|
||||
|
||||
// UpdateStatus updates an existing replication controller status
|
||||
func (c *replicationControllers) UpdateStatus(controller *api.ReplicationController) (result *api.ReplicationController, err error) {
|
||||
result = &api.ReplicationController{}
|
||||
err = c.r.Put().Namespace(c.ns).Resource("replicationControllers").Name(controller.Name).SubResource("status").Body(controller).Do().Into(result)
|
||||
return
|
||||
}
|
||||
|
||||
// Delete deletes an existing replication controller.
|
||||
func (c *replicationControllers) Delete(name string) error {
|
||||
return c.r.Delete().Namespace(c.ns).Resource("replicationControllers").Name(name).Do().Error()
|
||||
|
|
|
@ -124,6 +124,36 @@ func TestUpdateController(t *testing.T) {
|
|||
c.Validate(t, receivedController, err)
|
||||
}
|
||||
|
||||
func TestUpdateStatusController(t *testing.T) {
|
||||
ns := api.NamespaceDefault
|
||||
requestController := &api.ReplicationController{
|
||||
ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "1"},
|
||||
}
|
||||
c := &testClient{
|
||||
Request: testRequest{Method: "PUT", Path: testapi.Default.ResourcePath(getRCResourceName(), ns, "foo") + "/status", Query: buildQueryValues(nil)},
|
||||
Response: Response{
|
||||
StatusCode: 200,
|
||||
Body: &api.ReplicationController{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "foo",
|
||||
Labels: map[string]string{
|
||||
"foo": "bar",
|
||||
"name": "baz",
|
||||
},
|
||||
},
|
||||
Spec: api.ReplicationControllerSpec{
|
||||
Replicas: 2,
|
||||
Template: &api.PodTemplateSpec{},
|
||||
},
|
||||
Status: api.ReplicationControllerStatus{
|
||||
Replicas: 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
receivedController, err := c.Setup(t).ReplicationControllers(ns).UpdateStatus(requestController)
|
||||
c.Validate(t, receivedController, err)
|
||||
}
|
||||
func TestDeleteController(t *testing.T) {
|
||||
ns := api.NamespaceDefault
|
||||
c := &testClient{
|
||||
|
|
|
@ -66,6 +66,14 @@ func (c *FakeReplicationControllers) Update(controller *api.ReplicationControlle
|
|||
return obj.(*api.ReplicationController), err
|
||||
}
|
||||
|
||||
func (c *FakeReplicationControllers) UpdateStatus(controller *api.ReplicationController) (*api.ReplicationController, error) {
|
||||
obj, err := c.Fake.Invokes(NewUpdateSubresourceAction("replicationcontrollers", "status", c.Namespace, controller), &api.ReplicationController{})
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*api.ReplicationController), err
|
||||
}
|
||||
|
||||
func (c *FakeReplicationControllers) Delete(name string) error {
|
||||
_, err := c.Fake.Invokes(NewDeleteAction("replicationcontrollers", c.Namespace, name), &api.ReplicationController{})
|
||||
return err
|
||||
|
|
|
@ -250,7 +250,7 @@ func TestStatusUpdatesWithoutReplicasChange(t *testing.T) {
|
|||
|
||||
rc.Status.ObservedGeneration = rc.Generation
|
||||
updatedRc := runtime.EncodeOrDie(testapi.Default.Codec(), rc)
|
||||
fakeHandler.ValidateRequest(t, testapi.Default.ResourcePath(replicationControllerResourceName(), rc.Namespace, rc.Name), "PUT", &updatedRc)
|
||||
fakeHandler.ValidateRequest(t, testapi.Default.ResourcePath(replicationControllerResourceName(), rc.Namespace, rc.Name)+"/status", "PUT", &updatedRc)
|
||||
}
|
||||
|
||||
func TestControllerUpdateReplicas(t *testing.T) {
|
||||
|
@ -288,7 +288,7 @@ func TestControllerUpdateReplicas(t *testing.T) {
|
|||
rc.Status = api.ReplicationControllerStatus{Replicas: 4, ObservedGeneration: 1}
|
||||
|
||||
decRc := runtime.EncodeOrDie(testapi.Default.Codec(), rc)
|
||||
fakeHandler.ValidateRequest(t, testapi.Default.ResourcePath(replicationControllerResourceName(), rc.Namespace, rc.Name), "PUT", &decRc)
|
||||
fakeHandler.ValidateRequest(t, testapi.Default.ResourcePath(replicationControllerResourceName(), rc.Namespace, rc.Name)+"/status", "PUT", &decRc)
|
||||
validateSyncReplication(t, &fakePodControl, 1, 0)
|
||||
}
|
||||
|
||||
|
|
|
@ -43,7 +43,7 @@ func updateReplicaCount(rcClient client.ReplicationControllerInterface, controll
|
|||
controller.Name, controller.Status.Replicas, numReplicas, controller.Spec.Replicas, controller.Status.ObservedGeneration, generation)
|
||||
|
||||
rc.Status = api.ReplicationControllerStatus{Replicas: numReplicas, ObservedGeneration: generation}
|
||||
_, updateErr = rcClient.Update(rc)
|
||||
_, updateErr = rcClient.UpdateStatus(rc)
|
||||
if updateErr == nil || i >= statusUpdateRetries {
|
||||
return updateErr
|
||||
}
|
||||
|
|
|
@ -551,7 +551,7 @@ func (m *Master) init(c *Config) {
|
|||
})
|
||||
m.serviceNodePortAllocator = serviceNodePortRegistry
|
||||
|
||||
controllerStorage := controlleretcd.NewREST(dbClient("replicationControllers"))
|
||||
controllerStorage, controllerStatusStorage := controlleretcd.NewREST(dbClient("replicationControllers"))
|
||||
|
||||
// TODO: Factor out the core API registration
|
||||
m.storage = map[string]rest.Storage{
|
||||
|
@ -567,12 +567,13 @@ func (m *Master) init(c *Config) {
|
|||
|
||||
"podTemplates": podTemplateStorage,
|
||||
|
||||
"replicationControllers": controllerStorage,
|
||||
"services": service.NewStorage(m.serviceRegistry, m.endpointRegistry, serviceClusterIPAllocator, serviceNodePortAllocator),
|
||||
"endpoints": endpointsStorage,
|
||||
"nodes": nodeStorage,
|
||||
"nodes/status": nodeStatusStorage,
|
||||
"events": eventStorage,
|
||||
"replicationControllers": controllerStorage,
|
||||
"replicationControllers/status": controllerStatusStorage,
|
||||
"services": service.NewStorage(m.serviceRegistry, m.endpointRegistry, serviceClusterIPAllocator, serviceNodePortAllocator),
|
||||
"endpoints": endpointsStorage,
|
||||
"nodes": nodeStorage,
|
||||
"nodes/status": nodeStatusStorage,
|
||||
"events": eventStorage,
|
||||
|
||||
"limitRanges": limitRangeStorage,
|
||||
"resourceQuotas": resourceQuotaStorage,
|
||||
|
|
|
@ -32,7 +32,7 @@ type REST struct {
|
|||
}
|
||||
|
||||
// NewREST returns a RESTStorage object that will work against replication controllers.
|
||||
func NewREST(s storage.Interface) *REST {
|
||||
func NewREST(s storage.Interface) (*REST, *StatusREST) {
|
||||
prefix := "/controllers"
|
||||
store := &etcdgeneric.Etcd{
|
||||
NewFunc: func() runtime.Object { return &api.ReplicationController{} },
|
||||
|
@ -67,5 +67,22 @@ func NewREST(s storage.Interface) *REST {
|
|||
|
||||
Storage: s,
|
||||
}
|
||||
return &REST{store}
|
||||
statusStore := *store
|
||||
statusStore.UpdateStrategy = controller.StatusStrategy
|
||||
|
||||
return &REST{store}, &StatusREST{store: &statusStore}
|
||||
}
|
||||
|
||||
// StatusREST implements the REST endpoint for changing the status of a replication controller
|
||||
type StatusREST struct {
|
||||
store *etcdgeneric.Etcd
|
||||
}
|
||||
|
||||
func (r *StatusREST) New() runtime.Object {
|
||||
return &api.ReplicationController{}
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
|
|
@ -27,9 +27,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, "")
|
||||
return NewREST(etcdStorage), fakeClient
|
||||
storage, statusStorage := NewREST(etcdStorage)
|
||||
return storage, statusStorage, fakeClient
|
||||
}
|
||||
|
||||
// createController is a helper function that returns a controller with the updated resource version.
|
||||
|
@ -74,7 +75,7 @@ func validNewController() *api.ReplicationController {
|
|||
var validController = validNewController()
|
||||
|
||||
func TestCreate(t *testing.T) {
|
||||
storage, fakeClient := newStorage(t)
|
||||
storage, _, fakeClient := newStorage(t)
|
||||
test := registrytest.New(t, fakeClient, storage.Etcd)
|
||||
controller := validNewController()
|
||||
controller.ObjectMeta = api.ObjectMeta{}
|
||||
|
@ -93,7 +94,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
|
||||
|
@ -124,13 +125,13 @@ 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(validNewController())
|
||||
}
|
||||
|
||||
func TestGenerationNumber(t *testing.T) {
|
||||
storage, _ := newStorage(t)
|
||||
storage, _, _ := newStorage(t)
|
||||
modifiedSno := *validNewController()
|
||||
modifiedSno.Generation = 100
|
||||
modifiedSno.Status.ObservedGeneration = 10
|
||||
|
@ -179,19 +180,19 @@ func TestGenerationNumber(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
storage, fakeClient := newStorage(t)
|
||||
storage, _, fakeClient := newStorage(t)
|
||||
test := registrytest.New(t, fakeClient, storage.Etcd)
|
||||
test.TestGet(validNewController())
|
||||
}
|
||||
|
||||
func TestList(t *testing.T) {
|
||||
storage, fakeClient := newStorage(t)
|
||||
storage, _, fakeClient := newStorage(t)
|
||||
test := registrytest.New(t, fakeClient, storage.Etcd)
|
||||
test.TestList(validNewController())
|
||||
}
|
||||
|
||||
func TestWatch(t *testing.T) {
|
||||
storage, fakeClient := newStorage(t)
|
||||
storage, _, fakeClient := newStorage(t)
|
||||
test := registrytest.New(t, fakeClient, storage.Etcd)
|
||||
test.TestWatch(
|
||||
validController,
|
||||
|
@ -220,3 +221,5 @@ func TestWatch(t *testing.T) {
|
|||
},
|
||||
)
|
||||
}
|
||||
|
||||
//TODO TestUpdateStatus
|
||||
|
|
|
@ -50,17 +50,14 @@ func (rcStrategy) PrepareForCreate(obj runtime.Object) {
|
|||
controller.Status = api.ReplicationControllerStatus{}
|
||||
|
||||
controller.Generation = 1
|
||||
controller.Status.ObservedGeneration = 0
|
||||
}
|
||||
|
||||
// PrepareForUpdate clears fields that are not allowed to be set by end users on update.
|
||||
func (rcStrategy) PrepareForUpdate(obj, old runtime.Object) {
|
||||
// TODO: once RC has a status sub-resource we can enable this.
|
||||
//newController := obj.(*api.ReplicationController)
|
||||
//oldController := old.(*api.ReplicationController)
|
||||
//newController.Status = oldController.Status
|
||||
newController := obj.(*api.ReplicationController)
|
||||
oldController := old.(*api.ReplicationController)
|
||||
// update is not allowed to set status
|
||||
newController.Status = oldController.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
|
||||
|
@ -125,3 +122,20 @@ func MatchController(label labels.Selector, field fields.Selector) generic.Match
|
|||
},
|
||||
}
|
||||
}
|
||||
|
||||
type rcStatusStrategy struct {
|
||||
rcStrategy
|
||||
}
|
||||
|
||||
var StatusStrategy = rcStatusStrategy{Strategy}
|
||||
|
||||
func (rcStatusStrategy) PrepareForUpdate(obj, old runtime.Object) {
|
||||
newRc := obj.(*api.ReplicationController)
|
||||
oldRc := old.(*api.ReplicationController)
|
||||
// update is not allowed to set spec
|
||||
newRc.Spec = oldRc.Spec
|
||||
}
|
||||
|
||||
func (rcStatusStrategy) ValidateUpdate(ctx api.Context, obj, old runtime.Object) fielderrors.ValidationErrorList {
|
||||
return validation.ValidateReplicationControllerStatusUpdate(old.(*api.ReplicationController), obj.(*api.ReplicationController))
|
||||
}
|
||||
|
|
|
@ -38,7 +38,9 @@ type ContainerStorage struct {
|
|||
}
|
||||
|
||||
func NewStorage(s storage.Interface) ContainerStorage {
|
||||
rcRegistry := controller.NewRegistry(etcd.NewREST(s))
|
||||
// scale does not set status, only updates spec so we ignore the status
|
||||
controllerREST, _ := etcd.NewREST(s)
|
||||
rcRegistry := controller.NewRegistry(controllerREST)
|
||||
|
||||
return ContainerStorage{
|
||||
ReplicationController: &RcREST{},
|
||||
|
|
Loading…
Reference in New Issue