Switch to github.com/golang/dep for vendoring

Signed-off-by: Mrunal Patel <mrunalp@gmail.com>
This commit is contained in:
Mrunal Patel 2017-01-31 16:45:59 -08:00
parent d6ab91be27
commit 8e5b17cf13
15431 changed files with 3971413 additions and 8881 deletions

81
vendor/k8s.io/kubernetes/pkg/registry/BUILD generated vendored Normal file
View file

@ -0,0 +1,81 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["doc.go"],
tags = ["automanaged"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//pkg/registry/apps/petset:all-srcs",
"//pkg/registry/apps/rest:all-srcs",
"//pkg/registry/authentication/rest:all-srcs",
"//pkg/registry/authentication/tokenreview:all-srcs",
"//pkg/registry/authorization/localsubjectaccessreview:all-srcs",
"//pkg/registry/authorization/rest:all-srcs",
"//pkg/registry/authorization/selfsubjectaccessreview:all-srcs",
"//pkg/registry/authorization/subjectaccessreview:all-srcs",
"//pkg/registry/authorization/util:all-srcs",
"//pkg/registry/autoscaling/horizontalpodautoscaler:all-srcs",
"//pkg/registry/autoscaling/rest:all-srcs",
"//pkg/registry/batch/cronjob:all-srcs",
"//pkg/registry/batch/job:all-srcs",
"//pkg/registry/batch/rest:all-srcs",
"//pkg/registry/cachesize:all-srcs",
"//pkg/registry/certificates/certificates:all-srcs",
"//pkg/registry/certificates/rest:all-srcs",
"//pkg/registry/core/componentstatus:all-srcs",
"//pkg/registry/core/configmap:all-srcs",
"//pkg/registry/core/controller:all-srcs",
"//pkg/registry/core/endpoint:all-srcs",
"//pkg/registry/core/event:all-srcs",
"//pkg/registry/core/limitrange:all-srcs",
"//pkg/registry/core/namespace:all-srcs",
"//pkg/registry/core/node:all-srcs",
"//pkg/registry/core/persistentvolume:all-srcs",
"//pkg/registry/core/persistentvolumeclaim:all-srcs",
"//pkg/registry/core/pod:all-srcs",
"//pkg/registry/core/podtemplate:all-srcs",
"//pkg/registry/core/rangeallocation:all-srcs",
"//pkg/registry/core/resourcequota:all-srcs",
"//pkg/registry/core/rest:all-srcs",
"//pkg/registry/core/secret:all-srcs",
"//pkg/registry/core/service:all-srcs",
"//pkg/registry/core/serviceaccount:all-srcs",
"//pkg/registry/extensions/controller/storage:all-srcs",
"//pkg/registry/extensions/daemonset:all-srcs",
"//pkg/registry/extensions/deployment:all-srcs",
"//pkg/registry/extensions/ingress:all-srcs",
"//pkg/registry/extensions/networkpolicy:all-srcs",
"//pkg/registry/extensions/podsecuritypolicy:all-srcs",
"//pkg/registry/extensions/replicaset:all-srcs",
"//pkg/registry/extensions/rest:all-srcs",
"//pkg/registry/extensions/thirdpartyresource:all-srcs",
"//pkg/registry/extensions/thirdpartyresourcedata:all-srcs",
"//pkg/registry/generic:all-srcs",
"//pkg/registry/policy/poddisruptionbudget:all-srcs",
"//pkg/registry/policy/rest:all-srcs",
"//pkg/registry/rbac:all-srcs",
"//pkg/registry/registrytest:all-srcs",
"//pkg/registry/storage/rest:all-srcs",
"//pkg/registry/storage/storageclass:all-srcs",
],
tags = ["automanaged"],
)

41
vendor/k8s.io/kubernetes/pkg/registry/OWNERS generated vendored Normal file
View file

@ -0,0 +1,41 @@
approvers:
- lavalamp
- smarterclayton
- wojtek-t
reviewers:
- thockin
- lavalamp
- smarterclayton
- wojtek-t
- bgrant0607
- deads2k
- yujuhong
- derekwaynecarr
- caesarxuchao
- mikedanese
- liggitt
- nikhiljindal
- bprashanth
- gmarek
- erictune
- davidopp
- pmorie
- kargakis
- saad-ali
- zmerlynn
- luxas
- janetkuo
- justinsb
- pwittrock
- roberthbailey
- ncdc
- timstclair
- eparis
- mwielgus
- timothysc
- jlowdermilk
- soltysh
- piosz
- dims
- madhusudancs
- hongchaodeng

3
vendor/k8s.io/kubernetes/pkg/registry/apps/OWNERS generated vendored Executable file
View file

@ -0,0 +1,3 @@
reviewers:
- deads2k
- hongchaodeng

View file

@ -0,0 +1,60 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"doc.go",
"strategy.go",
],
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/apis/apps:go_default_library",
"//pkg/apis/apps/validation:go_default_library",
"//pkg/fields:go_default_library",
"//pkg/registry/generic:go_default_library",
"//pkg/storage:go_default_library",
"//vendor:k8s.io/apimachinery/pkg/labels",
"//vendor:k8s.io/apimachinery/pkg/runtime",
"//vendor:k8s.io/apimachinery/pkg/util/validation/field",
"//vendor:k8s.io/apiserver/pkg/request",
"//vendor:k8s.io/apiserver/pkg/storage/names",
],
)
go_test(
name = "go_default_test",
srcs = ["strategy_test.go"],
library = ":go_default_library",
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/apis/apps:go_default_library",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
"//vendor:k8s.io/apiserver/pkg/request",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//pkg/registry/apps/petset/storage:all-srcs",
],
tags = ["automanaged"],
)

View file

