mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-02 16:29:21 +00:00
Implemented Scale sub-resource for Deployment.
Implemented Scale sub-resource for Deployment.
This commit is contained in:
parent
755ff241e3
commit
53dab136ed
@ -826,7 +826,7 @@ func (m *Master) experimental(c *Config) *apiserver.APIGroupVersion {
|
||||
autoscalerStorage := horizontalpodautoscaleretcd.NewREST(c.ExpDatabaseStorage)
|
||||
thirdPartyResourceStorage := thirdpartyresourceetcd.NewREST(c.ExpDatabaseStorage)
|
||||
daemonSetStorage := daemonetcd.NewREST(c.ExpDatabaseStorage)
|
||||
deploymentStorage := deploymentetcd.NewREST(c.ExpDatabaseStorage)
|
||||
deploymentStorage := deploymentetcd.NewStorage(c.ExpDatabaseStorage)
|
||||
jobStorage := jobetcd.NewREST(c.ExpDatabaseStorage)
|
||||
|
||||
storage := map[string]rest.Storage{
|
||||
@ -835,7 +835,8 @@ func (m *Master) experimental(c *Config) *apiserver.APIGroupVersion {
|
||||
strings.ToLower("horizontalpodautoscalers"): autoscalerStorage,
|
||||
strings.ToLower("thirdpartyresources"): thirdPartyResourceStorage,
|
||||
strings.ToLower("daemonsets"): daemonSetStorage,
|
||||
strings.ToLower("deployments"): deploymentStorage,
|
||||
strings.ToLower("deployments"): deploymentStorage.Deployment,
|
||||
strings.ToLower("deployments/scale"): deploymentStorage.Scale,
|
||||
strings.ToLower("jobs"): jobStorage,
|
||||
}
|
||||
|
||||
|
@ -17,7 +17,11 @@ limitations under the License.
|
||||
package etcd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/errors"
|
||||
"k8s.io/kubernetes/pkg/api/rest"
|
||||
"k8s.io/kubernetes/pkg/apis/experimental"
|
||||
"k8s.io/kubernetes/pkg/fields"
|
||||
"k8s.io/kubernetes/pkg/labels"
|
||||
@ -28,6 +32,22 @@ import (
|
||||
"k8s.io/kubernetes/pkg/storage"
|
||||
)
|
||||
|
||||
// DeploymentStorage includes dummy storage for Deployments and for Scale subresource.
|
||||
type DeploymentStorage struct {
|
||||
Deployment *REST
|
||||
Scale *ScaleREST
|
||||
}
|
||||
|
||||
func NewStorage(s storage.Interface) DeploymentStorage {
|
||||
deploymentRest := NewREST(s)
|
||||
deploymentRegistry := deployment.NewRegistry(deploymentRest)
|
||||
|
||||
return DeploymentStorage{
|
||||
Deployment: deploymentRest,
|
||||
Scale: &ScaleREST{registry: &deploymentRegistry},
|
||||
}
|
||||
}
|
||||
|
||||
type REST struct {
|
||||
*etcdgeneric.Etcd
|
||||
}
|
||||
@ -69,3 +89,69 @@ func NewREST(s storage.Interface) *REST {
|
||||
}
|
||||
return &REST{store}
|
||||
}
|
||||
|
||||
type ScaleREST struct {
|
||||
registry *deployment.Registry
|
||||
}
|
||||
|
||||
// ScaleREST implements Patcher
|
||||
var _ = rest.Patcher(&ScaleREST{})
|
||||
|
||||
// New creates a new Scale object
|
||||
func (r *ScaleREST) New() runtime.Object {
|
||||
return &experimental.Scale{}
|
||||
}
|
||||
|
||||
func (r *ScaleREST) Get(ctx api.Context, name string) (runtime.Object, error) {
|
||||
deployment, err := (*r.registry).GetDeployment(ctx, name)
|
||||
if err != nil {
|
||||
return nil, errors.NewNotFound("scale", name)
|
||||
}
|
||||
return &experimental.Scale{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: deployment.Namespace,
|
||||
CreationTimestamp: deployment.CreationTimestamp,
|
||||
},
|
||||
Spec: experimental.ScaleSpec{
|
||||
Replicas: deployment.Spec.Replicas,
|
||||
},
|
||||
Status: experimental.ScaleStatus{
|
||||
Replicas: deployment.Status.Replicas,
|
||||
Selector: deployment.Spec.Selector,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *ScaleREST) Update(ctx api.Context, obj runtime.Object) (runtime.Object, bool, error) {
|
||||
if obj == nil {
|
||||
return nil, false, errors.NewBadRequest(fmt.Sprintf("nil update passed to Scale"))
|
||||
}
|
||||
scale, ok := obj.(*experimental.Scale)
|
||||
if !ok {
|
||||
return nil, false, errors.NewBadRequest(fmt.Sprintf("wrong object passed to Scale update: %v", obj))
|
||||
}
|
||||
deployment, err := (*r.registry).GetDeployment(ctx, scale.Name)
|
||||
if err != nil {
|
||||
return nil, false, errors.NewNotFound("scale", scale.Name)
|
||||
}
|
||||
deployment.Spec.Replicas = scale.Spec.Replicas
|
||||
deployment, err = (*r.registry).UpdateDeployment(ctx, deployment)
|
||||
if err != nil {
|
||||
return nil, false, errors.NewConflict("scale", scale.Name, err)
|
||||
}
|
||||
return &experimental.Scale{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: deployment.Name,
|
||||
Namespace: deployment.Namespace,
|
||||
CreationTimestamp: deployment.CreationTimestamp,
|
||||
},
|
||||
Spec: experimental.ScaleSpec{
|
||||
Replicas: deployment.Spec.Replicas,
|
||||
},
|
||||
Status: experimental.ScaleStatus{
|
||||
Replicas: deployment.Status.Replicas,
|
||||
Selector: deployment.Spec.Selector,
|
||||
},
|
||||
}, false, nil
|
||||
}
|
||||
|
@ -20,24 +20,31 @@ import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/testapi"
|
||||
"k8s.io/kubernetes/pkg/apis/experimental"
|
||||
"k8s.io/kubernetes/pkg/fields"
|
||||
"k8s.io/kubernetes/pkg/labels"
|
||||
"k8s.io/kubernetes/pkg/registry/registrytest"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/tools"
|
||||
"k8s.io/kubernetes/pkg/tools/etcdtest"
|
||||
"k8s.io/kubernetes/pkg/util"
|
||||
)
|
||||
|
||||
func newStorage(t *testing.T) (*REST, *tools.FakeEtcdClient) {
|
||||
func newStorage(t *testing.T) (*DeploymentStorage, *tools.FakeEtcdClient) {
|
||||
etcdStorage, fakeClient := registrytest.NewEtcdStorage(t, "experimental")
|
||||
return NewREST(etcdStorage), fakeClient
|
||||
deploymentStorage := NewStorage(etcdStorage)
|
||||
return &deploymentStorage, fakeClient
|
||||
}
|
||||
|
||||
var namespace = "foo-namespace"
|
||||
var name = "foo-deployment"
|
||||
|
||||
func validNewDeployment() *experimental.Deployment {
|
||||
return &experimental.Deployment{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "foo",
|
||||
Namespace: api.NamespaceDefault,
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: experimental.DeploymentSpec{
|
||||
Selector: map[string]string{"a": "b"},
|
||||
@ -58,15 +65,34 @@ func validNewDeployment() *experimental.Deployment {
|
||||
},
|
||||
},
|
||||
UniqueLabelKey: "my-label",
|
||||
Replicas: 7,
|
||||
},
|
||||
Status: experimental.DeploymentStatus{
|
||||
Replicas: 5,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
var validDeployment = *validNewDeployment()
|
||||
|
||||
func validNewScale() *experimental.Scale {
|
||||
return &experimental.Scale{
|
||||
ObjectMeta: api.ObjectMeta{Name: name, Namespace: namespace},
|
||||
Spec: experimental.ScaleSpec{
|
||||
Replicas: validDeployment.Spec.Replicas,
|
||||
},
|
||||
Status: experimental.ScaleStatus{
|
||||
Replicas: validDeployment.Status.Replicas,
|
||||
Selector: validDeployment.Spec.Template.Labels,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
var validScale = *validNewScale()
|
||||
|
||||
func TestCreate(t *testing.T) {
|
||||
storage, fakeClient := newStorage(t)
|
||||
test := registrytest.New(t, fakeClient, storage.Etcd)
|
||||
test := registrytest.New(t, fakeClient, storage.Deployment.Etcd)
|
||||
deployment := validNewDeployment()
|
||||
deployment.ObjectMeta = api.ObjectMeta{}
|
||||
test.TestCreate(
|
||||
@ -84,7 +110,7 @@ func TestCreate(t *testing.T) {
|
||||
|
||||
func TestUpdate(t *testing.T) {
|
||||
storage, fakeClient := newStorage(t)
|
||||
test := registrytest.New(t, fakeClient, storage.Etcd)
|
||||
test := registrytest.New(t, fakeClient, storage.Deployment.Etcd)
|
||||
test.TestUpdate(
|
||||
// valid
|
||||
validNewDeployment(),
|
||||
@ -120,25 +146,25 @@ func TestUpdate(t *testing.T) {
|
||||
|
||||
func TestDelete(t *testing.T) {
|
||||
storage, fakeClient := newStorage(t)
|
||||
test := registrytest.New(t, fakeClient, storage.Etcd)
|
||||
test := registrytest.New(t, fakeClient, storage.Deployment.Etcd)
|
||||
test.TestDelete(validNewDeployment())
|
||||
}
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
storage, fakeClient := newStorage(t)
|
||||
test := registrytest.New(t, fakeClient, storage.Etcd)
|
||||
test := registrytest.New(t, fakeClient, storage.Deployment.Etcd)
|
||||
test.TestGet(validNewDeployment())
|
||||
}
|
||||
|
||||
func TestList(t *testing.T) {
|
||||
storage, fakeClient := newStorage(t)
|
||||
test := registrytest.New(t, fakeClient, storage.Etcd)
|
||||
test := registrytest.New(t, fakeClient, storage.Deployment.Etcd)
|
||||
test.TestList(validNewDeployment())
|
||||
}
|
||||
|
||||
func TestWatch(t *testing.T) {
|
||||
storage, fakeClient := newStorage(t)
|
||||
test := registrytest.New(t, fakeClient, storage.Etcd)
|
||||
test := registrytest.New(t, fakeClient, storage.Deployment.Etcd)
|
||||
test.TestWatch(
|
||||
validNewDeployment(),
|
||||
// matching labels
|
||||
@ -150,12 +176,63 @@ func TestWatch(t *testing.T) {
|
||||
},
|
||||
// matching fields
|
||||
[]fields.Set{
|
||||
{"metadata.name": "foo"},
|
||||
{"metadata.name": name},
|
||||
},
|
||||
// not matchin fields
|
||||
[]fields.Set{
|
||||
{"metadata.name": "bar"},
|
||||
{"name": "foo"},
|
||||
{"name": name},
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func TestScaleGet(t *testing.T) {
|
||||
storage, fakeClient := newStorage(t)
|
||||
|
||||
ctx := api.WithNamespace(api.NewContext(), namespace)
|
||||
key := etcdtest.AddPrefix("/deployments/" + namespace + "/" + name)
|
||||
if _, err := fakeClient.Set(key, runtime.EncodeOrDie(testapi.Experimental.Codec(), &validDeployment), 0); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
expect := &validScale
|
||||
obj, err := storage.Scale.Get(ctx, name)
|
||||
scale := obj.(*experimental.Scale)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if e, a := expect, scale; !api.Semantic.DeepEqual(e, a) {
|
||||
t.Errorf("unexpected scale: %s", util.ObjectDiff(e, a))
|
||||
}
|
||||
}
|
||||
|
||||
func TestScaleUpdate(t *testing.T) {
|
||||
storage, fakeClient := newStorage(t)
|
||||
|
||||
ctx := api.WithNamespace(api.NewContext(), namespace)
|
||||
key := etcdtest.AddPrefix("/deployments/" + namespace + "/" + name)
|
||||
if _, err := fakeClient.Set(key, runtime.EncodeOrDie(testapi.Experimental.Codec(), &validDeployment), 0); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
replicas := 12
|
||||
update := experimental.Scale{
|
||||
ObjectMeta: api.ObjectMeta{Name: name, Namespace: namespace},
|
||||
Spec: experimental.ScaleSpec{
|
||||
Replicas: replicas,
|
||||
},
|
||||
}
|
||||
|
||||
if _, _, err := storage.Scale.Update(ctx, &update); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
response, err := fakeClient.Get(key, false, false)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
var deployment experimental.Deployment
|
||||
testapi.Experimental.Codec().DecodeInto([]byte(response.Node.Value), &deployment)
|
||||
if deployment.Spec.Replicas != replicas {
|
||||
t.Errorf("wrong replicas count expected: %d got: %d", replicas, deployment.Spec.Replicas)
|
||||
}
|
||||
}
|
||||
|
87
pkg/registry/deployment/registry.go
Normal file
87
pkg/registry/deployment/registry.go
Normal file
@ -0,0 +1,87 @@
|
||||
/*
|
||||
Copyright 2014 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package deployment
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/rest"
|
||||
"k8s.io/kubernetes/pkg/apis/experimental"
|
||||
"k8s.io/kubernetes/pkg/fields"
|
||||
"k8s.io/kubernetes/pkg/labels"
|
||||
)
|
||||
|
||||
// Registry is an interface for things that know how to store Deployments.
|
||||
type Registry interface {
|
||||
ListDeployments(ctx api.Context, label labels.Selector, field fields.Selector) (*experimental.DeploymentList, error)
|
||||
GetDeployment(ctx api.Context, deploymentID string) (*experimental.Deployment, error)
|
||||
CreateDeployment(ctx api.Context, deployment *experimental.Deployment) (*experimental.Deployment, error)
|
||||
UpdateDeployment(ctx api.Context, deployment *experimental.Deployment) (*experimental.Deployment, error)
|
||||
DeleteDeployment(ctx api.Context, deploymentID string) error
|
||||
}
|
||||
|
||||
// storage puts strong typing around storage calls
|
||||
type storage struct {
|
||||
rest.StandardStorage
|
||||
}
|
||||
|
||||
// NewRegistry returns a new Registry interface for the given Storage. Any mismatched types will panic.
|
||||
func NewRegistry(s rest.StandardStorage) Registry {
|
||||
return &storage{s}
|
||||
}
|
||||
|
||||
// List obtains a list of Deployments that match selector.
|
||||
func (s *storage) ListDeployments(ctx api.Context, label labels.Selector, field fields.Selector) (*experimental.DeploymentList, error) {
|
||||
if !field.Empty() {
|
||||
return nil, fmt.Errorf("field selector not supported yet")
|
||||
}
|
||||
obj, err := s.List(ctx, label, field)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*experimental.DeploymentList), err
|
||||
}
|
||||
|
||||
func (s *storage) GetDeployment(ctx api.Context, deploymentID string) (*experimental.Deployment, error) {
|
||||
obj, err := s.Get(ctx, deploymentID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*experimental.Deployment), nil
|
||||
}
|
||||
|
||||
func (s *storage) CreateDeployment(ctx api.Context, deployment *experimental.Deployment) (*experimental.Deployment, error) {
|
||||
obj, err := s.Create(ctx, deployment)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*experimental.Deployment), nil
|
||||
}
|
||||
|
||||
func (s *storage) UpdateDeployment(ctx api.Context, deployment *experimental.Deployment) (*experimental.Deployment, error) {
|
||||
obj, _, err := s.Update(ctx, deployment)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*experimental.Deployment), nil
|
||||
}
|
||||
|
||||
func (s *storage) DeleteDeployment(ctx api.Context, deploymentID string) error {
|
||||
_, err := s.Delete(ctx, deploymentID, nil)
|
||||
return err
|
||||
}
|
@ -50,7 +50,7 @@ type ScaleREST struct {
|
||||
registry *controller.Registry
|
||||
}
|
||||
|
||||
// LogREST implements GetterWithOptions
|
||||
// ScaleREST implements Patcher
|
||||
var _ = rest.Patcher(&ScaleREST{})
|
||||
|
||||
// New creates a new Scale object
|
||||
|
Loading…
Reference in New Issue
Block a user