vendor files

This commit is contained in:
Serguei Bezverkhi
2018-01-09 13:57:14 -05:00
parent 558bc6c02a
commit 7b24313bd6
16547 changed files with 4527373 additions and 0 deletions

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

@ -0,0 +1,90 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["doc.go"],
importpath = "k8s.io/kubernetes/pkg/registry",
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//pkg/registry/admissionregistration/initializerconfiguration:all-srcs",
"//pkg/registry/admissionregistration/mutatingwebhookconfiguration:all-srcs",
"//pkg/registry/admissionregistration/rest:all-srcs",
"//pkg/registry/admissionregistration/validatingwebhookconfiguration:all-srcs",
"//pkg/registry/apps/controllerrevision:all-srcs",
"//pkg/registry/apps/rest:all-srcs",
"//pkg/registry/apps/statefulset: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/selfsubjectrulesreview: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/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/replicationcontroller: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/events/event:all-srcs",
"//pkg/registry/events/rest: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/podsecuritypolicy:all-srcs",
"//pkg/registry/extensions/replicaset:all-srcs",
"//pkg/registry/extensions/rest:all-srcs",
"//pkg/registry/networking/networkpolicy:all-srcs",
"//pkg/registry/networking/rest: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/scheduling/priorityclass:all-srcs",
"//pkg/registry/scheduling/rest:all-srcs",
"//pkg/registry/settings/podpreset:all-srcs",
"//pkg/registry/settings/rest:all-srcs",
"//pkg/registry/storage/rest:all-srcs",
"//pkg/registry/storage/storageclass:all-srcs",
"//pkg/registry/storage/volumeattachment:all-srcs",
],
tags = ["automanaged"],
)

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

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

View File