@ -0,0 +1,17 @@
/*
Copyright 2015 The Kubernetes Authors.
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 petset // import "k8s.io/kubernetes/pkg/registry/apps/petset"

View file

@ -0,0 +1,57 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_test(
name = "go_default_test",
srcs = ["storage_test.go"],
library = ":go_default_library",
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/apis/apps:go_default_library",
"//pkg/fields:go_default_library",
"//pkg/genericapiserver/api/rest:go_default_library",
"//pkg/registry/generic:go_default_library",
"//pkg/registry/registrytest:go_default_library",
"//pkg/storage/etcd/testing:go_default_library",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
"//vendor:k8s.io/apimachinery/pkg/labels",
"//vendor:k8s.io/apiserver/pkg/request",
],
)
go_library(
name = "go_default_library",
srcs = ["storage.go"],
tags = ["automanaged"],
deps = [
"//pkg/apis/apps:go_default_library",
"//pkg/genericapiserver/api/rest:go_default_library",
"//pkg/registry/apps/petset:go_default_library",
"//pkg/registry/generic:go_default_library",
"//pkg/registry/generic/registry:go_default_library",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
"//vendor:k8s.io/apimachinery/pkg/runtime",
"//vendor:k8s.io/apiserver/pkg/request",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

View file

@ -0,0 +1,77 @@
/*
Copyright 2015 The Kubernetes Authors.
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 storage
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
genericapirequest "k8s.io/apiserver/pkg/request"
appsapi "k8s.io/kubernetes/pkg/apis/apps"
"k8s.io/kubernetes/pkg/genericapiserver/api/rest"
"k8s.io/kubernetes/pkg/registry/apps/petset"
"k8s.io/kubernetes/pkg/registry/generic"
genericregistry "k8s.io/kubernetes/pkg/registry/generic/registry"
)
// rest implements a RESTStorage for replication controllers against etcd
type REST struct {
*genericregistry.Store
}
// NewREST returns a RESTStorage object that will work against replication controllers.
func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST) {
store := &genericregistry.Store{
NewFunc: func() runtime.Object { return &appsapi.StatefulSet{} },
NewListFunc: func() runtime.Object { return &appsapi.StatefulSetList{} },
ObjectNameFunc: func(obj runtime.Object) (string, error) {
return obj.(*appsapi.StatefulSet).Name, nil
},
PredicateFunc: petset.MatchStatefulSet,
QualifiedResource: appsapi.Resource("statefulsets"),
CreateStrategy: petset.Strategy,
UpdateStrategy: petset.Strategy,
DeleteStrategy: petset.Strategy,
}
options := &generic.StoreOptions{RESTOptions: optsGetter, AttrFunc: petset.GetAttrs}
if err := store.CompleteWithOptions(options); err != nil {
panic(err) // TODO: Propagate error up
}
statusStore := *store
statusStore.UpdateStrategy = petset.StatusStrategy
return &REST{store}, &StatusREST{store: &statusStore}
}
// StatusREST implements the REST endpoint for changing the status of an statefulSet
type StatusREST struct {
store *genericregistry.Store
}
func (r *StatusREST) New() runtime.Object {
return &appsapi.StatefulSet{}
}
// Get retrieves the object from the storage. It is required to support Patch.
func (r *StatusREST) Get(ctx genericapirequest.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
return r.store.Get(ctx, name, options)
}
// Update alters the status subset of an object.
func (r *StatusREST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) {
return r.store.Update(ctx, name, objInfo)
}

View file

@ -0,0 +1,189 @@
/*
Copyright 2016 The Kubernetes Authors.
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 storage
import (
"testing"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
genericapirequest "k8s.io/apiserver/pkg/request"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/apps"
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/genericapiserver/api/rest"
"k8s.io/kubernetes/pkg/registry/generic"
"k8s.io/kubernetes/pkg/registry/registrytest"
etcdtesting "k8s.io/kubernetes/pkg/storage/etcd/testing"
)
// TODO: allow for global factory override
func newStorage(t *testing.T) (*REST, *StatusREST, *etcdtesting.EtcdTestServer) {
etcdStorage, server := registrytest.NewEtcdStorage(t, apps.GroupName)
restOptions := generic.RESTOptions{StorageConfig: etcdStorage, Decorator: generic.UndecoratedStorage, DeleteCollectionWorkers: 1, ResourcePrefix: "statefulsets"}
statefulSetStorage, statusStorage := NewREST(restOptions)
return statefulSetStorage, statusStorage, server
}
// createStatefulSet is a helper function that returns a StatefulSet with the updated resource version.
func createStatefulSet(storage *REST, ps apps.StatefulSet, t *testing.T) (apps.StatefulSet, error) {
ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), ps.Namespace)
obj, err := storage.Create(ctx, &ps)
if err != nil {
t.Errorf("Failed to create StatefulSet, %v", err)
}
newPS := obj.(*apps.StatefulSet)
return *newPS, nil
}
func validNewStatefulSet() *apps.StatefulSet {
return &apps.StatefulSet{
ObjectMeta: api.ObjectMeta{
Name: "foo",
Namespace: api.NamespaceDefault,
Labels: map[string]string{"a": "b"},
},
Spec: apps.StatefulSetSpec{
Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"a": "b"}},
Template: api.PodTemplateSpec{
ObjectMeta: api.ObjectMeta{
Labels: map[string]string{"a": "b"},
},
Spec: api.PodSpec{
Containers: []api.Container{
{
Name: "test",
Image: "test_image",
ImagePullPolicy: api.PullIfNotPresent,
},
},
RestartPolicy: api.RestartPolicyAlways,
DNSPolicy: api.DNSClusterFirst,
},
},
Replicas: 7,
},
Status: apps.StatefulSetStatus{},
}
}
func TestCreate(t *testing.T) {
storage, _, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := registrytest.New(t, storage.Store)
ps := validNewStatefulSet()
ps.ObjectMeta = api.ObjectMeta{}
test.TestCreate(
// valid
ps,
// TODO: Add an invalid case when we have validation.
)
}
// TODO: Test updates to spec when we allow them.
func TestStatusUpdate(t *testing.T) {
storage, statusStorage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), api.NamespaceDefault)
key := "/statefulsets/" + api.NamespaceDefault + "/foo"
validStatefulSet := validNewStatefulSet()
if err := storage.Storage.Create(ctx, key, validStatefulSet, nil, 0); err != nil {
t.Fatalf("unexpected error: %v", err)
}
update := apps.StatefulSet{
ObjectMeta: validStatefulSet.ObjectMeta,
Spec: apps.StatefulSetSpec{
Replicas: 7,
},
Status: apps.StatefulSetStatus{
Replicas: 7,
},
}
if _, _, err := statusStorage.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(&update, api.Scheme)); err != nil {
t.Fatalf("unexpected error: %v", err)
}
obj, err := storage.Get(ctx, "foo", &metav1.GetOptions{})
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
ps := obj.(*apps.StatefulSet)
if ps.Spec.Replicas != 7 {
t.Errorf("we expected .spec.replicas to not be updated but it was updated to %v", ps.Spec.Replicas)
}
if ps.Status.Replicas != 7 {
t.Errorf("we expected .status.replicas to be updated to %d but it was %v", 7, ps.Status.Replicas)
}
}
func TestGet(t *testing.T) {
storage, _, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := registrytest.New(t, storage.Store)
test.TestGet(validNewStatefulSet())
}
func TestList(t *testing.T) {
storage, _, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := registrytest.New(t, storage.Store)
test.TestList(validNewStatefulSet())
}
func TestDelete(t *testing.T) {
storage, _, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := registrytest.New(t, storage.Store)
test.TestDelete(validNewStatefulSet())
}
func TestWatch(t *testing.T) {
storage, _, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := registrytest.New(t, storage.Store)
test.TestWatch(
validNewStatefulSet(),
// matching labels
[]labels.Set{
{"a": "b"},
},
// not matching labels
[]labels.Set{
{"a": "c"},
{"foo": "bar"},
},
// matching fields
[]fields.Set{
{"metadata.name": "foo"},
},
// not matching fields
[]fields.Set{
{"metadata.name": "bar"},
},
)
}
// TODO: Test generation number.

View file

@ -0,0 +1,144 @@
/*
Copyright 2014 The Kubernetes Authors.
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 petset
import (
"fmt"
"reflect"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/validation/field"
genericapirequest "k8s.io/apiserver/pkg/request"
"k8s.io/apiserver/pkg/storage/names"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/apps"
"k8s.io/kubernetes/pkg/apis/apps/validation"
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/registry/generic"
"k8s.io/kubernetes/pkg/storage"
)
// statefulSetStrategy implements verification logic for Replication StatefulSets.
type statefulSetStrategy struct {
runtime.ObjectTyper
names.NameGenerator
}
// Strategy is the default logic that applies when creating and updating Replication StatefulSet objects.
var Strategy = statefulSetStrategy{api.Scheme, names.SimpleNameGenerator}
// NamespaceScoped returns true because all StatefulSet' need to be within a namespace.
func (statefulSetStrategy) NamespaceScoped() bool {
return true
}
// PrepareForCreate clears the status of an StatefulSet before creation.
func (statefulSetStrategy) PrepareForCreate(ctx genericapirequest.Context, obj runtime.Object) {
statefulSet := obj.(*apps.StatefulSet)
// create cannot set status
statefulSet.Status = apps.StatefulSetStatus{}
statefulSet.Generation = 1
}
// PrepareForUpdate clears fields that are not allowed to be set by end users on update.
func (statefulSetStrategy) PrepareForUpdate(ctx genericapirequest.Context, obj, old runtime.Object) {
newStatefulSet := obj.(*apps.StatefulSet)
oldStatefulSet := old.(*apps.StatefulSet)
// Update is not allowed to set status
newStatefulSet.Status = oldStatefulSet.Status
// Any changes to the spec increment the generation number, any changes to the
// status should reflect the generation number of the corresponding object.
// See api.ObjectMeta description for more information on Generation.
if !reflect.DeepEqual(oldStatefulSet.Spec, newStatefulSet.Spec) {
newStatefulSet.Generation = oldStatefulSet.Generation + 1
}
}
// Validate validates a new StatefulSet.
func (statefulSetStrategy) Validate(ctx genericapirequest.Context, obj runtime.Object) field.ErrorList {
statefulSet := obj.(*apps.StatefulSet)
return validation.ValidateStatefulSet(statefulSet)
}
// Canonicalize normalizes the object after validation.
func (statefulSetStrategy) Canonicalize(obj runtime.Object) {
}
// AllowCreateOnUpdate is false for StatefulSet; this means POST is needed to create one.
func (statefulSetStrategy) AllowCreateOnUpdate() bool {
return false
}
// ValidateUpdate is the default update validation for an end user.
func (statefulSetStrategy) ValidateUpdate(ctx genericapirequest.Context, obj, old runtime.Object) field.ErrorList {
validationErrorList := validation.ValidateStatefulSet(obj.(*apps.StatefulSet))
updateErrorList := validation.ValidateStatefulSetUpdate(obj.(*apps.StatefulSet), old.(*apps.StatefulSet))
return append(validationErrorList, updateErrorList...)
}
// AllowUnconditionalUpdate is the default update policy for StatefulSet objects.
func (statefulSetStrategy) AllowUnconditionalUpdate() bool {
return true
}
// StatefulSetToSelectableFields returns a field set that represents the object.
func StatefulSetToSelectableFields(statefulSet *apps.StatefulSet) fields.Set {
return generic.ObjectMetaFieldsSet(&statefulSet.ObjectMeta, true)
}
// GetAttrs returns labels and fields of a given object for filtering purposes.
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
statefulSet, ok := obj.(*apps.StatefulSet)
if !ok {
return nil, nil, fmt.Errorf("given object is not an StatefulSet.")
}
return labels.Set(statefulSet.ObjectMeta.Labels), StatefulSetToSelectableFields(statefulSet), nil
}
// MatchStatefulSet is the filter used by the generic etcd backend to watch events
// from etcd to clients of the apiserver only interested in specific labels/fields.
func MatchStatefulSet(label labels.Selector, field fields.Selector) storage.SelectionPredicate {
return storage.SelectionPredicate{
Label: label,
Field: field,
GetAttrs: GetAttrs,
}
}
type statefulSetStatusStrategy struct {
statefulSetStrategy
}
var StatusStrategy = statefulSetStatusStrategy{Strategy}
// PrepareForUpdate clears fields that are not allowed to be set by end users on update of status
func (statefulSetStatusStrategy) PrepareForUpdate(ctx genericapirequest.Context, obj, old runtime.Object) {
newStatefulSet := obj.(*apps.StatefulSet)
oldStatefulSet := old.(*apps.StatefulSet)
// status changes are not allowed to update spec
newStatefulSet.Spec = oldStatefulSet.Spec
}
// ValidateUpdate is the default update validation for an end user updating status
func (statefulSetStatusStrategy) ValidateUpdate(ctx genericapirequest.Context, obj, old runtime.Object) field.ErrorList {
// TODO: Validate status updates.
return validation.ValidateStatefulSetStatusUpdate(obj.(*apps.StatefulSet), old.(*apps.StatefulSet))
}

View file

@ -0,0 +1,145 @@
/*
Copyright 2016 The Kubernetes Authors.
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 petset
import (
"testing"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
genericapirequest "k8s.io/apiserver/pkg/request"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/apps"
)
func TestStatefulSetStrategy(t *testing.T) {
ctx := genericapirequest.NewDefaultContext()
if !Strategy.NamespaceScoped() {
t.Errorf("StatefulSet must be namespace scoped")
}
if Strategy.AllowCreateOnUpdate() {
t.Errorf("StatefulSet should not allow create on update")
}
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"}},
},
},
}
ps := &apps.StatefulSet{
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
Spec: apps.StatefulSetSpec{
Selector: &metav1.LabelSelector{MatchLabels: validSelector},
Template: validPodTemplate.Template,
},
Status: apps.StatefulSetStatus{Replicas: 3},
}
Strategy.PrepareForCreate(ctx, ps)
if ps.Status.Replicas != 0 {
t.Error("StatefulSet should not allow setting status.replicas on create")
}
errs := Strategy.Validate(ctx, ps)
if len(errs) != 0 {
t.Errorf("Unexpected error validating %v", errs)
}
// Just Spec.Replicas is allowed to change
validPs := &apps.StatefulSet{
ObjectMeta: api.ObjectMeta{Name: ps.Name, Namespace: ps.Namespace, ResourceVersion: "1", Generation: 1},
Spec: apps.StatefulSetSpec{
Selector: ps.Spec.Selector,
Template: validPodTemplate.Template,
},
Status: apps.StatefulSetStatus{Replicas: 4},
}
Strategy.PrepareForUpdate(ctx, validPs, ps)
errs = Strategy.ValidateUpdate(ctx, validPs, ps)
if len(errs) != 0 {
t.Errorf("Updating spec.Replicas is allowed on a statefulset: %v", errs)
}
validPs.Spec.Selector = &metav1.LabelSelector{MatchLabels: map[string]string{"a": "bar"}}
Strategy.PrepareForUpdate(ctx, validPs, ps)
errs = Strategy.ValidateUpdate(ctx, validPs, ps)
if len(errs) == 0 {
t.Errorf("Expected a validation error since updates are disallowed on statefulsets.")
}
}
func TestStatefulSetStatusStrategy(t *testing.T) {
ctx := genericapirequest.NewDefaultContext()
if !StatusStrategy.NamespaceScoped() {
t.Errorf("StatefulSet must be namespace scoped")
}
if StatusStrategy.AllowCreateOnUpdate() {
t.Errorf("StatefulSet should not allow create on update")
}
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"}},
},
},
}
oldPS := &apps.StatefulSet{
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault, ResourceVersion: "10"},
Spec: apps.StatefulSetSpec{
Replicas: 3,
Selector: &metav1.LabelSelector{MatchLabels: validSelector},
Template: validPodTemplate.Template,
},
Status: apps.StatefulSetStatus{
Replicas: 1,
},
}
newPS := &apps.StatefulSet{
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault, ResourceVersion: "9"},
Spec: apps.StatefulSetSpec{
Replicas: 1,
Selector: &metav1.LabelSelector{MatchLabels: validSelector},
Template: validPodTemplate.Template,
},
Status: apps.StatefulSetStatus{
Replicas: 2,
},
}
StatusStrategy.PrepareForUpdate(ctx, newPS, oldPS)
if newPS.Status.Replicas != 2 {
t.Errorf("StatefulSet status updates should allow change of pods: %v", newPS.Status.Replicas)
}
if newPS.Spec.Replicas != 3 {
t.Errorf("StatefulSet status updates should not clobber spec: %v", newPS.Spec)
}
errs := StatusStrategy.ValidateUpdate(ctx, newPS, oldPS)
if len(errs) != 0 {
t.Errorf("Unexpected error %v", errs)
}
}

35
vendor/k8s.io/kubernetes/pkg/registry/apps/rest/BUILD generated vendored Normal file
View file

@ -0,0 +1,35 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["storage_apps.go"],
tags = ["automanaged"],
deps = [
"//pkg/apis/apps:go_default_library",
"//pkg/apis/apps/v1beta1:go_default_library",
"//pkg/genericapiserver:go_default_library",
"//pkg/genericapiserver/api/rest:go_default_library",
"//pkg/registry/apps/petset/storage:go_default_library",
"//pkg/registry/generic:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

View file

@ -0,0 +1,55 @@
/*
Copyright 2016 The Kubernetes Authors.
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 rest
import (
"k8s.io/kubernetes/pkg/apis/apps"
appsapiv1beta1 "k8s.io/kubernetes/pkg/apis/apps/v1beta1"
"k8s.io/kubernetes/pkg/genericapiserver"
"k8s.io/kubernetes/pkg/genericapiserver/api/rest"
statefulsetstore "k8s.io/kubernetes/pkg/registry/apps/petset/storage"
"k8s.io/kubernetes/pkg/registry/generic"
)
type RESTStorageProvider struct{}
func (p RESTStorageProvider) NewRESTStorage(apiResourceConfigSource genericapiserver.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) (genericapiserver.APIGroupInfo, bool) {
apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(apps.GroupName)
if apiResourceConfigSource.AnyResourcesForVersionEnabled(appsapiv1beta1.SchemeGroupVersion) {
apiGroupInfo.VersionedResourcesStorageMap[appsapiv1beta1.SchemeGroupVersion.Version] = p.v1beta1Storage(apiResourceConfigSource, restOptionsGetter)
apiGroupInfo.GroupMeta.GroupVersion = appsapiv1beta1.SchemeGroupVersion
}
return apiGroupInfo, true
}
func (p RESTStorageProvider) v1beta1Storage(apiResourceConfigSource genericapiserver.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) map[string]rest.Storage {
version := appsapiv1beta1.SchemeGroupVersion
storage := map[string]rest.Storage{}
if apiResourceConfigSource.ResourceEnabled(version.WithResource("statefulsets")) {
statefulsetStorage, statefulsetStatusStorage := statefulsetstore.NewREST(restOptionsGetter)
storage["statefulsets"] = statefulsetStorage
storage["statefulsets/status"] = statefulsetStatusStorage
}
return storage
}
func (p RESTStorageProvider) GroupName() string {
return apps.GroupName
}

View file

@ -0,0 +1,2 @@
reviewers:
- deads2k

View file

@ -0,0 +1,36 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["storage_authentication.go"],
tags = ["automanaged"],
deps = [
"//pkg/apis/authentication:go_default_library",
"//pkg/apis/authentication/v1beta1:go_default_library",
"//pkg/genericapiserver:go_default_library",
"//pkg/genericapiserver/api/rest:go_default_library",
"//pkg/registry/authentication/tokenreview:go_default_library",
"//pkg/registry/generic:go_default_library",
"//vendor:k8s.io/apiserver/pkg/authentication/authenticator",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

View file

@ -0,0 +1,65 @@
/*
Copyright 2016 The Kubernetes Authors.
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 rest
import (
"k8s.io/apiserver/pkg/authentication/authenticator"
"k8s.io/kubernetes/pkg/apis/authentication"
authenticationv1beta1 "k8s.io/kubernetes/pkg/apis/authentication/v1beta1"
"k8s.io/kubernetes/pkg/genericapiserver"
"k8s.io/kubernetes/pkg/genericapiserver/api/rest"
"k8s.io/kubernetes/pkg/registry/authentication/tokenreview"
"k8s.io/kubernetes/pkg/registry/generic"
)
type RESTStorageProvider struct {
Authenticator authenticator.Request
}
func (p RESTStorageProvider) NewRESTStorage(apiResourceConfigSource genericapiserver.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) (genericapiserver.APIGroupInfo, bool) {
// TODO figure out how to make the swagger generation stable, while allowing this endpoint to be disabled.
// if p.Authenticator == nil {
// return genericapiserver.APIGroupInfo{}, false
// }
apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(authentication.GroupName)
if apiResourceConfigSource.AnyResourcesForVersionEnabled(authenticationv1beta1.SchemeGroupVersion) {
apiGroupInfo.VersionedResourcesStorageMap[authenticationv1beta1.SchemeGroupVersion.Version] = p.v1beta1Storage(apiResourceConfigSource, restOptionsGetter)
apiGroupInfo.GroupMeta.GroupVersion = authenticationv1beta1.SchemeGroupVersion
}
return apiGroupInfo, true
}
func (p RESTStorageProvider) v1beta1Storage(apiResourceConfigSource genericapiserver.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) map[string]rest.Storage {
version := authenticationv1beta1.SchemeGroupVersion
storage := map[string]rest.Storage{}
if apiResourceConfigSource.AnyResourcesForVersionEnabled(authenticationv1beta1.SchemeGroupVersion) {
if apiResourceConfigSource.ResourceEnabled(version.WithResource("tokenreviews")) {
tokenReviewStorage := tokenreview.NewREST(p.Authenticator)
storage["tokenreviews"] = tokenReviewStorage
}
}
return storage
}
func (p RESTStorageProvider) GroupName() string {
return authentication.GroupName
}

View file

@ -0,0 +1,34 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["storage.go"],
tags = ["automanaged"],
deps = [
"//pkg/apis/authentication:go_default_library",
"//vendor:k8s.io/apimachinery/pkg/api/errors",
"//vendor:k8s.io/apimachinery/pkg/runtime",
"//vendor:k8s.io/apiserver/pkg/authentication/authenticator",
"//vendor:k8s.io/apiserver/pkg/request",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

View file

@ -0,0 +1,78 @@
/*
Copyright 2015 The Kubernetes Authors.
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 tokenreview
import (
"fmt"
"net/http"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/authentication/authenticator"
genericapirequest "k8s.io/apiserver/pkg/request"
"k8s.io/kubernetes/pkg/apis/authentication"
)
type REST struct {
tokenAuthenticator authenticator.Request
}
func NewREST(tokenAuthenticator authenticator.Request) *REST {
return &REST{tokenAuthenticator: tokenAuthenticator}
}
func (r *REST) New() runtime.Object {
return &authentication.TokenReview{}
}
func (r *REST) Create(ctx genericapirequest.Context, obj runtime.Object) (runtime.Object, error) {
tokenReview, ok := obj.(*authentication.TokenReview)
if !ok {
return nil, apierrors.NewBadRequest(fmt.Sprintf("not a TokenReview: %#v", obj))
}
namespace := genericapirequest.NamespaceValue(ctx)
if len(namespace) != 0 {
return nil, apierrors.NewBadRequest(fmt.Sprintf("namespace is not allowed on this type: %v", namespace))
}
if r.tokenAuthenticator == nil {
return tokenReview, nil
}
// create a header that contains nothing but the token
fakeReq := &http.Request{Header: http.Header{}}
fakeReq.Header.Add("Authorization", "Bearer "+tokenReview.Spec.Token)
tokenUser, ok, err := r.tokenAuthenticator.AuthenticateRequest(fakeReq)
tokenReview.Status.Authenticated = ok
if err != nil {
tokenReview.Status.Error = err.Error()
}
if tokenUser != nil {
tokenReview.Status.User = authentication.UserInfo{
Username: tokenUser.GetName(),
UID: tokenUser.GetUID(),
Groups: tokenUser.GetGroups(),
Extra: map[string]authentication.ExtraValue{},
}
for k, v := range tokenUser.GetExtra() {
tokenReview.Status.User.Extra[k] = authentication.ExtraValue(v)
}
}
return tokenReview, nil
}

2
vendor/k8s.io/kubernetes/pkg/registry/authorization/OWNERS generated vendored Executable file
View file

@ -0,0 +1,2 @@
reviewers:
- deads2k

View file

@ -0,0 +1,36 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["rest.go"],
tags = ["automanaged"],
deps = [
"//pkg/apis/authorization:go_default_library",
"//pkg/apis/authorization/validation:go_default_library",
"//pkg/registry/authorization/util:go_default_library",
"//vendor:k8s.io/apimachinery/pkg/api/errors",
"//vendor:k8s.io/apimachinery/pkg/runtime",
"//vendor:k8s.io/apiserver/pkg/authorization/authorizer",
"//vendor:k8s.io/apiserver/pkg/request",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

View file

@ -0,0 +1,71 @@
/*
Copyright 2016 The Kubernetes Authors.
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 localsubjectaccessreview
import (
"fmt"
kapierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/authorization/authorizer"
genericapirequest "k8s.io/apiserver/pkg/request"
authorizationapi "k8s.io/kubernetes/pkg/apis/authorization"
authorizationvalidation "k8s.io/kubernetes/pkg/apis/authorization/validation"
authorizationutil "k8s.io/kubernetes/pkg/registry/authorization/util"
)
type REST struct {
authorizer authorizer.Authorizer
}
func NewREST(authorizer authorizer.Authorizer) *REST {
return &REST{authorizer}
}
func (r *REST) New() runtime.Object {
return &authorizationapi.LocalSubjectAccessReview{}
}
func (r *REST) Create(ctx genericapirequest.Context, obj runtime.Object) (runtime.Object, error) {
localSubjectAccessReview, ok := obj.(*authorizationapi.LocalSubjectAccessReview)
if !ok {
return nil, kapierrors.NewBadRequest(fmt.Sprintf("not a LocaLocalSubjectAccessReview: %#v", obj))
}
if errs := authorizationvalidation.ValidateLocalSubjectAccessReview(localSubjectAccessReview); len(errs) > 0 {
return nil, kapierrors.NewInvalid(authorizationapi.Kind(localSubjectAccessReview.Kind), "", errs)
}
namespace := genericapirequest.NamespaceValue(ctx)
if len(namespace) == 0 {
return nil, kapierrors.NewBadRequest(fmt.Sprintf("namespace is required on this type: %v", namespace))
}
if namespace != localSubjectAccessReview.Namespace {
return nil, kapierrors.NewBadRequest(fmt.Sprintf("spec.resourceAttributes.namespace must match namespace: %v", namespace))
}
authorizationAttributes := authorizationutil.AuthorizationAttributesFrom(localSubjectAccessReview.Spec)
allowed, reason, evaluationErr := r.authorizer.Authorize(authorizationAttributes)
localSubjectAccessReview.Status = authorizationapi.SubjectAccessReviewStatus{
Allowed: allowed,
Reason: reason,
}
if evaluationErr != nil {
localSubjectAccessReview.Status.EvaluationError = evaluationErr.Error()
}
return localSubjectAccessReview, nil
}

View file

@ -0,0 +1,38 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["storage_authorization.go"],
tags = ["automanaged"],
deps = [
"//pkg/apis/authorization:go_default_library",
"//pkg/apis/authorization/v1beta1:go_default_library",
"//pkg/genericapiserver:go_default_library",
"//pkg/genericapiserver/api/rest:go_default_library",
"//pkg/registry/authorization/localsubjectaccessreview:go_default_library",
"//pkg/registry/authorization/selfsubjectaccessreview:go_default_library",
"//pkg/registry/authorization/subjectaccessreview:go_default_library",
"//pkg/registry/generic:go_default_library",
"//vendor:k8s.io/apiserver/pkg/authorization/authorizer",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

View file

@ -0,0 +1,69 @@
/*
Copyright 2016 The Kubernetes Authors.
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 rest
import (
"k8s.io/apiserver/pkg/authorization/authorizer"
"k8s.io/kubernetes/pkg/apis/authorization"
authorizationv1beta1 "k8s.io/kubernetes/pkg/apis/authorization/v1beta1"
"k8s.io/kubernetes/pkg/genericapiserver"
"k8s.io/kubernetes/pkg/genericapiserver/api/rest"
"k8s.io/kubernetes/pkg/registry/authorization/localsubjectaccessreview"
"k8s.io/kubernetes/pkg/registry/authorization/selfsubjectaccessreview"
"k8s.io/kubernetes/pkg/registry/authorization/subjectaccessreview"
"k8s.io/kubernetes/pkg/registry/generic"
)
type RESTStorageProvider struct {
Authorizer authorizer.Authorizer
}
func (p RESTStorageProvider) NewRESTStorage(apiResourceConfigSource genericapiserver.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) (genericapiserver.APIGroupInfo, bool) {
if p.Authorizer == nil {
return genericapiserver.APIGroupInfo{}, false
}
apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(authorization.GroupName)
if apiResourceConfigSource.AnyResourcesForVersionEnabled(authorizationv1beta1.SchemeGroupVersion) {
apiGroupInfo.VersionedResourcesStorageMap[authorizationv1beta1.SchemeGroupVersion.Version] = p.v1beta1Storage(apiResourceConfigSource, restOptionsGetter)
apiGroupInfo.GroupMeta.GroupVersion = authorizationv1beta1.SchemeGroupVersion
}
return apiGroupInfo, true
}
func (p RESTStorageProvider) v1beta1Storage(apiResourceConfigSource genericapiserver.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) map[string]rest.Storage {
version := authorizationv1beta1.SchemeGroupVersion
storage := map[string]rest.Storage{}
if apiResourceConfigSource.ResourceEnabled(version.WithResource("subjectaccessreviews")) {
storage["subjectaccessreviews"] = subjectaccessreview.NewREST(p.Authorizer)
}
if apiResourceConfigSource.ResourceEnabled(version.WithResource("selfsubjectaccessreviews")) {
storage["selfsubjectaccessreviews"] = selfsubjectaccessreview.NewREST(p.Authorizer)
}
if apiResourceConfigSource.ResourceEnabled(version.WithResource("localsubjectaccessreviews")) {
storage["localsubjectaccessreviews"] = localsubjectaccessreview.NewREST(p.Authorizer)
}
return storage
}
func (p RESTStorageProvider) GroupName() string {
return authorization.GroupName
}

View file

@ -0,0 +1,36 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["rest.go"],
tags = ["automanaged"],
deps = [
"//pkg/apis/authorization:go_default_library",
"//pkg/apis/authorization/validation:go_default_library",
"//pkg/registry/authorization/util:go_default_library",
"//vendor:k8s.io/apimachinery/pkg/api/errors",
"//vendor:k8s.io/apimachinery/pkg/runtime",
"//vendor:k8s.io/apiserver/pkg/authorization/authorizer",
"//vendor:k8s.io/apiserver/pkg/request",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

View file

@ -0,0 +1,74 @@
/*
Copyright 2016 The Kubernetes Authors.
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 selfsubjectaccessreview
import (
"fmt"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/authorization/authorizer"
genericapirequest "k8s.io/apiserver/pkg/request"
authorizationapi "k8s.io/kubernetes/pkg/apis/authorization"
authorizationvalidation "k8s.io/kubernetes/pkg/apis/authorization/validation"
authorizationutil "k8s.io/kubernetes/pkg/registry/authorization/util"
)
type REST struct {
authorizer authorizer.Authorizer
}
func NewREST(authorizer authorizer.Authorizer) *REST {
return &REST{authorizer}
}
func (r *REST) New() runtime.Object {
return &authorizationapi.SelfSubjectAccessReview{}
}
func (r *REST) Create(ctx genericapirequest.Context, obj runtime.Object) (runtime.Object, error) {
selfSAR, ok := obj.(*authorizationapi.SelfSubjectAccessReview)
if !ok {
return nil, apierrors.NewBadRequest(fmt.Sprintf("not a SelfSubjectAccessReview: %#v", obj))
}
if errs := authorizationvalidation.ValidateSelfSubjectAccessReview(selfSAR); len(errs) > 0 {
return nil, apierrors.NewInvalid(authorizationapi.Kind(selfSAR.Kind), "", errs)
}
userToCheck, exists := genericapirequest.UserFrom(ctx)
if !exists {
return nil, apierrors.NewBadRequest("no user present on request")
}
var authorizationAttributes authorizer.AttributesRecord
if selfSAR.Spec.ResourceAttributes != nil {
authorizationAttributes = authorizationutil.ResourceAttributesFrom(userToCheck, *selfSAR.Spec.ResourceAttributes)
} else {
authorizationAttributes = authorizationutil.NonResourceAttributesFrom(userToCheck, *selfSAR.Spec.NonResourceAttributes)
}
allowed, reason, evaluationErr := r.authorizer.Authorize(authorizationAttributes)
selfSAR.Status = authorizationapi.SubjectAccessReviewStatus{
Allowed: allowed,
Reason: reason,
}
if evaluationErr != nil {
selfSAR.Status.EvaluationError = evaluationErr.Error()
}
return selfSAR, nil
}

View file

@ -0,0 +1,36 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["rest.go"],
tags = ["automanaged"],
deps = [
"//pkg/apis/authorization:go_default_library",
"//pkg/apis/authorization/validation:go_default_library",
"//pkg/registry/authorization/util:go_default_library",
"//vendor:k8s.io/apimachinery/pkg/api/errors",
"//vendor:k8s.io/apimachinery/pkg/runtime",
"//vendor:k8s.io/apiserver/pkg/authorization/authorizer",
"//vendor:k8s.io/apiserver/pkg/request",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

View file

@ -0,0 +1,64 @@
/*
Copyright 2016 The Kubernetes Authors.
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 subjectaccessreview
import (
"fmt"
kapierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/authorization/authorizer"
genericapirequest "k8s.io/apiserver/pkg/request"
authorizationapi "k8s.io/kubernetes/pkg/apis/authorization"
authorizationvalidation "k8s.io/kubernetes/pkg/apis/authorization/validation"
authorizationutil "k8s.io/kubernetes/pkg/registry/authorization/util"
)
type REST struct {
authorizer authorizer.Authorizer
}
func NewREST(authorizer authorizer.Authorizer) *REST {
return &REST{authorizer}
}
func (r *REST) New() runtime.Object {
return &authorizationapi.SubjectAccessReview{}
}
func (r *REST) Create(ctx genericapirequest.Context, obj runtime.Object) (runtime.Object, error) {
subjectAccessReview, ok := obj.(*authorizationapi.SubjectAccessReview)
if !ok {
return nil, kapierrors.NewBadRequest(fmt.Sprintf("not a SubjectAccessReview: %#v", obj))
}
if errs := authorizationvalidation.ValidateSubjectAccessReview(subjectAccessReview); len(errs) > 0 {
return nil, kapierrors.NewInvalid(authorizationapi.Kind(subjectAccessReview.Kind), "", errs)
}
authorizationAttributes := authorizationutil.AuthorizationAttributesFrom(subjectAccessReview.Spec)
allowed, reason, evaluationErr := r.authorizer.Authorize(authorizationAttributes)
subjectAccessReview.Status = authorizationapi.SubjectAccessReviewStatus{
Allowed: allowed,
Reason: reason,
}
if evaluationErr != nil {
subjectAccessReview.Status.EvaluationError = evaluationErr.Error()
}
return subjectAccessReview, nil
}

View file

@ -0,0 +1,32 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["helpers.go"],
tags = ["automanaged"],
deps = [
"//pkg/apis/authorization:go_default_library",
"//vendor:k8s.io/apiserver/pkg/authentication/user",
"//vendor:k8s.io/apiserver/pkg/authorization/authorizer",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

View file

@ -0,0 +1,75 @@
/*
Copyright 2016 The Kubernetes Authors.
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 util
import (
"k8s.io/apiserver/pkg/authentication/user"
"k8s.io/apiserver/pkg/authorization/authorizer"
authorizationapi "k8s.io/kubernetes/pkg/apis/authorization"
)
// ResourceAttributesFrom combines the API object information and the user.Info from the context to build a full authorizer.AttributesRecord for resource access
func ResourceAttributesFrom(user user.Info, in authorizationapi.ResourceAttributes) authorizer.AttributesRecord {
return authorizer.AttributesRecord{
User: user,
Verb: in.Verb,
Namespace: in.Namespace,
APIGroup: in.Group,
Resource: in.Resource,
ResourceRequest: true,
}
}
// NonResourceAttributesFrom combines the API object information and the user.Info from the context to build a full authorizer.AttributesRecord for non resource access
func NonResourceAttributesFrom(user user.Info, in authorizationapi.NonResourceAttributes) authorizer.AttributesRecord {
return authorizer.AttributesRecord{
User: user,
ResourceRequest: false,
Path: in.Path,
Verb: in.Verb,
}
}
func convertToUserInfoExtra(extra map[string]authorizationapi.ExtraValue) map[string][]string {
if extra == nil {
return nil
}
ret := map[string][]string{}
for k, v := range extra {
ret[k] = []string(v)
}
return ret
}
// AuthorizationAttributesFrom takes a spec and returns the proper authz attributes to check it.
func AuthorizationAttributesFrom(spec authorizationapi.SubjectAccessReviewSpec) authorizer.AttributesRecord {
userToCheck := &user.DefaultInfo{
Name: spec.User,
Groups: spec.Groups,
Extra: convertToUserInfoExtra(spec.Extra),
}
var authorizationAttributes authorizer.AttributesRecord
if spec.ResourceAttributes != nil {
authorizationAttributes = ResourceAttributesFrom(userToCheck, *spec.ResourceAttributes)
} else {
authorizationAttributes = NonResourceAttributesFrom(userToCheck, *spec.NonResourceAttributes)
}
return authorizationAttributes
}

3
vendor/k8s.io/kubernetes/pkg/registry/autoscaling/OWNERS generated vendored Executable file
View file

@ -0,0 +1,3 @@
reviewers:
- deads2k
- hongchaodeng

View file

@ -0,0 +1,59 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"doc.go",
"strategy.go",
],
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/apis/autoscaling:go_default_library",
"//pkg/apis/autoscaling/validation:go_default_library",
"//pkg/fields:go_default_library",
"//pkg/storage:go_default_library",
"//vendor:k8s.io/apimachinery/pkg/labels",
"//vendor:k8s.io/apimachinery/pkg/runtime",
"//vendor:k8s.io/apimachinery/pkg/util/validation/field",
"//vendor:k8s.io/apiserver/pkg/request",
"//vendor:k8s.io/apiserver/pkg/storage/names",
],
)
go_test(
name = "go_default_test",
srcs = ["strategy_test.go"],
library = ":go_default_library",
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/api/testapi:go_default_library",
"//pkg/api/testing:go_default_library",
"//pkg/apis/autoscaling:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//pkg/registry/autoscaling/horizontalpodautoscaler/storage:all-srcs",
],
tags = ["automanaged"],
)

View file

@ -0,0 +1,17 @@
/*
Copyright 2014 The Kubernetes Authors.
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 horizontalpodautoscaler // import "k8s.io/kubernetes/pkg/registry/autoscaling/horizontalpodautoscaler"

View file

@ -0,0 +1,56 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_test(
name = "go_default_test",
srcs = ["storage_test.go"],
library = ":go_default_library",
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/apis/autoscaling:go_default_library",
"//pkg/apis/autoscaling/v1:go_default_library",
"//pkg/fields:go_default_library",
"//pkg/registry/generic:go_default_library",
"//pkg/registry/registrytest:go_default_library",
"//pkg/storage/etcd/testing:go_default_library",
"//vendor:k8s.io/apimachinery/pkg/labels",
"//vendor:k8s.io/apimachinery/pkg/runtime",
],
)
go_library(
name = "go_default_library",
srcs = ["storage.go"],
tags = ["automanaged"],
deps = [
"//pkg/apis/autoscaling:go_default_library",
"//pkg/genericapiserver/api/rest:go_default_library",
"//pkg/registry/autoscaling/horizontalpodautoscaler:go_default_library",
"//pkg/registry/generic:go_default_library",
"//pkg/registry/generic/registry:go_default_library",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
"//vendor:k8s.io/apimachinery/pkg/runtime",
"//vendor:k8s.io/apiserver/pkg/request",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

View file

@ -0,0 +1,76 @@
/*
Copyright 2014 The Kubernetes Authors.
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 storage
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
genericapirequest "k8s.io/apiserver/pkg/request"
"k8s.io/kubernetes/pkg/apis/autoscaling"
"k8s.io/kubernetes/pkg/genericapiserver/api/rest"
"k8s.io/kubernetes/pkg/registry/autoscaling/horizontalpodautoscaler"
"k8s.io/kubernetes/pkg/registry/generic"
genericregistry "k8s.io/kubernetes/pkg/registry/generic/registry"
)
type REST struct {
*genericregistry.Store
}
// NewREST returns a RESTStorage object that will work against horizontal pod autoscalers.
func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST) {
store := &genericregistry.Store{
NewFunc: func() runtime.Object { return &autoscaling.HorizontalPodAutoscaler{} },
NewListFunc: func() runtime.Object { return &autoscaling.HorizontalPodAutoscalerList{} },
ObjectNameFunc: func(obj runtime.Object) (string, error) {
return obj.(*autoscaling.HorizontalPodAutoscaler).Name, nil
},
PredicateFunc: horizontalpodautoscaler.MatchAutoscaler,
QualifiedResource: autoscaling.Resource("horizontalpodautoscalers"),
CreateStrategy: horizontalpodautoscaler.Strategy,
UpdateStrategy: horizontalpodautoscaler.Strategy,
DeleteStrategy: horizontalpodautoscaler.Strategy,
}
options := &generic.StoreOptions{RESTOptions: optsGetter, AttrFunc: horizontalpodautoscaler.GetAttrs}
if err := store.CompleteWithOptions(options); err != nil {
panic(err) // TODO: Propagate error up
}
statusStore := *store
statusStore.UpdateStrategy = horizontalpodautoscaler.StatusStrategy
return &REST{store}, &StatusREST{store: &statusStore}
}
// StatusREST implements the REST endpoint for changing the status of a daemonset
type StatusREST struct {
store *genericregistry.Store
}
func (r *StatusREST) New() runtime.Object {
return &autoscaling.HorizontalPodAutoscaler{}
}
// Get retrieves the object from the storage. It is required to support Patch.
func (r *StatusREST) Get(ctx genericapirequest.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
return r.store.Get(ctx, name, options)
}
// Update alters the status subset of an object.
func (r *StatusREST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) {
return r.store.Update(ctx, name, objInfo)
}

View file

@ -0,0 +1,143 @@
/*
Copyright 2015 The Kubernetes Authors.
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 storage
import (
"testing"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/autoscaling"
// Ensure that autoscaling/v1 package is initialized.
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
_ "k8s.io/kubernetes/pkg/apis/autoscaling/v1"
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/registry/generic"
"k8s.io/kubernetes/pkg/registry/registrytest"
etcdtesting "k8s.io/kubernetes/pkg/storage/etcd/testing"
)
func newStorage(t *testing.T) (*REST, *StatusREST, *etcdtesting.EtcdTestServer) {
etcdStorage, server := registrytest.NewEtcdStorage(t, autoscaling.GroupName)
restOptions := generic.RESTOptions{
StorageConfig: etcdStorage,
Decorator: generic.UndecoratedStorage,
DeleteCollectionWorkers: 1,
ResourcePrefix: "horizontalpodautoscalers",
}
horizontalPodAutoscalerStorage, statusStorage := NewREST(restOptions)
return horizontalPodAutoscalerStorage, statusStorage, server
}
func validNewHorizontalPodAutoscaler(name string) *autoscaling.HorizontalPodAutoscaler {
cpu := int32(70)
return &autoscaling.HorizontalPodAutoscaler{
ObjectMeta: api.ObjectMeta{
Name: name,
Namespace: api.NamespaceDefault,
},
Spec: autoscaling.HorizontalPodAutoscalerSpec{
ScaleTargetRef: autoscaling.CrossVersionObjectReference{
Kind: "ReplicationController",
Name: "myrc",
},
MaxReplicas: 5,
TargetCPUUtilizationPercentage: &cpu,
},
}
}
func TestCreate(t *testing.T) {
storage, _, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := registrytest.New(t, storage.Store)
autoscaler := validNewHorizontalPodAutoscaler("foo")
autoscaler.ObjectMeta = api.ObjectMeta{}
test.TestCreate(
// valid
autoscaler,
// invalid
&autoscaling.HorizontalPodAutoscaler{},
)
}
func TestUpdate(t *testing.T) {
storage, _, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := registrytest.New(t, storage.Store)
test.TestUpdate(
// valid
validNewHorizontalPodAutoscaler("foo"),
// updateFunc
func(obj runtime.Object) runtime.Object {
object := obj.(*autoscaling.HorizontalPodAutoscaler)
object.Spec.MaxReplicas = object.Spec.MaxReplicas + 1
return object
},
)
}
func TestDelete(t *testing.T) {
storage, _, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := registrytest.New(t, storage.Store)
test.TestDelete(validNewHorizontalPodAutoscaler("foo"))
}
func TestGet(t *testing.T) {
storage, _, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := registrytest.New(t, storage.Store)
test.TestGet(validNewHorizontalPodAutoscaler("foo"))
}
func TestList(t *testing.T) {
storage, _, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := registrytest.New(t, storage.Store)
test.TestList(validNewHorizontalPodAutoscaler("foo"))
}
func TestWatch(t *testing.T) {
storage, _, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := registrytest.New(t, storage.Store)
test.TestWatch(
validNewHorizontalPodAutoscaler("foo"),
// matching labels
[]labels.Set{},
// not matching labels
[]labels.Set{
{"foo": "bar"},
},
// matching fields
[]fields.Set{},
// not matching fields
[]fields.Set{
{"metadata.name": "bar"},
{"name": "foo"},
},
)
}
// TODO TestUpdateStatus

View file

@ -0,0 +1,125 @@
/*
Copyright 2015 The Kubernetes Authors.
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 horizontalpodautoscaler
import (
"fmt"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/validation/field"
genericapirequest "k8s.io/apiserver/pkg/request"
"k8s.io/apiserver/pkg/storage/names"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/autoscaling"
"k8s.io/kubernetes/pkg/apis/autoscaling/validation"
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/storage"
)
// autoscalerStrategy implements behavior for HorizontalPodAutoscalers
type autoscalerStrategy struct {
runtime.ObjectTyper
names.NameGenerator
}
// Strategy is the default logic that applies when creating and updating HorizontalPodAutoscaler
// objects via the REST API.
var Strategy = autoscalerStrategy{api.Scheme, names.SimpleNameGenerator}
// NamespaceScoped is true for autoscaler.
func (autoscalerStrategy) NamespaceScoped() bool {
return true
}
// PrepareForCreate clears fields that are not allowed to be set by end users on creation.
func (autoscalerStrategy) PrepareForCreate(ctx genericapirequest.Context, obj runtime.Object) {
newHPA := obj.(*autoscaling.HorizontalPodAutoscaler)
// create cannot set status
newHPA.Status = autoscaling.HorizontalPodAutoscalerStatus{}
}
// Validate validates a new autoscaler.
func (autoscalerStrategy) Validate(ctx genericapirequest.Context, obj runtime.Object) field.ErrorList {
autoscaler := obj.(*autoscaling.HorizontalPodAutoscaler)
return validation.ValidateHorizontalPodAutoscaler(autoscaler)
}
// Canonicalize normalizes the object after validation.
func (autoscalerStrategy) Canonicalize(obj runtime.Object) {
}
// AllowCreateOnUpdate is false for autoscalers.
func (autoscalerStrategy) AllowCreateOnUpdate() bool {
return false
}
// PrepareForUpdate clears fields that are not allowed to be set by end users on update.
func (autoscalerStrategy) PrepareForUpdate(ctx genericapirequest.Context, obj, old runtime.Object) {
newHPA := obj.(*autoscaling.HorizontalPodAutoscaler)
oldHPA := old.(*autoscaling.HorizontalPodAutoscaler)
// Update is not allowed to set status
newHPA.Status = oldHPA.Status
}
// ValidateUpdate is the default update validation for an end user.
func (autoscalerStrategy) ValidateUpdate(ctx genericapirequest.Context, obj, old runtime.Object) field.ErrorList {
return validation.ValidateHorizontalPodAutoscalerUpdate(obj.(*autoscaling.HorizontalPodAutoscaler), old.(*autoscaling.HorizontalPodAutoscaler))
}
func (autoscalerStrategy) AllowUnconditionalUpdate() bool {
return true
}
func AutoscalerToSelectableFields(hpa *autoscaling.HorizontalPodAutoscaler) fields.Set {
return nil
}
// GetAttrs returns labels and fields of a given object for filtering purposes.
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
hpa, ok := obj.(*autoscaling.HorizontalPodAutoscaler)
if !ok {
return nil, nil, fmt.Errorf("given object is not a horizontal pod autoscaler.")
}
return labels.Set(hpa.ObjectMeta.Labels), AutoscalerToSelectableFields(hpa), nil
}
func MatchAutoscaler(label labels.Selector, field fields.Selector) storage.SelectionPredicate {
return storage.SelectionPredicate{
Label: label,
Field: field,
GetAttrs: GetAttrs,
}
}
type autoscalerStatusStrategy struct {
autoscalerStrategy
}
var StatusStrategy = autoscalerStatusStrategy{Strategy}
func (autoscalerStatusStrategy) PrepareForUpdate(ctx genericapirequest.Context, obj, old runtime.Object) {
newAutoscaler := obj.(*autoscaling.HorizontalPodAutoscaler)
oldAutoscaler := old.(*autoscaling.HorizontalPodAutoscaler)
// status changes are not allowed to update spec
newAutoscaler.Spec = oldAutoscaler.Spec
}
func (autoscalerStatusStrategy) ValidateUpdate(ctx genericapirequest.Context, obj, old runtime.Object) field.ErrorList {
return validation.ValidateHorizontalPodAutoscalerStatusUpdate(obj.(*autoscaling.HorizontalPodAutoscaler), old.(*autoscaling.HorizontalPodAutoscaler))
}

View file

@ -0,0 +1,35 @@
/*
Copyright 2015 The Kubernetes Authors.
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 horizontalpodautoscaler
import (
"testing"
_ "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/testapi"
apitesting "k8s.io/kubernetes/pkg/api/testing"
"k8s.io/kubernetes/pkg/apis/autoscaling"
)
func TestSelectableFieldLabelConversions(t *testing.T) {
apitesting.TestSelectableFieldLabelConversionsOfKind(t,
testapi.Autoscaling.GroupVersion().String(),
"Autoscaler",
AutoscalerToSelectableFields(&autoscaling.HorizontalPodAutoscaler{}),
nil,
)
}

View file

@ -0,0 +1,35 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["storage_autoscaling.go"],
tags = ["automanaged"],
deps = [
"//pkg/apis/autoscaling:go_default_library",
"//pkg/apis/autoscaling/v1:go_default_library",
"//pkg/genericapiserver:go_default_library",
"//pkg/genericapiserver/api/rest:go_default_library",
"//pkg/registry/autoscaling/horizontalpodautoscaler/storage:go_default_library",
"//pkg/registry/generic:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

View file

@ -0,0 +1,55 @@
/*
Copyright 2016 The Kubernetes Authors.
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 rest
import (
"k8s.io/kubernetes/pkg/apis/autoscaling"
autoscalingapiv1 "k8s.io/kubernetes/pkg/apis/autoscaling/v1"
"k8s.io/kubernetes/pkg/genericapiserver"
"k8s.io/kubernetes/pkg/genericapiserver/api/rest"
horizontalpodautoscalerstore "k8s.io/kubernetes/pkg/registry/autoscaling/horizontalpodautoscaler/storage"
"k8s.io/kubernetes/pkg/registry/generic"
)
type RESTStorageProvider struct{}
func (p RESTStorageProvider) NewRESTStorage(apiResourceConfigSource genericapiserver.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) (genericapiserver.APIGroupInfo, bool) {
apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(autoscaling.GroupName)
if apiResourceConfigSource.AnyResourcesForVersionEnabled(autoscalingapiv1.SchemeGroupVersion) {
apiGroupInfo.VersionedResourcesStorageMap[autoscalingapiv1.SchemeGroupVersion.Version] = p.v1Storage(apiResourceConfigSource, restOptionsGetter)
apiGroupInfo.GroupMeta.GroupVersion = autoscalingapiv1.SchemeGroupVersion
}
return apiGroupInfo, true
}
func (p RESTStorageProvider) v1Storage(apiResourceConfigSource genericapiserver.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) map[string]rest.Storage {
version := autoscalingapiv1.SchemeGroupVersion
storage := map[string]rest.Storage{}
if apiResourceConfigSource.ResourceEnabled(version.WithResource("horizontalpodautoscalers")) {
hpaStorage, hpaStatusStorage := horizontalpodautoscalerstore.NewREST(restOptionsGetter)
storage["horizontalpodautoscalers"] = hpaStorage
storage["horizontalpodautoscalers/status"] = hpaStatusStorage
}
return storage
}
func (p RESTStorageProvider) GroupName() string {
return autoscaling.GroupName
}

3
vendor/k8s.io/kubernetes/pkg/registry/batch/OWNERS generated vendored Executable file
View file

@ -0,0 +1,3 @@
reviewers:
- deads2k
- hongchaodeng

View file

@ -0,0 +1,61 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"doc.go",
"strategy.go",
],
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/apis/batch:go_default_library",
"//pkg/apis/batch/validation:go_default_library",
"//pkg/fields:go_default_library",
"//pkg/registry/generic:go_default_library",
"//pkg/storage:go_default_library",
"//vendor:k8s.io/apimachinery/pkg/labels",
"//vendor:k8s.io/apimachinery/pkg/runtime",
"//vendor:k8s.io/apimachinery/pkg/util/validation/field",
"//vendor:k8s.io/apiserver/pkg/request",
"//vendor:k8s.io/apiserver/pkg/storage/names",
],
)
go_test(
name = "go_default_test",
srcs = ["strategy_test.go"],
library = ":go_default_library",
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/api/testing:go_default_library",
"//pkg/apis/batch:go_default_library",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
"//vendor:k8s.io/apiserver/pkg/request",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//pkg/registry/batch/cronjob/storage:all-srcs",
],
tags = ["automanaged"],
)

View file

@ -0,0 +1,19 @@
/*
Copyright 2016 The Kubernetes Authors.
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 cronjob provides Registry interface and it's RESTStorage
// implementation for storing CronJob api objects.
package cronjob // import "k8s.io/kubernetes/pkg/registry/batch/cronjob"

View file

@ -0,0 +1,57 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_test(
name = "go_default_test",
srcs = ["storage_test.go"],
library = ":go_default_library",
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/api/testapi:go_default_library",
"//pkg/apis/batch:go_default_library",
"//pkg/apis/batch/v2alpha1:go_default_library",
"//pkg/fields:go_default_library",
"//pkg/registry/generic:go_default_library",
"//pkg/registry/registrytest:go_default_library",
"//pkg/storage/etcd/testing:go_default_library",
"//vendor:k8s.io/apimachinery/pkg/labels",
"//vendor:k8s.io/apimachinery/pkg/runtime",
],
)
go_library(
name = "go_default_library",
srcs = ["storage.go"],
tags = ["automanaged"],
deps = [
"//pkg/apis/batch:go_default_library",
"//pkg/genericapiserver/api/rest:go_default_library",
"//pkg/registry/batch/cronjob:go_default_library",
"//pkg/registry/generic:go_default_library",
"//pkg/registry/generic/registry:go_default_library",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
"//vendor:k8s.io/apimachinery/pkg/runtime",
"//vendor:k8s.io/apiserver/pkg/request",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

View file

@ -0,0 +1,78 @@
/*
Copyright 2016 The Kubernetes Authors.
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 storage
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
genericapirequest "k8s.io/apiserver/pkg/request"
"k8s.io/kubernetes/pkg/apis/batch"
"k8s.io/kubernetes/pkg/genericapiserver/api/rest"
"k8s.io/kubernetes/pkg/registry/batch/cronjob"
"k8s.io/kubernetes/pkg/registry/generic"
genericregistry "k8s.io/kubernetes/pkg/registry/generic/registry"
)
// REST implements a RESTStorage for scheduled jobs against etcd
type REST struct {
*genericregistry.Store
}
// NewREST returns a RESTStorage object that will work against CronJobs.
func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST) {
store := &genericregistry.Store{
NewFunc: func() runtime.Object { return &batch.CronJob{} },
NewListFunc: func() runtime.Object { return &batch.CronJobList{} },
ObjectNameFunc: func(obj runtime.Object) (string, error) {
return obj.(*batch.CronJob).Name, nil
},
PredicateFunc: cronjob.MatchCronJob,
QualifiedResource: batch.Resource("cronjobs"),
CreateStrategy: cronjob.Strategy,
UpdateStrategy: cronjob.Strategy,
DeleteStrategy: cronjob.Strategy,
}
options := &generic.StoreOptions{RESTOptions: optsGetter, AttrFunc: cronjob.GetAttrs}
if err := store.CompleteWithOptions(options); err != nil {
panic(err) // TODO: Propagate error up
}
statusStore := *store
statusStore.UpdateStrategy = cronjob.StatusStrategy
return &REST{store}, &StatusREST{store: &statusStore}
}
// StatusREST implements the REST endpoint for changing the status of a resourcequota.
type StatusREST struct {
store *genericregistry.Store
}
func (r *StatusREST) New() runtime.Object {
return &batch.CronJob{}
}
// Get retrieves the object from the storage. It is required to support Patch.
func (r *StatusREST) Get(ctx genericapirequest.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
return r.store.Get(ctx, name, options)
}
// Update alters the status subset of an object.
func (r *StatusREST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) {
return r.store.Update(ctx, name, objInfo)
}

View file

@ -0,0 +1,184 @@
/*
Copyright 2016 The Kubernetes Authors.
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 storage
import (
"testing"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/testapi"
"k8s.io/kubernetes/pkg/apis/batch"
"k8s.io/kubernetes/pkg/apis/batch/v2alpha1"
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/registry/generic"
"k8s.io/kubernetes/pkg/registry/registrytest"
etcdtesting "k8s.io/kubernetes/pkg/storage/etcd/testing"
)
// TODO: allow for global factory override
func newStorage(t *testing.T) (*REST, *StatusREST, *etcdtesting.EtcdTestServer) {
etcdStorage, server := registrytest.NewEtcdStorage(t, batch.GroupName)
restOptions := generic.RESTOptions{StorageConfig: etcdStorage, Decorator: generic.UndecoratedStorage, DeleteCollectionWorkers: 1}
storage, statusStorage := NewREST(restOptions)
return storage, statusStorage, server
}
func validNewCronJob() *batch.CronJob {
return &batch.CronJob{
ObjectMeta: api.ObjectMeta{
Name: "foo",
Namespace: api.NamespaceDefault,
},
Spec: batch.CronJobSpec{
Schedule: "* * * * ?",
ConcurrencyPolicy: batch.AllowConcurrent,
JobTemplate: batch.JobTemplateSpec{
Spec: batch.JobSpec{
Template: api.PodTemplateSpec{
Spec: api.PodSpec{
RestartPolicy: api.RestartPolicyOnFailure,
DNSPolicy: api.DNSClusterFirst,
Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: api.PullIfNotPresent}},
},
},
},
},
},
}
}
func TestCreate(t *testing.T) {
// scheduled jobs should be tested only when batch/v2alpha1 is enabled
if *testapi.Batch.GroupVersion() != v2alpha1.SchemeGroupVersion {
return
}
storage, _, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := registrytest.New(t, storage.Store)
validCronJob := validNewCronJob()
validCronJob.ObjectMeta = api.ObjectMeta{}
test.TestCreate(
// valid
validCronJob,
// invalid (empty spec)
&batch.CronJob{
Spec: batch.CronJobSpec{},
},
)
}
func TestUpdate(t *testing.T) {
// scheduled jobs should be tested only when batch/v2alpha1 is enabled
if *testapi.Batch.GroupVersion() != v2alpha1.SchemeGroupVersion {
return
}
storage, _, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := registrytest.New(t, storage.Store)
schedule := "1 1 1 1 ?"
test.TestUpdate(
// valid
validNewCronJob(),
// updateFunc
func(obj runtime.Object) runtime.Object {
object := obj.(*batch.CronJob)
object.Spec.Schedule = schedule
return object
},
// invalid updateFunc
func(obj runtime.Object) runtime.Object {
object := obj.(*batch.CronJob)
object.Spec.Schedule = "* * *"
return object
},
)
}
func TestDelete(t *testing.T) {
// scheduled jobs should be tested only when batch/v2alpha1 is enabled
if *testapi.Batch.GroupVersion() != v2alpha1.SchemeGroupVersion {
return
}
storage, _, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := registrytest.New(t, storage.Store)
test.TestDelete(validNewCronJob())
}
func TestGet(t *testing.T) {
// scheduled jobs should be tested only when batch/v2alpha1 is enabled
if *testapi.Batch.GroupVersion() != v2alpha1.SchemeGroupVersion {
return
}
storage, _, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := registrytest.New(t, storage.Store)
test.TestGet(validNewCronJob())
}
func TestList(t *testing.T) {
// scheduled jobs should be tested only when batch/v2alpha1 is enabled
if *testapi.Batch.GroupVersion() != v2alpha1.SchemeGroupVersion {
return
}
storage, _, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := registrytest.New(t, storage.Store)
test.TestList(validNewCronJob())
}
func TestWatch(t *testing.T) {
// scheduled jobs should be tested only when batch/v2alpha1 is enabled
if *testapi.Batch.GroupVersion() != v2alpha1.SchemeGroupVersion {
return
}
storage, _, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := registrytest.New(t, storage.Store)
test.TestWatch(
validNewCronJob(),
// matching labels
[]labels.Set{},
// not matching labels
[]labels.Set{
{"x": "y"},
},
// matching fields
[]fields.Set{},
// not matching fields
[]fields.Set{
{"metadata.name": "xyz"},
{"name": "foo"},
},
)
}
// TODO: test update /status

View file

@ -0,0 +1,125 @@
/*
Copyright 2016 The Kubernetes Authors.
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 cronjob
import (
"fmt"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/validation/field"
genericapirequest "k8s.io/apiserver/pkg/request"
"k8s.io/apiserver/pkg/storage/names"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/batch"
"k8s.io/kubernetes/pkg/apis/batch/validation"
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/registry/generic"
"k8s.io/kubernetes/pkg/storage"
)
// scheduledJobStrategy implements verification logic for Replication Controllers.
type scheduledJobStrategy struct {
runtime.ObjectTyper
names.NameGenerator
}
// Strategy is the default logic that applies when creating and updating CronJob objects.
var Strategy = scheduledJobStrategy{api.Scheme, names.SimpleNameGenerator}
// NamespaceScoped returns true because all scheduled jobs need to be within a namespace.
func (scheduledJobStrategy) NamespaceScoped() bool {
return true
}
// PrepareForCreate clears the status of a scheduled job before creation.
func (scheduledJobStrategy) PrepareForCreate(ctx genericapirequest.Context, obj runtime.Object) {
scheduledJob := obj.(*batch.CronJob)
scheduledJob.Status = batch.CronJobStatus{}
}
// PrepareForUpdate clears fields that are not allowed to be set by end users on update.
func (scheduledJobStrategy) PrepareForUpdate(ctx genericapirequest.Context, obj, old runtime.Object) {
newCronJob := obj.(*batch.CronJob)
oldCronJob := old.(*batch.CronJob)
newCronJob.Status = oldCronJob.Status
}
// Validate validates a new scheduled job.
func (scheduledJobStrategy) Validate(ctx genericapirequest.Context, obj runtime.Object) field.ErrorList {
scheduledJob := obj.(*batch.CronJob)
return validation.ValidateCronJob(scheduledJob)
}
// Canonicalize normalizes the object after validation.
func (scheduledJobStrategy) Canonicalize(obj runtime.Object) {
}
func (scheduledJobStrategy) AllowUnconditionalUpdate() bool {
return true
}
// AllowCreateOnUpdate is false for scheduled jobs; this means a POST is needed to create one.
func (scheduledJobStrategy) AllowCreateOnUpdate() bool {
return false
}
// ValidateUpdate is the default update validation for an end user.
func (scheduledJobStrategy) ValidateUpdate(ctx genericapirequest.Context, obj, old runtime.Object) field.ErrorList {
return validation.ValidateCronJob(obj.(*batch.CronJob))
}
type scheduledJobStatusStrategy struct {
scheduledJobStrategy
}
var StatusStrategy = scheduledJobStatusStrategy{Strategy}
func (scheduledJobStatusStrategy) PrepareForUpdate(ctx genericapirequest.Context, obj, old runtime.Object) {
newJob := obj.(*batch.CronJob)
oldJob := old.(*batch.CronJob)
newJob.Spec = oldJob.Spec
}
func (scheduledJobStatusStrategy) ValidateUpdate(ctx genericapirequest.Context, obj, old runtime.Object) field.ErrorList {
return field.ErrorList{}
}
// CronJobToSelectableFields returns a field set that represents the object for matching purposes.
func CronJobToSelectableFields(scheduledJob *batch.CronJob) fields.Set {
return generic.ObjectMetaFieldsSet(&scheduledJob.ObjectMeta, true)
}
// GetAttrs returns labels and fields of a given object for filtering purposes.
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
scheduledJob, ok := obj.(*batch.CronJob)
if !ok {
return nil, nil, fmt.Errorf("Given object is not a scheduled job.")
}
return labels.Set(scheduledJob.ObjectMeta.Labels), CronJobToSelectableFields(scheduledJob), nil
}
// MatchCronJob is the filter used by the generic etcd backend to route
// watch events from etcd to clients of the apiserver only interested in specific
// labels/fields.
func MatchCronJob(label labels.Selector, field fields.Selector) storage.SelectionPredicate {
return storage.SelectionPredicate{
Label: label,
Field: field,
GetAttrs: GetAttrs,
}
}

View file

@ -0,0 +1,174 @@
/*
Copyright 2016 The Kubernetes Authors.
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 cronjob
import (
"testing"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
genericapirequest "k8s.io/apiserver/pkg/request"
"k8s.io/kubernetes/pkg/api"
apitesting "k8s.io/kubernetes/pkg/api/testing"
"k8s.io/kubernetes/pkg/apis/batch"
)
func newBool(a bool) *bool {
r := new(bool)
*r = a
return r
}
func TestCronJobStrategy(t *testing.T) {
ctx := genericapirequest.NewDefaultContext()
if !Strategy.NamespaceScoped() {
t.Errorf("CronJob must be namespace scoped")
}
if Strategy.AllowCreateOnUpdate() {
t.Errorf("CronJob should not allow create on update")
}
validPodTemplateSpec := api.PodTemplateSpec{
Spec: api.PodSpec{
RestartPolicy: api.RestartPolicyOnFailure,
DNSPolicy: api.DNSClusterFirst,
Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}},
},
}
scheduledJob := &batch.CronJob{
ObjectMeta: api.ObjectMeta{
Name: "mycronjob",
Namespace: api.NamespaceDefault,
},
Spec: batch.CronJobSpec{
Schedule: "* * * * ?",
ConcurrencyPolicy: batch.AllowConcurrent,
JobTemplate: batch.JobTemplateSpec{
Spec: batch.JobSpec{
Template: validPodTemplateSpec,
},
},
},
}
Strategy.PrepareForCreate(ctx, scheduledJob)
if len(scheduledJob.Status.Active) != 0 {
t.Errorf("CronJob does not allow setting status on create")
}
errs := Strategy.Validate(ctx, scheduledJob)
if len(errs) != 0 {
t.Errorf("Unexpected error validating %v", errs)
}
now := metav1.Now()
updatedCronJob := &batch.CronJob{
ObjectMeta: api.ObjectMeta{Name: "bar", ResourceVersion: "4"},
Spec: batch.CronJobSpec{
Schedule: "5 5 5 * ?",
},
Status: batch.CronJobStatus{
LastScheduleTime: &now,
},
}
// ensure we do not change status
Strategy.PrepareForUpdate(ctx, updatedCronJob, scheduledJob)
if updatedCronJob.Status.Active != nil {
t.Errorf("PrepareForUpdate should have preserved prior version status")
}
errs = Strategy.ValidateUpdate(ctx, updatedCronJob, scheduledJob)
if len(errs) == 0 {
t.Errorf("Expected a validation error")
}
}
func TestCronJobStatusStrategy(t *testing.T) {
ctx := genericapirequest.NewDefaultContext()
if !StatusStrategy.NamespaceScoped() {
t.Errorf("CronJob must be namespace scoped")
}
if StatusStrategy.AllowCreateOnUpdate() {
t.Errorf("CronJob should not allow create on update")
}
validPodTemplateSpec := api.PodTemplateSpec{
Spec: api.PodSpec{
RestartPolicy: api.RestartPolicyOnFailure,
DNSPolicy: api.DNSClusterFirst,
Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}},
},
}
oldSchedule := "* * * * ?"
oldCronJob := &batch.CronJob{
ObjectMeta: api.ObjectMeta{
Name: "mycronjob",
Namespace: api.NamespaceDefault,
ResourceVersion: "10",
},
Spec: batch.CronJobSpec{
Schedule: oldSchedule,
ConcurrencyPolicy: batch.AllowConcurrent,
JobTemplate: batch.JobTemplateSpec{
Spec: batch.JobSpec{
Template: validPodTemplateSpec,
},
},
},
}
now := metav1.Now()
newCronJob := &batch.CronJob{
ObjectMeta: api.ObjectMeta{
Name: "mycronjob",
Namespace: api.NamespaceDefault,
ResourceVersion: "9",
},
Spec: batch.CronJobSpec{
Schedule: "5 5 * * ?",
ConcurrencyPolicy: batch.AllowConcurrent,
JobTemplate: batch.JobTemplateSpec{
Spec: batch.JobSpec{
Template: validPodTemplateSpec,
},
},
},
Status: batch.CronJobStatus{
LastScheduleTime: &now,
},
}
StatusStrategy.PrepareForUpdate(ctx, newCronJob, oldCronJob)
if newCronJob.Status.LastScheduleTime == nil {
t.Errorf("CronJob status updates must allow changes to scheduledJob status")
}
if newCronJob.Spec.Schedule != oldSchedule {
t.Errorf("CronJob status updates must now allow changes to scheduledJob spec")
}
errs := StatusStrategy.ValidateUpdate(ctx, newCronJob, oldCronJob)
if len(errs) != 0 {
t.Errorf("Unexpected error %v", errs)
}
if newCronJob.ResourceVersion != "9" {
t.Errorf("Incoming resource version on update should not be mutated")
}
}
// FIXME: this is failing conversion.go
func TestSelectableFieldLabelConversions(t *testing.T) {
apitesting.TestSelectableFieldLabelConversionsOfKind(t,
"batch/v2alpha1",
"CronJob",
CronJobToSelectableFields(&batch.CronJob{}),
nil,
)
}

64
vendor/k8s.io/kubernetes/pkg/registry/batch/job/BUILD generated vendored Normal file
View file

@ -0,0 +1,64 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"doc.go",
"strategy.go",
],
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/apis/batch:go_default_library",
"//pkg/apis/batch/validation:go_default_library",
"//pkg/fields:go_default_library",
"//pkg/registry/generic:go_default_library",
"//pkg/storage:go_default_library",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
"//vendor:k8s.io/apimachinery/pkg/labels",
"//vendor:k8s.io/apimachinery/pkg/runtime",
"//vendor:k8s.io/apimachinery/pkg/util/validation/field",
"//vendor:k8s.io/apiserver/pkg/request",
"//vendor:k8s.io/apiserver/pkg/storage/names",
],
)
go_test(
name = "go_default_test",
srcs = ["strategy_test.go"],
library = ":go_default_library",
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/api/testapi:go_default_library",
"//pkg/api/testing:go_default_library",
"//pkg/apis/batch:go_default_library",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
"//vendor:k8s.io/apimachinery/pkg/types",
"//vendor:k8s.io/apiserver/pkg/request",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//pkg/registry/batch/job/storage:all-srcs",
],
tags = ["automanaged"],
)

19
vendor/k8s.io/kubernetes/pkg/registry/batch/job/doc.go generated vendored Normal file
View file

@ -0,0 +1,19 @@
/*
Copyright 2015 The Kubernetes Authors.
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 job provides Registry interface and it's RESTStorage
// implementation for storing Job api objects.
package job // import "k8s.io/kubernetes/pkg/registry/batch/job"

View file

@ -0,0 +1,56 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_test(
name = "go_default_test",
srcs = ["storage_test.go"],
library = ":go_default_library",
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/apis/batch:go_default_library",
"//pkg/fields:go_default_library",
"//pkg/registry/generic:go_default_library",
"//pkg/registry/registrytest:go_default_library",
"//pkg/storage/etcd/testing:go_default_library",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
"//vendor:k8s.io/apimachinery/pkg/labels",
"//vendor:k8s.io/apimachinery/pkg/runtime",
],
)
go_library(
name = "go_default_library",
srcs = ["storage.go"],
tags = ["automanaged"],
deps = [
"//pkg/apis/batch:go_default_library",
"//pkg/genericapiserver/api/rest:go_default_library",
"//pkg/registry/batch/job:go_default_library",
"//pkg/registry/generic:go_default_library",
"//pkg/registry/generic/registry:go_default_library",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
"//vendor:k8s.io/apimachinery/pkg/runtime",
"//vendor:k8s.io/apiserver/pkg/request",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

View file

@ -0,0 +1,93 @@
/*
Copyright 2015 The Kubernetes Authors.
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 storage
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
genericapirequest "k8s.io/apiserver/pkg/request"
"k8s.io/kubernetes/pkg/apis/batch"
"k8s.io/kubernetes/pkg/genericapiserver/api/rest"
"k8s.io/kubernetes/pkg/registry/batch/job"
"k8s.io/kubernetes/pkg/registry/generic"
genericregistry "k8s.io/kubernetes/pkg/registry/generic/registry"
)
// JobStorage includes dummy storage for Job.
type JobStorage struct {
Job *REST
Status *StatusREST
}
func NewStorage(optsGetter generic.RESTOptionsGetter) JobStorage {
jobRest, jobStatusRest := NewREST(optsGetter)
return JobStorage{
Job: jobRest,
Status: jobStatusRest,
}
}
// REST implements a RESTStorage for jobs against etcd
type REST struct {
*genericregistry.Store
}
// NewREST returns a RESTStorage object that will work against Jobs.
func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST) {
store := &genericregistry.Store{
NewFunc: func() runtime.Object { return &batch.Job{} },
NewListFunc: func() runtime.Object { return &batch.JobList{} },
ObjectNameFunc: func(obj runtime.Object) (string, error) {
return obj.(*batch.Job).Name, nil
},
PredicateFunc: job.MatchJob,
QualifiedResource: batch.Resource("jobs"),
CreateStrategy: job.Strategy,
UpdateStrategy: job.Strategy,
DeleteStrategy: job.Strategy,
}
options := &generic.StoreOptions{RESTOptions: optsGetter, AttrFunc: job.GetAttrs}
if err := store.CompleteWithOptions(options); err != nil {
panic(err) // TODO: Propagate error up
}
statusStore := *store
statusStore.UpdateStrategy = job.StatusStrategy
return &REST{store}, &StatusREST{store: &statusStore}
}
// StatusREST implements the REST endpoint for changing the status of a resourcequota.
type StatusREST struct {
store *genericregistry.Store
}
func (r *StatusREST) New() runtime.Object {
return &batch.Job{}
}
// Get retrieves the object from the storage. It is required to support Patch.
func (r *StatusREST) Get(ctx genericapirequest.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
return r.store.Get(ctx, name, options)
}
// Update alters the status subset of an object.
func (r *StatusREST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) {
return r.store.Update(ctx, name, objInfo)
}

View file

@ -0,0 +1,183 @@
/*
Copyright 2015 The Kubernetes Authors.
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 storage
import (
"testing"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/batch"
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/registry/generic"
"k8s.io/kubernetes/pkg/registry/registrytest"
etcdtesting "k8s.io/kubernetes/pkg/storage/etcd/testing"
)
func newStorage(t *testing.T) (*JobStorage, *etcdtesting.EtcdTestServer) {
etcdStorage, server := registrytest.NewEtcdStorage(t, batch.GroupName)
restOptions := generic.RESTOptions{
StorageConfig: etcdStorage,
Decorator: generic.UndecoratedStorage,
DeleteCollectionWorkers: 1,
ResourcePrefix: "jobs",
}
jobStorage := NewStorage(restOptions)
return &jobStorage, server
}
func validNewJob() *batch.Job {
completions := int32(1)
parallelism := int32(1)
return &batch.Job{
ObjectMeta: api.ObjectMeta{
Name: "foo",
Namespace: "default",
},
Spec: batch.JobSpec{
Completions: &completions,
Parallelism: &parallelism,
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{"a": "b"},
},
ManualSelector: newBool(true),
Template: api.PodTemplateSpec{
ObjectMeta: api.ObjectMeta{
Labels: map[string]string{"a": "b"},
},
Spec: api.PodSpec{
Containers: []api.Container{
{
Name: "test",
Image: "test_image",
ImagePullPolicy: api.PullIfNotPresent,
},
},
RestartPolicy: api.RestartPolicyOnFailure,
DNSPolicy: api.DNSClusterFirst,
},
},
},
}
}
func TestCreate(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Job.Store.DestroyFunc()
test := registrytest.New(t, storage.Job.Store)
validJob := validNewJob()
validJob.ObjectMeta = api.ObjectMeta{}
test.TestCreate(
// valid
validJob,
// invalid (empty selector)
&batch.Job{
Spec: batch.JobSpec{
Completions: validJob.Spec.Completions,
Selector: &metav1.LabelSelector{},
Template: validJob.Spec.Template,
},
},
)
}
func TestUpdate(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Job.Store.DestroyFunc()
test := registrytest.New(t, storage.Job.Store)
two := int32(2)
test.TestUpdate(
// valid
validNewJob(),
// updateFunc
func(obj runtime.Object) runtime.Object {
object := obj.(*batch.Job)
object.Spec.Parallelism = &two
return object
},
// invalid updateFunc
func(obj runtime.Object) runtime.Object {
object := obj.(*batch.Job)
object.Spec.Selector = &metav1.LabelSelector{}
return object
},
func(obj runtime.Object) runtime.Object {
object := obj.(*batch.Job)
object.Spec.Completions = &two
return object
},
)
}
func TestDelete(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Job.Store.DestroyFunc()
test := registrytest.New(t, storage.Job.Store)
test.TestDelete(validNewJob())
}
func TestGet(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Job.Store.DestroyFunc()
test := registrytest.New(t, storage.Job.Store)
test.TestGet(validNewJob())
}
func TestList(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Job.Store.DestroyFunc()
test := registrytest.New(t, storage.Job.Store)
test.TestList(validNewJob())
}
func TestWatch(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Job.Store.DestroyFunc()
test := registrytest.New(t, storage.Job.Store)
test.TestWatch(
validNewJob(),
// matching labels
[]labels.Set{},
// not matching labels
[]labels.Set{
{"x": "y"},
},
// matching fields
[]fields.Set{},
// not matching fields
[]fields.Set{
{"metadata.name": "xyz"},
{"name": "foo"},
},
)
}
// TODO: test update /status
func newBool(val bool) *bool {
p := new(bool)
*p = val
return p
}

View file

@ -0,0 +1,187 @@
/*
Copyright 2015 The Kubernetes Authors.
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 job
import (
"fmt"
"strconv"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/validation/field"
genericapirequest "k8s.io/apiserver/pkg/request"
"k8s.io/apiserver/pkg/storage/names"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/batch"
"k8s.io/kubernetes/pkg/apis/batch/validation"
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/registry/generic"
"k8s.io/kubernetes/pkg/storage"
)
// jobStrategy implements verification logic for Replication Controllers.
type jobStrategy struct {
runtime.ObjectTyper
names.NameGenerator
}
// Strategy is the default logic that applies when creating and updating Replication Controller objects.
var Strategy = jobStrategy{api.Scheme, names.SimpleNameGenerator}
// NamespaceScoped returns true because all jobs need to be within a namespace.
func (jobStrategy) NamespaceScoped() bool {
return true
}
// PrepareForCreate clears the status of a job before creation.
func (jobStrategy) PrepareForCreate(ctx genericapirequest.Context, obj runtime.Object) {
job := obj.(*batch.Job)
job.Status = batch.JobStatus{}
}
// PrepareForUpdate clears fields that are not allowed to be set by end users on update.
func (jobStrategy) PrepareForUpdate(ctx genericapirequest.Context, obj, old runtime.Object) {
newJob := obj.(*batch.Job)
oldJob := old.(*batch.Job)
newJob.Status = oldJob.Status
}
// Validate validates a new job.
func (jobStrategy) Validate(ctx genericapirequest.Context, obj runtime.Object) field.ErrorList {
job := obj.(*batch.Job)
// TODO: move UID generation earlier and do this in defaulting logic?
if job.Spec.ManualSelector == nil || *job.Spec.ManualSelector == false {
generateSelector(job)
}
return validation.ValidateJob(job)
}
// generateSelector adds a selector to a job and labels to its template
// which can be used to uniquely identify the pods created by that job,
// if the user has requested this behavior.
func generateSelector(obj *batch.Job) {
if obj.Spec.Template.Labels == nil {
obj.Spec.Template.Labels = make(map[string]string)
}
// The job-name label is unique except in cases that are expected to be
// quite uncommon, and is more user friendly than uid. So, we add it as
// a label.
_, found := obj.Spec.Template.Labels["job-name"]
if found {
// User asked us to not automatically generate a selector and labels,
// but set a possibly conflicting value. If there is a conflict,
// we will reject in validation.
} else {
obj.Spec.Template.Labels["job-name"] = string(obj.ObjectMeta.Name)
}
// The controller-uid label makes the pods that belong to this job
// only match this job.
_, found = obj.Spec.Template.Labels["controller-uid"]
if found {
// User asked us to automatically generate a selector and labels,
// but set a possibly conflicting value. If there is a conflict,
// we will reject in validation.
} else {
obj.Spec.Template.Labels["controller-uid"] = string(obj.ObjectMeta.UID)
}
// Select the controller-uid label. This is sufficient for uniqueness.
if obj.Spec.Selector == nil {
obj.Spec.Selector = &metav1.LabelSelector{}
}
if obj.Spec.Selector.MatchLabels == nil {
obj.Spec.Selector.MatchLabels = make(map[string]string)
}
if _, found := obj.Spec.Selector.MatchLabels["controller-uid"]; !found {
obj.Spec.Selector.MatchLabels["controller-uid"] = string(obj.ObjectMeta.UID)
}
// If the user specified matchLabel controller-uid=$WRONGUID, then it should fail
// in validation, either because the selector does not match the pod template
// (controller-uid=$WRONGUID does not match controller-uid=$UID, which we applied
// above, or we will reject in validation because the template has the wrong
// labels.
}
// TODO: generalize generateSelector so it can work for other controller
// objects such as ReplicaSet. Can use pkg/api/meta to generically get the
// UID, but need some way to generically access the selector and pod labels
// fields.
// Canonicalize normalizes the object after validation.
func (jobStrategy) Canonicalize(obj runtime.Object) {
}
func (jobStrategy) AllowUnconditionalUpdate() bool {
return true
}
// AllowCreateOnUpdate is false for jobs; this means a POST is needed to create one.
func (jobStrategy) AllowCreateOnUpdate() bool {
return false
}
// ValidateUpdate is the default update validation for an end user.
func (jobStrategy) ValidateUpdate(ctx genericapirequest.Context, obj, old runtime.Object) field.ErrorList {
validationErrorList := validation.ValidateJob(obj.(*batch.Job))
updateErrorList := validation.ValidateJobUpdate(obj.(*batch.Job), old.(*batch.Job))
return append(validationErrorList, updateErrorList...)
}
type jobStatusStrategy struct {
jobStrategy
}
var StatusStrategy = jobStatusStrategy{Strategy}
func (jobStatusStrategy) PrepareForUpdate(ctx genericapirequest.Context, obj, old runtime.Object) {
newJob := obj.(*batch.Job)
oldJob := old.(*batch.Job)
newJob.Spec = oldJob.Spec
}
func (jobStatusStrategy) ValidateUpdate(ctx genericapirequest.Context, obj, old runtime.Object) field.ErrorList {
return validation.ValidateJobUpdateStatus(obj.(*batch.Job), old.(*batch.Job))
}
// JobSelectableFields returns a field set that represents the object for matching purposes.
func JobToSelectableFields(job *batch.Job) fields.Set {
objectMetaFieldsSet := generic.ObjectMetaFieldsSet(&job.ObjectMeta, true)
specificFieldsSet := fields.Set{
"status.successful": strconv.Itoa(int(job.Status.Succeeded)),
}
return generic.MergeFieldsSets(objectMetaFieldsSet, specificFieldsSet)
}
// GetAttrs returns labels and fields of a given object for filtering purposes.
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
job, ok := obj.(*batch.Job)
if !ok {
return nil, nil, fmt.Errorf("Given object is not a job.")
}
return labels.Set(job.ObjectMeta.Labels), JobToSelectableFields(job), nil
}
// MatchJob is the filter used by the generic etcd backend to route
// watch events from etcd to clients of the apiserver only interested in specific
// labels/fields.
func MatchJob(label labels.Selector, field fields.Selector) storage.SelectionPredicate {
return storage.SelectionPredicate{
Label: label,
Field: field,
GetAttrs: GetAttrs,
}
}

View file

@ -0,0 +1,232 @@
/*
Copyright 2015 The Kubernetes Authors.
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 job
import (
"reflect"
"testing"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
genericapirequest "k8s.io/apiserver/pkg/request"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/testapi"
apitesting "k8s.io/kubernetes/pkg/api/testing"
"k8s.io/kubernetes/pkg/apis/batch"
)
func newBool(a bool) *bool {
r := new(bool)
*r = a
return r
}
func TestJobStrategy(t *testing.T) {
ctx := genericapirequest.NewDefaultContext()
if !Strategy.NamespaceScoped() {
t.Errorf("Job must be namespace scoped")
}
if Strategy.AllowCreateOnUpdate() {
t.Errorf("Job should not allow create on update")
}
validSelector := &metav1.LabelSelector{
MatchLabels: map[string]string{"a": "b"},
}
validPodTemplateSpec := api.PodTemplateSpec{
ObjectMeta: api.ObjectMeta{
Labels: validSelector.MatchLabels,
},
Spec: api.PodSpec{
RestartPolicy: api.RestartPolicyOnFailure,
DNSPolicy: api.DNSClusterFirst,
Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}},
},
}
job := &batch.Job{
ObjectMeta: api.ObjectMeta{
Name: "myjob",
Namespace: api.NamespaceDefault,
},
Spec: batch.JobSpec{
Selector: validSelector,
Template: validPodTemplateSpec,
ManualSelector: newBool(true),
},
Status: batch.JobStatus{
Active: 11,
},
}
Strategy.PrepareForCreate(ctx, job)
if job.Status.Active != 0 {
t.Errorf("Job does not allow setting status on create")
}
errs := Strategy.Validate(ctx, job)
if len(errs) != 0 {
t.Errorf("Unexpected error validating %v", errs)
}
parallelism := int32(10)
updatedJob := &batch.Job{
ObjectMeta: api.ObjectMeta{Name: "bar", ResourceVersion: "4"},
Spec: batch.JobSpec{
Parallelism: &parallelism,
},
Status: batch.JobStatus{
Active: 11,
},
}
// ensure we do not change status
job.Status.Active = 10
Strategy.PrepareForUpdate(ctx, updatedJob, job)
if updatedJob.Status.Active != 10 {
t.Errorf("PrepareForUpdate should have preserved prior version status")
}
errs = Strategy.ValidateUpdate(ctx, updatedJob, job)
if len(errs) == 0 {
t.Errorf("Expected a validation error")
}
}
func TestJobStrategyWithGeneration(t *testing.T) {
ctx := genericapirequest.NewDefaultContext()
theUID := types.UID("1a2b3c4d5e6f7g8h9i0k")
validPodTemplateSpec := api.PodTemplateSpec{
Spec: api.PodSpec{
RestartPolicy: api.RestartPolicyOnFailure,
DNSPolicy: api.DNSClusterFirst,
Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}},
},
}
job := &batch.Job{
ObjectMeta: api.ObjectMeta{
Name: "myjob2",
Namespace: api.NamespaceDefault,
UID: theUID,
},
Spec: batch.JobSpec{
Selector: nil,
Template: validPodTemplateSpec,
},
}
Strategy.PrepareForCreate(ctx, job)
errs := Strategy.Validate(ctx, job)
if len(errs) != 0 {
t.Errorf("Unexpected error validating %v", errs)
}
// Validate the stuff that validation should have validated.
if job.Spec.Selector == nil {
t.Errorf("Selector not generated")
}
expectedLabels := make(map[string]string)
expectedLabels["controller-uid"] = string(theUID)
if !reflect.DeepEqual(job.Spec.Selector.MatchLabels, expectedLabels) {
t.Errorf("Expected label selector not generated")
}
if job.Spec.Template.ObjectMeta.Labels == nil {
t.Errorf("Expected template labels not generated")
}
if v, ok := job.Spec.Template.ObjectMeta.Labels["job-name"]; !ok || v != "myjob2" {
t.Errorf("Expected template labels not present")
}
if v, ok := job.Spec.Template.ObjectMeta.Labels["controller-uid"]; !ok || v != string(theUID) {
t.Errorf("Expected template labels not present: ok: %v, v: %v", ok, v)
}
}
func TestJobStatusStrategy(t *testing.T) {
ctx := genericapirequest.NewDefaultContext()
if !StatusStrategy.NamespaceScoped() {
t.Errorf("Job must be namespace scoped")
}
if StatusStrategy.AllowCreateOnUpdate() {
t.Errorf("Job should not allow create on update")
}
validSelector := &metav1.LabelSelector{
MatchLabels: map[string]string{"a": "b"},
}
validPodTemplateSpec := api.PodTemplateSpec{
ObjectMeta: api.ObjectMeta{
Labels: validSelector.MatchLabels,
},
Spec: api.PodSpec{
RestartPolicy: api.RestartPolicyOnFailure,
DNSPolicy: api.DNSClusterFirst,
Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}},
},
}
oldParallelism := int32(10)
newParallelism := int32(11)
oldJob := &batch.Job{
ObjectMeta: api.ObjectMeta{
Name: "myjob",
Namespace: api.NamespaceDefault,
ResourceVersion: "10",
},
Spec: batch.JobSpec{
Selector: validSelector,
Template: validPodTemplateSpec,
Parallelism: &oldParallelism,
},
Status: batch.JobStatus{
Active: 11,
},
}
newJob := &batch.Job{
ObjectMeta: api.ObjectMeta{
Name: "myjob",
Namespace: api.NamespaceDefault,
ResourceVersion: "9",
},
Spec: batch.JobSpec{
Selector: validSelector,
Template: validPodTemplateSpec,
Parallelism: &newParallelism,
},
Status: batch.JobStatus{
Active: 12,
},
}
StatusStrategy.PrepareForUpdate(ctx, newJob, oldJob)
if newJob.Status.Active != 12 {
t.Errorf("Job status updates must allow changes to job status")
}
if *newJob.Spec.Parallelism != 10 {
t.Errorf("Job status updates must now allow changes to job spec")
}
errs := StatusStrategy.ValidateUpdate(ctx, newJob, oldJob)
if len(errs) != 0 {
t.Errorf("Unexpected error %v", errs)
}
if newJob.ResourceVersion != "9" {
t.Errorf("Incoming resource version on update should not be mutated")
}
}
func TestSelectableFieldLabelConversions(t *testing.T) {
apitesting.TestSelectableFieldLabelConversionsOfKind(t,
testapi.Batch.GroupVersion().String(),
"Job",
JobToSelectableFields(&batch.Job{}),
nil,
)
}

38
vendor/k8s.io/kubernetes/pkg/registry/batch/rest/BUILD generated vendored Normal file
View file

@ -0,0 +1,38 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["storage_batch.go"],
tags = ["automanaged"],
deps = [
"//pkg/apis/batch:go_default_library",
"//pkg/apis/batch/v1:go_default_library",
"//pkg/apis/batch/v2alpha1:go_default_library",
"//pkg/genericapiserver:go_default_library",
"//pkg/genericapiserver/api/rest:go_default_library",
"//pkg/registry/batch/cronjob/storage:go_default_library",
"//pkg/registry/batch/job/storage:go_default_library",
"//pkg/registry/generic:go_default_library",
"//vendor:k8s.io/apimachinery/pkg/runtime/schema",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

View file

@ -0,0 +1,85 @@
/*
Copyright 2016 The Kubernetes Authors.
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 rest
import (
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/kubernetes/pkg/apis/batch"
batchapiv1 "k8s.io/kubernetes/pkg/apis/batch/v1"
batchapiv2alpha1 "k8s.io/kubernetes/pkg/apis/batch/v2alpha1"
"k8s.io/kubernetes/pkg/genericapiserver"
"k8s.io/kubernetes/pkg/genericapiserver/api/rest"
cronjobstore "k8s.io/kubernetes/pkg/registry/batch/cronjob/storage"
jobstore "k8s.io/kubernetes/pkg/registry/batch/job/storage"
"k8s.io/kubernetes/pkg/registry/generic"
)
type RESTStorageProvider struct{}
func (p RESTStorageProvider) NewRESTStorage(apiResourceConfigSource genericapiserver.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) (genericapiserver.APIGroupInfo, bool) {
apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(batch.GroupName)
if apiResourceConfigSource.AnyResourcesForVersionEnabled(batchapiv2alpha1.SchemeGroupVersion) {
apiGroupInfo.VersionedResourcesStorageMap[batchapiv2alpha1.SchemeGroupVersion.Version] = p.v2alpha1Storage(apiResourceConfigSource, restOptionsGetter)
apiGroupInfo.GroupMeta.GroupVersion = batchapiv2alpha1.SchemeGroupVersion
apiGroupInfo.SubresourceGroupVersionKind = map[string]schema.GroupVersionKind{
"scheduledjobs": batchapiv2alpha1.SchemeGroupVersion.WithKind("ScheduledJob"),
"scheduledjobs/status": batchapiv2alpha1.SchemeGroupVersion.WithKind("ScheduledJob"),
}
}
if apiResourceConfigSource.AnyResourcesForVersionEnabled(batchapiv1.SchemeGroupVersion) {
apiGroupInfo.VersionedResourcesStorageMap[batchapiv1.SchemeGroupVersion.Version] = p.v1Storage(apiResourceConfigSource, restOptionsGetter)
apiGroupInfo.GroupMeta.GroupVersion = batchapiv1.SchemeGroupVersion
}
return apiGroupInfo, true
}
func (p RESTStorageProvider) v1Storage(apiResourceConfigSource genericapiserver.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) map[string]rest.Storage {
version := batchapiv1.SchemeGroupVersion
storage := map[string]rest.Storage{}
if apiResourceConfigSource.ResourceEnabled(version.WithResource("jobs")) {
jobsStorage, jobsStatusStorage := jobstore.NewREST(restOptionsGetter)
storage["jobs"] = jobsStorage
storage["jobs/status"] = jobsStatusStorage
}
return storage
}
func (p RESTStorageProvider) v2alpha1Storage(apiResourceConfigSource genericapiserver.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) map[string]rest.Storage {
version := batchapiv2alpha1.SchemeGroupVersion
storage := map[string]rest.Storage{}
if apiResourceConfigSource.ResourceEnabled(version.WithResource("jobs")) {
jobsStorage, jobsStatusStorage := jobstore.NewREST(restOptionsGetter)
storage["jobs"] = jobsStorage
storage["jobs/status"] = jobsStatusStorage
}
if apiResourceConfigSource.ResourceEnabled(version.WithResource("cronjobs")) {
cronJobsStorage, cronJobsStatusStorage := cronjobstore.NewREST(restOptionsGetter)
storage["cronjobs"] = cronJobsStorage
storage["cronjobs/status"] = cronJobsStatusStorage
storage["scheduledjobs"] = cronJobsStorage
storage["scheduledjobs/status"] = cronJobsStatusStorage
}
return storage
}
func (p RESTStorageProvider) GroupName() string {
return batch.GroupName
}

28
vendor/k8s.io/kubernetes/pkg/registry/cachesize/BUILD generated vendored Normal file
View file

@ -0,0 +1,28 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["cachesize.go"],
tags = ["automanaged"],
deps = ["//vendor:github.com/golang/glog"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

9
vendor/k8s.io/kubernetes/pkg/registry/cachesize/OWNERS generated vendored Executable file
View file

@ -0,0 +1,9 @@
reviewers:
- wojtek-t
- bprashanth
- gmarek
- soltysh
- madhusudancs
- mml
- ericchiang
- caseydavenport

View file

@ -0,0 +1,127 @@
/*
Copyright 2014 The Kubernetes Authors.
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.
*/
//use for --watch-cache-sizes param of kube-apiserver
//make watch cache size of resources configurable
package cachesize
import (
"strconv"
"strings"
"github.com/golang/glog"
)
type Resource string
const (
APIServices Resource = "apiservices"
CertificateSigningRequests Resource = "certificatesigningrequests"
ClusterRoles Resource = "clusterroles"
ClusterRoleBindings Resource = "clusterrolebindings"
ConfigMaps Resource = "configmaps"
Controllers Resource = "controllers"
Daemonsets Resource = "daemonsets"
Deployments Resource = "deployments"
Endpoints Resource = "endpoints"
HorizontalPodAutoscalers Resource = "horizontalpodautoscalers"
Ingress Resource = "ingress"
PodDisruptionBudget Resource = "poddisruptionbudgets"
StatefulSet Resource = "statefulset"
Jobs Resource = "jobs"
LimitRanges Resource = "limitranges"
Namespaces Resource = "namespaces"
NetworkPolicys Resource = "networkpolicies"
Nodes Resource = "nodes"
PersistentVolumes Resource = "persistentvolumes"
PersistentVolumeClaims Resource = "persistentvolumeclaims"
Pods Resource = "pods"
PodSecurityPolicies Resource = "podsecuritypolicies"
PodTemplates Resource = "podtemplates"
Replicasets Resource = "replicasets"
ResourceQuotas Resource = "resourcequotas"
CronJobs Resource = "cronjobs"
Roles Resource = "roles"
RoleBindings Resource = "rolebindings"
Secrets Resource = "secrets"
ServiceAccounts Resource = "serviceaccounts"
Services Resource = "services"
StorageClasses Resource = "storageclasses"
// Default value of watch cache size for a resource if not specified.
defaultWatchCacheSize = 100
)
// TODO: This shouldn't be a global variable.
var watchCacheSizes map[Resource]int
func init() {
watchCacheSizes = make(map[Resource]int)
}
func InitializeWatchCacheSizes(expectedRAMCapacityMB int) {
// This is the heuristics that from memory capacity is trying to infer
// the maximum number of nodes in the cluster and set cache sizes based
// on that value.
// From our documentation, we officially recomment 120GB machines for
// 2000 nodes, and we scale from that point. Thus we assume ~60MB of
// capacity per node.
// TODO: Revisit this heuristics
clusterSize := expectedRAMCapacityMB / 60
// We should specify cache size for a given resource only if it
// is supposed to have non-default value.
//
// TODO: Figure out which resource we should have non-default value.
watchCacheSizes[Controllers] = maxInt(5*clusterSize, 100)
watchCacheSizes[Endpoints] = maxInt(10*clusterSize, 1000)
watchCacheSizes[Nodes] = maxInt(5*clusterSize, 1000)
watchCacheSizes[Pods] = maxInt(50*clusterSize, 1000)
watchCacheSizes[Services] = maxInt(5*clusterSize, 1000)
watchCacheSizes[APIServices] = maxInt(5*clusterSize, 1000)
}
func SetWatchCacheSizes(cacheSizes []string) {
for _, c := range cacheSizes {
tokens := strings.Split(c, "#")
if len(tokens) != 2 {
glog.Errorf("invalid value of watch cache capabilities: %s", c)
continue
}
size, err := strconv.Atoi(tokens[1])
if err != nil {
glog.Errorf("invalid size of watch cache capabilities: %s", c)
continue
}
watchCacheSizes[Resource(strings.ToLower(tokens[0]))] = size
}
}
func GetWatchCacheSizeByResource(resource Resource) int { // TODO this should use schema.GroupResource for lookups
if value, found := watchCacheSizes[resource]; found {
return value
}
return defaultWatchCacheSize
}
func maxInt(a, b int) int {
if a > b {
return a
}
return b
}

10
vendor/k8s.io/kubernetes/pkg/registry/certificates/OWNERS generated vendored Executable file
View file

@ -0,0 +1,10 @@
reviewers:
- smarterclayton
- wojtek-t
- deads2k
- mikedanese
- liggitt
- timothysc
- dims
- hongchaodeng
- david-mcmahon

View file

@ -0,0 +1,65 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"doc.go",
"registry.go",
"strategy.go",
],
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/apis/certificates:go_default_library",
"//pkg/apis/certificates/validation:go_default_library",
"//pkg/fields:go_default_library",
"//pkg/genericapiserver/api/rest:go_default_library",
"//pkg/registry/generic:go_default_library",
"//pkg/storage:go_default_library",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
"//vendor:k8s.io/apimachinery/pkg/labels",
"//vendor:k8s.io/apimachinery/pkg/runtime",
"//vendor:k8s.io/apimachinery/pkg/util/validation/field",
"//vendor:k8s.io/apimachinery/pkg/watch",
"//vendor:k8s.io/apiserver/pkg/request",
"//vendor:k8s.io/apiserver/pkg/storage/names",
],
)
go_test(
name = "go_default_test",
srcs = ["strategy_test.go"],
library = ":go_default_library",
tags = ["automanaged"],
deps = [
"//pkg/apis/certificates:go_default_library",
"//vendor:k8s.io/apimachinery/pkg/runtime",
"//vendor:k8s.io/apimachinery/pkg/util/diff",
"//vendor:k8s.io/apiserver/pkg/authentication/user",
"//vendor:k8s.io/apiserver/pkg/request",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//pkg/registry/certificates/certificates/storage:all-srcs",
],
tags = ["automanaged"],
)