@ -0,0 +1,40 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"doc.go",
"strategy.go",
],
importpath = "k8s.io/kubernetes/pkg/registry/admissionregistration/initializerconfiguration",
deps = [
"//pkg/api/legacyscheme:go_default_library",
"//pkg/apis/admissionregistration:go_default_library",
"//pkg/apis/admissionregistration/validation:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
"//vendor/k8s.io/apiserver/pkg/storage/names:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//pkg/registry/admissionregistration/initializerconfiguration/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 initializerconfiguration // import "k8s.io/kubernetes/pkg/registry/admissionregistration/initializerconfiguration"

View File

@ -0,0 +1,32 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["storage.go"],
importpath = "k8s.io/kubernetes/pkg/registry/admissionregistration/initializerconfiguration/storage",
deps = [
"//pkg/apis/admissionregistration:go_default_library",
"//pkg/registry/admissionregistration/initializerconfiguration:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/generic/registry: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,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/apiserver/pkg/registry/generic"
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
"k8s.io/kubernetes/pkg/apis/admissionregistration"
"k8s.io/kubernetes/pkg/registry/admissionregistration/initializerconfiguration"
)
// rest implements a RESTStorage for pod disruption budgets against etcd
type REST struct {
*genericregistry.Store
}
// NewREST returns a RESTStorage object that will work against pod disruption budgets.
func NewREST(optsGetter generic.RESTOptionsGetter) *REST {
store := &genericregistry.Store{
NewFunc: func() runtime.Object { return &admissionregistration.InitializerConfiguration{} },
NewListFunc: func() runtime.Object { return &admissionregistration.InitializerConfigurationList{} },
ObjectNameFunc: func(obj runtime.Object) (string, error) {
return obj.(*admissionregistration.InitializerConfiguration).Name, nil
},
DefaultQualifiedResource: admissionregistration.Resource("initializerconfigurations"),
CreateStrategy: initializerconfiguration.Strategy,
UpdateStrategy: initializerconfiguration.Strategy,
DeleteStrategy: initializerconfiguration.Strategy,
}
options := &generic.StoreOptions{RESTOptions: optsGetter}
if err := store.CompleteWithOptions(options); err != nil {
panic(err) // TODO: Propagate error up
}
return &REST{store}
}

View File

@ -0,0 +1,90 @@
/*
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 initializerconfiguration
import (
"reflect"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/validation/field"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/storage/names"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/apis/admissionregistration"
"k8s.io/kubernetes/pkg/apis/admissionregistration/validation"
)
// initializerConfigurationStrategy implements verification logic for InitializerConfigurations.
type initializerConfigurationStrategy struct {
runtime.ObjectTyper
names.NameGenerator
}
// Strategy is the default logic that applies when creating and updating InitializerConfiguration objects.
var Strategy = initializerConfigurationStrategy{legacyscheme.Scheme, names.SimpleNameGenerator}
// NamespaceScoped returns true because all InitializerConfiguration' need to be within a namespace.
func (initializerConfigurationStrategy) NamespaceScoped() bool {
return false
}
// PrepareForCreate clears the status of an InitializerConfiguration before creation.
func (initializerConfigurationStrategy) PrepareForCreate(ctx genericapirequest.Context, obj runtime.Object) {
ic := obj.(*admissionregistration.InitializerConfiguration)
ic.Generation = 1
}
// PrepareForUpdate clears fields that are not allowed to be set by end users on update.
func (initializerConfigurationStrategy) PrepareForUpdate(ctx genericapirequest.Context, obj, old runtime.Object) {
newIC := obj.(*admissionregistration.InitializerConfiguration)
oldIC := old.(*admissionregistration.InitializerConfiguration)
// Any changes to the spec increment the generation number, any changes to the
// status should reflect the generation number of the corresponding object.
// See metav1.ObjectMeta description for more information on Generation.
if !reflect.DeepEqual(oldIC.Initializers, newIC.Initializers) {
newIC.Generation = oldIC.Generation + 1
}
}
// Validate validates a new InitializerConfiguration.
func (initializerConfigurationStrategy) Validate(ctx genericapirequest.Context, obj runtime.Object) field.ErrorList {
ic := obj.(*admissionregistration.InitializerConfiguration)
return validation.ValidateInitializerConfiguration(ic)
}
// Canonicalize normalizes the object after validation.
func (initializerConfigurationStrategy) Canonicalize(obj runtime.Object) {
}
// AllowCreateOnUpdate is true for InitializerConfiguration; this means you may create one with a PUT request.
func (initializerConfigurationStrategy) AllowCreateOnUpdate() bool {
return false
}
// ValidateUpdate is the default update validation for an end user.
func (initializerConfigurationStrategy) ValidateUpdate(ctx genericapirequest.Context, obj, old runtime.Object) field.ErrorList {
validationErrorList := validation.ValidateInitializerConfiguration(obj.(*admissionregistration.InitializerConfiguration))
updateErrorList := validation.ValidateInitializerConfigurationUpdate(obj.(*admissionregistration.InitializerConfiguration), old.(*admissionregistration.InitializerConfiguration))
return append(validationErrorList, updateErrorList...)
}
// AllowUnconditionalUpdate is the default update policy for InitializerConfiguration objects. Status update should
// only be allowed if version match.
func (initializerConfigurationStrategy) AllowUnconditionalUpdate() bool {
return false
}

View File

@ -0,0 +1,40 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"doc.go",
"strategy.go",
],
importpath = "k8s.io/kubernetes/pkg/registry/admissionregistration/mutatingwebhookconfiguration",
deps = [
"//pkg/api/legacyscheme:go_default_library",
"//pkg/apis/admissionregistration:go_default_library",
"//pkg/apis/admissionregistration/validation:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
"//vendor/k8s.io/apiserver/pkg/storage/names:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//pkg/registry/admissionregistration/mutatingwebhookconfiguration/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 mutatingwebhookconfiguration // import "k8s.io/kubernetes/pkg/registry/admissionregistration/mutatingwebhookconfiguration"

View File

@ -0,0 +1,32 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["storage.go"],
importpath = "k8s.io/kubernetes/pkg/registry/admissionregistration/mutatingwebhookconfiguration/storage",
deps = [
"//pkg/apis/admissionregistration:go_default_library",
"//pkg/registry/admissionregistration/mutatingwebhookconfiguration:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/generic/registry: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,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/apiserver/pkg/registry/generic"
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
"k8s.io/kubernetes/pkg/apis/admissionregistration"
"k8s.io/kubernetes/pkg/registry/admissionregistration/mutatingwebhookconfiguration"
)
// rest implements a RESTStorage for pod disruption budgets against etcd
type REST struct {
*genericregistry.Store
}
// NewREST returns a RESTStorage object that will work against pod disruption budgets.
func NewREST(optsGetter generic.RESTOptionsGetter) *REST {
store := &genericregistry.Store{
NewFunc: func() runtime.Object { return &admissionregistration.MutatingWebhookConfiguration{} },
NewListFunc: func() runtime.Object { return &admissionregistration.MutatingWebhookConfigurationList{} },
ObjectNameFunc: func(obj runtime.Object) (string, error) {
return obj.(*admissionregistration.MutatingWebhookConfiguration).Name, nil
},
DefaultQualifiedResource: admissionregistration.Resource("mutatingwebhookconfigurations"),
CreateStrategy: mutatingwebhookconfiguration.Strategy,
UpdateStrategy: mutatingwebhookconfiguration.Strategy,
DeleteStrategy: mutatingwebhookconfiguration.Strategy,
}
options := &generic.StoreOptions{RESTOptions: optsGetter}
if err := store.CompleteWithOptions(options); err != nil {
panic(err) // TODO: Propagate error up
}
return &REST{store}
}

View File

@ -0,0 +1,90 @@
/*
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 mutatingwebhookconfiguration
import (
"reflect"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/validation/field"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/storage/names"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/apis/admissionregistration"
"k8s.io/kubernetes/pkg/apis/admissionregistration/validation"
)
// mutatingWebhookConfigurationStrategy implements verification logic for mutatingWebhookConfiguration.
type mutatingWebhookConfigurationStrategy struct {
runtime.ObjectTyper
names.NameGenerator
}
// Strategy is the default logic that applies when creating and updating mutatingWebhookConfiguration objects.
var Strategy = mutatingWebhookConfigurationStrategy{legacyscheme.Scheme, names.SimpleNameGenerator}
// NamespaceScoped returns true because all mutatingWebhookConfiguration' need to be within a namespace.
func (mutatingWebhookConfigurationStrategy) NamespaceScoped() bool {
return false
}
// PrepareForCreate clears the status of an mutatingWebhookConfiguration before creation.
func (mutatingWebhookConfigurationStrategy) PrepareForCreate(ctx genericapirequest.Context, obj runtime.Object) {
ic := obj.(*admissionregistration.MutatingWebhookConfiguration)
ic.Generation = 1
}
// PrepareForUpdate clears fields that are not allowed to be set by end users on update.
func (mutatingWebhookConfigurationStrategy) PrepareForUpdate(ctx genericapirequest.Context, obj, old runtime.Object) {
newIC := obj.(*admissionregistration.MutatingWebhookConfiguration)
oldIC := old.(*admissionregistration.MutatingWebhookConfiguration)
// Any changes to the spec increment the generation number, any changes to the
// status should reflect the generation number of the corresponding object.
// See metav1.ObjectMeta description for more information on Generation.
if !reflect.DeepEqual(oldIC.Webhooks, newIC.Webhooks) {
newIC.Generation = oldIC.Generation + 1
}
}
// Validate validates a new mutatingWebhookConfiguration.
func (mutatingWebhookConfigurationStrategy) Validate(ctx genericapirequest.Context, obj runtime.Object) field.ErrorList {
ic := obj.(*admissionregistration.MutatingWebhookConfiguration)
return validation.ValidateMutatingWebhookConfiguration(ic)
}
// Canonicalize normalizes the object after validation.
func (mutatingWebhookConfigurationStrategy) Canonicalize(obj runtime.Object) {
}
// AllowCreateOnUpdate is true for mutatingWebhookConfiguration; this means you may create one with a PUT request.
func (mutatingWebhookConfigurationStrategy) AllowCreateOnUpdate() bool {
return false
}
// ValidateUpdate is the default update validation for an end user.
func (mutatingWebhookConfigurationStrategy) ValidateUpdate(ctx genericapirequest.Context, obj, old runtime.Object) field.ErrorList {
validationErrorList := validation.ValidateMutatingWebhookConfiguration(obj.(*admissionregistration.MutatingWebhookConfiguration))
updateErrorList := validation.ValidateMutatingWebhookConfigurationUpdate(obj.(*admissionregistration.MutatingWebhookConfiguration), old.(*admissionregistration.MutatingWebhookConfiguration))
return append(validationErrorList, updateErrorList...)
}
// AllowUnconditionalUpdate is the default update policy for mutatingWebhookConfiguration objects. Status update should
// only be allowed if version match.
func (mutatingWebhookConfigurationStrategy) AllowUnconditionalUpdate() bool {
return false
}

View File

@ -0,0 +1,38 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["storage_apiserver.go"],
importpath = "k8s.io/kubernetes/pkg/registry/admissionregistration/rest",
deps = [
"//pkg/api/legacyscheme:go_default_library",
"//pkg/apis/admissionregistration:go_default_library",
"//pkg/registry/admissionregistration/initializerconfiguration/storage:go_default_library",
"//pkg/registry/admissionregistration/mutatingwebhookconfiguration/storage:go_default_library",
"//pkg/registry/admissionregistration/validatingwebhookconfiguration/storage:go_default_library",
"//vendor/k8s.io/api/admissionregistration/v1alpha1:go_default_library",
"//vendor/k8s.io/api/admissionregistration/v1beta1:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library",
"//vendor/k8s.io/apiserver/pkg/server:go_default_library",
"//vendor/k8s.io/apiserver/pkg/server/storage: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,77 @@
/*
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 (
admissionregistrationv1alpha1 "k8s.io/api/admissionregistration/v1alpha1"
admissionregistrationv1beta1 "k8s.io/api/admissionregistration/v1beta1"
"k8s.io/apiserver/pkg/registry/generic"
"k8s.io/apiserver/pkg/registry/rest"
genericapiserver "k8s.io/apiserver/pkg/server"
serverstorage "k8s.io/apiserver/pkg/server/storage"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/apis/admissionregistration"
initializerconfigurationstorage "k8s.io/kubernetes/pkg/registry/admissionregistration/initializerconfiguration/storage"
mutatingwebhookconfigurationstorage "k8s.io/kubernetes/pkg/registry/admissionregistration/mutatingwebhookconfiguration/storage"
validatingwebhookconfigurationstorage "k8s.io/kubernetes/pkg/registry/admissionregistration/validatingwebhookconfiguration/storage"
)
type RESTStorageProvider struct{}
func (p RESTStorageProvider) NewRESTStorage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) (genericapiserver.APIGroupInfo, bool) {
apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(admissionregistration.GroupName, legacyscheme.Registry, legacyscheme.Scheme, legacyscheme.ParameterCodec, legacyscheme.Codecs)
// If you add a version here, be sure to add an entry in `k8s.io/kubernetes/cmd/kube-apiserver/app/aggregator.go with specific priorities.
// TODO refactor the plumbing to provide the information in the APIGroupInfo
if apiResourceConfigSource.AnyResourcesForVersionEnabled(admissionregistrationv1alpha1.SchemeGroupVersion) {
apiGroupInfo.VersionedResourcesStorageMap[admissionregistrationv1alpha1.SchemeGroupVersion.Version] = p.v1alpha1Storage(apiResourceConfigSource, restOptionsGetter)
apiGroupInfo.GroupMeta.GroupVersion = admissionregistrationv1alpha1.SchemeGroupVersion
}
if apiResourceConfigSource.AnyResourcesForVersionEnabled(admissionregistrationv1beta1.SchemeGroupVersion) {
apiGroupInfo.VersionedResourcesStorageMap[admissionregistrationv1beta1.SchemeGroupVersion.Version] = p.v1beta1Storage(apiResourceConfigSource, restOptionsGetter)
apiGroupInfo.GroupMeta.GroupVersion = admissionregistrationv1beta1.SchemeGroupVersion
}
return apiGroupInfo, true
}
func (p RESTStorageProvider) v1alpha1Storage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) map[string]rest.Storage {
version := admissionregistrationv1alpha1.SchemeGroupVersion
storage := map[string]rest.Storage{}
if apiResourceConfigSource.ResourceEnabled(version.WithResource("initializerconfigurations")) {
s := initializerconfigurationstorage.NewREST(restOptionsGetter)
storage["initializerconfigurations"] = s
}
return storage
}
func (p RESTStorageProvider) v1beta1Storage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) map[string]rest.Storage {
version := admissionregistrationv1beta1.SchemeGroupVersion
storage := map[string]rest.Storage{}
if apiResourceConfigSource.ResourceEnabled(version.WithResource("validatingwebhookconfigurations")) {
s := validatingwebhookconfigurationstorage.NewREST(restOptionsGetter)
storage["validatingwebhookconfigurations"] = s
}
if apiResourceConfigSource.ResourceEnabled(version.WithResource("mutatingwebhookconfigurations")) {
s := mutatingwebhookconfigurationstorage.NewREST(restOptionsGetter)
storage["mutatingwebhookconfigurations"] = s
}
return storage
}
func (p RESTStorageProvider) GroupName() string {
return admissionregistration.GroupName
}

View File

@ -0,0 +1,40 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"doc.go",
"strategy.go",
],
importpath = "k8s.io/kubernetes/pkg/registry/admissionregistration/validatingwebhookconfiguration",
deps = [
"//pkg/api/legacyscheme:go_default_library",
"//pkg/apis/admissionregistration:go_default_library",
"//pkg/apis/admissionregistration/validation:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
"//vendor/k8s.io/apiserver/pkg/storage/names:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//pkg/registry/admissionregistration/validatingwebhookconfiguration/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 validatingwebhookconfiguration // import "k8s.io/kubernetes/pkg/registry/admissionregistration/validatingwebhookconfiguration"

View File

@ -0,0 +1,32 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["storage.go"],
importpath = "k8s.io/kubernetes/pkg/registry/admissionregistration/validatingwebhookconfiguration/storage",
deps = [
"//pkg/apis/admissionregistration:go_default_library",
"//pkg/registry/admissionregistration/validatingwebhookconfiguration:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/generic/registry: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,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/apiserver/pkg/registry/generic"
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
"k8s.io/kubernetes/pkg/apis/admissionregistration"
"k8s.io/kubernetes/pkg/registry/admissionregistration/validatingwebhookconfiguration"
)
// rest implements a RESTStorage for pod disruption budgets against etcd
type REST struct {
*genericregistry.Store
}
// NewREST returns a RESTStorage object that will work against pod disruption budgets.
func NewREST(optsGetter generic.RESTOptionsGetter) *REST {
store := &genericregistry.Store{
NewFunc: func() runtime.Object { return &admissionregistration.ValidatingWebhookConfiguration{} },
NewListFunc: func() runtime.Object { return &admissionregistration.ValidatingWebhookConfigurationList{} },
ObjectNameFunc: func(obj runtime.Object) (string, error) {
return obj.(*admissionregistration.ValidatingWebhookConfiguration).Name, nil
},
DefaultQualifiedResource: admissionregistration.Resource("validatingwebhookconfigurations"),
CreateStrategy: validatingwebhookconfiguration.Strategy,
UpdateStrategy: validatingwebhookconfiguration.Strategy,
DeleteStrategy: validatingwebhookconfiguration.Strategy,
}
options := &generic.StoreOptions{RESTOptions: optsGetter}
if err := store.CompleteWithOptions(options); err != nil {
panic(err) // TODO: Propagate error up
}
return &REST{store}
}

View File

@ -0,0 +1,90 @@
/*
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 validatingwebhookconfiguration
import (
"reflect"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/validation/field"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/storage/names"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/apis/admissionregistration"
"k8s.io/kubernetes/pkg/apis/admissionregistration/validation"
)
// validatingWebhookConfigurationStrategy implements verification logic for validatingWebhookConfiguration.
type validatingWebhookConfigurationStrategy struct {
runtime.ObjectTyper
names.NameGenerator
}
// Strategy is the default logic that applies when creating and updating validatingWebhookConfiguration objects.
var Strategy = validatingWebhookConfigurationStrategy{legacyscheme.Scheme, names.SimpleNameGenerator}
// NamespaceScoped returns true because all validatingWebhookConfiguration' need to be within a namespace.
func (validatingWebhookConfigurationStrategy) NamespaceScoped() bool {
return false
}
// PrepareForCreate clears the status of an validatingWebhookConfiguration before creation.
func (validatingWebhookConfigurationStrategy) PrepareForCreate(ctx genericapirequest.Context, obj runtime.Object) {
ic := obj.(*admissionregistration.ValidatingWebhookConfiguration)
ic.Generation = 1
}
// PrepareForUpdate clears fields that are not allowed to be set by end users on update.
func (validatingWebhookConfigurationStrategy) PrepareForUpdate(ctx genericapirequest.Context, obj, old runtime.Object) {
newIC := obj.(*admissionregistration.ValidatingWebhookConfiguration)
oldIC := old.(*admissionregistration.ValidatingWebhookConfiguration)
// Any changes to the spec increment the generation number, any changes to the
// status should reflect the generation number of the corresponding object.
// See metav1.ObjectMeta description for more information on Generation.
if !reflect.DeepEqual(oldIC.Webhooks, newIC.Webhooks) {
newIC.Generation = oldIC.Generation + 1
}
}
// Validate validates a new validatingWebhookConfiguration.
func (validatingWebhookConfigurationStrategy) Validate(ctx genericapirequest.Context, obj runtime.Object) field.ErrorList {
ic := obj.(*admissionregistration.ValidatingWebhookConfiguration)
return validation.ValidateValidatingWebhookConfiguration(ic)
}
// Canonicalize normalizes the object after validation.
func (validatingWebhookConfigurationStrategy) Canonicalize(obj runtime.Object) {
}
// AllowCreateOnUpdate is true for validatingWebhookConfiguration; this means you may create one with a PUT request.
func (validatingWebhookConfigurationStrategy) AllowCreateOnUpdate() bool {
return false
}
// ValidateUpdate is the default update validation for an end user.
func (validatingWebhookConfigurationStrategy) ValidateUpdate(ctx genericapirequest.Context, obj, old runtime.Object) field.ErrorList {
validationErrorList := validation.ValidateValidatingWebhookConfiguration(obj.(*admissionregistration.ValidatingWebhookConfiguration))
updateErrorList := validation.ValidateValidatingWebhookConfigurationUpdate(obj.(*admissionregistration.ValidatingWebhookConfiguration), old.(*admissionregistration.ValidatingWebhookConfiguration))
return append(validationErrorList, updateErrorList...)
}
// AllowUnconditionalUpdate is the default update policy for validatingWebhookConfiguration objects. Status update should
// only be allowed if version match.
func (validatingWebhookConfigurationStrategy) AllowUnconditionalUpdate() bool {
return false
}

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,56 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_test(
name = "go_default_test",
srcs = ["strategy_test.go"],
importpath = "k8s.io/kubernetes/pkg/registry/apps/controllerrevision",
library = ":go_default_library",
deps = [
"//pkg/apis/apps:go_default_library",
"//pkg/apis/core:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"doc.go",
"strategy.go",
],
importpath = "k8s.io/kubernetes/pkg/registry/apps/controllerrevision",
deps = [
"//pkg/api/legacyscheme:go_default_library",
"//pkg/apis/apps:go_default_library",
"//pkg/apis/apps/validation:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library",
"//vendor/k8s.io/apiserver/pkg/storage/names:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//pkg/registry/apps/controllerrevision/storage:all-srcs",
],
tags = ["automanaged"],
)

View File

@ -0,0 +1,17 @@
/*
Copyright 2017 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 controllerrevision // import "k8s.io/kubernetes/pkg/registry/apps/controllerrevision"

View File

@ -0,0 +1,52 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_test(
name = "go_default_test",
srcs = ["storage_test.go"],
importpath = "k8s.io/kubernetes/pkg/registry/apps/controllerrevision/storage",
library = ":go_default_library",
deps = [
"//pkg/apis/apps:go_default_library",
"//pkg/apis/core:go_default_library",
"//pkg/registry/registrytest:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/fields:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/labels:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/generic/testing:go_default_library",
"//vendor/k8s.io/apiserver/pkg/storage/etcd/testing:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = ["storage.go"],
importpath = "k8s.io/kubernetes/pkg/registry/apps/controllerrevision/storage",
deps = [
"//pkg/apis/apps:go_default_library",
"//pkg/registry/apps/controllerrevision:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/generic/registry: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,48 @@
/*
Copyright 2017 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/apiserver/pkg/registry/generic"
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
"k8s.io/kubernetes/pkg/apis/apps"
"k8s.io/kubernetes/pkg/registry/apps/controllerrevision"
)
// REST implements a RESTStorage for ControllerRevision
type REST struct {
*genericregistry.Store
}
// NewREST returns a RESTStorage object that will work with ControllerRevision objects.
func NewREST(optsGetter generic.RESTOptionsGetter) *REST {
store := &genericregistry.Store{
NewFunc: func() runtime.Object { return &apps.ControllerRevision{} },
NewListFunc: func() runtime.Object { return &apps.ControllerRevisionList{} },
DefaultQualifiedResource: apps.Resource("controllerrevisions"),
CreateStrategy: controllerrevision.Strategy,
UpdateStrategy: controllerrevision.Strategy,
DeleteStrategy: controllerrevision.Strategy,
}
options := &generic.StoreOptions{RESTOptions: optsGetter}
if err := store.CompleteWithOptions(options); err != nil {
panic(err)
}
return &REST{store}
}

View File

@ -0,0 +1,183 @@
/*
Copyright 2017 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/fields"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/registry/generic"
genericregistrytest "k8s.io/apiserver/pkg/registry/generic/testing"
etcdtesting "k8s.io/apiserver/pkg/storage/etcd/testing"
"k8s.io/kubernetes/pkg/apis/apps"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/registry/registrytest"
)
func TestCreate(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := genericregistrytest.New(t, storage.Store)
var (
valid = stripObjectMeta(newControllerRevision("validname", metav1.NamespaceDefault, newObject(), 0))
badRevision = stripObjectMeta(newControllerRevision("validname", "validns", newObject(), -1))
emptyName = stripObjectMeta(newControllerRevision("", "validns", newObject(), 0))
invalidName = stripObjectMeta(newControllerRevision("NoUppercaseOrSpecialCharsLike=Equals", "validns", newObject(), 0))
emptyNs = stripObjectMeta(newControllerRevision("validname", "", newObject(), 100))
invalidNs = stripObjectMeta(newControllerRevision("validname", "NoUppercaseOrSpecialCharsLike=Equals", newObject(), 100))
nilData = stripObjectMeta(newControllerRevision("validname", "validns", nil, 0))
)
test.TestCreate(
valid,
badRevision,
emptyName,
invalidName,
emptyNs,
invalidNs,
nilData)
}
func TestUpdate(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := genericregistrytest.New(t, storage.Store)
addLabel := func(obj runtime.Object) runtime.Object {
rev := obj.(*apps.ControllerRevision)
update := &apps.ControllerRevision{
ObjectMeta: rev.ObjectMeta,
Data: rev.Data,
Revision: rev.Revision,
}
update.ObjectMeta.Labels = map[string]string{"foo": "bar"}
return update
}
updateData := func(obj runtime.Object) runtime.Object {
rev := obj.(*apps.ControllerRevision)
modified := newObject()
ss := modified.(*apps.StatefulSet)
ss.Name = "cde"
update := &apps.ControllerRevision{
ObjectMeta: rev.ObjectMeta,
Data: ss,
Revision: rev.Revision + 1,
}
return update
}
test.TestUpdate(stripObjectMeta(newControllerRevision("validname", metav1.NamespaceDefault, newObject(), 0)),
addLabel,
updateData)
}
func TestGet(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := genericregistrytest.New(t, storage.Store)
test.TestGet(newControllerRevision("valid", metav1.NamespaceDefault, newObject(), 0))
}
func TestList(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := genericregistrytest.New(t, storage.Store)
test.TestList(newControllerRevision("valid", metav1.NamespaceDefault, newObject(), 0))
}
func TestDelete(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := genericregistrytest.New(t, storage.Store)
test.TestDelete(newControllerRevision("valid", metav1.NamespaceDefault, newObject(), 0))
}
func TestWatch(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := genericregistrytest.New(t, storage.Store)
test.TestWatch(
newControllerRevision("valid", metav1.NamespaceDefault, newObject(), 0),
[]labels.Set{
{"foo": "bar"},
},
[]labels.Set{
{"hoo": "baz"},
},
[]fields.Set{
{"metadata.name": "valid"},
},
[]fields.Set{
{"metadata.name": "nomatch"},
},
)
}
func newControllerRevision(name, namespace string, data runtime.Object, revision int64) *apps.ControllerRevision {
return &apps.ControllerRevision{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
Labels: map[string]string{"foo": "bar"},
},
Data: data,
Revision: revision,
}
}
func stripObjectMeta(revision *apps.ControllerRevision) *apps.ControllerRevision {
revision.ObjectMeta = metav1.ObjectMeta{}
return revision
}
func newStorage(t *testing.T) (*REST, *etcdtesting.EtcdTestServer) {
etcdStorage, server := registrytest.NewEtcdStorage(t, apps.GroupName)
restOptions := generic.RESTOptions{
StorageConfig: etcdStorage,
Decorator: generic.UndecoratedStorage,
DeleteCollectionWorkers: 1,
ResourcePrefix: "controllerrevisions"}
storage := NewREST(restOptions)
return storage, server
}
func newObject() runtime.Object {
return &apps.StatefulSet{
ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
Spec: apps.StatefulSetSpec{
Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}},
Template: api.PodTemplateSpec{
Spec: api.PodSpec{
RestartPolicy: api.RestartPolicyAlways,
DNSPolicy: api.DNSClusterFirst,
},
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{"foo": "bar"},
},
},
},
}
}

View File

@ -0,0 +1,79 @@
/*
Copyright 2017 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 controllerrevision
import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/validation/field"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/registry/rest"
"k8s.io/apiserver/pkg/storage/names"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/apis/apps"
"k8s.io/kubernetes/pkg/apis/apps/validation"
)
// strategy implements behavior for ConfigMap objects
type strategy struct {
runtime.ObjectTyper
names.NameGenerator
}
// Strategy is the default logic that applies when creating and updating ControllerRevision
// objects via the REST API.
var Strategy = strategy{legacyscheme.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) Canonicalize(obj runtime.Object) {
}
func (strategy) AllowCreateOnUpdate() bool {
return false
}
func (strategy) PrepareForCreate(ctx genericapirequest.Context, obj runtime.Object) {
_ = obj.(*apps.ControllerRevision)
}
func (strategy) Validate(ctx genericapirequest.Context, obj runtime.Object) field.ErrorList {
revision := obj.(*apps.ControllerRevision)
return validation.ValidateControllerRevision(revision)
}
func (strategy) PrepareForUpdate(ctx genericapirequest.Context, newObj, oldObj runtime.Object) {
_ = oldObj.(*apps.ControllerRevision)
_ = newObj.(*apps.ControllerRevision)
}
func (strategy) AllowUnconditionalUpdate() bool {
return true
}
func (strategy) ValidateUpdate(ctx genericapirequest.Context, newObj, oldObj runtime.Object) field.ErrorList {
oldRevision, newRevision := oldObj.(*apps.ControllerRevision), newObj.(*apps.ControllerRevision)
return validation.ValidateControllerRevisionUpdate(newRevision, oldRevision)
}

View File

@ -0,0 +1,157 @@
/*
Copyright 2017 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 controllerrevision
import (
"testing"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/kubernetes/pkg/apis/apps"
api "k8s.io/kubernetes/pkg/apis/core"
)
func TestStrategy_NamespaceScoped(t *testing.T) {
if !Strategy.NamespaceScoped() {
t.Error("ControllerRevision strategy must be namespace scoped")
}
}
func TestStrategy_AllowCreateOnUpdate(t *testing.T) {
if Strategy.AllowCreateOnUpdate() {
t.Error("ControllerRevision should not be created on update")
}
}
func TestStrategy_Validate(t *testing.T) {
ctx := genericapirequest.NewDefaultContext()
var (
valid = newControllerRevision("validname", "validns", newObject(), 0)
badRevision = newControllerRevision("validname", "validns", newObject(), -1)
emptyName = newControllerRevision("", "validns", newObject(), 0)
invalidName = newControllerRevision("NoUppercaseOrSpecialCharsLike=Equals", "validns", newObject(), 0)
emptyNs = newControllerRevision("validname", "", newObject(), 100)
invalidNs = newControllerRevision("validname", "NoUppercaseOrSpecialCharsLike=Equals", newObject(), 100)
nilData = newControllerRevision("validname", "validns", nil, 0)
)
tests := map[string]struct {
history *apps.ControllerRevision
isValid bool
}{
"valid": {valid, true},
"negative revision": {badRevision, false},
"empty name": {emptyName, false},
"invalid name": {invalidName, false},
"empty namespace": {emptyNs, false},
"invalid namespace": {invalidNs, false},
"nil data": {nilData, false},
}
for name, tc := range tests {
errs := Strategy.Validate(ctx, tc.history)
if tc.isValid && len(errs) > 0 {
t.Errorf("%v: unexpected error: %v", name, errs)
}
if !tc.isValid && len(errs) == 0 {
t.Errorf("%v: unexpected non-error", name)
}
}
}
func TestStrategy_ValidateUpdate(t *testing.T) {
ctx := genericapirequest.NewDefaultContext()
var (
valid = newControllerRevision("validname", "validns", newObject(), 0)
changedData = newControllerRevision("validname", "validns",
func() runtime.Object {
modified := newObject()
ss := modified.(*apps.StatefulSet)
ss.Name = "cde"
return modified
}(), 0)
changedRevision = newControllerRevision("validname", "validns", newObject(), 1)
)
cases := []struct {
name string
newHistory *apps.ControllerRevision
oldHistory *apps.ControllerRevision
isValid bool
}{
{
name: "valid",
newHistory: valid,
oldHistory: valid,
isValid: true,
},
{
name: "changed data",
newHistory: changedData,
oldHistory: valid,
isValid: false,
},
{
name: "changed revision",
newHistory: changedRevision,
oldHistory: valid,
isValid: true,
},
}
for _, tc := range cases {
errs := Strategy.ValidateUpdate(ctx, tc.newHistory, tc.oldHistory)
if tc.isValid && len(errs) > 0 {
t.Errorf("%v: unexpected error: %v", tc.name, errs)
}
if !tc.isValid && len(errs) == 0 {
t.Errorf("%v: unexpected non-error", tc.name)
}
}
}
func newControllerRevision(name, namespace string, data runtime.Object, revision int64) *apps.ControllerRevision {
return &apps.ControllerRevision{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
ResourceVersion: "1",
Labels: map[string]string{"foo": "bar"},
},
Data: data,
Revision: revision,
}
}
func newObject() runtime.Object {
return &apps.StatefulSet{
ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
Spec: apps.StatefulSetSpec{
Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}},
Template: api.PodTemplateSpec{
Spec: api.PodSpec{
RestartPolicy: api.RestartPolicyAlways,
DNSPolicy: api.DNSClusterFirst,
},
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{"foo": "bar"},
},
},
},
}
}

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

@ -0,0 +1,41 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["storage_apps.go"],
importpath = "k8s.io/kubernetes/pkg/registry/apps/rest",
deps = [
"//pkg/api/legacyscheme:go_default_library",
"//pkg/apis/apps:go_default_library",
"//pkg/registry/apps/controllerrevision/storage:go_default_library",
"//pkg/registry/apps/statefulset/storage:go_default_library",
"//pkg/registry/extensions/daemonset/storage:go_default_library",
"//pkg/registry/extensions/deployment/storage:go_default_library",
"//pkg/registry/extensions/replicaset/storage:go_default_library",
"//vendor/k8s.io/api/apps/v1:go_default_library",
"//vendor/k8s.io/api/apps/v1beta1:go_default_library",
"//vendor/k8s.io/api/apps/v1beta2:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library",
"//vendor/k8s.io/apiserver/pkg/server:go_default_library",
"//vendor/k8s.io/apiserver/pkg/server/storage: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,153 @@
/*
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 (
appsapiv1 "k8s.io/api/apps/v1"
appsapiv1beta1 "k8s.io/api/apps/v1beta1"
appsapiv1beta2 "k8s.io/api/apps/v1beta2"
"k8s.io/apiserver/pkg/registry/generic"
"k8s.io/apiserver/pkg/registry/rest"
genericapiserver "k8s.io/apiserver/pkg/server"
serverstorage "k8s.io/apiserver/pkg/server/storage"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/apis/apps"
controllerrevisionsstore "k8s.io/kubernetes/pkg/registry/apps/controllerrevision/storage"
statefulsetstore "k8s.io/kubernetes/pkg/registry/apps/statefulset/storage"
daemonsetstore "k8s.io/kubernetes/pkg/registry/extensions/daemonset/storage"
deploymentstore "k8s.io/kubernetes/pkg/registry/extensions/deployment/storage"
replicasetstore "k8s.io/kubernetes/pkg/registry/extensions/replicaset/storage"
)
type RESTStorageProvider struct{}
func (p RESTStorageProvider) NewRESTStorage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) (genericapiserver.APIGroupInfo, bool) {
apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(apps.GroupName, legacyscheme.Registry, legacyscheme.Scheme, legacyscheme.ParameterCodec, legacyscheme.Codecs)
// If you add a version here, be sure to add an entry in `k8s.io/kubernetes/cmd/kube-apiserver/app/aggregator.go with specific priorities.
// TODO refactor the plumbing to provide the information in the APIGroupInfo
if apiResourceConfigSource.AnyResourcesForVersionEnabled(appsapiv1beta1.SchemeGroupVersion) {
apiGroupInfo.VersionedResourcesStorageMap[appsapiv1beta1.SchemeGroupVersion.Version] = p.v1beta1Storage(apiResourceConfigSource, restOptionsGetter)
apiGroupInfo.GroupMeta.GroupVersion = appsapiv1beta1.SchemeGroupVersion
}
if apiResourceConfigSource.AnyResourcesForVersionEnabled(appsapiv1beta2.SchemeGroupVersion) {
apiGroupInfo.VersionedResourcesStorageMap[appsapiv1beta2.SchemeGroupVersion.Version] = p.v1beta2Storage(apiResourceConfigSource, restOptionsGetter)
apiGroupInfo.GroupMeta.GroupVersion = appsapiv1beta2.SchemeGroupVersion
}
if apiResourceConfigSource.AnyResourcesForVersionEnabled(appsapiv1.SchemeGroupVersion) {
apiGroupInfo.VersionedResourcesStorageMap[appsapiv1.SchemeGroupVersion.Version] = p.v1Storage(apiResourceConfigSource, restOptionsGetter)
apiGroupInfo.GroupMeta.GroupVersion = appsapiv1.SchemeGroupVersion
}
return apiGroupInfo, true
}
func (p RESTStorageProvider) v1beta1Storage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) map[string]rest.Storage {
version := appsapiv1beta1.SchemeGroupVersion
storage := map[string]rest.Storage{}
if apiResourceConfigSource.ResourceEnabled(version.WithResource("deployments")) {
deploymentStorage := deploymentstore.NewStorage(restOptionsGetter)
storage["deployments"] = deploymentStorage.Deployment
storage["deployments/status"] = deploymentStorage.Status
storage["deployments/rollback"] = deploymentStorage.Rollback
storage["deployments/scale"] = deploymentStorage.Scale
}
if apiResourceConfigSource.ResourceEnabled(version.WithResource("statefulsets")) {
statefulSetStorage := statefulsetstore.NewStorage(restOptionsGetter)
storage["statefulsets"] = statefulSetStorage.StatefulSet
storage["statefulsets/status"] = statefulSetStorage.Status
storage["statefulsets/scale"] = statefulSetStorage.Scale
}
if apiResourceConfigSource.ResourceEnabled(version.WithResource("controllerrevisions")) {
historyStorage := controllerrevisionsstore.NewREST(restOptionsGetter)
storage["controllerrevisions"] = historyStorage
}
return storage
}
func (p RESTStorageProvider) v1beta2Storage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) map[string]rest.Storage {
version := appsapiv1beta2.SchemeGroupVersion
storage := map[string]rest.Storage{}
if apiResourceConfigSource.ResourceEnabled(version.WithResource("deployments")) {
deploymentStorage := deploymentstore.NewStorage(restOptionsGetter)
storage["deployments"] = deploymentStorage.Deployment
storage["deployments/status"] = deploymentStorage.Status
storage["deployments/scale"] = deploymentStorage.Scale
}
if apiResourceConfigSource.ResourceEnabled(version.WithResource("statefulsets")) {
statefulSetStorage := statefulsetstore.NewStorage(restOptionsGetter)
storage["statefulsets"] = statefulSetStorage.StatefulSet
storage["statefulsets/status"] = statefulSetStorage.Status
storage["statefulsets/scale"] = statefulSetStorage.Scale
}
if apiResourceConfigSource.ResourceEnabled(version.WithResource("daemonsets")) {
daemonSetStorage, daemonSetStatusStorage := daemonsetstore.NewREST(restOptionsGetter)
storage["daemonsets"] = daemonSetStorage
storage["daemonsets/status"] = daemonSetStatusStorage
}
if apiResourceConfigSource.ResourceEnabled(version.WithResource("replicasets")) {
replicaSetStorage := replicasetstore.NewStorage(restOptionsGetter)
storage["replicasets"] = replicaSetStorage.ReplicaSet
storage["replicasets/status"] = replicaSetStorage.Status
storage["replicasets/scale"] = replicaSetStorage.Scale
}
if apiResourceConfigSource.ResourceEnabled(version.WithResource("controllerrevisions")) {
historyStorage := controllerrevisionsstore.NewREST(restOptionsGetter)
storage["controllerrevisions"] = historyStorage
}
return storage
}
func (p RESTStorageProvider) v1Storage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) map[string]rest.Storage {
version := appsapiv1.SchemeGroupVersion
storage := map[string]rest.Storage{}
if apiResourceConfigSource.ResourceEnabled(version.WithResource("deployments")) {
deploymentStorage := deploymentstore.NewStorage(restOptionsGetter)
storage["deployments"] = deploymentStorage.Deployment
storage["deployments/status"] = deploymentStorage.Status
storage["deployments/scale"] = deploymentStorage.Scale
}
if apiResourceConfigSource.ResourceEnabled(version.WithResource("statefulsets")) {
statefulSetStorage := statefulsetstore.NewStorage(restOptionsGetter)
storage["statefulsets"] = statefulSetStorage.StatefulSet
storage["statefulsets/status"] = statefulSetStorage.Status
storage["statefulsets/scale"] = statefulSetStorage.Scale
}
if apiResourceConfigSource.ResourceEnabled(version.WithResource("daemonsets")) {
daemonSetStorage, daemonSetStatusStorage := daemonsetstore.NewREST(restOptionsGetter)
storage["daemonsets"] = daemonSetStorage
storage["daemonsets/status"] = daemonSetStatusStorage
}
if apiResourceConfigSource.ResourceEnabled(version.WithResource("replicasets")) {
replicaSetStorage := replicasetstore.NewStorage(restOptionsGetter)
storage["replicasets"] = replicaSetStorage.ReplicaSet
storage["replicasets/status"] = replicaSetStorage.Status
storage["replicasets/scale"] = replicaSetStorage.Scale
}
if apiResourceConfigSource.ResourceEnabled(version.WithResource("controllerrevisions")) {
historyStorage := controllerrevisionsstore.NewREST(restOptionsGetter)
storage["controllerrevisions"] = historyStorage
}
return storage
}
func (p RESTStorageProvider) GroupName() string {
return apps.GroupName
}

View File

@ -0,0 +1,66 @@
package(default_visibility = ["//visibility:public"])
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",
],
importpath = "k8s.io/kubernetes/pkg/registry/apps/statefulset",
deps = [
"//pkg/api/legacyscheme:go_default_library",
"//pkg/api/pod:go_default_library",
"//pkg/apis/apps:go_default_library",
"//pkg/apis/apps/validation:go_default_library",
"//vendor/k8s.io/api/apps/v1beta1:go_default_library",
"//vendor/k8s.io/api/apps/v1beta2:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/internalversion:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/watch:go_default_library",
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library",
"//vendor/k8s.io/apiserver/pkg/storage/names:go_default_library",
],
)
go_test(
name = "go_default_test",
srcs = ["strategy_test.go"],
importpath = "k8s.io/kubernetes/pkg/registry/apps/statefulset",
library = ":go_default_library",
deps = [
"//pkg/apis/apps:go_default_library",
"//pkg/apis/core:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//pkg/registry/apps/statefulset/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 statefulset // import "k8s.io/kubernetes/pkg/registry/apps/statefulset"

View File

@ -0,0 +1,94 @@
/*
Copyright 2017 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 statefulset
import (
"fmt"
"k8s.io/apimachinery/pkg/api/errors"
metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/watch"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/registry/rest"
"k8s.io/kubernetes/pkg/apis/apps"
)
// Registry is an interface for things that know how to store StatefulSets.
type Registry interface {
ListStatefulSets(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (*apps.StatefulSetList, error)
WatchStatefulSets(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (watch.Interface, error)
GetStatefulSet(ctx genericapirequest.Context, statefulSetID string, options *metav1.GetOptions) (*apps.StatefulSet, error)
CreateStatefulSet(ctx genericapirequest.Context, statefulSet *apps.StatefulSet, createValidation rest.ValidateObjectFunc) (*apps.StatefulSet, error)
UpdateStatefulSet(ctx genericapirequest.Context, statefulSet *apps.StatefulSet, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) (*apps.StatefulSet, error)
DeleteStatefulSet(ctx genericapirequest.Context, statefulSetID 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) ListStatefulSets(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (*apps.StatefulSetList, 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.(*apps.StatefulSetList), err
}
func (s *storage) WatchStatefulSets(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (watch.Interface, error) {
return s.Watch(ctx, options)
}
func (s *storage) GetStatefulSet(ctx genericapirequest.Context, statefulSetID string, options *metav1.GetOptions) (*apps.StatefulSet, error) {
obj, err := s.Get(ctx, statefulSetID, options)
if err != nil {
return nil, errors.NewNotFound(apps.Resource("statefulsets/scale"), statefulSetID)
}
return obj.(*apps.StatefulSet), nil
}
func (s *storage) CreateStatefulSet(ctx genericapirequest.Context, statefulSet *apps.StatefulSet, createValidation rest.ValidateObjectFunc) (*apps.StatefulSet, error) {
obj, err := s.Create(ctx, statefulSet, rest.ValidateAllObjectFunc, false)
if err != nil {
return nil, err
}
return obj.(*apps.StatefulSet), nil
}
func (s *storage) UpdateStatefulSet(ctx genericapirequest.Context, statefulSet *apps.StatefulSet, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) (*apps.StatefulSet, error) {
obj, _, err := s.Update(ctx, statefulSet.Name, rest.DefaultUpdatedObjectInfo(statefulSet), createValidation, updateValidation)
if err != nil {
return nil, err
}
return obj.(*apps.StatefulSet), nil
}
func (s *storage) DeleteStatefulSet(ctx genericapirequest.Context, statefulSetID string) error {
_, _, err := s.Delete(ctx, statefulSetID, nil)
return err
}

View File

@ -0,0 +1,71 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_test(
name = "go_default_test",
srcs = ["storage_test.go"],
importpath = "k8s.io/kubernetes/pkg/registry/apps/statefulset/storage",
library = ":go_default_library",
deps = [
"//pkg/apis/apps:go_default_library",
"//pkg/apis/autoscaling:go_default_library",
"//pkg/apis/core:go_default_library",
"//pkg/registry/registrytest:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/fields:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/labels:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/diff:go_default_library",
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/generic/testing:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library",
"//vendor/k8s.io/apiserver/pkg/storage/etcd/testing:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = ["storage.go"],
importpath = "k8s.io/kubernetes/pkg/registry/apps/statefulset/storage",
deps = [
"//pkg/apis/apps:go_default_library",
"//pkg/apis/apps/v1beta1:go_default_library",
"//pkg/apis/apps/v1beta2:go_default_library",
"//pkg/apis/autoscaling:go_default_library",
"//pkg/apis/autoscaling/v1:go_default_library",
"//pkg/apis/autoscaling/validation:go_default_library",
"//pkg/apis/extensions:go_default_library",
"//pkg/printers:go_default_library",
"//pkg/printers/internalversion:go_default_library",
"//pkg/printers/storage:go_default_library",
"//pkg/registry/apps/statefulset:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/generic/registry:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/rest: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,223 @@
/*
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 (
"fmt"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/registry/generic"
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
"k8s.io/apiserver/pkg/registry/rest"
"k8s.io/kubernetes/pkg/apis/apps"
appsv1beta1 "k8s.io/kubernetes/pkg/apis/apps/v1beta1"
appsv1beta2 "k8s.io/kubernetes/pkg/apis/apps/v1beta2"
"k8s.io/kubernetes/pkg/apis/autoscaling"
autoscalingv1 "k8s.io/kubernetes/pkg/apis/autoscaling/v1"
autoscalingvalidation "k8s.io/kubernetes/pkg/apis/autoscaling/validation"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/printers"
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
printerstorage "k8s.io/kubernetes/pkg/printers/storage"
"k8s.io/kubernetes/pkg/registry/apps/statefulset"
)
// StatefulSetStorage includes dummy storage for StatefulSets, and their Status and Scale subresource.
type StatefulSetStorage struct {
StatefulSet *REST
Status *StatusREST
Scale *ScaleREST
}
func NewStorage(optsGetter generic.RESTOptionsGetter) StatefulSetStorage {
statefulSetRest, statefulSetStatusRest := NewREST(optsGetter)
statefulSetRegistry := statefulset.NewRegistry(statefulSetRest)
return StatefulSetStorage{
StatefulSet: statefulSetRest,
Status: statefulSetStatusRest,
Scale: &ScaleREST{registry: statefulSetRegistry},
}
}
// rest implements a RESTStorage for statefulsets against etcd
type REST struct {
*genericregistry.Store
}
// NewREST returns a RESTStorage object that will work against statefulsets.
func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST) {
store := &genericregistry.Store{
NewFunc: func() runtime.Object { return &apps.StatefulSet{} },
NewListFunc: func() runtime.Object { return &apps.StatefulSetList{} },
DefaultQualifiedResource: apps.Resource("statefulsets"),
CreateStrategy: statefulset.Strategy,
UpdateStrategy: statefulset.Strategy,
DeleteStrategy: statefulset.Strategy,
TableConvertor: printerstorage.TableConvertor{TablePrinter: printers.NewTablePrinter().With(printersinternal.AddHandlers)},
}
options := &generic.StoreOptions{RESTOptions: optsGetter}
if err := store.CompleteWithOptions(options); err != nil {
panic(err) // TODO: Propagate error up
}
statusStore := *store
statusStore.UpdateStrategy = statefulset.StatusStrategy
return &REST{store}, &StatusREST{store: &statusStore}
}
// Implement CategoriesProvider
var _ rest.CategoriesProvider = &REST{}
// Categories implements the CategoriesProvider interface. Returns a list of categories a resource is part of.
func (r *REST) Categories() []string {
return []string{"all"}
}
// 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 &apps.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, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) (runtime.Object, bool, error) {
return r.store.Update(ctx, name, objInfo, createValidation, updateValidation)
}
// Implement ShortNamesProvider
var _ rest.ShortNamesProvider = &REST{}
// ShortNames implements the ShortNamesProvider interface. Returns a list of short names for a resource.
func (r *REST) ShortNames() []string {
return []string{"sts"}
}
type ScaleREST struct {
registry statefulset.Registry
}
// ScaleREST implements Patcher
var _ = rest.Patcher(&ScaleREST{})
var _ = rest.GroupVersionKindProvider(&ScaleREST{})
func (r *ScaleREST) GroupVersionKind(containingGV schema.GroupVersion) schema.GroupVersionKind {
switch containingGV {
case appsv1beta1.SchemeGroupVersion:
return appsv1beta1.SchemeGroupVersion.WithKind("Scale")
case appsv1beta2.SchemeGroupVersion:
return appsv1beta2.SchemeGroupVersion.WithKind("Scale")
default:
return autoscalingv1.SchemeGroupVersion.WithKind("Scale")
}
}
// 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) {
ss, err := r.registry.GetStatefulSet(ctx, name, options)
if err != nil {
return nil, err
}
scale, err := scaleFromStatefulSet(ss)
if err != nil {
return nil, errors.NewBadRequest(fmt.Sprintf("%v", err))
}
return scale, err
}
func (r *ScaleREST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) (runtime.Object, bool, error) {
ss, err := r.registry.GetStatefulSet(ctx, name, &metav1.GetOptions{})
if err != nil {
return nil, false, err
}
oldScale, err := scaleFromStatefulSet(ss)
if err != nil {
return nil, false, err
}
obj, err := objInfo.UpdatedObject(ctx, oldScale)
if err != nil {
return nil, false, err
}
if obj == nil {
return nil, false, errors.NewBadRequest(fmt.Sprintf("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 := autoscalingvalidation.ValidateScale(scale); len(errs) > 0 {
return nil, false, errors.NewInvalid(extensions.Kind("Scale"), scale.Name, errs)
}
ss.Spec.Replicas = scale.Spec.Replicas
ss.ResourceVersion = scale.ResourceVersion
ss, err = r.registry.UpdateStatefulSet(ctx, ss, createValidation, updateValidation)
if err != nil {
return nil, false, err
}
newScale, err := scaleFromStatefulSet(ss)
if err != nil {
return nil, false, errors.NewBadRequest(fmt.Sprintf("%v", err))
}
return newScale, false, err
}
// scaleFromStatefulSet returns a scale subresource for a statefulset.
func scaleFromStatefulSet(ss *apps.StatefulSet) (*autoscaling.Scale, error) {
selector, err := metav1.LabelSelectorAsSelector(ss.Spec.Selector)
if err != nil {
return nil, err
}
return &autoscaling.Scale{
// TODO: Create a variant of ObjectMeta type that only contains the fields below.
ObjectMeta: metav1.ObjectMeta{
Name: ss.Name,
Namespace: ss.Namespace,
UID: ss.UID,
ResourceVersion: ss.ResourceVersion,
CreationTimestamp: ss.CreationTimestamp,
},
Spec: autoscaling.ScaleSpec{
Replicas: ss.Spec.Replicas,
},
Status: autoscaling.ScaleStatus{
Replicas: ss.Status.Replicas,
Selector: selector.String(),
},
}, nil
}

View File

@ -0,0 +1,303 @@
/*
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"
apiequality "k8s.io/apimachinery/pkg/api/equality"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/util/diff"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/registry/generic"
genericregistrytest "k8s.io/apiserver/pkg/registry/generic/testing"
"k8s.io/apiserver/pkg/registry/rest"
etcdtesting "k8s.io/apiserver/pkg/storage/etcd/testing"
"k8s.io/kubernetes/pkg/apis/apps"
"k8s.io/kubernetes/pkg/apis/autoscaling"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/registry/registrytest"
)
// TODO: allow for global factory override
func newStorage(t *testing.T) (StatefulSetStorage, *etcdtesting.EtcdTestServer) {
etcdStorage, server := registrytest.NewEtcdStorage(t, apps.GroupName)
restOptions := generic.RESTOptions{StorageConfig: etcdStorage, Decorator: generic.UndecoratedStorage, DeleteCollectionWorkers: 1, ResourcePrefix: "statefulsets"}
storage := NewStorage(restOptions)
return storage, 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, rest.ValidateAllObjectFunc, false)
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: metav1.ObjectMeta{
Name: "foo",
Namespace: metav1.NamespaceDefault,
Labels: map[string]string{"a": "b"},
},
Spec: apps.StatefulSetSpec{
PodManagementPolicy: apps.OrderedReadyPodManagement,
Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"a": "b"}},
Template: api.PodTemplateSpec{
ObjectMeta: metav1.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,
UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType},
},
Status: apps.StatefulSetStatus{},
}
}
var validStatefulSet = *validNewStatefulSet()
func TestCreate(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.StatefulSet.Store.DestroyFunc()
test := genericregistrytest.New(t, storage.StatefulSet.Store)
ps := validNewStatefulSet()
ps.ObjectMeta = metav1.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, server := newStorage(t)
defer server.Terminate(t)
defer storage.StatefulSet.Store.DestroyFunc()
ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), metav1.NamespaceDefault)
key := "/statefulsets/" + metav1.NamespaceDefault + "/foo"
validStatefulSet := validNewStatefulSet()
if err := storage.StatefulSet.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 := storage.Status.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(&update), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc); err != nil {
t.Fatalf("unexpected error: %v", err)
}
obj, err := storage.StatefulSet.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.StatefulSet.Store.DestroyFunc()
test := genericregistrytest.New(t, storage.StatefulSet.Store)
test.TestGet(validNewStatefulSet())
}
func TestList(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.StatefulSet.Store.DestroyFunc()
test := genericregistrytest.New(t, storage.StatefulSet.Store)
test.TestList(validNewStatefulSet())
}
func TestDelete(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.StatefulSet.Store.DestroyFunc()
test := genericregistrytest.New(t, storage.StatefulSet.Store)
test.TestDelete(validNewStatefulSet())
}
func TestWatch(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.StatefulSet.Store.DestroyFunc()
test := genericregistrytest.New(t, storage.StatefulSet.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"},
},
)
}
func TestCategories(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.StatefulSet.Store.DestroyFunc()
expected := []string{"all"}
registrytest.AssertCategories(t, storage.StatefulSet, expected)
}
func TestShortNames(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.StatefulSet.Store.DestroyFunc()
expected := []string{"sts"}
registrytest.AssertShortNames(t, storage.StatefulSet, expected)
}
func TestScaleGet(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.StatefulSet.Store.DestroyFunc()
name := "foo"
var sts apps.StatefulSet
ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), metav1.NamespaceDefault)
key := "/statefulsets/" + metav1.NamespaceDefault + "/" + name
if err := storage.StatefulSet.Storage.Create(ctx, key, &validStatefulSet, &sts, 0); err != nil {
t.Fatalf("error setting new statefulset (key: %s) %v: %v", key, validStatefulSet, err)
}
selector, err := metav1.LabelSelectorAsSelector(validStatefulSet.Spec.Selector)
if err != nil {
t.Fatal(err)
}
want := &autoscaling.Scale{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: metav1.NamespaceDefault,
UID: sts.UID,
ResourceVersion: sts.ResourceVersion,
CreationTimestamp: sts.CreationTimestamp,
},
Spec: autoscaling.ScaleSpec{
Replicas: validStatefulSet.Spec.Replicas,
},
Status: autoscaling.ScaleStatus{
Replicas: validStatefulSet.Status.Replicas,
Selector: selector.String(),
},
}
obj, err := storage.Scale.Get(ctx, name, &metav1.GetOptions{})
got := obj.(*autoscaling.Scale)
if err != nil {
t.Fatalf("error fetching scale for %s: %v", name, err)
}
if !apiequality.Semantic.DeepEqual(got, want) {
t.Errorf("unexpected scale: %s", diff.ObjectDiff(got, want))
}
}
func TestScaleUpdate(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.StatefulSet.Store.DestroyFunc()
name := "foo"
var sts apps.StatefulSet
ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), metav1.NamespaceDefault)
key := "/statefulsets/" + metav1.NamespaceDefault + "/" + name
if err := storage.StatefulSet.Storage.Create(ctx, key, &validStatefulSet, &sts, 0); err != nil {
t.Fatalf("error setting new statefulset (key: %s) %v: %v", key, validStatefulSet, err)
}
replicas := 12
update := autoscaling.Scale{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: metav1.NamespaceDefault,
},
Spec: autoscaling.ScaleSpec{
Replicas: int32(replicas),
},
}
if _, _, err := storage.Scale.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(&update), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc); 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 != int32(replicas) {
t.Errorf("wrong replicas count expected: %d got: %d", replicas, scale.Spec.Replicas)
}
update.ResourceVersion = sts.ResourceVersion
update.Spec.Replicas = 15
if _, _, err = storage.Scale.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(&update), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc); err != nil && !errors.IsConflict(err) {
t.Fatalf("unexpected error, expecting an update conflict but got %v", err)
}
}
// TODO: Test generation number.

View File

@ -0,0 +1,139 @@
/*
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 statefulset
import (
appsv1beta1 "k8s.io/api/apps/v1beta1"
appsv1beta2 "k8s.io/api/apps/v1beta2"
apiequality "k8s.io/apimachinery/pkg/api/equality"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/validation/field"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/registry/rest"
"k8s.io/apiserver/pkg/storage/names"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/api/pod"
"k8s.io/kubernetes/pkg/apis/apps"
"k8s.io/kubernetes/pkg/apis/apps/validation"
)
// 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{legacyscheme.Scheme, names.SimpleNameGenerator}
// DefaultGarbageCollectionPolicy returns OrphanDependents by default. For apps/v1, returns DeleteDependents.
func (statefulSetStrategy) DefaultGarbageCollectionPolicy(ctx genericapirequest.Context) rest.GarbageCollectionPolicy {
if requestInfo, found := genericapirequest.RequestInfoFrom(ctx); found {
groupVersion := schema.GroupVersion{Group: requestInfo.APIGroup, Version: requestInfo.APIVersion}
switch groupVersion {
case appsv1beta1.SchemeGroupVersion, appsv1beta2.SchemeGroupVersion:
// for back compatibility
return rest.OrphanDependents
default:
return rest.DeleteDependents
}
}
return rest.OrphanDependents
}
// 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
pod.DropDisabledAlphaFields(&statefulSet.Spec.Template.Spec)
}
// 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
pod.DropDisabledAlphaFields(&newStatefulSet.Spec.Template.Spec)
pod.DropDisabledAlphaFields(&oldStatefulSet.Spec.Template.Spec)
// Any changes to the spec increment the generation number, any changes to the
// status should reflect the generation number of the corresponding object.
// See metav1.ObjectMeta description for more information on Generation.
if !apiequality.Semantic.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
}
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,206 @@
/*
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 statefulset
import (
"testing"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/registry/rest"
"k8s.io/kubernetes/pkg/apis/apps"
api "k8s.io/kubernetes/pkg/apis/core"
)
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: metav1.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: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
Spec: apps.StatefulSetSpec{
PodManagementPolicy: apps.OrderedReadyPodManagement,
Selector: &metav1.LabelSelector{MatchLabels: validSelector},
Template: validPodTemplate.Template,
UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType},
},
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: metav1.ObjectMeta{Name: ps.Name, Namespace: ps.Namespace, ResourceVersion: "1", Generation: 1},
Spec: apps.StatefulSetSpec{
PodManagementPolicy: apps.OrderedReadyPodManagement,
Selector: ps.Spec.Selector,
Template: validPodTemplate.Template,
UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType},
},
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 TestStatefulsetDefaultGarbageCollectionPolicy(t *testing.T) {
// Make sure we correctly implement the interface.
// Otherwise a typo could silently change the default.
var gcds rest.GarbageCollectionDeleteStrategy = Strategy
tests := []struct {
requestInfo genericapirequest.RequestInfo
expectedGCPolicy rest.GarbageCollectionPolicy
isNilRequestInfo bool
}{
{
genericapirequest.RequestInfo{
APIGroup: "apps",
APIVersion: "v1beta1",
Resource: "statefulsets",
},
rest.OrphanDependents,
false,
},
{
genericapirequest.RequestInfo{
APIGroup: "apps",
APIVersion: "v1beta2",
Resource: "statefulsets",
},
rest.OrphanDependents,
false,
},
{
genericapirequest.RequestInfo{
APIGroup: "apps",
APIVersion: "v1",
Resource: "statefulsets",
},
rest.DeleteDependents,
false,
},
{
expectedGCPolicy: rest.OrphanDependents,
isNilRequestInfo: true,
},
}
for _, test := range tests {
context := genericapirequest.NewContext()
if !test.isNilRequestInfo {
context = genericapirequest.WithRequestInfo(context, &test.requestInfo)
}
if got, want := gcds.DefaultGarbageCollectionPolicy(context), test.expectedGCPolicy; got != want {
t.Errorf("%s/%s: DefaultGarbageCollectionPolicy() = %#v, want %#v", test.requestInfo.APIGroup,
test.requestInfo.APIVersion, got, want)
}
}
}
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: metav1.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: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault, ResourceVersion: "10"},
Spec: apps.StatefulSetSpec{
Replicas: 3,
Selector: &metav1.LabelSelector{MatchLabels: validSelector},
Template: validPodTemplate.Template,
UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType},
},
Status: apps.StatefulSetStatus{
Replicas: 1,
},
}
newPS := &apps.StatefulSet{
ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault, ResourceVersion: "9"},
Spec: apps.StatefulSetSpec{
Replicas: 1,
Selector: &metav1.LabelSelector{MatchLabels: validSelector},
Template: validPodTemplate.Template,
UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType},
},
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)
}
}

View File

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

View File

@ -0,0 +1,37 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["storage_authentication.go"],
importpath = "k8s.io/kubernetes/pkg/registry/authentication/rest",
deps = [
"//pkg/api/legacyscheme:go_default_library",
"//pkg/apis/authentication:go_default_library",
"//pkg/registry/authentication/tokenreview:go_default_library",
"//vendor/k8s.io/api/authentication/v1:go_default_library",
"//vendor/k8s.io/api/authentication/v1beta1:go_default_library",
"//vendor/k8s.io/apiserver/pkg/authentication/authenticator:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library",
"//vendor/k8s.io/apiserver/pkg/server:go_default_library",
"//vendor/k8s.io/apiserver/pkg/server/storage: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,88 @@
/*
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 (
authenticationv1 "k8s.io/api/authentication/v1"
authenticationv1beta1 "k8s.io/api/authentication/v1beta1"
"k8s.io/apiserver/pkg/authentication/authenticator"
"k8s.io/apiserver/pkg/registry/generic"
"k8s.io/apiserver/pkg/registry/rest"
genericapiserver "k8s.io/apiserver/pkg/server"
serverstorage "k8s.io/apiserver/pkg/server/storage"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/apis/authentication"
"k8s.io/kubernetes/pkg/registry/authentication/tokenreview"
)
type RESTStorageProvider struct {
Authenticator authenticator.Request
}
func (p RESTStorageProvider) NewRESTStorage(apiResourceConfigSource serverstorage.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, legacyscheme.Registry, legacyscheme.Scheme, legacyscheme.ParameterCodec, legacyscheme.Codecs)
// If you add a version here, be sure to add an entry in `k8s.io/kubernetes/cmd/kube-apiserver/app/aggregator.go with specific priorities.
// TODO refactor the plumbing to provide the information in the APIGroupInfo
if apiResourceConfigSource.AnyResourcesForVersionEnabled(authenticationv1beta1.SchemeGroupVersion) {
apiGroupInfo.VersionedResourcesStorageMap[authenticationv1beta1.SchemeGroupVersion.Version] = p.v1beta1Storage(apiResourceConfigSource, restOptionsGetter)
apiGroupInfo.GroupMeta.GroupVersion = authenticationv1beta1.SchemeGroupVersion
}
if apiResourceConfigSource.AnyResourcesForVersionEnabled(authenticationv1.SchemeGroupVersion) {
apiGroupInfo.VersionedResourcesStorageMap[authenticationv1.SchemeGroupVersion.Version] = p.v1Storage(apiResourceConfigSource, restOptionsGetter)
apiGroupInfo.GroupMeta.GroupVersion = authenticationv1.SchemeGroupVersion
}
return apiGroupInfo, true
}
func (p RESTStorageProvider) v1beta1Storage(apiResourceConfigSource serverstorage.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) v1Storage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) map[string]rest.Storage {
version := authenticationv1.SchemeGroupVersion
storage := map[string]rest.Storage{}
if apiResourceConfigSource.AnyResourcesForVersionEnabled(authenticationv1.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,33 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["storage.go"],
importpath = "k8s.io/kubernetes/pkg/registry/authentication/tokenreview",
deps = [
"//pkg/apis/authentication:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apiserver/pkg/authentication/authenticator:go_default_library",
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/rest: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,83 @@
/*
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/endpoints/request"
"k8s.io/apiserver/pkg/registry/rest"
"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, createValidation rest.ValidateObjectFunc, includeUninitialized bool) (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 len(tokenReview.Spec.Token) == 0 {
return nil, apierrors.NewBadRequest(fmt.Sprintf("token is required for TokenReview in authentication"))
}
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
}

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

@ -0,0 +1,4 @@
reviewers:
- deads2k
- liggitt
- enj

View File

@ -0,0 +1,35 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["rest.go"],
importpath = "k8s.io/kubernetes/pkg/registry/authorization/localsubjectaccessreview",
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:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library",
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/rest: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,73 @@
/*
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/endpoints/request"
"k8s.io/apiserver/pkg/registry/rest"
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, createValidation rest.ValidateObjectFunc, includeUninitialized bool) (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)
decision, reason, evaluationErr := r.authorizer.Authorize(authorizationAttributes)
localSubjectAccessReview.Status = authorizationapi.SubjectAccessReviewStatus{
Allowed: (decision == authorizer.DecisionAllow),
Denied: (decision == authorizer.DecisionDeny),
Reason: reason,
}
if evaluationErr != nil {
localSubjectAccessReview.Status.EvaluationError = evaluationErr.Error()
}
return localSubjectAccessReview, nil
}

View File

@ -0,0 +1,40 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["storage_authorization.go"],
importpath = "k8s.io/kubernetes/pkg/registry/authorization/rest",
deps = [
"//pkg/api/legacyscheme:go_default_library",
"//pkg/apis/authorization:go_default_library",
"//pkg/registry/authorization/localsubjectaccessreview:go_default_library",
"//pkg/registry/authorization/selfsubjectaccessreview:go_default_library",
"//pkg/registry/authorization/selfsubjectrulesreview:go_default_library",
"//pkg/registry/authorization/subjectaccessreview:go_default_library",
"//vendor/k8s.io/api/authorization/v1:go_default_library",
"//vendor/k8s.io/api/authorization/v1beta1:go_default_library",
"//vendor/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library",
"//vendor/k8s.io/apiserver/pkg/server:go_default_library",
"//vendor/k8s.io/apiserver/pkg/server/storage: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,104 @@
/*
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 (
authorizationv1 "k8s.io/api/authorization/v1"
authorizationv1beta1 "k8s.io/api/authorization/v1beta1"
"k8s.io/apiserver/pkg/authorization/authorizer"
"k8s.io/apiserver/pkg/registry/generic"
"k8s.io/apiserver/pkg/registry/rest"
genericapiserver "k8s.io/apiserver/pkg/server"
serverstorage "k8s.io/apiserver/pkg/server/storage"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/apis/authorization"
"k8s.io/kubernetes/pkg/registry/authorization/localsubjectaccessreview"
"k8s.io/kubernetes/pkg/registry/authorization/selfsubjectaccessreview"
"k8s.io/kubernetes/pkg/registry/authorization/selfsubjectrulesreview"
"k8s.io/kubernetes/pkg/registry/authorization/subjectaccessreview"
)
type RESTStorageProvider struct {
Authorizer authorizer.Authorizer
RuleResolver authorizer.RuleResolver
}
func (p RESTStorageProvider) NewRESTStorage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) (genericapiserver.APIGroupInfo, bool) {
if p.Authorizer == nil {
return genericapiserver.APIGroupInfo{}, false
}
apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(authorization.GroupName, legacyscheme.Registry, legacyscheme.Scheme, legacyscheme.ParameterCodec, legacyscheme.Codecs)
// If you add a version here, be sure to add an entry in `k8s.io/kubernetes/cmd/kube-apiserver/app/aggregator.go with specific priorities.
// TODO refactor the plumbing to provide the information in the APIGroupInfo
if apiResourceConfigSource.AnyResourcesForVersionEnabled(authorizationv1beta1.SchemeGroupVersion) {
apiGroupInfo.VersionedResourcesStorageMap[authorizationv1beta1.SchemeGroupVersion.Version] = p.v1beta1Storage(apiResourceConfigSource, restOptionsGetter)
apiGroupInfo.GroupMeta.GroupVersion = authorizationv1beta1.SchemeGroupVersion
}
if apiResourceConfigSource.AnyResourcesForVersionEnabled(authorizationv1.SchemeGroupVersion) {
apiGroupInfo.VersionedResourcesStorageMap[authorizationv1.SchemeGroupVersion.Version] = p.v1Storage(apiResourceConfigSource, restOptionsGetter)
apiGroupInfo.GroupMeta.GroupVersion = authorizationv1.SchemeGroupVersion
}
return apiGroupInfo, true
}
func (p RESTStorageProvider) v1beta1Storage(apiResourceConfigSource serverstorage.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)
}
if apiResourceConfigSource.ResourceEnabled(version.WithResource("selfsubjectrulesreviews")) {
storage["selfsubjectrulesreviews"] = selfsubjectrulesreview.NewREST(p.RuleResolver)
}
return storage
}
func (p RESTStorageProvider) v1Storage(apiResourceConfigSource serverstorage.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)
}
if apiResourceConfigSource.ResourceEnabled(version.WithResource("selfsubjectrulesreviews")) {
storage["selfsubjectrulesreviews"] = selfsubjectrulesreview.NewREST(p.RuleResolver)
}
return storage
}
func (p RESTStorageProvider) GroupName() string {
return authorization.GroupName
}

View File

@ -0,0 +1,35 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["rest.go"],
importpath = "k8s.io/kubernetes/pkg/registry/authorization/selfsubjectaccessreview",
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:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library",
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/rest: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,76 @@
/*
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/endpoints/request"
"k8s.io/apiserver/pkg/registry/rest"
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, createValidation rest.ValidateObjectFunc, includeUninitialized bool) (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)
}
decision, reason, evaluationErr := r.authorizer.Authorize(authorizationAttributes)
selfSAR.Status = authorizationapi.SubjectAccessReviewStatus{
Allowed: (decision == authorizer.DecisionAllow),
Denied: (decision == authorizer.DecisionDeny),
Reason: reason,
}
if evaluationErr != nil {
selfSAR.Status.EvaluationError = evaluationErr.Error()
}
return selfSAR, nil
}

View File

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

View File

@ -0,0 +1,100 @@
/*
Copyright 2017 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 selfsubjectrulesreview
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/endpoints/request"
"k8s.io/apiserver/pkg/registry/rest"
authorizationapi "k8s.io/kubernetes/pkg/apis/authorization"
)
// REST implements a RESTStorage for selfsubjectrulesreview.
type REST struct {
ruleResolver authorizer.RuleResolver
}
// NewREST returns a RESTStorage object that will work against selfsubjectrulesreview.
func NewREST(ruleResolver authorizer.RuleResolver) *REST {
return &REST{ruleResolver}
}
// New creates a new selfsubjectrulesreview object.
func (r *REST) New() runtime.Object {
return &authorizationapi.SelfSubjectRulesReview{}
}
// Create attempts to get self subject rules in specific namespace.
func (r *REST) Create(ctx genericapirequest.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, includeUninitialized bool) (runtime.Object, error) {
selfSRR, ok := obj.(*authorizationapi.SelfSubjectRulesReview)
if !ok {
return nil, apierrors.NewBadRequest(fmt.Sprintf("not a SelfSubjectRulesReview: %#v", obj))
}
user, ok := genericapirequest.UserFrom(ctx)
if !ok {
return nil, apierrors.NewBadRequest("no user present on request")
}
namespace := selfSRR.Spec.Namespace
if namespace == "" {
return nil, apierrors.NewBadRequest("no namespace on request")
}
resourceInfo, nonResourceInfo, incomplete, err := r.ruleResolver.RulesFor(user, namespace)
ret := &authorizationapi.SelfSubjectRulesReview{
Status: authorizationapi.SubjectRulesReviewStatus{
ResourceRules: getResourceRules(resourceInfo),
NonResourceRules: getNonResourceRules(nonResourceInfo),
Incomplete: incomplete,
},
}
if err != nil {
ret.Status.EvaluationError = err.Error()
}
return ret, nil
}
func getResourceRules(infos []authorizer.ResourceRuleInfo) []authorizationapi.ResourceRule {
rules := make([]authorizationapi.ResourceRule, len(infos))
for i, info := range infos {
rules[i] = authorizationapi.ResourceRule{
Verbs: info.GetVerbs(),
APIGroups: info.GetAPIGroups(),
Resources: info.GetResources(),
ResourceNames: info.GetResourceNames(),
}
}
return rules
}
func getNonResourceRules(infos []authorizer.NonResourceRuleInfo) []authorizationapi.NonResourceRule {
rules := make([]authorizationapi.NonResourceRule, len(infos))
for i, info := range infos {
rules[i] = authorizationapi.NonResourceRule{
Verbs: info.GetVerbs(),
NonResourceURLs: info.GetNonResourceURLs(),
}
}
return rules
}

View File

@ -0,0 +1,50 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = ["rest.go"],
importpath = "k8s.io/kubernetes/pkg/registry/authorization/subjectaccessreview",
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:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library",
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)
go_test(
name = "go_default_test",
srcs = ["rest_test.go"],
importpath = "k8s.io/kubernetes/pkg/registry/authorization/subjectaccessreview",
library = ":go_default_library",
deps = [
"//pkg/apis/authorization:go_default_library",
"//vendor/k8s.io/apiserver/pkg/authentication/user:go_default_library",
"//vendor/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library",
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library",
],
)

View File

@ -0,0 +1,66 @@
/*
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/endpoints/request"
"k8s.io/apiserver/pkg/registry/rest"
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, createValidation rest.ValidateObjectFunc, includeUninitialized bool) (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)
decision, reason, evaluationErr := r.authorizer.Authorize(authorizationAttributes)
subjectAccessReview.Status = authorizationapi.SubjectAccessReviewStatus{
Allowed: (decision == authorizer.DecisionAllow),
Denied: (decision == authorizer.DecisionDeny),
Reason: reason,
}
if evaluationErr != nil {
subjectAccessReview.Status.EvaluationError = evaluationErr.Error()
}
return subjectAccessReview, nil
}

View File

@ -0,0 +1,217 @@
/*
Copyright 2017 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 (
"errors"
"strings"
"testing"
"reflect"
"k8s.io/apiserver/pkg/authentication/user"
"k8s.io/apiserver/pkg/authorization/authorizer"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/registry/rest"
authorizationapi "k8s.io/kubernetes/pkg/apis/authorization"
)
type fakeAuthorizer struct {
attrs authorizer.Attributes
decision authorizer.Decision
reason string
err error
}
func (f *fakeAuthorizer) Authorize(attrs authorizer.Attributes) (authorizer.Decision, string, error) {
f.attrs = attrs
return f.decision, f.reason, f.err
}
func TestCreate(t *testing.T) {
testcases := map[string]struct {
spec authorizationapi.SubjectAccessReviewSpec
decision authorizer.Decision
reason string
err error
expectedErr string
expectedAttrs authorizer.Attributes
expectedStatus authorizationapi.SubjectAccessReviewStatus
}{
"empty": {
expectedErr: "nonResourceAttributes or resourceAttributes",
},
"nonresource rejected": {
spec: authorizationapi.SubjectAccessReviewSpec{
User: "bob",
NonResourceAttributes: &authorizationapi.NonResourceAttributes{Verb: "get", Path: "/mypath"},
},
decision: authorizer.DecisionNoOpinion,
reason: "myreason",
err: errors.New("myerror"),
expectedAttrs: authorizer.AttributesRecord{
User: &user.DefaultInfo{Name: "bob"},
Verb: "get",
Path: "/mypath",
ResourceRequest: false,
},
expectedStatus: authorizationapi.SubjectAccessReviewStatus{
Allowed: false,
Reason: "myreason",
EvaluationError: "myerror",
},
},
"nonresource allowed": {
spec: authorizationapi.SubjectAccessReviewSpec{
User: "bob",
NonResourceAttributes: &authorizationapi.NonResourceAttributes{Verb: "get", Path: "/mypath"},
},
decision: authorizer.DecisionAllow,
reason: "allowed",
err: nil,
expectedAttrs: authorizer.AttributesRecord{
User: &user.DefaultInfo{Name: "bob"},
Verb: "get",
Path: "/mypath",
ResourceRequest: false,
},
expectedStatus: authorizationapi.SubjectAccessReviewStatus{
Allowed: true,
Reason: "allowed",
EvaluationError: "",
},
},
"resource rejected": {
spec: authorizationapi.SubjectAccessReviewSpec{
User: "bob",
ResourceAttributes: &authorizationapi.ResourceAttributes{
Namespace: "myns",
Verb: "create",
Group: "extensions",
Version: "v1beta1",
Resource: "deployments",
Subresource: "scale",
Name: "mydeployment",
},
},
decision: authorizer.DecisionNoOpinion,
reason: "myreason",
err: errors.New("myerror"),
expectedAttrs: authorizer.AttributesRecord{
User: &user.DefaultInfo{Name: "bob"},
Namespace: "myns",
Verb: "create",
APIGroup: "extensions",
APIVersion: "v1beta1",
Resource: "deployments",
Subresource: "scale",
Name: "mydeployment",
ResourceRequest: true,
},
expectedStatus: authorizationapi.SubjectAccessReviewStatus{
Allowed: false,
Denied: false,
Reason: "myreason",
EvaluationError: "myerror",
},
},
"resource allowed": {
spec: authorizationapi.SubjectAccessReviewSpec{
User: "bob",
ResourceAttributes: &authorizationapi.ResourceAttributes{
Namespace: "myns",
Verb: "create",
Group: "extensions",
Version: "v1beta1",
Resource: "deployments",
Subresource: "scale",
Name: "mydeployment",
},
},
decision: authorizer.DecisionAllow,
reason: "allowed",
err: nil,
expectedAttrs: authorizer.AttributesRecord{
User: &user.DefaultInfo{Name: "bob"},
Namespace: "myns",
Verb: "create",
APIGroup: "extensions",
APIVersion: "v1beta1",
Resource: "deployments",
Subresource: "scale",
Name: "mydeployment",
ResourceRequest: true,
},
expectedStatus: authorizationapi.SubjectAccessReviewStatus{
Allowed: true,
Denied: false,
Reason: "allowed",
EvaluationError: "",
},
},
"resource denied": {
spec: authorizationapi.SubjectAccessReviewSpec{
User: "bob",
ResourceAttributes: &authorizationapi.ResourceAttributes{},
},
decision: authorizer.DecisionDeny,
expectedAttrs: authorizer.AttributesRecord{
User: &user.DefaultInfo{Name: "bob"},
ResourceRequest: true,
},
expectedStatus: authorizationapi.SubjectAccessReviewStatus{
Allowed: false,
Denied: true,
},
},
}
for k, tc := range testcases {
auth := &fakeAuthorizer{
decision: tc.decision,
reason: tc.reason,
err: tc.err,
}
storage := NewREST(auth)
result, err := storage.Create(genericapirequest.NewContext(), &authorizationapi.SubjectAccessReview{Spec: tc.spec}, rest.ValidateAllObjectFunc, false)
if err != nil {
if tc.expectedErr != "" {
if !strings.Contains(err.Error(), tc.expectedErr) {
t.Errorf("%s: expected %s to contain %q", k, err, tc.expectedErr)
}
} else {
t.Errorf("%s: %v", k, err)
}
continue
}
if !reflect.DeepEqual(auth.attrs, tc.expectedAttrs) {
t.Errorf("%s: expected\n%#v\ngot\n%#v", k, tc.expectedAttrs, auth.attrs)
}
status := result.(*authorizationapi.SubjectAccessReview).Status
if !reflect.DeepEqual(status, tc.expectedStatus) {
t.Errorf("%s: expected\n%#v\ngot\n%#v", k, tc.expectedStatus, status)
}
}
}

View File

@ -0,0 +1,43 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = ["helpers.go"],
importpath = "k8s.io/kubernetes/pkg/registry/authorization/util",
deps = [
"//pkg/apis/authorization:go_default_library",
"//vendor/k8s.io/apiserver/pkg/authentication/user:go_default_library",
"//vendor/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)
go_test(
name = "go_default_test",
srcs = ["helpers_test.go"],
importpath = "k8s.io/kubernetes/pkg/registry/authorization/util",
library = ":go_default_library",
deps = [
"//pkg/apis/authorization:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//vendor/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library",
],
)

View File

@ -0,0 +1,79 @@
/*
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,
APIVersion: in.Version,
Resource: in.Resource,
Subresource: in.Subresource,
Name: in.Name,
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,
UID: spec.UID,
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
}

View File

@ -0,0 +1,74 @@
/*
Copyright 2017 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 (
"reflect"
"testing"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apiserver/pkg/authorization/authorizer"
authorizationapi "k8s.io/kubernetes/pkg/apis/authorization"
)
func TestResourceAttributesFrom(t *testing.T) {
knownResourceAttributesNames := sets.NewString(
// Fields we copy in ResourceAttributesFrom
"Verb",
"Namespace",
"Group",
"Version",
"Resource",
"Subresource",
"Name",
// Fields we copy in NonResourceAttributesFrom
"Path",
"Verb",
)
reflect.TypeOf(authorizationapi.ResourceAttributes{}).FieldByNameFunc(func(name string) bool {
if !knownResourceAttributesNames.Has(name) {
t.Errorf("authorizationapi.ResourceAttributes has a new field: %q. Add to ResourceAttributesFrom/NonResourceAttributesFrom as appropriate, then add to knownResourceAttributesNames", name)
}
return false
})
knownAttributesRecordFieldNames := sets.NewString(
// Fields we set in ResourceAttributesFrom
"User",
"Verb",
"Namespace",
"APIGroup",
"APIVersion",
"Resource",
"Subresource",
"Name",
"ResourceRequest",
// Fields we set in NonResourceAttributesFrom
"User",
"ResourceRequest",
"Path",
"Verb",
)
reflect.TypeOf(authorizer.AttributesRecord{}).FieldByNameFunc(func(name string) bool {
if !knownAttributesRecordFieldNames.Has(name) {
t.Errorf("authorizer.AttributesRecord has a new field: %q. Add to ResourceAttributesFrom/NonResourceAttributesFrom as appropriate, then add to knownAttributesRecordFieldNames", name)
}
return false
})
}

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,40 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"doc.go",
"strategy.go",
],
importpath = "k8s.io/kubernetes/pkg/registry/autoscaling/horizontalpodautoscaler",
deps = [
"//pkg/api/legacyscheme:go_default_library",
"//pkg/apis/autoscaling:go_default_library",
"//pkg/apis/autoscaling/validation:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
"//vendor/k8s.io/apiserver/pkg/storage/names: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"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_test(
name = "go_default_test",
srcs = ["storage_test.go"],
importpath = "k8s.io/kubernetes/pkg/registry/autoscaling/horizontalpodautoscaler/storage",
library = ":go_default_library",
deps = [
"//pkg/apis/autoscaling:go_default_library",
"//pkg/apis/core:go_default_library",
"//pkg/registry/registrytest:go_default_library",
"//vendor/k8s.io/api/autoscaling/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/fields:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/labels:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/generic/testing:go_default_library",
"//vendor/k8s.io/apiserver/pkg/storage/etcd/testing:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = ["storage.go"],
importpath = "k8s.io/kubernetes/pkg/registry/autoscaling/horizontalpodautoscaler/storage",
deps = [
"//pkg/apis/autoscaling:go_default_library",
"//pkg/registry/autoscaling/horizontalpodautoscaler:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/generic/registry:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/rest: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,88 @@
/*
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/endpoints/request"
"k8s.io/apiserver/pkg/registry/generic"
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
"k8s.io/apiserver/pkg/registry/rest"
"k8s.io/kubernetes/pkg/apis/autoscaling"
"k8s.io/kubernetes/pkg/registry/autoscaling/horizontalpodautoscaler"
)
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{} },
DefaultQualifiedResource: autoscaling.Resource("horizontalpodautoscalers"),
CreateStrategy: horizontalpodautoscaler.Strategy,
UpdateStrategy: horizontalpodautoscaler.Strategy,
DeleteStrategy: horizontalpodautoscaler.Strategy,
}
options := &generic.StoreOptions{RESTOptions: optsGetter}
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}
}
// Implement ShortNamesProvider
var _ rest.ShortNamesProvider = &REST{}
// ShortNames implements the ShortNamesProvider interface. Returns a list of short names for a resource.
func (r *REST) ShortNames() []string {
return []string{"hpa"}
}
// Implement CategoriesProvider
var _ rest.CategoriesProvider = &REST{}
// Categories implements the CategoriesProvider interface. Returns a list of categories a resource is part of.
func (r *REST) Categories() []string {
return []string{"all"}
}
/// 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, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) (runtime.Object, bool, error) {
return r.store.Update(ctx, name, objInfo, createValidation, updateValidation)
}

View File

@ -0,0 +1,169 @@
/*
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/kubernetes/pkg/apis/autoscaling"
api "k8s.io/kubernetes/pkg/apis/core"
// Ensure that autoscaling/v1 package is initialized.
_ "k8s.io/api/autoscaling/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/registry/generic"
genericregistrytest "k8s.io/apiserver/pkg/registry/generic/testing"
etcdtesting "k8s.io/apiserver/pkg/storage/etcd/testing"
"k8s.io/kubernetes/pkg/registry/registrytest"
)
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: metav1.ObjectMeta{
Name: name,
Namespace: metav1.NamespaceDefault,
},
Spec: autoscaling.HorizontalPodAutoscalerSpec{
ScaleTargetRef: autoscaling.CrossVersionObjectReference{
Kind: "ReplicationController",
Name: "myrc",
},
MaxReplicas: 5,
Metrics: []autoscaling.MetricSpec{
{
Type: autoscaling.ResourceMetricSourceType,
Resource: &autoscaling.ResourceMetricSource{
Name: api.ResourceCPU,
TargetAverageUtilization: &cpu,
},
},
},
},
}
}
func TestCreate(t *testing.T) {
storage, _, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := genericregistrytest.New(t, storage.Store)
autoscaler := validNewHorizontalPodAutoscaler("foo")
autoscaler.ObjectMeta = metav1.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 := genericregistrytest.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 := genericregistrytest.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 := genericregistrytest.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 := genericregistrytest.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 := genericregistrytest.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"},
},
)
}
func TestShortNames(t *testing.T) {
storage, _, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
expected := []string{"hpa"}
registrytest.AssertShortNames(t, storage, expected)
}
func TestCategories(t *testing.T) {
storage, _, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
expected := []string{"all"}
registrytest.AssertCategories(t, storage, expected)
}
// TODO TestUpdateStatus

View File

@ -0,0 +1,99 @@
/*
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 (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/validation/field"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/storage/names"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/apis/autoscaling"
"k8s.io/kubernetes/pkg/apis/autoscaling/validation"
)
// 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{legacyscheme.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
}
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,36 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["storage_autoscaling.go"],
importpath = "k8s.io/kubernetes/pkg/registry/autoscaling/rest",
deps = [
"//pkg/api/legacyscheme:go_default_library",
"//pkg/apis/autoscaling:go_default_library",
"//pkg/registry/autoscaling/horizontalpodautoscaler/storage:go_default_library",
"//vendor/k8s.io/api/autoscaling/v1:go_default_library",
"//vendor/k8s.io/api/autoscaling/v2beta1:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library",
"//vendor/k8s.io/apiserver/pkg/server:go_default_library",
"//vendor/k8s.io/apiserver/pkg/server/storage: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,76 @@
/*
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 (
autoscalingapiv1 "k8s.io/api/autoscaling/v1"
autoscalingapiv2beta1 "k8s.io/api/autoscaling/v2beta1"
"k8s.io/apiserver/pkg/registry/generic"
"k8s.io/apiserver/pkg/registry/rest"
genericapiserver "k8s.io/apiserver/pkg/server"
serverstorage "k8s.io/apiserver/pkg/server/storage"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/apis/autoscaling"
horizontalpodautoscalerstore "k8s.io/kubernetes/pkg/registry/autoscaling/horizontalpodautoscaler/storage"
)
type RESTStorageProvider struct{}
func (p RESTStorageProvider) NewRESTStorage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) (genericapiserver.APIGroupInfo, bool) {
apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(autoscaling.GroupName, legacyscheme.Registry, legacyscheme.Scheme, legacyscheme.ParameterCodec, legacyscheme.Codecs)
// If you add a version here, be sure to add an entry in `k8s.io/kubernetes/cmd/kube-apiserver/app/aggregator.go with specific priorities.
// TODO refactor the plumbing to provide the information in the APIGroupInfo
if apiResourceConfigSource.AnyResourcesForVersionEnabled(autoscalingapiv2beta1.SchemeGroupVersion) {
apiGroupInfo.VersionedResourcesStorageMap[autoscalingapiv2beta1.SchemeGroupVersion.Version] = p.v2beta1Storage(apiResourceConfigSource, restOptionsGetter)
apiGroupInfo.GroupMeta.GroupVersion = autoscalingapiv2beta1.SchemeGroupVersion
}
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 serverstorage.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) v2beta1Storage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) map[string]rest.Storage {
version := autoscalingapiv2beta1.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,57 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"doc.go",
"strategy.go",
],
importpath = "k8s.io/kubernetes/pkg/registry/batch/cronjob",
deps = [
"//pkg/api/legacyscheme:go_default_library",
"//pkg/api/pod:go_default_library",
"//pkg/apis/batch:go_default_library",
"//pkg/apis/batch/validation:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library",
"//vendor/k8s.io/apiserver/pkg/storage/names:go_default_library",
],
)
go_test(
name = "go_default_test",
srcs = ["strategy_test.go"],
importpath = "k8s.io/kubernetes/pkg/registry/batch/cronjob",
library = ":go_default_library",
deps = [
"//pkg/apis/batch:go_default_library",
"//pkg/apis/core:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library",
],
)
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,60 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_test(
name = "go_default_test",
srcs = ["storage_test.go"],
importpath = "k8s.io/kubernetes/pkg/registry/batch/cronjob/storage",
library = ":go_default_library",
deps = [
"//pkg/api/testapi:go_default_library",
"//pkg/apis/batch:go_default_library",
"//pkg/apis/core:go_default_library",
"//pkg/registry/registrytest:go_default_library",
"//vendor/k8s.io/api/batch/v2alpha1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/fields:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/labels:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/generic/testing:go_default_library",
"//vendor/k8s.io/apiserver/pkg/storage/etcd/testing:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = ["storage.go"],
importpath = "k8s.io/kubernetes/pkg/registry/batch/cronjob/storage",
deps = [
"//pkg/apis/batch:go_default_library",
"//pkg/printers:go_default_library",
"//pkg/printers/internalversion:go_default_library",
"//pkg/printers/storage:go_default_library",
"//pkg/registry/batch/cronjob:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/generic/registry:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/rest: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,86 @@
/*
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/endpoints/request"
"k8s.io/apiserver/pkg/registry/generic"
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
"k8s.io/apiserver/pkg/registry/rest"
"k8s.io/kubernetes/pkg/apis/batch"
"k8s.io/kubernetes/pkg/printers"
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
printerstorage "k8s.io/kubernetes/pkg/printers/storage"
"k8s.io/kubernetes/pkg/registry/batch/cronjob"
)
// 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{} },
DefaultQualifiedResource: batch.Resource("cronjobs"),
CreateStrategy: cronjob.Strategy,
UpdateStrategy: cronjob.Strategy,
DeleteStrategy: cronjob.Strategy,
TableConvertor: printerstorage.TableConvertor{TablePrinter: printers.NewTablePrinter().With(printersinternal.AddHandlers)},
}
options := &generic.StoreOptions{RESTOptions: optsGetter}
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}
}
var _ rest.CategoriesProvider = &REST{}
// Categories implements the CategoriesProvider interface. Returns a list of categories a resource is part of.
func (r *REST) Categories() []string {
return []string{"all"}
}
// 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, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) (runtime.Object, bool, error) {
return r.store.Update(ctx, name, objInfo, createValidation, updateValidation)
}

View File

@ -0,0 +1,186 @@
/*
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/api/batch/v2alpha1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/registry/generic"
genericregistrytest "k8s.io/apiserver/pkg/registry/generic/testing"
etcdtesting "k8s.io/apiserver/pkg/storage/etcd/testing"
"k8s.io/kubernetes/pkg/api/testapi"
"k8s.io/kubernetes/pkg/apis/batch"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/registry/registrytest"
)
// 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: metav1.ObjectMeta{
Name: "foo",
Namespace: metav1.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 := genericregistrytest.New(t, storage.Store)
validCronJob := validNewCronJob()
validCronJob.ObjectMeta = metav1.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 := genericregistrytest.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 := genericregistrytest.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 := genericregistrytest.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 := genericregistrytest.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 := genericregistrytest.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,107 @@
/*
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 (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/validation/field"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/registry/rest"
"k8s.io/apiserver/pkg/storage/names"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/api/pod"
"k8s.io/kubernetes/pkg/apis/batch"
"k8s.io/kubernetes/pkg/apis/batch/validation"
)
// cronJobStrategy implements verification logic for Replication Controllers.
type cronJobStrategy struct {
runtime.ObjectTyper
names.NameGenerator
}
// Strategy is the default logic that applies when creating and updating CronJob objects.
var Strategy = cronJobStrategy{legacyscheme.Scheme, names.SimpleNameGenerator}
// DefaultGarbageCollectionPolicy returns Orphan because that was the default
// behavior before the server-side garbage collection was implemented.
func (cronJobStrategy) DefaultGarbageCollectionPolicy(ctx genericapirequest.Context) rest.GarbageCollectionPolicy {
return rest.OrphanDependents
}
// NamespaceScoped returns true because all scheduled jobs need to be within a namespace.
func (cronJobStrategy) NamespaceScoped() bool {
return true
}
// PrepareForCreate clears the status of a scheduled job before creation.
func (cronJobStrategy) PrepareForCreate(ctx genericapirequest.Context, obj runtime.Object) {
cronJob := obj.(*batch.CronJob)
cronJob.Status = batch.CronJobStatus{}
pod.DropDisabledAlphaFields(&cronJob.Spec.JobTemplate.Spec.Template.Spec)
}
// PrepareForUpdate clears fields that are not allowed to be set by end users on update.
func (cronJobStrategy) PrepareForUpdate(ctx genericapirequest.Context, obj, old runtime.Object) {
newCronJob := obj.(*batch.CronJob)
oldCronJob := old.(*batch.CronJob)
newCronJob.Status = oldCronJob.Status
pod.DropDisabledAlphaFields(&newCronJob.Spec.JobTemplate.Spec.Template.Spec)
pod.DropDisabledAlphaFields(&oldCronJob.Spec.JobTemplate.Spec.Template.Spec)
}
// Validate validates a new scheduled job.
func (cronJobStrategy) Validate(ctx genericapirequest.Context, obj runtime.Object) field.ErrorList {
cronJob := obj.(*batch.CronJob)
return validation.ValidateCronJob(cronJob)
}
// Canonicalize normalizes the object after validation.
func (cronJobStrategy) Canonicalize(obj runtime.Object) {
}
func (cronJobStrategy) AllowUnconditionalUpdate() bool {
return true
}
// AllowCreateOnUpdate is false for scheduled jobs; this means a POST is needed to create one.
func (cronJobStrategy) AllowCreateOnUpdate() bool {
return false
}
// ValidateUpdate is the default update validation for an end user.
func (cronJobStrategy) ValidateUpdate(ctx genericapirequest.Context, obj, old runtime.Object) field.ErrorList {
return validation.ValidateCronJobUpdate(obj.(*batch.CronJob), old.(*batch.CronJob))
}
type cronJobStatusStrategy struct {
cronJobStrategy
}
var StatusStrategy = cronJobStatusStrategy{Strategy}
func (cronJobStatusStrategy) PrepareForUpdate(ctx genericapirequest.Context, obj, old runtime.Object) {
newJob := obj.(*batch.CronJob)
oldJob := old.(*batch.CronJob)
newJob.Spec = oldJob.Spec
}
func (cronJobStatusStrategy) ValidateUpdate(ctx genericapirequest.Context, obj, old runtime.Object) field.ErrorList {
return field.ErrorList{}
}

View File

@ -0,0 +1,171 @@
/*
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/endpoints/request"
"k8s.io/apiserver/pkg/registry/rest"
"k8s.io/kubernetes/pkg/apis/batch"
api "k8s.io/kubernetes/pkg/apis/core"
)
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", TerminationMessagePolicy: api.TerminationMessageReadFile}},
},
}
scheduledJob := &batch.CronJob{
ObjectMeta: metav1.ObjectMeta{
Name: "mycronjob",
Namespace: metav1.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: metav1.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")
}
// Make sure we correctly implement the interface.
// Otherwise a typo could silently change the default.
var gcds rest.GarbageCollectionDeleteStrategy = Strategy
if got, want := gcds.DefaultGarbageCollectionPolicy(genericapirequest.NewContext()), rest.OrphanDependents; got != want {
t.Errorf("DefaultGarbageCollectionPolicy() = %#v, want %#v", got, want)
}
}
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", TerminationMessagePolicy: api.TerminationMessageReadFile}},
},
}
oldSchedule := "* * * * ?"
oldCronJob := &batch.CronJob{
ObjectMeta: metav1.ObjectMeta{
Name: "mycronjob",
Namespace: metav1.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: metav1.ObjectMeta{
Name: "mycronjob",
Namespace: metav1.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")
}
}

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

@ -0,0 +1,65 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"doc.go",
"strategy.go",
],
importpath = "k8s.io/kubernetes/pkg/registry/batch/job",
deps = [
"//pkg/api/legacyscheme:go_default_library",
"//pkg/api/pod:go_default_library",
"//pkg/apis/batch:go_default_library",
"//pkg/apis/batch/validation:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/fields:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/labels:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library",
"//vendor/k8s.io/apiserver/pkg/storage:go_default_library",
"//vendor/k8s.io/apiserver/pkg/storage/names:go_default_library",
],
)
go_test(
name = "go_default_test",
srcs = ["strategy_test.go"],
importpath = "k8s.io/kubernetes/pkg/registry/batch/job",
library = ":go_default_library",
deps = [
"//pkg/api/testapi:go_default_library",
"//pkg/api/testing:go_default_library",
"//pkg/apis/batch:go_default_library",
"//pkg/apis/core:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library",
],
)
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,58 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_test(
name = "go_default_test",
srcs = ["storage_test.go"],
importpath = "k8s.io/kubernetes/pkg/registry/batch/job/storage",
library = ":go_default_library",
deps = [
"//pkg/apis/batch:go_default_library",
"//pkg/apis/core:go_default_library",
"//pkg/registry/registrytest:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/fields:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/labels:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/generic/testing:go_default_library",
"//vendor/k8s.io/apiserver/pkg/storage/etcd/testing:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = ["storage.go"],
importpath = "k8s.io/kubernetes/pkg/registry/batch/job/storage",
deps = [
"//pkg/apis/batch:go_default_library",
"//pkg/printers:go_default_library",
"//pkg/printers/internalversion:go_default_library",
"//pkg/printers/storage:go_default_library",
"//pkg/registry/batch/job:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/generic/registry:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/rest: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,103 @@
/*
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/endpoints/request"
"k8s.io/apiserver/pkg/registry/generic"
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
"k8s.io/apiserver/pkg/registry/rest"
"k8s.io/kubernetes/pkg/apis/batch"
"k8s.io/kubernetes/pkg/printers"
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
printerstorage "k8s.io/kubernetes/pkg/printers/storage"
"k8s.io/kubernetes/pkg/registry/batch/job"
)
// 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{} },
PredicateFunc: job.MatchJob,
DefaultQualifiedResource: batch.Resource("jobs"),
CreateStrategy: job.Strategy,
UpdateStrategy: job.Strategy,
DeleteStrategy: job.Strategy,
TableConvertor: printerstorage.TableConvertor{TablePrinter: printers.NewTablePrinter().With(printersinternal.AddHandlers)},
}
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}
}
// Implement CategoriesProvider
var _ rest.CategoriesProvider = &REST{}
// Categories implements the CategoriesProvider interface. Returns a list of categories a resource is part of.
func (r *REST) Categories() []string {
return []string{"all"}
}
// 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, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) (runtime.Object, bool, error) {
return r.store.Update(ctx, name, objInfo, createValidation, updateValidation)
}

View File

@ -0,0 +1,193 @@
/*
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/fields"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/registry/generic"
genericregistrytest "k8s.io/apiserver/pkg/registry/generic/testing"
etcdtesting "k8s.io/apiserver/pkg/storage/etcd/testing"
"k8s.io/kubernetes/pkg/apis/batch"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/registry/registrytest"
)
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: metav1.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: metav1.ObjectMeta{
Labels: map[string]string{"a": "b"},
},
Spec: api.PodSpec{
Containers: []api.Container{
{
Name: "test",
Image: "test_image",
ImagePullPolicy: api.PullIfNotPresent,
TerminationMessagePolicy: api.TerminationMessageReadFile,
},
},
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 := genericregistrytest.New(t, storage.Job.Store)
validJob := validNewJob()
validJob.ObjectMeta = metav1.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 := genericregistrytest.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 := genericregistrytest.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 := genericregistrytest.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 := genericregistrytest.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 := genericregistrytest.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
}
func TestCategories(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Job.Store.DestroyFunc()
expected := []string{"all"}
registrytest.AssertCategories(t, storage.Job, expected)
}

View File

@ -0,0 +1,200 @@
/*
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/fields"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/validation/field"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/registry/generic"
"k8s.io/apiserver/pkg/registry/rest"
"k8s.io/apiserver/pkg/storage"
"k8s.io/apiserver/pkg/storage/names"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/api/pod"
"k8s.io/kubernetes/pkg/apis/batch"
"k8s.io/kubernetes/pkg/apis/batch/validation"
)
// 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{legacyscheme.Scheme, names.SimpleNameGenerator}
// DefaultGarbageCollectionPolicy returns Orphan because that was the default
// behavior before the server-side garbage collection was implemented.
func (jobStrategy) DefaultGarbageCollectionPolicy(ctx genericapirequest.Context) rest.GarbageCollectionPolicy {
return rest.OrphanDependents
}
// 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{}
pod.DropDisabledAlphaFields(&job.Spec.Template.Spec)
}
// 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
pod.DropDisabledAlphaFields(&newJob.Spec.Template.Spec)
pod.DropDisabledAlphaFields(&oldJob.Spec.Template.Spec)
}
// 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, bool, error) {
job, ok := obj.(*batch.Job)
if !ok {
return nil, nil, false, fmt.Errorf("given object is not a job.")
}
return labels.Set(job.ObjectMeta.Labels), JobToSelectableFields(job), job.Initializers != nil, 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,240 @@
/*
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/endpoints/request"
"k8s.io/apiserver/pkg/registry/rest"
"k8s.io/kubernetes/pkg/api/testapi"
apitesting "k8s.io/kubernetes/pkg/api/testing"
"k8s.io/kubernetes/pkg/apis/batch"
api "k8s.io/kubernetes/pkg/apis/core"
)
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: metav1.ObjectMeta{
Labels: validSelector.MatchLabels,
},
Spec: api.PodSpec{
RestartPolicy: api.RestartPolicyOnFailure,
DNSPolicy: api.DNSClusterFirst,
Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: api.TerminationMessageReadFile}},
},
}
job := &batch.Job{
ObjectMeta: metav1.ObjectMeta{
Name: "myjob",
Namespace: metav1.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: metav1.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")
}
// Make sure we correctly implement the interface.
// Otherwise a typo could silently change the default.
var gcds rest.GarbageCollectionDeleteStrategy = Strategy
if got, want := gcds.DefaultGarbageCollectionPolicy(genericapirequest.NewContext()), rest.OrphanDependents; got != want {
t.Errorf("DefaultGarbageCollectionPolicy() = %#v, want %#v", got, want)
}
}
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", TerminationMessagePolicy: api.TerminationMessageReadFile}},
},
}
job := &batch.Job{
ObjectMeta: metav1.ObjectMeta{
Name: "myjob2",
Namespace: metav1.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: metav1.ObjectMeta{
Labels: validSelector.MatchLabels,
},
Spec: api.PodSpec{
RestartPolicy: api.RestartPolicyOnFailure,
DNSPolicy: api.DNSClusterFirst,
Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: api.TerminationMessageReadFile}},
},
}
oldParallelism := int32(10)
newParallelism := int32(11)
oldJob := &batch.Job{
ObjectMeta: metav1.ObjectMeta{
Name: "myjob",
Namespace: metav1.NamespaceDefault,
ResourceVersion: "10",
},
Spec: batch.JobSpec{
Selector: validSelector,
Template: validPodTemplateSpec,
Parallelism: &oldParallelism,
},
Status: batch.JobStatus{
Active: 11,
},
}
newJob := &batch.Job{
ObjectMeta: metav1.ObjectMeta{
Name: "myjob",
Namespace: metav1.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"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["storage_batch.go"],
importpath = "k8s.io/kubernetes/pkg/registry/batch/rest",
deps = [
"//pkg/api/legacyscheme:go_default_library",
"//pkg/apis/batch:go_default_library",
"//pkg/registry/batch/cronjob/storage:go_default_library",
"//pkg/registry/batch/job/storage:go_default_library",
"//vendor/k8s.io/api/batch/v1:go_default_library",
"//vendor/k8s.io/api/batch/v1beta1:go_default_library",
"//vendor/k8s.io/api/batch/v2alpha1:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library",
"//vendor/k8s.io/apiserver/pkg/server:go_default_library",
"//vendor/k8s.io/apiserver/pkg/server/storage: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,94 @@
/*
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 (
batchapiv1 "k8s.io/api/batch/v1"
batchapiv1beta1 "k8s.io/api/batch/v1beta1"
batchapiv2alpha1 "k8s.io/api/batch/v2alpha1"
"k8s.io/apiserver/pkg/registry/generic"
"k8s.io/apiserver/pkg/registry/rest"
genericapiserver "k8s.io/apiserver/pkg/server"
serverstorage "k8s.io/apiserver/pkg/server/storage"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/apis/batch"
cronjobstore "k8s.io/kubernetes/pkg/registry/batch/cronjob/storage"
jobstore "k8s.io/kubernetes/pkg/registry/batch/job/storage"
)
type RESTStorageProvider struct{}
func (p RESTStorageProvider) NewRESTStorage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) (genericapiserver.APIGroupInfo, bool) {
apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(batch.GroupName, legacyscheme.Registry, legacyscheme.Scheme, legacyscheme.ParameterCodec, legacyscheme.Codecs)
// If you add a version here, be sure to add an entry in `k8s.io/kubernetes/cmd/kube-apiserver/app/aggregator.go with specific priorities.
// TODO refactor the plumbing to provide the information in the APIGroupInfo
if apiResourceConfigSource.AnyResourcesForVersionEnabled(batchapiv1.SchemeGroupVersion) {
apiGroupInfo.VersionedResourcesStorageMap[batchapiv1.SchemeGroupVersion.Version] = p.v1Storage(apiResourceConfigSource, restOptionsGetter)
apiGroupInfo.GroupMeta.GroupVersion = batchapiv1.SchemeGroupVersion
}
if apiResourceConfigSource.AnyResourcesForVersionEnabled(batchapiv1beta1.SchemeGroupVersion) {
apiGroupInfo.VersionedResourcesStorageMap[batchapiv1beta1.SchemeGroupVersion.Version] = p.v1beta1Storage(apiResourceConfigSource, restOptionsGetter)
apiGroupInfo.GroupMeta.GroupVersion = batchapiv1beta1.SchemeGroupVersion
}
if apiResourceConfigSource.AnyResourcesForVersionEnabled(batchapiv2alpha1.SchemeGroupVersion) {
apiGroupInfo.VersionedResourcesStorageMap[batchapiv2alpha1.SchemeGroupVersion.Version] = p.v2alpha1Storage(apiResourceConfigSource, restOptionsGetter)
apiGroupInfo.GroupMeta.GroupVersion = batchapiv2alpha1.SchemeGroupVersion
}
return apiGroupInfo, true
}
func (p RESTStorageProvider) v1Storage(apiResourceConfigSource serverstorage.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) v1beta1Storage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) map[string]rest.Storage {
version := batchapiv1beta1.SchemeGroupVersion
storage := map[string]rest.Storage{}
if apiResourceConfigSource.ResourceEnabled(version.WithResource("cronjobs")) {
cronJobsStorage, cronJobsStatusStorage := cronjobstore.NewREST(restOptionsGetter)
storage["cronjobs"] = cronJobsStorage
storage["cronjobs/status"] = cronJobsStatusStorage
}
return storage
}
func (p RESTStorageProvider) v2alpha1Storage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) map[string]rest.Storage {
version := batchapiv2alpha1.SchemeGroupVersion
storage := map[string]rest.Storage{}
if apiResourceConfigSource.ResourceEnabled(version.WithResource("cronjobs")) {
cronJobsStorage, cronJobsStatusStorage := cronjobstore.NewREST(restOptionsGetter)
storage["cronjobs"] = cronJobsStorage
storage["cronjobs/status"] = cronJobsStatusStorage
}
return storage
}
func (p RESTStorageProvider) GroupName() string {
return batch.GroupName
}

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

@ -0,0 +1,26 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["cachesize.go"],
importpath = "k8s.io/kubernetes/pkg/registry/cachesize",
deps = ["//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

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

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

View File

@ -0,0 +1,51 @@
/*
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 cachesize
import (
"k8s.io/apimachinery/pkg/runtime/schema"
)
// NewHeuristicWatchCacheSizes returns a map of suggested watch cache sizes based on total
// memory.
func NewHeuristicWatchCacheSizes(expectedRAMCapacityMB int) map[schema.GroupResource]int {
// From our documentation, we officially recommend 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 := make(map[schema.GroupResource]int)
watchCacheSizes[schema.GroupResource{Resource: "replicationcontrollers"}] = maxInt(5*clusterSize, 100)
watchCacheSizes[schema.GroupResource{Resource: "endpoints"}] = maxInt(10*clusterSize, 1000)
watchCacheSizes[schema.GroupResource{Resource: "nodes"}] = maxInt(5*clusterSize, 1000)
watchCacheSizes[schema.GroupResource{Resource: "pods"}] = maxInt(50*clusterSize, 1000)
watchCacheSizes[schema.GroupResource{Resource: "services"}] = maxInt(5*clusterSize, 1000)
watchCacheSizes[schema.GroupResource{Resource: "apiservices", Group: "apiregistration.k8s.io"}] = maxInt(5*clusterSize, 1000)
return watchCacheSizes
}
func maxInt(a, b int) int {
if a > b {
return a
}
return b
}

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

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

View File

@ -0,0 +1,60 @@
package(default_visibility = ["//visibility:public"])
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",
],
importpath = "k8s.io/kubernetes/pkg/registry/certificates/certificates",
deps = [
"//pkg/api/legacyscheme:go_default_library",
"//pkg/apis/certificates:go_default_library",
"//pkg/apis/certificates/validation:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/internalversion:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/watch:go_default_library",
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library",
"//vendor/k8s.io/apiserver/pkg/storage/names:go_default_library",
],
)
go_test(
name = "go_default_test",
srcs = ["strategy_test.go"],
importpath = "k8s.io/kubernetes/pkg/registry/certificates/certificates",
library = ":go_default_library",
deps = [
"//pkg/apis/certificates:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/diff:go_default_library",
"//vendor/k8s.io/apiserver/pkg/authentication/user:go_default_library",
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
],
)
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 (
metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/watch"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/registry/rest"
"k8s.io/kubernetes/pkg/apis/certificates"
)
// Registry is an interface for things that know how to store CSRs.
type Registry interface {
ListCSRs(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (*certificates.CertificateSigningRequestList, error)
CreateCSR(ctx genericapirequest.Context, csr *certificates.CertificateSigningRequest, createValidation rest.ValidateObjectFunc) error
UpdateCSR(ctx genericapirequest.Context, csr *certificates.CertificateSigningRequest, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) 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 *metainternalversion.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 *metainternalversion.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, createValidation rest.ValidateObjectFunc) error {
_, err := s.Create(ctx, csr, createValidation, false)
return err
}
func (s *storage) UpdateCSR(ctx genericapirequest.Context, csr *certificates.CertificateSigningRequest, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) error {
_, _, err := s.Update(ctx, csr.Name, rest.DefaultUpdatedObjectInfo(csr), createValidation, updateValidation)
return err
}
func (s *storage) WatchCSRs(ctx genericapirequest.Context, options *metainternalversion.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,34 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["storage.go"],
importpath = "k8s.io/kubernetes/pkg/registry/certificates/certificates/storage",
deps = [
"//pkg/apis/certificates:go_default_library",
"//pkg/registry/certificates/certificates:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/generic/registry:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/rest: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,97 @@
/*
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/endpoints/request"
"k8s.io/apiserver/pkg/registry/generic"
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
"k8s.io/apiserver/pkg/registry/rest"
"k8s.io/kubernetes/pkg/apis/certificates"
csrregistry "k8s.io/kubernetes/pkg/registry/certificates/certificates"
)
// 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{} },
DefaultQualifiedResource: certificates.Resource("certificatesigningrequests"),
CreateStrategy: csrregistry.Strategy,
UpdateStrategy: csrregistry.Strategy,
DeleteStrategy: csrregistry.Strategy,
ExportStrategy: csrregistry.Strategy,
}
options := &generic.StoreOptions{RESTOptions: optsGetter}
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}
}
// Implement ShortNamesProvider
var _ rest.ShortNamesProvider = &REST{}
// ShortNames implements the ShortNamesProvider interface. Returns a list of short names for a resource.
func (r *REST) ShortNames() []string {
return []string{"csr"}
}
// 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, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) (runtime.Object, bool, error) {
return r.store.Update(ctx, name, objInfo, createValidation, updateValidation)
}
// 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, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) (runtime.Object, bool, error) {
return r.store.Update(ctx, name, objInfo, createValidation, updateValidation)
}

View File

@ -0,0 +1,191 @@
/*
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"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/validation/field"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/storage/names"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/apis/certificates"
"k8s.io/kubernetes/pkg/apis/certificates/validation"
)
// 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{legacyscheme.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
csr.Spec.Extra = 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()
if extra := user.GetExtra(); len(extra) > 0 {
csr.Spec.Extra = map[string]certificates.ExtraValue{}
for k, v := range extra {
csr.Spec.Extra[k] = certificates.ExtraValue(v)
}
}
}
// 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
for i := range newCSR.Status.Conditions {
if newCSR.Status.Conditions[i].LastUpdateTime.IsZero() {
newCSR.Status.Conditions[i].LastUpdateTime = metav1.Now()
}
}
}
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}
// PrepareForUpdate prepares the new certificate signing request by limiting
// the data that is updated to only the conditions. Also, if there is no
// existing LastUpdateTime on a condition, the current date/time will be set.
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
for i := range newCSR.Status.Conditions {
// The Conditions are an array of values, some of which may be
// pre-existing and unaltered by this update, so a LastUpdateTime is
// added only if one isn't already set.
if newCSR.Status.Conditions[i].LastUpdateTime.IsZero() {
newCSR.Status.Conditions[i].LastUpdateTime = metav1.Now()
}
}
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))
}

View File

@ -0,0 +1,122 @@
/*
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/endpoints/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"},
Extra: map[string]certapi.ExtraValue{"foo": {"bar"}},
},
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"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["storage_certificates.go"],
importpath = "k8s.io/kubernetes/pkg/registry/certificates/rest",
deps = [
"//pkg/api/legacyscheme:go_default_library",
"//pkg/apis/certificates:go_default_library",
"//pkg/registry/certificates/certificates/storage:go_default_library",
"//vendor/k8s.io/api/certificates/v1beta1:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library",
"//vendor/k8s.io/apiserver/pkg/server:go_default_library",
"//vendor/k8s.io/apiserver/pkg/server/storage: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,60 @@
/*
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 (
certificatesapiv1beta1 "k8s.io/api/certificates/v1beta1"
"k8s.io/apiserver/pkg/registry/generic"
"k8s.io/apiserver/pkg/registry/rest"
genericapiserver "k8s.io/apiserver/pkg/server"
serverstorage "k8s.io/apiserver/pkg/server/storage"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/apis/certificates"
certificatestore "k8s.io/kubernetes/pkg/registry/certificates/certificates/storage"
)
type RESTStorageProvider struct{}
func (p RESTStorageProvider) NewRESTStorage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) (genericapiserver.APIGroupInfo, bool) {
apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(certificates.GroupName, legacyscheme.Registry, legacyscheme.Scheme, legacyscheme.ParameterCodec, legacyscheme.Codecs)
// If you add a version here, be sure to add an entry in `k8s.io/kubernetes/cmd/kube-apiserver/app/aggregator.go with specific priorities.
// TODO refactor the plumbing to provide the information in the APIGroupInfo
if apiResourceConfigSource.AnyResourcesForVersionEnabled(certificatesapiv1beta1.SchemeGroupVersion) {
apiGroupInfo.VersionedResourcesStorageMap[certificatesapiv1beta1.SchemeGroupVersion.Version] = p.v1beta1Storage(apiResourceConfigSource, restOptionsGetter)
apiGroupInfo.GroupMeta.GroupVersion = certificatesapiv1beta1.SchemeGroupVersion
}
return apiGroupInfo, true
}
func (p RESTStorageProvider) v1beta1Storage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) map[string]rest.Storage {
version := certificatesapiv1beta1.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"])
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",
],
importpath = "k8s.io/kubernetes/pkg/registry/core/componentstatus",
deps = [
"//pkg/apis/core:go_default_library",
"//pkg/probe:go_default_library",
"//pkg/probe/http:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/internalversion:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/net:go_default_library",
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library",
],
)
go_test(
name = "go_default_test",
srcs = [
"rest_test.go",
"validator_test.go",
],
importpath = "k8s.io/kubernetes/pkg/registry/core/componentstatus",
library = ":go_default_library",
deps = [
"//pkg/apis/core:go_default_library",
"//pkg/probe:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/diff:go_default_library",
"//vendor/k8s.io/apiserver/pkg/endpoints/request: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,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,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 componentstatus
import (
"fmt"
"sync"
metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/registry/rest"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/probe"
)
type REST struct {
GetServersToValidate func() map[string]*Server
}
// NewStorage returns a new REST.
func NewStorage(serverRetriever func() map[string]*Server) *REST {
return &REST{
GetServersToValidate: serverRetriever,
}
}
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 *metainternalversion.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()
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
}
// Implement ShortNamesProvider
var _ rest.ShortNamesProvider = &REST{}
// ShortNames implements the ShortNamesProvider interface. Returns a list of short names for a resource.
func (r *REST) ShortNames() []string {
return []string{"cs"}
}

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