View file

@ -0,0 +1,19 @@
/*
Copyright 2016 The Kubernetes Authors.
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 certificates provides Registry interface and its RESTStorage
// implementation for storing CertificateSigningRequest objects.
package certificates // import "k8s.io/kubernetes/pkg/registry/certificates/certificates"

View file

@ -0,0 +1,83 @@
/*
Copyright 2016 The Kubernetes Authors.
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 certificates
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/watch"
genericapirequest "k8s.io/apiserver/pkg/request"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/certificates"
"k8s.io/kubernetes/pkg/genericapiserver/api/rest"
)
// Registry is an interface for things that know how to store CSRs.
type Registry interface {
ListCSRs(ctx genericapirequest.Context, options *api.ListOptions) (*certificates.CertificateSigningRequestList, error)
CreateCSR(ctx genericapirequest.Context, csr *certificates.CertificateSigningRequest) error
UpdateCSR(ctx genericapirequest.Context, csr *certificates.CertificateSigningRequest) error
GetCSR(ctx genericapirequest.Context, csrID string, options *metav1.GetOptions) (*certificates.CertificateSigningRequest, error)
DeleteCSR(ctx genericapirequest.Context, csrID string) error
WatchCSRs(ctx genericapirequest.Context, options *api.ListOptions) (watch.Interface, 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}
}
func (s *storage) ListCSRs(ctx genericapirequest.Context, options *api.ListOptions) (*certificates.CertificateSigningRequestList, error) {
obj, err := s.List(ctx, options)
if err != nil {
return nil, err
}
return obj.(*certificates.CertificateSigningRequestList), nil
}
func (s *storage) CreateCSR(ctx genericapirequest.Context, csr *certificates.CertificateSigningRequest) error {
_, err := s.Create(ctx, csr)
return err
}
func (s *storage) UpdateCSR(ctx genericapirequest.Context, csr *certificates.CertificateSigningRequest) error {
_, _, err := s.Update(ctx, csr.Name, rest.DefaultUpdatedObjectInfo(csr, api.Scheme))
return err
}
func (s *storage) WatchCSRs(ctx genericapirequest.Context, options *api.ListOptions) (watch.Interface, error) {
return s.Watch(ctx, options)
}
func (s *storage) GetCSR(ctx genericapirequest.Context, name string, options *metav1.GetOptions) (*certificates.CertificateSigningRequest, error) {
obj, err := s.Get(ctx, name, options)
if err != nil {
return nil, err
}
return obj.(*certificates.CertificateSigningRequest), nil
}
func (s *storage) DeleteCSR(ctx genericapirequest.Context, name string) error {
_, err := s.Delete(ctx, name, nil)
return err
}

View file

@ -0,0 +1,36 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["storage.go"],
tags = ["automanaged"],
deps = [
"//pkg/apis/certificates:go_default_library",
"//pkg/genericapiserver/api/rest:go_default_library",
"//pkg/registry/certificates/certificates:go_default_library",
"//pkg/registry/generic:go_default_library",
"//pkg/registry/generic/registry:go_default_library",
"//vendor:k8s.io/apimachinery/pkg/runtime",
"//vendor:k8s.io/apiserver/pkg/request",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

View file

@ -0,0 +1,92 @@
/*
Copyright 2016 The Kubernetes Authors.
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 storage
import (
"k8s.io/apimachinery/pkg/runtime"
genericapirequest "k8s.io/apiserver/pkg/request"
"k8s.io/kubernetes/pkg/apis/certificates"
"k8s.io/kubernetes/pkg/genericapiserver/api/rest"
csrregistry "k8s.io/kubernetes/pkg/registry/certificates/certificates"
"k8s.io/kubernetes/pkg/registry/generic"
genericregistry "k8s.io/kubernetes/pkg/registry/generic/registry"
)
// REST implements a RESTStorage for CertificateSigningRequest
type REST struct {
*genericregistry.Store
}
// NewREST returns a registry which will store CertificateSigningRequest in the given helper
func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, *ApprovalREST) {
store := &genericregistry.Store{
NewFunc: func() runtime.Object { return &certificates.CertificateSigningRequest{} },
NewListFunc: func() runtime.Object { return &certificates.CertificateSigningRequestList{} },
ObjectNameFunc: func(obj runtime.Object) (string, error) {
return obj.(*certificates.CertificateSigningRequest).Name, nil
},
PredicateFunc: csrregistry.Matcher,
QualifiedResource: certificates.Resource("certificatesigningrequests"),
CreateStrategy: csrregistry.Strategy,
UpdateStrategy: csrregistry.Strategy,
DeleteStrategy: csrregistry.Strategy,
}
options := &generic.StoreOptions{RESTOptions: optsGetter, AttrFunc: csrregistry.GetAttrs}
if err := store.CompleteWithOptions(options); err != nil {
panic(err) // TODO: Propagate error up
}
// Subresources use the same store and creation strategy, which only
// allows empty subs. Updates to an existing subresource are handled by
// dedicated strategies.
statusStore := *store
statusStore.UpdateStrategy = csrregistry.StatusStrategy
approvalStore := *store
approvalStore.UpdateStrategy = csrregistry.ApprovalStrategy
return &REST{store}, &StatusREST{store: &statusStore}, &ApprovalREST{store: &approvalStore}
}
// StatusREST implements the REST endpoint for changing the status of a CSR.
type StatusREST struct {
store *genericregistry.Store
}
func (r *StatusREST) New() runtime.Object {
return &certificates.CertificateSigningRequest{}
}
// Update alters the status subset of an object.
func (r *StatusREST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) {
return r.store.Update(ctx, name, objInfo)
}
// ApprovalREST implements the REST endpoint for changing the approval state of a CSR.
type ApprovalREST struct {
store *genericregistry.Store
}
func (r *ApprovalREST) New() runtime.Object {
return &certificates.CertificateSigningRequest{}
}
// Update alters the approval subset of an object.
func (r *ApprovalREST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) {
return r.store.Update(ctx, name, objInfo)
}

View file

@ -0,0 +1,194 @@
/*
Copyright 2016 The Kubernetes Authors.
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 certificates
import (
"fmt"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/validation/field"
genericapirequest "k8s.io/apiserver/pkg/request"
"k8s.io/apiserver/pkg/storage/names"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/certificates"
"k8s.io/kubernetes/pkg/apis/certificates/validation"
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/registry/generic"
apistorage "k8s.io/kubernetes/pkg/storage"
)
// csrStrategy implements behavior for CSRs
type csrStrategy struct {
runtime.ObjectTyper
names.NameGenerator
}
// csrStrategy is the default logic that applies when creating and updating
// CSR objects.
var Strategy = csrStrategy{api.Scheme, names.SimpleNameGenerator}
// NamespaceScoped is true for CSRs.
func (csrStrategy) NamespaceScoped() bool {
return false
}
// AllowCreateOnUpdate is false for CSRs.
func (csrStrategy) AllowCreateOnUpdate() bool {
return false
}
// PrepareForCreate clears fields that are not allowed to be set by end users
// on creation.
func (csrStrategy) PrepareForCreate(ctx genericapirequest.Context, obj runtime.Object) {
csr := obj.(*certificates.CertificateSigningRequest)
// Clear any user-specified info
csr.Spec.Username = ""
csr.Spec.UID = ""
csr.Spec.Groups = nil
// Inject user.Info from request context
if user, ok := genericapirequest.UserFrom(ctx); ok {
csr.Spec.Username = user.GetName()
csr.Spec.UID = user.GetUID()
csr.Spec.Groups = user.GetGroups()
}
// Be explicit that users cannot create pre-approved certificate requests.
csr.Status = certificates.CertificateSigningRequestStatus{}
csr.Status.Conditions = []certificates.CertificateSigningRequestCondition{}
}
// PrepareForUpdate clears fields that are not allowed to be set by end users
// on update. Certificate requests are immutable after creation except via subresources.
func (csrStrategy) PrepareForUpdate(ctx genericapirequest.Context, obj, old runtime.Object) {
newCSR := obj.(*certificates.CertificateSigningRequest)
oldCSR := old.(*certificates.CertificateSigningRequest)
newCSR.Spec = oldCSR.Spec
newCSR.Status = oldCSR.Status
}
// Validate validates a new CSR. Validation must check for a correct signature.
func (csrStrategy) Validate(ctx genericapirequest.Context, obj runtime.Object) field.ErrorList {
csr := obj.(*certificates.CertificateSigningRequest)
return validation.ValidateCertificateSigningRequest(csr)
}
// Canonicalize normalizes the object after validation (which includes a signature check).
func (csrStrategy) Canonicalize(obj runtime.Object) {}
// ValidateUpdate is the default update validation for an end user.
func (csrStrategy) ValidateUpdate(ctx genericapirequest.Context, obj, old runtime.Object) field.ErrorList {
oldCSR := old.(*certificates.CertificateSigningRequest)
newCSR := obj.(*certificates.CertificateSigningRequest)
return validation.ValidateCertificateSigningRequestUpdate(newCSR, oldCSR)
}
// If AllowUnconditionalUpdate() is true and the object specified by
// the user does not have a resource version, then generic Update()
// populates it with the latest version. Else, it checks that the
// version specified by the user matches the version of latest etcd
// object.
func (csrStrategy) AllowUnconditionalUpdate() bool {
return true
}
func (s csrStrategy) Export(ctx genericapirequest.Context, obj runtime.Object, exact bool) error {
csr, ok := obj.(*certificates.CertificateSigningRequest)
if !ok {
// unexpected programmer error
return fmt.Errorf("unexpected object: %v", obj)
}
s.PrepareForCreate(ctx, obj)
if exact {
return nil
}
// CSRs allow direct subresource edits, we clear them without exact so the CSR value can be reused.
csr.Status = certificates.CertificateSigningRequestStatus{}
return nil
}
// Storage strategy for the Status subresource
type csrStatusStrategy struct {
csrStrategy
}
var StatusStrategy = csrStatusStrategy{Strategy}
func (csrStatusStrategy) PrepareForUpdate(ctx genericapirequest.Context, obj, old runtime.Object) {
newCSR := obj.(*certificates.CertificateSigningRequest)
oldCSR := old.(*certificates.CertificateSigningRequest)
// Updating the Status should only update the Status and not the spec
// or approval conditions. The intent is to separate the concerns of
// approval and certificate issuance.
newCSR.Spec = oldCSR.Spec
newCSR.Status.Conditions = oldCSR.Status.Conditions
}
func (csrStatusStrategy) ValidateUpdate(ctx genericapirequest.Context, obj, old runtime.Object) field.ErrorList {
return validation.ValidateCertificateSigningRequestUpdate(obj.(*certificates.CertificateSigningRequest), old.(*certificates.CertificateSigningRequest))
}
// Canonicalize normalizes the object after validation.
func (csrStatusStrategy) Canonicalize(obj runtime.Object) {
}
// Storage strategy for the Approval subresource
type csrApprovalStrategy struct {
csrStrategy
}
var ApprovalStrategy = csrApprovalStrategy{Strategy}
func (csrApprovalStrategy) PrepareForUpdate(ctx genericapirequest.Context, obj, old runtime.Object) {
newCSR := obj.(*certificates.CertificateSigningRequest)
oldCSR := old.(*certificates.CertificateSigningRequest)
// Updating the approval should only update the conditions.
newCSR.Spec = oldCSR.Spec
oldCSR.Status.Conditions = newCSR.Status.Conditions
newCSR.Status = oldCSR.Status
}
func (csrApprovalStrategy) ValidateUpdate(ctx genericapirequest.Context, obj, old runtime.Object) field.ErrorList {
return validation.ValidateCertificateSigningRequestUpdate(obj.(*certificates.CertificateSigningRequest), old.(*certificates.CertificateSigningRequest))
}
// GetAttrs returns labels and fields of a given object for filtering purposes.
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
sa, ok := obj.(*certificates.CertificateSigningRequest)
if !ok {
return nil, nil, fmt.Errorf("not a CertificateSigningRequest")
}
return labels.Set(sa.Labels), SelectableFields(sa), nil
}
// Matcher returns a generic matcher for a given label and field selector.
func Matcher(label labels.Selector, field fields.Selector) apistorage.SelectionPredicate {
return apistorage.SelectionPredicate{
Label: label,
Field: field,
GetAttrs: GetAttrs,
}
}
// SelectableFields returns a field set that can be used for filter selection
func SelectableFields(obj *certificates.CertificateSigningRequest) fields.Set {
return generic.ObjectMetaFieldsSet(&obj.ObjectMeta, false)
}

View file

@ -0,0 +1,121 @@
/*
Copyright 2016 The Kubernetes Authors.
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 certificates
import (
"reflect"
"testing"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/diff"
"k8s.io/apiserver/pkg/authentication/user"
genericapirequest "k8s.io/apiserver/pkg/request"
certapi "k8s.io/kubernetes/pkg/apis/certificates"
)
func TestStrategyCreate(t *testing.T) {
tests := map[string]struct {
ctx genericapirequest.Context
obj runtime.Object
expectedObj runtime.Object
}{
"no user in context, no user in obj": {
ctx: genericapirequest.NewContext(),
obj: &certapi.CertificateSigningRequest{},
expectedObj: &certapi.CertificateSigningRequest{
Status: certapi.CertificateSigningRequestStatus{Conditions: []certapi.CertificateSigningRequestCondition{}},
},
},
"user in context, no user in obj": {
ctx: genericapirequest.WithUser(
genericapirequest.NewContext(),
&user.DefaultInfo{
Name: "bob",
UID: "123",
Groups: []string{"group1"},
Extra: map[string][]string{"foo": {"bar"}},
},
),
obj: &certapi.CertificateSigningRequest{},
expectedObj: &certapi.CertificateSigningRequest{
Spec: certapi.CertificateSigningRequestSpec{
Username: "bob",
UID: "123",
Groups: []string{"group1"},
},
Status: certapi.CertificateSigningRequestStatus{Conditions: []certapi.CertificateSigningRequestCondition{}},
},
},
"no user in context, user in obj": {
ctx: genericapirequest.NewContext(),
obj: &certapi.CertificateSigningRequest{
Spec: certapi.CertificateSigningRequestSpec{
Username: "bob",
UID: "123",
Groups: []string{"group1"},
},
},
expectedObj: &certapi.CertificateSigningRequest{
Status: certapi.CertificateSigningRequestStatus{Conditions: []certapi.CertificateSigningRequestCondition{}},
},
},
"user in context, user in obj": {
ctx: genericapirequest.WithUser(
genericapirequest.NewContext(),
&user.DefaultInfo{
Name: "alice",
UID: "234",
},
),
obj: &certapi.CertificateSigningRequest{
Spec: certapi.CertificateSigningRequestSpec{
Username: "bob",
UID: "123",
Groups: []string{"group1"},
},
},
expectedObj: &certapi.CertificateSigningRequest{
Spec: certapi.CertificateSigningRequestSpec{
Username: "alice",
UID: "234",
Groups: nil,
},
Status: certapi.CertificateSigningRequestStatus{Conditions: []certapi.CertificateSigningRequestCondition{}},
},
},
"pre-approved status": {
ctx: genericapirequest.NewContext(),
obj: &certapi.CertificateSigningRequest{
Status: certapi.CertificateSigningRequestStatus{
Conditions: []certapi.CertificateSigningRequestCondition{
{Type: certapi.CertificateApproved},
},
},
},
expectedObj: &certapi.CertificateSigningRequest{
Status: certapi.CertificateSigningRequestStatus{Conditions: []certapi.CertificateSigningRequestCondition{}},
}},
}
for k, tc := range tests {
obj := tc.obj
Strategy.PrepareForCreate(tc.ctx, obj)
if !reflect.DeepEqual(obj, tc.expectedObj) {
t.Errorf("%s: object diff: %s", k, diff.ObjectDiff(obj, tc.expectedObj))
}
}
}

View file

@ -0,0 +1,35 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["storage_certificates.go"],
tags = ["automanaged"],
deps = [
"//pkg/apis/certificates:go_default_library",
"//pkg/apis/certificates/v1alpha1:go_default_library",
"//pkg/genericapiserver:go_default_library",
"//pkg/genericapiserver/api/rest:go_default_library",
"//pkg/registry/certificates/certificates/storage:go_default_library",
"//pkg/registry/generic:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

View file

@ -0,0 +1,56 @@
/*
Copyright 2016 The Kubernetes Authors.
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 rest
import (
"k8s.io/kubernetes/pkg/apis/certificates"
certificatesapiv1alpha1 "k8s.io/kubernetes/pkg/apis/certificates/v1alpha1"
"k8s.io/kubernetes/pkg/genericapiserver"
"k8s.io/kubernetes/pkg/genericapiserver/api/rest"
certificatestore "k8s.io/kubernetes/pkg/registry/certificates/certificates/storage"
"k8s.io/kubernetes/pkg/registry/generic"
)
type RESTStorageProvider struct{}
func (p RESTStorageProvider) NewRESTStorage(apiResourceConfigSource genericapiserver.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) (genericapiserver.APIGroupInfo, bool) {
apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(certificates.GroupName)
if apiResourceConfigSource.AnyResourcesForVersionEnabled(certificatesapiv1alpha1.SchemeGroupVersion) {
apiGroupInfo.VersionedResourcesStorageMap[certificatesapiv1alpha1.SchemeGroupVersion.Version] = p.v1alpha1Storage(apiResourceConfigSource, restOptionsGetter)
apiGroupInfo.GroupMeta.GroupVersion = certificatesapiv1alpha1.SchemeGroupVersion
}
return apiGroupInfo, true
}
func (p RESTStorageProvider) v1alpha1Storage(apiResourceConfigSource genericapiserver.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) map[string]rest.Storage {
version := certificatesapiv1alpha1.SchemeGroupVersion
storage := map[string]rest.Storage{}
if apiResourceConfigSource.ResourceEnabled(version.WithResource("certificatesigningrequests")) {
csrStorage, csrStatusStorage, csrApprovalStorage := certificatestore.NewREST(restOptionsGetter)
storage["certificatesigningrequests"] = csrStorage
storage["certificatesigningrequests/status"] = csrStatusStorage
storage["certificatesigningrequests/approval"] = csrApprovalStorage
}
return storage
}
func (p RESTStorageProvider) GroupName() string {
return certificates.GroupName
}

7
vendor/k8s.io/kubernetes/pkg/registry/core/OWNERS generated vendored Executable file
View file

@ -0,0 +1,7 @@
reviewers:
- wojtek-t
- deads2k
- caesarxuchao
- justinsb
- hongchaodeng
- juanvallejo

View file

@ -0,0 +1,58 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"doc.go",
"rest.go",
"validator.go",
],
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/probe:go_default_library",
"//pkg/probe/http:go_default_library",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
"//vendor:k8s.io/apimachinery/pkg/runtime",
"//vendor:k8s.io/apimachinery/pkg/util/net",
"//vendor:k8s.io/apiserver/pkg/request",
],
)
go_test(
name = "go_default_test",
srcs = [
"rest_test.go",
"validator_test.go",
],
library = ":go_default_library",
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/probe:go_default_library",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
"//vendor:k8s.io/apimachinery/pkg/util/diff",
"//vendor:k8s.io/apiserver/pkg/request",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

View file

@ -0,0 +1,19 @@
/*
Copyright 2015 The Kubernetes Authors.
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 componentstatus provides interfaces and implementation for retrieving cluster
// component status.
package componentstatus // import "k8s.io/kubernetes/pkg/registry/core/componentstatus"

View file

@ -0,0 +1,118 @@
/*
Copyright 2015 The Kubernetes Authors.
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 componentstatus
import (
"fmt"
"sync"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
genericapirequest "k8s.io/apiserver/pkg/request"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/probe"
httpprober "k8s.io/kubernetes/pkg/probe/http"
)
type REST struct {
GetServersToValidate func() map[string]Server
prober httpprober.HTTPProber
}
// NewStorage returns a new REST.
func NewStorage(serverRetriever func() map[string]Server) *REST {
return &REST{
GetServersToValidate: serverRetriever,
prober: httpprober.New(),
}
}
func (rs *REST) New() runtime.Object {
return &api.ComponentStatus{}
}
func (rs *REST) NewList() runtime.Object {
return &api.ComponentStatusList{}
}
// Returns the list of component status. Note that the label and field are both ignored.
// Note that this call doesn't support labels or selectors.
func (rs *REST) List(ctx genericapirequest.Context, options *api.ListOptions) (runtime.Object, error) {
servers := rs.GetServersToValidate()
wait := sync.WaitGroup{}
wait.Add(len(servers))
statuses := make(chan api.ComponentStatus, len(servers))
for k, v := range servers {
go func(name string, server Server) {
defer wait.Done()
status := rs.getComponentStatus(name, server)
statuses <- *status
}(k, v)
}
wait.Wait()
close(statuses)
reply := []api.ComponentStatus{}
for status := range statuses {
reply = append(reply, status)
}
return &api.ComponentStatusList{Items: reply}, nil
}
func (rs *REST) Get(ctx genericapirequest.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
servers := rs.GetServersToValidate()
if server, ok := servers[name]; !ok {
return nil, fmt.Errorf("Component not found: %s", name)
} else {
return rs.getComponentStatus(name, server), nil
}
}
func ToConditionStatus(s probe.Result) api.ConditionStatus {
switch s {
case probe.Success:
return api.ConditionTrue
case probe.Failure:
return api.ConditionFalse
default:
return api.ConditionUnknown
}
}
func (rs *REST) getComponentStatus(name string, server Server) *api.ComponentStatus {
status, msg, err := server.DoServerCheck(rs.prober)
errorMsg := ""
if err != nil {
errorMsg = err.Error()
}
c := &api.ComponentCondition{
Type: api.ComponentHealthy,
Status: ToConditionStatus(status),
Message: msg,
Error: errorMsg,
}
retVal := &api.ComponentStatus{
Conditions: []api.ComponentCondition{*c},
}
retVal.Name = name
return retVal
}

View file

@ -0,0 +1,142 @@
/*
Copyright 2015 The Kubernetes Authors.
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 componentstatus
import (
"fmt"
"reflect"
"strings"
"testing"
"net/http"
"net/url"
"time"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/diff"
genericapirequest "k8s.io/apiserver/pkg/request"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/probe"
)
type fakeHttpProber struct {
result probe.Result
body string
err error
}
func (f *fakeHttpProber) Probe(*url.URL, http.Header, time.Duration) (probe.Result, string, error) {
return f.result, f.body, f.err
}
type testResponse struct {
result probe.Result
data string
err error
}
func NewTestREST(resp testResponse) *REST {
return &REST{
GetServersToValidate: func() map[string]Server {
return map[string]Server{
"test1": {Addr: "testserver1", Port: 8000, Path: "/healthz"},
}
},
prober: &fakeHttpProber{
result: resp.result,
body: resp.data,
err: resp.err,
},
}
}
func createTestStatus(name string, status api.ConditionStatus, msg string, err string) *api.ComponentStatus {
retVal := &api.ComponentStatus{
Conditions: []api.ComponentCondition{
{Type: api.ComponentHealthy, Status: status, Message: msg, Error: err},
},
}
retVal.Name = name
return retVal
}
func TestList_NoError(t *testing.T) {
r := NewTestREST(testResponse{result: probe.Success, data: "ok"})
got, err := r.List(genericapirequest.NewContext(), nil)
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
expect := &api.ComponentStatusList{
Items: []api.ComponentStatus{*(createTestStatus("test1", api.ConditionTrue, "ok", ""))},
}
if e, a := expect, got; !reflect.DeepEqual(e, a) {
t.Errorf("Got unexpected object. Diff: %s", diff.ObjectDiff(e, a))
}
}
func TestList_FailedCheck(t *testing.T) {
r := NewTestREST(testResponse{result: probe.Failure, data: ""})
got, err := r.List(genericapirequest.NewContext(), nil)
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
expect := &api.ComponentStatusList{
Items: []api.ComponentStatus{
*(createTestStatus("test1", api.ConditionFalse, "", ""))},
}
if e, a := expect, got; !reflect.DeepEqual(e, a) {
t.Errorf("Got unexpected object. Diff: %s", diff.ObjectDiff(e, a))
}
}
func TestList_UnknownError(t *testing.T) {
r := NewTestREST(testResponse{result: probe.Unknown, data: "", err: fmt.Errorf("fizzbuzz error")})
got, err := r.List(genericapirequest.NewContext(), nil)
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
expect := &api.ComponentStatusList{
Items: []api.ComponentStatus{
*(createTestStatus("test1", api.ConditionUnknown, "", "fizzbuzz error"))},
}
if e, a := expect, got; !reflect.DeepEqual(e, a) {
t.Errorf("Got unexpected object. Diff: %s", diff.ObjectDiff(e, a))
}
}
func TestGet_NoError(t *testing.T) {
r := NewTestREST(testResponse{result: probe.Success, data: "ok"})
got, err := r.Get(genericapirequest.NewContext(), "test1", &metav1.GetOptions{})
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
expect := createTestStatus("test1", api.ConditionTrue, "ok", "")
if e, a := expect, got; !reflect.DeepEqual(e, a) {
t.Errorf("Got unexpected object. Diff: %s", diff.ObjectDiff(e, a))
}
}
func TestGet_BadName(t *testing.T) {
r := NewTestREST(testResponse{result: probe.Success, data: "ok"})
_, err := r.Get(genericapirequest.NewContext(), "invalidname", &metav1.GetOptions{})
if err == nil {
t.Fatalf("Expected error, but did not get one")
}
if !strings.Contains(err.Error(), "Component not found: invalidname") {
t.Fatalf("Got unexpected error: %v", err)
}
}

View file

@ -0,0 +1,82 @@
/*
Copyright 2014 The Kubernetes Authors.
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 componentstatus
import (
"net/http"
"time"
utilnet "k8s.io/apimachinery/pkg/util/net"
"k8s.io/kubernetes/pkg/probe"
httpprober "k8s.io/kubernetes/pkg/probe/http"
)
const (
probeTimeOut = 20 * time.Second
)
// TODO: this basic interface is duplicated in N places. consolidate?
type httpGet interface {
Get(url string) (*http.Response, error)
}
type ValidatorFn func([]byte) error
type Server struct {
Addr string
Port int
Path string
EnableHTTPS bool
Validate ValidatorFn
}
type ServerStatus struct {
// +optional
Component string `json:"component,omitempty"`
// +optional
Health string `json:"health,omitempty"`
// +optional
HealthCode probe.Result `json:"healthCode,omitempty"`
// +optional
Msg string `json:"msg,omitempty"`
// +optional
Err string `json:"err,omitempty"`
}
func (server *Server) DoServerCheck(prober httpprober.HTTPProber) (probe.Result, string, error) {
scheme := "http"
if server.EnableHTTPS {
scheme = "https"
}
url := utilnet.FormatURL(scheme, server.Addr, server.Port, server.Path)
result, data, err := prober.Probe(url, nil, probeTimeOut)
if err != nil {
return probe.Unknown, "", err
}
if result == probe.Failure {
return probe.Failure, string(data), err
}
if server.Validate != nil {
if err := server.Validate([]byte(data)); err != nil {
return probe.Failure, string(data), err
}
}
return result, string(data), nil
}

View file

@ -0,0 +1,76 @@
/*
Copyright 2014 The Kubernetes Authors.
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 componentstatus
import (
"errors"
"fmt"
"testing"
"k8s.io/kubernetes/pkg/probe"
)
func matchError(data []byte) error {
if string(data) != "bar" {
return errors.New("match error")
}
return nil
}
func TestValidate(t *testing.T) {
tests := []struct {
probeResult probe.Result
probeData string
probeErr error
expectResult probe.Result
expectData string
expectErr bool
validator ValidatorFn
}{
{probe.Unknown, "", fmt.Errorf("probe error"), probe.Unknown, "", true, nil},
{probe.Failure, "", nil, probe.Failure, "", false, nil},
{probe.Success, "foo", nil, probe.Failure, "foo", true, matchError},
{probe.Success, "foo", nil, probe.Success, "foo", false, nil},
}
s := Server{Addr: "foo.com", Port: 8080, Path: "/healthz"}
for _, test := range tests {
fakeProber := &fakeHttpProber{
result: test.probeResult,
body: test.probeData,
err: test.probeErr,
}
s.Validate = test.validator
result, data, err := s.DoServerCheck(fakeProber)
if test.expectErr && err == nil {
t.Error("unexpected non-error")
}
if !test.expectErr && err != nil {
t.Errorf("unexpected error: %v", err)
}
if data != test.expectData {
t.Errorf("expected %s, got %s", test.expectData, data)
}
if result != test.expectResult {
t.Errorf("expected %s, got %s", test.expectResult, result)
}
}
}

View file

@ -0,0 +1,62 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"doc.go",
"registry.go",
"strategy.go",
],
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/api/validation:go_default_library",
"//pkg/fields:go_default_library",
"//pkg/genericapiserver/api/rest:go_default_library",
"//pkg/registry/generic:go_default_library",
"//pkg/storage:go_default_library",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
"//vendor:k8s.io/apimachinery/pkg/labels",
"//vendor:k8s.io/apimachinery/pkg/runtime",
"//vendor:k8s.io/apimachinery/pkg/util/validation/field",
"//vendor:k8s.io/apimachinery/pkg/watch",
"//vendor:k8s.io/apiserver/pkg/request",
"//vendor:k8s.io/apiserver/pkg/storage/names",
],
)
go_test(
name = "go_default_test",
srcs = ["strategy_test.go"],
library = ":go_default_library",
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/api/testing:go_default_library",
"//vendor:k8s.io/apiserver/pkg/request",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//pkg/registry/core/configmap/storage:all-srcs",
],
tags = ["automanaged"],
)

View file

@ -0,0 +1,20 @@
/*
Copyright 2015 The Kubernetes Authors.
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 configmap provides Registry interface
// and its REST implementation for storing
// ConfigMap API objects.
package configmap // import "k8s.io/kubernetes/pkg/registry/core/configmap"

View file

@ -0,0 +1,92 @@
/*
Copyright 2015 The Kubernetes Authors.
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 configmap
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/watch"
genericapirequest "k8s.io/apiserver/pkg/request"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/genericapiserver/api/rest"
)
// Registry is an interface for things that know how to store ConfigMaps.
type Registry interface {
ListConfigMaps(ctx genericapirequest.Context, options *api.ListOptions) (*api.ConfigMapList, error)
WatchConfigMaps(ctx genericapirequest.Context, options *api.ListOptions) (watch.Interface, error)
GetConfigMap(ctx genericapirequest.Context, name string, options *metav1.GetOptions) (*api.ConfigMap, error)
CreateConfigMap(ctx genericapirequest.Context, cfg *api.ConfigMap) (*api.ConfigMap, error)
UpdateConfigMap(ctx genericapirequest.Context, cfg *api.ConfigMap) (*api.ConfigMap, error)
DeleteConfigMap(ctx genericapirequest.Context, name 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}
}
func (s *storage) ListConfigMaps(ctx genericapirequest.Context, options *api.ListOptions) (*api.ConfigMapList, error) {
obj, err := s.List(ctx, options)
if err != nil {
return nil, err
}
return obj.(*api.ConfigMapList), err
}
func (s *storage) WatchConfigMaps(ctx genericapirequest.Context, options *api.ListOptions) (watch.Interface, error) {
return s.Watch(ctx, options)
}
func (s *storage) GetConfigMap(ctx genericapirequest.Context, name string, options *metav1.GetOptions) (*api.ConfigMap, error) {
obj, err := s.Get(ctx, name, options)
if err != nil {
return nil, err
}
return obj.(*api.ConfigMap), nil
}
func (s *storage) CreateConfigMap(ctx genericapirequest.Context, cfg *api.ConfigMap) (*api.ConfigMap, error) {
obj, err := s.Create(ctx, cfg)
if err != nil {
return nil, err
}
return obj.(*api.ConfigMap), nil
}
func (s *storage) UpdateConfigMap(ctx genericapirequest.Context, cfg *api.ConfigMap) (*api.ConfigMap, error) {
obj, _, err := s.Update(ctx, cfg.Name, rest.DefaultUpdatedObjectInfo(cfg, api.Scheme))
if err != nil {
return nil, err
}
return obj.(*api.ConfigMap), nil
}
func (s *storage) DeleteConfigMap(ctx genericapirequest.Context, name string) error {
_, err := s.Delete(ctx, name, nil)
return err
}

View file

@ -0,0 +1,51 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_test(
name = "go_default_test",
srcs = ["storage_test.go"],
library = ":go_default_library",
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/fields:go_default_library",
"//pkg/registry/generic:go_default_library",
"//pkg/registry/registrytest:go_default_library",
"//pkg/storage/etcd/testing:go_default_library",
"//vendor:k8s.io/apimachinery/pkg/labels",
"//vendor:k8s.io/apimachinery/pkg/runtime",
],
)
go_library(
name = "go_default_library",
srcs = ["storage.go"],
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/registry/core/configmap:go_default_library",
"//pkg/registry/generic:go_default_library",
"//pkg/registry/generic/registry:go_default_library",
"//vendor:k8s.io/apimachinery/pkg/runtime",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

View file

@ -0,0 +1,52 @@
/*
Copyright 2015 The Kubernetes Authors.
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 storage
import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/registry/core/configmap"
"k8s.io/kubernetes/pkg/registry/generic"
genericregistry "k8s.io/kubernetes/pkg/registry/generic/registry"
)
// REST implements a RESTStorage for ConfigMap
type REST struct {
*genericregistry.Store
}
// NewREST returns a RESTStorage object that will work with ConfigMap objects.
func NewREST(optsGetter generic.RESTOptionsGetter) *REST {
store := &genericregistry.Store{
NewFunc: func() runtime.Object { return &api.ConfigMap{} },
NewListFunc: func() runtime.Object { return &api.ConfigMapList{} },
ObjectNameFunc: func(obj runtime.Object) (string, error) {
return obj.(*api.ConfigMap).Name, nil
},
PredicateFunc: configmap.MatchConfigMap,
QualifiedResource: api.Resource("configmaps"),
CreateStrategy: configmap.Strategy,
UpdateStrategy: configmap.Strategy,
DeleteStrategy: configmap.Strategy,
}
options := &generic.StoreOptions{RESTOptions: optsGetter, AttrFunc: configmap.GetAttrs}
if err := store.CompleteWithOptions(options); err != nil {
panic(err) // TODO: Propagate error up
}
return &REST{store}
}

View file

@ -0,0 +1,160 @@
/*
Copyright 2015 The Kubernetes Authors.
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 storage
import (
"testing"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/registry/generic"
"k8s.io/kubernetes/pkg/registry/registrytest"
etcdtesting "k8s.io/kubernetes/pkg/storage/etcd/testing"
)
func newStorage(t *testing.T) (*REST, *etcdtesting.EtcdTestServer) {
etcdStorage, server := registrytest.NewEtcdStorage(t, "")
restOptions := generic.RESTOptions{
StorageConfig: etcdStorage,
Decorator: generic.UndecoratedStorage,
DeleteCollectionWorkers: 1,
ResourcePrefix: "configmaps",
}
return NewREST(restOptions), server
}
func validNewConfigMap() *api.ConfigMap {
return &api.ConfigMap{
ObjectMeta: api.ObjectMeta{
Name: "foo",
Namespace: "default",
Labels: map[string]string{
"label-1": "value-1",
"label-2": "value-2",
},
},
Data: map[string]string{
"test": "data",
},
}
}
func TestCreate(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := registrytest.New(t, storage.Store)
validConfigMap := validNewConfigMap()
validConfigMap.ObjectMeta = api.ObjectMeta{
GenerateName: "foo-",
}
test.TestCreate(
validConfigMap,
&api.ConfigMap{
ObjectMeta: api.ObjectMeta{Name: "badName"},
Data: map[string]string{
"key": "value",
},
},
&api.ConfigMap{
ObjectMeta: api.ObjectMeta{Name: "name-2"},
Data: map[string]string{
"..dotfile": "do: nothing\n",
},
},
)
}
func TestUpdate(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := registrytest.New(t, storage.Store)
test.TestUpdate(
// valid
validNewConfigMap(),
// updateFunc
func(obj runtime.Object) runtime.Object {
cfg := obj.(*api.ConfigMap)
cfg.Data["update-test"] = "value"
return cfg
},
// invalid updateFunc
func(obj runtime.Object) runtime.Object {
cfg := obj.(*api.ConfigMap)
cfg.Data["bad*Key"] = "value"
return cfg
},
)
}
func TestDelete(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := registrytest.New(t, storage.Store)
test.TestDelete(validNewConfigMap())
}
func TestGet(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := registrytest.New(t, storage.Store)
test.TestGet(validNewConfigMap())
}
func TestList(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := registrytest.New(t, storage.Store)
test.TestList(validNewConfigMap())
}
func TestWatch(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := registrytest.New(t, storage.Store)
test.TestWatch(
validNewConfigMap(),
// matching labels
[]labels.Set{
{"label-1": "value-1"},
{"label-2": "value-2"},
},
// not matching labels
[]labels.Set{
{"foo": "bar"},
},
// matching fields
[]fields.Set{
{"metadata.namespace": "default"},
{"metadata.name": "foo"},
},
// not matching fields
[]fields.Set{
{"metadata.name": "bar"},
{"name": "foo"},
},
)
}

View file

@ -0,0 +1,109 @@
/*
Copyright 2015 The Kubernetes Authors.
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 configmap
import (
"fmt"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/validation/field"
genericapirequest "k8s.io/apiserver/pkg/request"
"k8s.io/apiserver/pkg/storage/names"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/validation"
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/genericapiserver/api/rest"
"k8s.io/kubernetes/pkg/registry/generic"
apistorage "k8s.io/kubernetes/pkg/storage"
)
// strategy implements behavior for ConfigMap objects
type strategy struct {
runtime.ObjectTyper
names.NameGenerator
}
// Strategy is the default logic that applies when creating and updating ConfigMap
// objects via the REST API.
var Strategy = strategy{api.Scheme, names.SimpleNameGenerator}
// Strategy should implement rest.RESTCreateStrategy
var _ rest.RESTCreateStrategy = Strategy
// Strategy should implement rest.RESTUpdateStrategy
var _ rest.RESTUpdateStrategy = Strategy
func (strategy) NamespaceScoped() bool {
return true
}
func (strategy) PrepareForCreate(ctx genericapirequest.Context, obj runtime.Object) {
_ = obj.(*api.ConfigMap)
}
func (strategy) Validate(ctx genericapirequest.Context, obj runtime.Object) field.ErrorList {
cfg := obj.(*api.ConfigMap)
return validation.ValidateConfigMap(cfg)
}
// Canonicalize normalizes the object after validation.
func (strategy) Canonicalize(obj runtime.Object) {
}
func (strategy) AllowCreateOnUpdate() bool {
return false
}
func (strategy) PrepareForUpdate(ctx genericapirequest.Context, newObj, oldObj runtime.Object) {
_ = oldObj.(*api.ConfigMap)
_ = newObj.(*api.ConfigMap)
}
func (strategy) AllowUnconditionalUpdate() bool {
return true
}
func (strategy) ValidateUpdate(ctx genericapirequest.Context, newObj, oldObj runtime.Object) field.ErrorList {
oldCfg, newCfg := oldObj.(*api.ConfigMap), newObj.(*api.ConfigMap)
return validation.ValidateConfigMapUpdate(newCfg, oldCfg)
}
// ConfigMapToSelectableFields returns a field set that represents the object for matching purposes.
func ConfigMapToSelectableFields(cfg *api.ConfigMap) fields.Set {
return generic.ObjectMetaFieldsSet(&cfg.ObjectMeta, true)
}
// GetAttrs returns labels and fields of a given object for filtering purposes.
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
cfg, ok := obj.(*api.ConfigMap)
if !ok {
return nil, nil, fmt.Errorf("given object is not a ConfigMap")
}
return labels.Set(cfg.ObjectMeta.Labels), ConfigMapToSelectableFields(cfg), nil
}
// MatchConfigMap returns a generic matcher for a given label and field selector.
func MatchConfigMap(label labels.Selector, field fields.Selector) apistorage.SelectionPredicate {
return apistorage.SelectionPredicate{
Label: label,
Field: field,
GetAttrs: GetAttrs,
}
}

View file

@ -0,0 +1,79 @@
/*
Copyright 2015 The Kubernetes Authors.
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 configmap
import (
"testing"
genericapirequest "k8s.io/apiserver/pkg/request"
"k8s.io/kubernetes/pkg/api"
apitesting "k8s.io/kubernetes/pkg/api/testing"
)
func TestConfigMapStrategy(t *testing.T) {
ctx := genericapirequest.NewDefaultContext()
if !Strategy.NamespaceScoped() {
t.Errorf("ConfigMap must be namespace scoped")
}
if Strategy.AllowCreateOnUpdate() {
t.Errorf("ConfigMap should not allow create on update")
}
cfg := &api.ConfigMap{
ObjectMeta: api.ObjectMeta{
Name: "valid-config-data",
Namespace: api.NamespaceDefault,
},
Data: map[string]string{
"foo": "bar",
},
}
Strategy.PrepareForCreate(ctx, cfg)
errs := Strategy.Validate(ctx, cfg)
if len(errs) != 0 {
t.Errorf("unexpected error validating %v", errs)
}
newCfg := &api.ConfigMap{
ObjectMeta: api.ObjectMeta{
Name: "valid-config-data-2",
Namespace: api.NamespaceDefault,
ResourceVersion: "4",
},
Data: map[string]string{
"invalidKey": "updatedValue",
},
}
Strategy.PrepareForUpdate(ctx, newCfg, cfg)
errs = Strategy.ValidateUpdate(ctx, newCfg, cfg)
if len(errs) == 0 {
t.Errorf("Expected a validation error")
}
}
func TestSelectableFieldLabelConversions(t *testing.T) {
apitesting.TestSelectableFieldLabelConversionsOfKind(t,
api.Registry.GroupOrDie(api.GroupName).GroupVersion.String(),
"ConfigMap",
ConfigMapToSelectableFields(&api.ConfigMap{}),
nil,
)
}

View file

@ -0,0 +1,62 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"doc.go",
"registry.go",
"strategy.go",
],
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/api/validation:go_default_library",
"//pkg/fields:go_default_library",
"//pkg/genericapiserver/api/rest:go_default_library",
"//pkg/registry/generic:go_default_library",
"//pkg/storage:go_default_library",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
"//vendor:k8s.io/apimachinery/pkg/labels",
"//vendor:k8s.io/apimachinery/pkg/runtime",
"//vendor:k8s.io/apimachinery/pkg/util/validation/field",
"//vendor:k8s.io/apimachinery/pkg/watch",
"//vendor:k8s.io/apiserver/pkg/request",
"//vendor:k8s.io/apiserver/pkg/storage/names",
],
)
go_test(
name = "go_default_test",
srcs = ["strategy_test.go"],
library = ":go_default_library",
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/api/testing:go_default_library",
"//vendor:k8s.io/apiserver/pkg/request",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//pkg/registry/core/controller/storage:all-srcs",
],
tags = ["automanaged"],
)

View file

@ -0,0 +1,19 @@
/*
Copyright 2014 The Kubernetes Authors.
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 controller provides Registry interface and it's RESTStorage
// implementation for storing ReplicationController api objects.
package controller // import "k8s.io/kubernetes/pkg/registry/core/controller"

View file

@ -0,0 +1,94 @@
/*
Copyright 2014 The Kubernetes Authors.
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.
*/
// If you make changes to this file, you should also make the corresponding change in ReplicaSet.
package controller
import (
"fmt"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/watch"
genericapirequest "k8s.io/apiserver/pkg/request"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/genericapiserver/api/rest"
)
// Registry is an interface for things that know how to store ReplicationControllers.
type Registry interface {
ListControllers(ctx genericapirequest.Context, options *api.ListOptions) (*api.ReplicationControllerList, error)
WatchControllers(ctx genericapirequest.Context, options *api.ListOptions) (watch.Interface, error)
GetController(ctx genericapirequest.Context, controllerID string, options *metav1.GetOptions) (*api.ReplicationController, error)
CreateController(ctx genericapirequest.Context, controller *api.ReplicationController) (*api.ReplicationController, error)
UpdateController(ctx genericapirequest.Context, controller *api.ReplicationController) (*api.ReplicationController, error)
DeleteController(ctx genericapirequest.Context, controllerID 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}
}
func (s *storage) ListControllers(ctx genericapirequest.Context, options *api.ListOptions) (*api.ReplicationControllerList, error) {
if options != nil && options.FieldSelector != nil && !options.FieldSelector.Empty() {
return nil, fmt.Errorf("field selector not supported yet")
}
obj, err := s.List(ctx, options)
if err != nil {
return nil, err
}
return obj.(*api.ReplicationControllerList), err
}
func (s *storage) WatchControllers(ctx genericapirequest.Context, options *api.ListOptions) (watch.Interface, error) {
return s.Watch(ctx, options)
}
func (s *storage) GetController(ctx genericapirequest.Context, controllerID string, options *metav1.GetOptions) (*api.ReplicationController, error) {
obj, err := s.Get(ctx, controllerID, options)
if err != nil {
return nil, err
}
return obj.(*api.ReplicationController), nil
}
func (s *storage) CreateController(ctx genericapirequest.Context, controller *api.ReplicationController) (*api.ReplicationController, error) {
obj, err := s.Create(ctx, controller)
if err != nil {
return nil, err
}
return obj.(*api.ReplicationController), nil
}
func (s *storage) UpdateController(ctx genericapirequest.Context, controller *api.ReplicationController) (*api.ReplicationController, error) {
obj, _, err := s.Update(ctx, controller.Name, rest.DefaultUpdatedObjectInfo(controller, api.Scheme))
if err != nil {
return nil, err
}
return obj.(*api.ReplicationController), nil
}
func (s *storage) DeleteController(ctx genericapirequest.Context, controllerID string) error {
_, err := s.Delete(ctx, controllerID, nil)
return err
}

View file

@ -0,0 +1,64 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_test(
name = "go_default_test",
srcs = ["storage_test.go"],
library = ":go_default_library",
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/apis/autoscaling:go_default_library",
"//pkg/fields:go_default_library",
"//pkg/genericapiserver/api/rest:go_default_library",
"//pkg/registry/generic:go_default_library",
"//pkg/registry/registrytest:go_default_library",
"//pkg/storage/etcd/testing:go_default_library",
"//vendor:k8s.io/apimachinery/pkg/api/errors",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
"//vendor:k8s.io/apimachinery/pkg/labels",
"//vendor:k8s.io/apimachinery/pkg/runtime",
"//vendor:k8s.io/apimachinery/pkg/util/diff",
"//vendor:k8s.io/apiserver/pkg/request",
],
)
go_library(
name = "go_default_library",
srcs = ["storage.go"],
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/apis/autoscaling:go_default_library",
"//pkg/apis/autoscaling/validation:go_default_library",
"//pkg/genericapiserver/api/rest:go_default_library",
"//pkg/registry/core/controller:go_default_library",
"//pkg/registry/generic:go_default_library",
"//pkg/registry/generic/registry:go_default_library",
"//vendor:k8s.io/apimachinery/pkg/api/errors",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
"//vendor:k8s.io/apimachinery/pkg/labels",
"//vendor:k8s.io/apimachinery/pkg/runtime",
"//vendor:k8s.io/apiserver/pkg/request",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

View file

@ -0,0 +1,176 @@
/*
Copyright 2014 The Kubernetes Authors.
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.
*/
// If you make changes to this file, you should also make the corresponding change in ReplicaSet.
package storage
import (
"fmt"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
genericapirequest "k8s.io/apiserver/pkg/request"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/autoscaling"
"k8s.io/kubernetes/pkg/apis/autoscaling/validation"
"k8s.io/kubernetes/pkg/genericapiserver/api/rest"
"k8s.io/kubernetes/pkg/registry/core/controller"
"k8s.io/kubernetes/pkg/registry/generic"
genericregistry "k8s.io/kubernetes/pkg/registry/generic/registry"
)
// ControllerStorage includes dummy storage for Replication Controllers and for Scale subresource.
type ControllerStorage struct {
Controller *REST
Status *StatusREST
Scale *ScaleREST
}
func NewStorage(optsGetter generic.RESTOptionsGetter) ControllerStorage {
controllerREST, statusREST := NewREST(optsGetter)
controllerRegistry := controller.NewRegistry(controllerREST)
return ControllerStorage{
Controller: controllerREST,
Status: statusREST,
Scale: &ScaleREST{registry: controllerRegistry},
}
}
type REST struct {
*genericregistry.Store
}
// NewREST returns a RESTStorage object that will work against replication controllers.
func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST) {
store := &genericregistry.Store{
NewFunc: func() runtime.Object { return &api.ReplicationController{} },
NewListFunc: func() runtime.Object { return &api.ReplicationControllerList{} },
ObjectNameFunc: func(obj runtime.Object) (string, error) {
return obj.(*api.ReplicationController).Name, nil
},
PredicateFunc: controller.MatchController,
QualifiedResource: api.Resource("replicationcontrollers"),
CreateStrategy: controller.Strategy,
UpdateStrategy: controller.Strategy,
DeleteStrategy: controller.Strategy,
}
options := &generic.StoreOptions{RESTOptions: optsGetter, AttrFunc: controller.GetAttrs}
if err := store.CompleteWithOptions(options); err != nil {
panic(err) // TODO: Propagate error up
}
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 *genericregistry.Store
}
func (r *StatusREST) New() runtime.Object {
return &api.ReplicationController{}
}
// Get retrieves the object from the storage. It is required to support Patch.
func (r *StatusREST) Get(ctx genericapirequest.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
return r.store.Get(ctx, name, options)
}
// Update alters the status subset of an object.
func (r *StatusREST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) {
return r.store.Update(ctx, name, objInfo)
}
type ScaleREST struct {
registry controller.Registry
}
// ScaleREST implements Patcher
var _ = rest.Patcher(&ScaleREST{})
// New creates a new Scale object
func (r *ScaleREST) New() runtime.Object {
return &autoscaling.Scale{}
}
func (r *ScaleREST) Get(ctx genericapirequest.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
rc, err := r.registry.GetController(ctx, name, options)
if err != nil {
return nil, errors.NewNotFound(autoscaling.Resource("replicationcontrollers/scale"), name)
}
return scaleFromRC(rc), nil
}
func (r *ScaleREST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) {
rc, err := r.registry.GetController(ctx, name, &metav1.GetOptions{})
if err != nil {
return nil, false, errors.NewNotFound(autoscaling.Resource("replicationcontrollers/scale"), name)
}
oldScale := scaleFromRC(rc)
obj, err := objInfo.UpdatedObject(ctx, oldScale)
if err != nil {
return nil, false, err
}
if obj == nil {
return nil, false, errors.NewBadRequest("nil update passed to Scale")
}
scale, ok := obj.(*autoscaling.Scale)
if !ok {
return nil, false, errors.NewBadRequest(fmt.Sprintf("wrong object passed to Scale update: %v", obj))
}
if errs := validation.ValidateScale(scale); len(errs) > 0 {
return nil, false, errors.NewInvalid(autoscaling.Kind("Scale"), scale.Name, errs)
}
rc.Spec.Replicas = scale.Spec.Replicas
rc.ResourceVersion = scale.ResourceVersion
rc, err = r.registry.UpdateController(ctx, rc)
if err != nil {
return nil, false, err
}
return scaleFromRC(rc), false, nil
}
// scaleFromRC returns a scale subresource for a replication controller.
func scaleFromRC(rc *api.ReplicationController) *autoscaling.Scale {
return &autoscaling.Scale{
ObjectMeta: api.ObjectMeta{
Name: rc.Name,
Namespace: rc.Namespace,
UID: rc.UID,
ResourceVersion: rc.ResourceVersion,
CreationTimestamp: rc.CreationTimestamp,
},
Spec: autoscaling.ScaleSpec{
Replicas: rc.Spec.Replicas,
},
Status: autoscaling.ScaleStatus{
Replicas: rc.Status.Replicas,
Selector: labels.SelectorFromSet(rc.Spec.Selector).String(),
},
}
}

View file

@ -0,0 +1,327 @@
/*
Copyright 2014 The Kubernetes Authors.
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 storage
import (
"testing"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/diff"
genericapirequest "k8s.io/apiserver/pkg/request"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/autoscaling"
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/genericapiserver/api/rest"
"k8s.io/kubernetes/pkg/registry/generic"
"k8s.io/kubernetes/pkg/registry/registrytest"
etcdtesting "k8s.io/kubernetes/pkg/storage/etcd/testing"
)
const (
namespace = api.NamespaceDefault
name = "foo"
)
func newStorage(t *testing.T) (ControllerStorage, *etcdtesting.EtcdTestServer) {
etcdStorage, server := registrytest.NewEtcdStorage(t, "")
restOptions := generic.RESTOptions{
StorageConfig: etcdStorage,
Decorator: generic.UndecoratedStorage,
DeleteCollectionWorkers: 1,
ResourcePrefix: "replicationcontrollers",
}
storage := NewStorage(restOptions)
return storage, server
}
// createController is a helper function that returns a controller with the updated resource version.
func createController(storage *REST, rc api.ReplicationController, t *testing.T) (api.ReplicationController, error) {
ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), rc.Namespace)
obj, err := storage.Create(ctx, &rc)
if err != nil {
t.Errorf("Failed to create controller, %v", err)
}
newRc := obj.(*api.ReplicationController)
return *newRc, nil
}
func validNewController() *api.ReplicationController {
return &api.ReplicationController{
ObjectMeta: api.ObjectMeta{
Name: name,
Namespace: namespace,
},
Spec: api.ReplicationControllerSpec{
Selector: map[string]string{"a": "b"},
Template: &api.PodTemplateSpec{
ObjectMeta: api.ObjectMeta{
Labels: map[string]string{"a": "b"},
},
Spec: api.PodSpec{
Containers: []api.Container{
{
Name: "test",
Image: "test_image",
ImagePullPolicy: api.PullIfNotPresent,
},
},
RestartPolicy: api.RestartPolicyAlways,
DNSPolicy: api.DNSClusterFirst,
},
},
},
}
}
var validController = validNewController()
func TestCreate(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Controller.Store.DestroyFunc()
test := registrytest.New(t, storage.Controller.Store)
controller := validNewController()
controller.ObjectMeta = api.ObjectMeta{}
test.TestCreate(
// valid
controller,
// invalid (invalid selector)
&api.ReplicationController{
Spec: api.ReplicationControllerSpec{
Replicas: 2,
Selector: map[string]string{},
Template: validController.Spec.Template,
},
},
)
}
func TestUpdate(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Controller.Store.DestroyFunc()
test := registrytest.New(t, storage.Controller.Store)
test.TestUpdate(
// valid
validNewController(),
// valid updateFunc
func(obj runtime.Object) runtime.Object {
object := obj.(*api.ReplicationController)
object.Spec.Replicas = object.Spec.Replicas + 1
return object
},
// invalid updateFunc
func(obj runtime.Object) runtime.Object {
object := obj.(*api.ReplicationController)
object.Name = ""
return object
},
func(obj runtime.Object) runtime.Object {
object := obj.(*api.ReplicationController)
object.Spec.Selector = map[string]string{}
return object
},
)
}
func TestDelete(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Controller.Store.DestroyFunc()
test := registrytest.New(t, storage.Controller.Store)
test.TestDelete(validNewController())
}
func TestGenerationNumber(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Controller.Store.DestroyFunc()
modifiedSno := *validNewController()
modifiedSno.Generation = 100
modifiedSno.Status.ObservedGeneration = 10
ctx := genericapirequest.NewDefaultContext()
rc, err := createController(storage.Controller, modifiedSno, t)
ctrl, err := storage.Controller.Get(ctx, rc.Name, &metav1.GetOptions{})
if err != nil {
t.Errorf("unexpected error: %v", err)
}
controller, _ := ctrl.(*api.ReplicationController)
// Generation initialization
if controller.Generation != 1 && controller.Status.ObservedGeneration != 0 {
t.Fatalf("Unexpected generation number %v, status generation %v", controller.Generation, controller.Status.ObservedGeneration)
}
// Updates to spec should increment the generation number
controller.Spec.Replicas += 1
storage.Controller.Update(ctx, controller.Name, rest.DefaultUpdatedObjectInfo(controller, api.Scheme))
if err != nil {
t.Errorf("unexpected error: %v", err)
}
ctrl, err = storage.Controller.Get(ctx, rc.Name, &metav1.GetOptions{})
if err != nil {
t.Errorf("unexpected error: %v", err)
}
controller, _ = ctrl.(*api.ReplicationController)
if controller.Generation != 2 || controller.Status.ObservedGeneration != 0 {
t.Fatalf("Unexpected generation, spec: %v, status: %v", controller.Generation, controller.Status.ObservedGeneration)
}
// Updates to status should not increment either spec or status generation numbers
controller.Status.Replicas += 1
storage.Controller.Update(ctx, controller.Name, rest.DefaultUpdatedObjectInfo(controller, api.Scheme))
if err != nil {
t.Errorf("unexpected error: %v", err)
}
ctrl, err = storage.Controller.Get(ctx, rc.Name, &metav1.GetOptions{})
if err != nil {
t.Errorf("unexpected error: %v", err)
}
controller, _ = ctrl.(*api.ReplicationController)
if controller.Generation != 2 || controller.Status.ObservedGeneration != 0 {
t.Fatalf("Unexpected generation number, spec: %v, status: %v", controller.Generation, controller.Status.ObservedGeneration)
}
}
func TestGet(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Controller.Store.DestroyFunc()
test := registrytest.New(t, storage.Controller.Store)
test.TestGet(validNewController())
}
func TestList(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Controller.Store.DestroyFunc()
test := registrytest.New(t, storage.Controller.Store)
test.TestList(validNewController())
}
func TestWatch(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Controller.Store.DestroyFunc()
test := registrytest.New(t, storage.Controller.Store)
test.TestWatch(
validController,
// matching labels
[]labels.Set{
{"a": "b"},
},
// not matching labels
[]labels.Set{
{"a": "c"},
{"foo": "bar"},
},
// matching fields
[]fields.Set{
{"status.replicas": "0"},
{"metadata.name": "foo"},
{"status.replicas": "0", "metadata.name": "foo"},
},
// not matchin fields
[]fields.Set{
{"status.replicas": "10"},
{"metadata.name": "bar"},
{"name": "foo"},
{"status.replicas": "10", "metadata.name": "foo"},
{"status.replicas": "0", "metadata.name": "bar"},
},
)
}
//TODO TestUpdateStatus
func TestScaleGet(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Controller.Store.DestroyFunc()
ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), namespace)
rc, err := createController(storage.Controller, *validController, t)
if err != nil {
t.Fatalf("error setting new replication controller %v: %v", *validController, err)
}
want := &autoscaling.Scale{
ObjectMeta: api.ObjectMeta{
Name: name,
Namespace: namespace,
UID: rc.UID,
ResourceVersion: rc.ResourceVersion,
CreationTimestamp: rc.CreationTimestamp,
},
Spec: autoscaling.ScaleSpec{
Replicas: validController.Spec.Replicas,
},
Status: autoscaling.ScaleStatus{
Replicas: validController.Status.Replicas,
Selector: labels.SelectorFromSet(validController.Spec.Template.Labels).String(),
},
}
obj, err := storage.Scale.Get(ctx, name, &metav1.GetOptions{})
if err != nil {
t.Fatalf("error fetching scale for %s: %v", name, err)
}
got := obj.(*autoscaling.Scale)
if !api.Semantic.DeepEqual(want, got) {
t.Errorf("unexpected scale: %s", diff.ObjectDiff(want, got))
}
}
func TestScaleUpdate(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Controller.Store.DestroyFunc()
ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), namespace)
rc, err := createController(storage.Controller, *validController, t)
if err != nil {
t.Fatalf("error setting new replication controller %v: %v", *validController, err)
}
replicas := int32(12)
update := autoscaling.Scale{
ObjectMeta: api.ObjectMeta{Name: name, Namespace: namespace},
Spec: autoscaling.ScaleSpec{
Replicas: replicas,
},
}
if _, _, err := storage.Scale.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(&update, api.Scheme)); err != nil {
t.Fatalf("error updating scale %v: %v", update, err)
}
obj, err := storage.Scale.Get(ctx, name, &metav1.GetOptions{})
if err != nil {
t.Fatalf("error fetching scale for %s: %v", name, err)
}
scale := obj.(*autoscaling.Scale)
if scale.Spec.Replicas != replicas {
t.Errorf("wrong replicas count expected: %d got: %d", replicas, rc.Spec.Replicas)
}
update.ResourceVersion = rc.ResourceVersion
update.Spec.Replicas = 15
if _, _, err = storage.Scale.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(&update, api.Scheme)); err != nil && !errors.IsConflict(err) {
t.Fatalf("unexpected error, expecting an update conflict but got %v", err)
}
}

View file

@ -0,0 +1,158 @@
/*
Copyright 2014 The Kubernetes Authors.
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.
*/
// If you make changes to this file, you should also make the corresponding change in ReplicaSet.
package controller
import (
"fmt"
"reflect"
"strconv"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/validation/field"
genericapirequest "k8s.io/apiserver/pkg/request"
"k8s.io/apiserver/pkg/storage/names"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/validation"
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/genericapiserver/api/rest"
"k8s.io/kubernetes/pkg/registry/generic"
apistorage "k8s.io/kubernetes/pkg/storage"
)
// rcStrategy implements verification logic for Replication Controllers.
type rcStrategy struct {
runtime.ObjectTyper
names.NameGenerator
}
// Strategy is the default logic that applies when creating and updating Replication Controller objects.
var Strategy = rcStrategy{api.Scheme, names.SimpleNameGenerator}
// DefaultGarbageCollectionPolicy returns Orphan because that was the default
// behavior before the server-side garbage collection was implemented.
func (rcStrategy) DefaultGarbageCollectionPolicy() rest.GarbageCollectionPolicy {
return rest.OrphanDependents
}
// NamespaceScoped returns true because all Replication Controllers need to be within a namespace.
func (rcStrategy) NamespaceScoped() bool {
return true
}
// PrepareForCreate clears the status of a replication controller before creation.
func (rcStrategy) PrepareForCreate(ctx genericapirequest.Context, obj runtime.Object) {
controller := obj.(*api.ReplicationController)
controller.Status = api.ReplicationControllerStatus{}
controller.Generation = 1
}
// PrepareForUpdate clears fields that are not allowed to be set by end users on update.
func (rcStrategy) PrepareForUpdate(ctx genericapirequest.Context, obj, old runtime.Object) {
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
// the burden of managing the status onto the clients because we can't (in general)
// know here what version of spec the writer of the status has seen. It may seem like
// we can at first -- since obj contains spec -- but in the future we will probably make
// status its own object, and even if we don't, writes may be the result of a
// read-update-write loop, so the contents of spec may not actually be the spec that
// the controller has *seen*.
if !reflect.DeepEqual(oldController.Spec, newController.Spec) {
newController.Generation = oldController.Generation + 1
}
}
// Validate validates a new replication controller.
func (rcStrategy) Validate(ctx genericapirequest.Context, obj runtime.Object) field.ErrorList {
controller := obj.(*api.ReplicationController)
return validation.ValidateReplicationController(controller)
}
// Canonicalize normalizes the object after validation.
func (rcStrategy) Canonicalize(obj runtime.Object) {
}
// AllowCreateOnUpdate is false for replication controllers; this means a POST is
// needed to create one.
func (rcStrategy) AllowCreateOnUpdate() bool {
return false
}
// ValidateUpdate is the default update validation for an end user.
func (rcStrategy) ValidateUpdate(ctx genericapirequest.Context, obj, old runtime.Object) field.ErrorList {
validationErrorList := validation.ValidateReplicationController(obj.(*api.ReplicationController))
updateErrorList := validation.ValidateReplicationControllerUpdate(obj.(*api.ReplicationController), old.(*api.ReplicationController))
return append(validationErrorList, updateErrorList...)
}
func (rcStrategy) AllowUnconditionalUpdate() bool {
return true
}
// ControllerToSelectableFields returns a field set that represents the object.
func ControllerToSelectableFields(controller *api.ReplicationController) fields.Set {
objectMetaFieldsSet := generic.ObjectMetaFieldsSet(&controller.ObjectMeta, true)
controllerSpecificFieldsSet := fields.Set{
"status.replicas": strconv.Itoa(int(controller.Status.Replicas)),
}
return generic.MergeFieldsSets(objectMetaFieldsSet, controllerSpecificFieldsSet)
}
// GetAttrs returns labels and fields of a given object for filtering purposes.
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
rc, ok := obj.(*api.ReplicationController)
if !ok {
return nil, nil, fmt.Errorf("Given object is not a replication controller.")
}
return labels.Set(rc.ObjectMeta.Labels), ControllerToSelectableFields(rc), nil
}
// MatchController is the filter used by the generic etcd backend to route
// watch events from etcd to clients of the apiserver only interested in specific
// labels/fields.
func MatchController(label labels.Selector, field fields.Selector) apistorage.SelectionPredicate {
return apistorage.SelectionPredicate{
Label: label,
Field: field,
GetAttrs: GetAttrs,
}
}
type rcStatusStrategy struct {
rcStrategy
}
var StatusStrategy = rcStatusStrategy{Strategy}
func (rcStatusStrategy) PrepareForUpdate(ctx genericapirequest.Context, 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 genericapirequest.Context, obj, old runtime.Object) field.ErrorList {
return validation.ValidateReplicationControllerStatusUpdate(obj.(*api.ReplicationController), old.(*api.ReplicationController))
}

View file

@ -0,0 +1,151 @@
/*
Copyright 2015 The Kubernetes Authors.
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 controller
import (
"testing"
genericapirequest "k8s.io/apiserver/pkg/request"
"k8s.io/kubernetes/pkg/api"
apitesting "k8s.io/kubernetes/pkg/api/testing"
)
func TestControllerStrategy(t *testing.T) {
ctx := genericapirequest.NewDefaultContext()
if !Strategy.NamespaceScoped() {
t.Errorf("ReplicationController must be namespace scoped")
}
if Strategy.AllowCreateOnUpdate() {
t.Errorf("ReplicationController should not allow create on update")
}
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"}},
},
},
}
rc := &api.ReplicationController{
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
Spec: api.ReplicationControllerSpec{
Selector: validSelector,
Template: &validPodTemplate.Template,
},
Status: api.ReplicationControllerStatus{
Replicas: 1,
ObservedGeneration: int64(10),
},
}
Strategy.PrepareForCreate(ctx, rc)
if rc.Status.Replicas != 0 {
t.Error("ReplicationController should not allow setting status.replicas on create")
}
if rc.Status.ObservedGeneration != int64(0) {
t.Error("ReplicationController should not allow setting status.observedGeneration on create")
}
errs := Strategy.Validate(ctx, rc)
if len(errs) != 0 {
t.Errorf("Unexpected error validating %v", errs)
}
invalidRc := &api.ReplicationController{
ObjectMeta: api.ObjectMeta{Name: "bar", ResourceVersion: "4"},
}
Strategy.PrepareForUpdate(ctx, invalidRc, rc)
errs = Strategy.ValidateUpdate(ctx, invalidRc, rc)
if len(errs) == 0 {
t.Errorf("Expected a validation error")
}
if invalidRc.ResourceVersion != "4" {
t.Errorf("Incoming resource version on update should not be mutated")
}
}
func TestControllerStatusStrategy(t *testing.T) {
ctx := genericapirequest.NewDefaultContext()
if !StatusStrategy.NamespaceScoped() {
t.Errorf("ReplicationController must be namespace scoped")
}
if StatusStrategy.AllowCreateOnUpdate() {
t.Errorf("ReplicationController should not allow create on update")
}
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"}},
},
},
}
oldController := &api.ReplicationController{
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault, ResourceVersion: "10"},
Spec: api.ReplicationControllerSpec{
Replicas: 3,
Selector: validSelector,
Template: &validPodTemplate.Template,
},
Status: api.ReplicationControllerStatus{
Replicas: 1,
ObservedGeneration: int64(10),
},
}
newController := &api.ReplicationController{
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault, ResourceVersion: "9"},
Spec: api.ReplicationControllerSpec{
Replicas: 1,
Selector: validSelector,
Template: &validPodTemplate.Template,
},
Status: api.ReplicationControllerStatus{
Replicas: 3,
ObservedGeneration: int64(11),
},
}
StatusStrategy.PrepareForUpdate(ctx, newController, oldController)
if newController.Status.Replicas != 3 {
t.Errorf("Replication controller status updates should allow change of replicas: %v", newController.Status.Replicas)
}
if newController.Spec.Replicas != 3 {
t.Errorf("PrepareForUpdate should have preferred spec")
}
errs := StatusStrategy.ValidateUpdate(ctx, newController, oldController)
if len(errs) != 0 {
t.Errorf("Unexpected error %v", errs)
}
}
func TestSelectableFieldLabelConversions(t *testing.T) {
apitesting.TestSelectableFieldLabelConversionsOfKind(t,
api.Registry.GroupOrDie(api.GroupName).GroupVersion.String(),
"ReplicationController",
ControllerToSelectableFields(&api.ReplicationController{}),
nil,
)
}

View file

@ -0,0 +1,62 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"doc.go",
"registry.go",
"strategy.go",
],
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/api/endpoints:go_default_library",
"//pkg/api/validation:go_default_library",
"//pkg/fields:go_default_library",
"//pkg/genericapiserver/api/rest:go_default_library",
"//pkg/registry/generic:go_default_library",
"//pkg/storage:go_default_library",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
"//vendor:k8s.io/apimachinery/pkg/labels",
"//vendor:k8s.io/apimachinery/pkg/runtime",
"//vendor:k8s.io/apimachinery/pkg/util/validation/field",
"//vendor:k8s.io/apimachinery/pkg/watch",
"//vendor:k8s.io/apiserver/pkg/request",
"//vendor:k8s.io/apiserver/pkg/storage/names",
],
)
go_test(
name = "go_default_test",
srcs = ["strategy_test.go"],
library = ":go_default_library",
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/api/testing:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//pkg/registry/core/endpoint/storage:all-srcs",
],
tags = ["automanaged"],
)

View file

@ -0,0 +1,19 @@
/*
Copyright 2014 The Kubernetes Authors.
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 endpoint provides Registry interface and it's RESTStorage
// implementation for storing Endpoint api objects.
package endpoint // import "k8s.io/kubernetes/pkg/registry/core/endpoint"

View file

@ -0,0 +1,75 @@
/*
Copyright 2014 The Kubernetes Authors.
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 endpoint
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/watch"
genericapirequest "k8s.io/apiserver/pkg/request"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/genericapiserver/api/rest"
)
// Registry is an interface for things that know how to store endpoints.
type Registry interface {
ListEndpoints(ctx genericapirequest.Context, options *api.ListOptions) (*api.EndpointsList, error)
GetEndpoints(ctx genericapirequest.Context, name string, options *metav1.GetOptions) (*api.Endpoints, error)
WatchEndpoints(ctx genericapirequest.Context, options *api.ListOptions) (watch.Interface, error)
UpdateEndpoints(ctx genericapirequest.Context, e *api.Endpoints) error
DeleteEndpoints(ctx genericapirequest.Context, name 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}
}
func (s *storage) ListEndpoints(ctx genericapirequest.Context, options *api.ListOptions) (*api.EndpointsList, error) {
obj, err := s.List(ctx, options)
if err != nil {
return nil, err
}
return obj.(*api.EndpointsList), nil
}
func (s *storage) WatchEndpoints(ctx genericapirequest.Context, options *api.ListOptions) (watch.Interface, error) {
return s.Watch(ctx, options)
}
func (s *storage) GetEndpoints(ctx genericapirequest.Context, name string, options *metav1.GetOptions) (*api.Endpoints, error) {
obj, err := s.Get(ctx, name, options)
if err != nil {
return nil, err
}
return obj.(*api.Endpoints), nil
}
func (s *storage) UpdateEndpoints(ctx genericapirequest.Context, endpoints *api.Endpoints) error {
_, _, err := s.Update(ctx, endpoints.Name, rest.DefaultUpdatedObjectInfo(endpoints, api.Scheme))
return err
}
func (s *storage) DeleteEndpoints(ctx genericapirequest.Context, name string) error {
_, err := s.Delete(ctx, name, nil)
return err
}

View file

@ -0,0 +1,51 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_test(
name = "go_default_test",
srcs = ["storage_test.go"],
library = ":go_default_library",
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/fields:go_default_library",
"//pkg/registry/generic:go_default_library",
"//pkg/registry/registrytest:go_default_library",
"//pkg/storage/etcd/testing:go_default_library",
"//vendor:k8s.io/apimachinery/pkg/labels",
"//vendor:k8s.io/apimachinery/pkg/runtime",
],
)
go_library(
name = "go_default_library",
srcs = ["storage.go"],
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/registry/core/endpoint:go_default_library",
"//pkg/registry/generic:go_default_library",
"//pkg/registry/generic/registry:go_default_library",
"//vendor:k8s.io/apimachinery/pkg/runtime",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

View file

@ -0,0 +1,51 @@
/*
Copyright 2015 The Kubernetes Authors.
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 storage
import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/registry/core/endpoint"
"k8s.io/kubernetes/pkg/registry/generic"
genericregistry "k8s.io/kubernetes/pkg/registry/generic/registry"
)
type REST struct {
*genericregistry.Store
}
// NewREST returns a RESTStorage object that will work against endpoints.
func NewREST(optsGetter generic.RESTOptionsGetter) *REST {
store := &genericregistry.Store{
NewFunc: func() runtime.Object { return &api.Endpoints{} },
NewListFunc: func() runtime.Object { return &api.EndpointsList{} },
ObjectNameFunc: func(obj runtime.Object) (string, error) {
return obj.(*api.Endpoints).Name, nil
},
PredicateFunc: endpoint.MatchEndpoints,
QualifiedResource: api.Resource("endpoints"),
CreateStrategy: endpoint.Strategy,
UpdateStrategy: endpoint.Strategy,
DeleteStrategy: endpoint.Strategy,
}
options := &generic.StoreOptions{RESTOptions: optsGetter, AttrFunc: endpoint.GetAttrs}
if err := store.CompleteWithOptions(options); err != nil {
panic(err) // TODO: Propagate error up
}
return &REST{store}
}

View file

@ -0,0 +1,149 @@
/*
Copyright 2015 The Kubernetes Authors.
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 storage
import (
"testing"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/registry/generic"
"k8s.io/kubernetes/pkg/registry/registrytest"
etcdtesting "k8s.io/kubernetes/pkg/storage/etcd/testing"
)
func newStorage(t *testing.T) (*REST, *etcdtesting.EtcdTestServer) {
etcdStorage, server := registrytest.NewEtcdStorage(t, "")
restOptions := generic.RESTOptions{
StorageConfig: etcdStorage,
Decorator: generic.UndecoratedStorage,
DeleteCollectionWorkers: 1,
ResourcePrefix: "endpoints",
}
return NewREST(restOptions), server
}
func validNewEndpoints() *api.Endpoints {
return &api.Endpoints{
ObjectMeta: api.ObjectMeta{
Name: "foo",
Namespace: api.NamespaceDefault,
},
Subsets: []api.EndpointSubset{{
Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}},
Ports: []api.EndpointPort{{Port: 80, Protocol: "TCP"}},
}},
}
}
func validChangedEndpoints() *api.Endpoints {
endpoints := validNewEndpoints()
endpoints.ResourceVersion = "1"
endpoints.Subsets = []api.EndpointSubset{{
Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}, {IP: "5.6.7.8"}},
Ports: []api.EndpointPort{{Port: 80, Protocol: "TCP"}},
}}
return endpoints
}
func TestCreate(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := registrytest.New(t, storage.Store)
endpoints := validNewEndpoints()
endpoints.ObjectMeta = api.ObjectMeta{}
test.TestCreate(
// valid
endpoints,
// invalid
&api.Endpoints{
ObjectMeta: api.ObjectMeta{Name: "_-a123-a_"},
},
)
}
func TestUpdate(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := registrytest.New(t, storage.Store).AllowCreateOnUpdate()
test.TestUpdate(
// valid
validNewEndpoints(),
// updateFunc
func(obj runtime.Object) runtime.Object {
object := obj.(*api.Endpoints)
object.Subsets = []api.EndpointSubset{{
Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}, {IP: "5.6.7.8"}},
Ports: []api.EndpointPort{{Port: 80, Protocol: "TCP"}},
}}
return object
},
)
}
func TestDelete(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := registrytest.New(t, storage.Store)
test.TestDelete(validNewEndpoints())
}
func TestGet(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := registrytest.New(t, storage.Store)
test.TestGet(validNewEndpoints())
}
func TestList(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := registrytest.New(t, storage.Store)
test.TestList(validNewEndpoints())
}
func TestWatch(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := registrytest.New(t, storage.Store)
test.TestWatch(
validNewEndpoints(),
// matching labels
[]labels.Set{},
// not matching labels
[]labels.Set{
{"foo": "bar"},
},
// matching fields
[]fields.Set{
{"metadata.name": "foo"},
},
// not matching fields
[]fields.Set{
{"metadata.name": "bar"},
{"name": "foo"},
},
)
}

View file

@ -0,0 +1,106 @@
/*
Copyright 2014 The Kubernetes Authors.
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 endpoint
import (
"fmt"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/validation/field"
genericapirequest "k8s.io/apiserver/pkg/request"
"k8s.io/apiserver/pkg/storage/names"
"k8s.io/kubernetes/pkg/api"
endptspkg "k8s.io/kubernetes/pkg/api/endpoints"
"k8s.io/kubernetes/pkg/api/validation"
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/registry/generic"
pkgstorage "k8s.io/kubernetes/pkg/storage"
)
// endpointsStrategy implements behavior for Endpoints
type endpointsStrategy struct {
runtime.ObjectTyper
names.NameGenerator
}
// Strategy is the default logic that applies when creating and updating Endpoint
// objects via the REST API.
var Strategy = endpointsStrategy{api.Scheme, names.SimpleNameGenerator}
// NamespaceScoped is true for endpoints.
func (endpointsStrategy) NamespaceScoped() bool {
return true
}
// PrepareForCreate clears fields that are not allowed to be set by end users on creation.
func (endpointsStrategy) PrepareForCreate(ctx genericapirequest.Context, obj runtime.Object) {
}
// PrepareForUpdate clears fields that are not allowed to be set by end users on update.
func (endpointsStrategy) PrepareForUpdate(ctx genericapirequest.Context, obj, old runtime.Object) {
}
// Validate validates a new endpoints.
func (endpointsStrategy) Validate(ctx genericapirequest.Context, obj runtime.Object) field.ErrorList {
return validation.ValidateEndpoints(obj.(*api.Endpoints))
}
// Canonicalize normalizes the object after validation.
func (endpointsStrategy) Canonicalize(obj runtime.Object) {
endpoints := obj.(*api.Endpoints)
endpoints.Subsets = endptspkg.RepackSubsets(endpoints.Subsets)
}
// AllowCreateOnUpdate is true for endpoints.
func (endpointsStrategy) AllowCreateOnUpdate() bool {
return true
}
// ValidateUpdate is the default update validation for an end user.
func (endpointsStrategy) ValidateUpdate(ctx genericapirequest.Context, obj, old runtime.Object) field.ErrorList {
errorList := validation.ValidateEndpoints(obj.(*api.Endpoints))
return append(errorList, validation.ValidateEndpointsUpdate(obj.(*api.Endpoints), old.(*api.Endpoints))...)
}
func (endpointsStrategy) AllowUnconditionalUpdate() bool {
return true
}
// GetAttrs returns labels and fields of a given object for filtering purposes.
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
endpoints, ok := obj.(*api.Endpoints)
if !ok {
return nil, nil, fmt.Errorf("invalid object type %#v", obj)
}
return endpoints.Labels, EndpointsToSelectableFields(endpoints), nil
}
// MatchEndpoints returns a generic matcher for a given label and field selector.
func MatchEndpoints(label labels.Selector, field fields.Selector) pkgstorage.SelectionPredicate {
return pkgstorage.SelectionPredicate{
Label: label,
Field: field,
GetAttrs: GetAttrs,
}
}
// EndpointsToSelectableFields returns a field set that represents the object
// TODO: fields are not labels, and the validation rules for them do not apply.
func EndpointsToSelectableFields(endpoints *api.Endpoints) fields.Set {
return generic.ObjectMetaFieldsSet(&endpoints.ObjectMeta, true)
}

View file

@ -0,0 +1,33 @@
/*
Copyright 2014 The Kubernetes Authors.
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 endpoint
import (
"testing"
"k8s.io/kubernetes/pkg/api"
apitesting "k8s.io/kubernetes/pkg/api/testing"
)
func TestSelectableFieldLabelConversions(t *testing.T) {
apitesting.TestSelectableFieldLabelConversionsOfKind(t,
api.Registry.GroupOrDie(api.GroupName).GroupVersion.String(),
"Endpoints",
EndpointsToSelectableFields(&api.Endpoints{}),
nil,
)
}

59
vendor/k8s.io/kubernetes/pkg/registry/core/event/BUILD generated vendored Normal file
View file

@ -0,0 +1,59 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"doc.go",
"strategy.go",
],
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/api/validation:go_default_library",
"//pkg/fields:go_default_library",
"//pkg/registry/generic:go_default_library",
"//pkg/storage:go_default_library",
"//vendor:k8s.io/apimachinery/pkg/labels",
"//vendor:k8s.io/apimachinery/pkg/runtime",
"//vendor:k8s.io/apimachinery/pkg/util/validation/field",
"//vendor:k8s.io/apiserver/pkg/request",
"//vendor:k8s.io/apiserver/pkg/storage/names",
],
)
go_test(
name = "go_default_test",
srcs = ["strategy_test.go"],
library = ":go_default_library",
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/api/testing:go_default_library",
"//pkg/fields:go_default_library",
"//vendor:k8s.io/apimachinery/pkg/util/diff",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//pkg/registry/core/event/storage:all-srcs",
],
tags = ["automanaged"],
)

Some files were not shown because too many files have changed in this diff Show more