ConfigMap resource and its REST implementation

This commit is contained in:
Tamer Tas 2015-04-01 00:36:25 +03:00 committed by Paul Morie
parent 7242cf96fa
commit c798a6ef01
17 changed files with 917 additions and 0 deletions

View File

@ -65,6 +65,8 @@ func addKnownTypes() {
&Ingress{},
&IngressList{},
&api.ListOptions{},
&ConfigMap{},
&ConfigMapList{},
)
}
@ -86,3 +88,5 @@ func (obj *ThirdPartyResourceData) GetObjectKind() unversioned.ObjectKind {
func (obj *ThirdPartyResourceDataList) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
func (obj *Ingress) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
func (obj *IngressList) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
func (obj *ConfigMap) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
func (obj *ConfigMapList) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }

View File

@ -691,3 +691,26 @@ const (
LabelSelectorOpExists LabelSelectorOperator = "Exists"
LabelSelectorOpDoesNotExist LabelSelectorOperator = "DoesNotExist"
)
// ConfigMap holds configuration data for components or applications to consume.
type ConfigMap struct {
unversioned.TypeMeta `json:",inline"`
// Standard object metadata; More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata.
api.ObjectMeta `json:"metadata,omitempty"`
// Data contains the configuration data.
// Each key must be a valid DNS_SUBDOMAIN with an optional leading dot.
Data map[string]string `json:"data,omitempty"`
}
// ConfigMapList is a resource containing a list of ConfigMap objects.
type ConfigMapList struct {
unversioned.TypeMeta `json:",inline"`
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata
unversioned.ListMeta `json:"metadata,omitempty"`
// Items is the list of ConfigMaps.
Items []ConfigMap `json:"items,omitempty"`
}

View File

@ -118,5 +118,10 @@ func addDefaultingFuncs() {
obj.Spec.CPUUtilization = &CPUTargetUtilization{TargetPercentage: 80}
}
},
func(obj *ConfigMap) {
if obj.Data == nil {
obj.Data = make(map[string]string)
}
},
)
}

View File

@ -58,6 +58,8 @@ func addKnownTypes() {
&Ingress{},
&IngressList{},
&ListOptions{},
&ConfigMap{},
&ConfigMapList{},
)
}
@ -80,3 +82,5 @@ func (obj *ThirdPartyResourceDataList) GetObjectKind() unversioned.ObjectKind {
func (obj *Ingress) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
func (obj *IngressList) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
func (obj *ListOptions) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
func (obj *ConfigMap) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
func (obj *ConfigMapList) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }

View File

@ -719,3 +719,26 @@ const (
LabelSelectorOpExists LabelSelectorOperator = "Exists"
LabelSelectorOpDoesNotExist LabelSelectorOperator = "DoesNotExist"
)
// ConfigMap holds configuration data for pods to consume.
type ConfigMap struct {
unversioned.TypeMeta `json:",inline"`
// Standard object metadata; More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata.
v1.ObjectMeta `json:"metadata,omitempty"`
// Data contains the configuration data.
// Each key must be a valid DNS_SUBDOMAIN with an optional leading dot.
Data map[string]string `json:"data,omitempty"`
}
// ConfigMapList is a resource containing a list of ConfigMap objects.
type ConfigMapList struct {
unversioned.TypeMeta `json:",inline"`
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata
unversioned.ListMeta `json:"metadata,omitempty"`
// Items is the list of ConfigMaps.
Items []ConfigMap `json:"items,omitempty"`
}

View File

@ -17,6 +17,7 @@ limitations under the License.
package validation
import (
"fmt"
"net"
"regexp"
"strconv"
@ -593,3 +594,33 @@ func ValidateScale(scale *extensions.Scale) field.ErrorList {
return allErrs
}
// ValidateConfigMapName can be used to check whether the given ConfigMap name is valid.
// Prefix indicates this name will be used as part of generation, in which case
// trailing dashes are allowed.
func ValidateConfigMapName(name string, prefix bool) (bool, string) {
return apivalidation.NameIsDNSSubdomain(name, prefix)
}
// ValidateConfigMap tests whether required fields in the ConfigMap are set.
func ValidateConfigMap(cfg *extensions.ConfigMap) field.ErrorList {
allErrs := field.ErrorList{}
allErrs = append(allErrs, apivalidation.ValidateObjectMeta(&cfg.ObjectMeta, true, ValidateConfigMapName, field.NewPath("metadata"))...)
for key := range cfg.Data {
if !apivalidation.IsSecretKey(key) {
allErrs = append(allErrs, field.Invalid(field.NewPath("data").Key(key), key, fmt.Sprintf("must have at most %d characters and match regex %s", validation.DNS1123SubdomainMaxLength, apivalidation.SecretKeyFmt)))
}
}
return allErrs
}
// ValidateConfigMapUpdate tests if required fields in the ConfigMap are set.
func ValidateConfigMapUpdate(newCfg, oldCfg *extensions.ConfigMap) field.ErrorList {
allErrs := field.ErrorList{}
allErrs = append(allErrs, apivalidation.ValidateObjectMetaUpdate(&newCfg.ObjectMeta, &oldCfg.ObjectMeta, field.NewPath("metadata"))...)
allErrs = append(allErrs, ValidateConfigMap(newCfg)...)
return allErrs
}

View File

@ -1300,3 +1300,105 @@ func newInt(val int) *int {
*p = val
return p
}
func TestValidateConfigMap(t *testing.T) {
newConfigMap := func(name, namespace string, data map[string]string) extensions.ConfigMap {
return extensions.ConfigMap{
ObjectMeta: api.ObjectMeta{
Name: name,
Namespace: namespace,
},
Data: data,
}
}
var (
validConfigMap = newConfigMap("validname", "validns", map[string]string{"key": "value"})
maxKeyLength = newConfigMap("validname", "validns", map[string]string{strings.Repeat("a", 253): "value"})
emptyName = newConfigMap("", "validns", nil)
invalidName = newConfigMap("NoUppercaseOrSpecialCharsLike=Equals", "validns", nil)
emptyNs = newConfigMap("validname", "", nil)
invalidNs = newConfigMap("validname", "NoUppercaseOrSpecialCharsLike=Equals", nil)
invalidKey = newConfigMap("validname", "validns", map[string]string{"a..b": "value"})
leadingDotKey = newConfigMap("validname", "validns", map[string]string{".ab": "value"})
dotKey = newConfigMap("validname", "validns", map[string]string{".": "value"})
doubleDotKey = newConfigMap("validname", "validns", map[string]string{"..": "value"})
overMaxKeyLength = newConfigMap("validname", "validns", map[string]string{strings.Repeat("a", 254): "value"})
)
tests := map[string]struct {
cfg extensions.ConfigMap
isValid bool
}{
"valid": {validConfigMap, true},
"max key length": {maxKeyLength, true},
"leading dot key": {leadingDotKey, true},
"empty name": {emptyName, false},
"invalid name": {invalidName, false},
"invalid key": {invalidKey, false},
"empty namespace": {emptyNs, false},
"invalid namespace": {invalidNs, false},
"dot key": {dotKey, false},
"double dot key": {doubleDotKey, false},
"over max key length": {overMaxKeyLength, false},
}
for name, tc := range tests {
errs := ValidateConfigMap(&tc.cfg)
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 TestValidateConfigMapUpdate(t *testing.T) {
newConfigMap := func(version, name, namespace string, data map[string]string) extensions.ConfigMap {
return extensions.ConfigMap{
ObjectMeta: api.ObjectMeta{
Name: name,
Namespace: namespace,
ResourceVersion: version,
},
Data: data,
}
}
var (
validConfigMap = newConfigMap("1", "validname", "validns", map[string]string{"key": "value"})
noVersion = newConfigMap("", "validname", "validns", map[string]string{"key": "value"})
)
cases := []struct {
name string
newCfg extensions.ConfigMap
oldCfg extensions.ConfigMap
isValid bool
}{
{
name: "valid",
newCfg: validConfigMap,
oldCfg: validConfigMap,
isValid: true,
},
{
name: "invalid",
newCfg: noVersion,
oldCfg: validConfigMap,
isValid: false,
},
}
for _, tc := range cases {
errs := ValidateConfigMapUpdate(&tc.newCfg, &tc.oldCfg)
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)
}
}
}

View File

@ -0,0 +1,123 @@
/*
Copyright 2015 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package unversioned
import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/watch"
)
const (
ConfigMapResourceName string = "configmaps"
)
type ConfigMapsNamespacer interface {
ConfigMaps(namespace string) ConfigMapsInterface
}
type ConfigMapsInterface interface {
Get(string) (*extensions.ConfigMap, error)
List(opts api.ListOptions) (*extensions.ConfigMapList, error)
Create(*extensions.ConfigMap) (*extensions.ConfigMap, error)
Delete(string) error
Update(*extensions.ConfigMap) (*extensions.ConfigMap, error)
Watch(api.ListOptions) (watch.Interface, error)
}
type ConfigMaps struct {
client *ExtensionsClient
namespace string
}
// ConfigMaps should implement ConfigMapsInterface
var _ ConfigMapsInterface = &ConfigMaps{}
func newConfigMaps(c *ExtensionsClient, ns string) *ConfigMaps {
return &ConfigMaps{
client: c,
namespace: ns,
}
}
func (c *ConfigMaps) Get(name string) (*extensions.ConfigMap, error) {
result := &extensions.ConfigMap{}
err := c.client.Get().
Namespace(c.namespace).
Resource(ConfigMapResourceName).
Name(name).
Do().
Into(result)
return result, err
}
func (c *ConfigMaps) List(opts api.ListOptions) (*extensions.ConfigMapList, error) {
result := &extensions.ConfigMapList{}
err := c.client.Get().
Namespace(c.namespace).
Resource(ConfigMapResourceName).
VersionedParams(&opts, api.Scheme).
Do().
Into(result)
return result, err
}
func (c *ConfigMaps) Create(cfg *extensions.ConfigMap) (*extensions.ConfigMap, error) {
result := &extensions.ConfigMap{}
err := c.client.Post().
Namespace(c.namespace).
Resource(ConfigMapResourceName).
Body(cfg).
Do().
Into(result)
return result, err
}
func (c *ConfigMaps) Delete(name string) error {
return c.client.Delete().
Namespace(c.namespace).
Resource(ConfigMapResourceName).
Name(name).
Do().
Error()
}
func (c *ConfigMaps) Update(cfg *extensions.ConfigMap) (*extensions.ConfigMap, error) {
result := &extensions.ConfigMap{}
err := c.client.Put().
Namespace(c.namespace).
Resource(ConfigMapResourceName).
Name(cfg.Name).
Body(cfg).
Do().
Into(result)
return result, err
}
func (c *ConfigMaps) Watch(opts api.ListOptions) (watch.Interface, error) {
return c.client.Get().
Prefix("watch").
Namespace(c.namespace).
Resource(ConfigMapResourceName).
VersionedParams(&opts, api.Scheme).
Watch()
}

View File

@ -39,6 +39,7 @@ type ExtensionsInterface interface {
JobsNamespacer
IngressNamespacer
ThirdPartyResourceNamespacer
ConfigMapsNamespacer
}
// ExtensionsClient is used to interact with experimental Kubernetes features.
@ -101,6 +102,10 @@ func (c *ExtensionsClient) Ingress(namespace string) IngressInterface {
return newIngress(c, namespace)
}
func (c *ExtensionsClient) ConfigMaps(namespace string) ConfigMapsInterface {
return newConfigMaps(c, namespace)
}
func (c *ExtensionsClient) ThirdPartyResources(namespace string) ThirdPartyResourceInterface {
return newThirdPartyResources(c, namespace)
}

View File

@ -0,0 +1,79 @@
/*
Copyright 2015 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package testclient
import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/watch"
)
const (
configMapResourceName string = "configMaps"
)
// FakeConfigMaps implements ConfigMapInterface. Meant to be embedded into a struct to get a default
// implementation. This makes faking out just the method you want to test easier.
type FakeConfigMaps struct {
Fake *FakeExperimental
Namespace string
}
func (c *FakeConfigMaps) Get(name string) (*extensions.ConfigMap, error) {
obj, err := c.Fake.Invokes(NewGetAction(configMapResourceName, c.Namespace, name), &extensions.ConfigMap{})
if obj == nil {
return nil, err
}
return obj.(*extensions.ConfigMap), err
}
func (c *FakeConfigMaps) List(opts api.ListOptions) (*extensions.ConfigMapList, error) {
obj, err := c.Fake.Invokes(NewListAction(configMapResourceName, c.Namespace, opts), &extensions.ConfigMapList{})
if obj == nil {
return nil, err
}
return obj.(*extensions.ConfigMapList), err
}
func (c *FakeConfigMaps) Create(cfg *extensions.ConfigMap) (*extensions.ConfigMap, error) {
obj, err := c.Fake.Invokes(NewCreateAction(configMapResourceName, c.Namespace, cfg), cfg)
if obj == nil {
return nil, err
}
return obj.(*extensions.ConfigMap), err
}
func (c *FakeConfigMaps) Update(cfg *extensions.ConfigMap) (*extensions.ConfigMap, error) {
obj, err := c.Fake.Invokes(NewUpdateAction(configMapResourceName, c.Namespace, cfg), cfg)
if obj == nil {
return nil, err
}
return obj.(*extensions.ConfigMap), err
}
func (c *FakeConfigMaps) Delete(name string) error {
_, err := c.Fake.Invokes(NewDeleteAction(configMapResourceName, c.Namespace, name), &extensions.ConfigMap{})
return err
}
func (c *FakeConfigMaps) Watch(opts api.ListOptions) (watch.Interface, error) {
return c.Fake.InvokesWatch(NewWatchAction(configMapResourceName, c.Namespace, opts))
}

View File

@ -353,6 +353,10 @@ func (c *FakeExperimental) Ingress(namespace string) client.IngressInterface {
return &FakeIngress{Fake: c, Namespace: namespace}
}
func (c *FakeExperimental) ConfigMaps(namespace string) client.ConfigMapsInterface {
return &FakeConfigMaps{Fake: c, Namespace: namespace}
}
func (c *FakeExperimental) ThirdPartyResources(namespace string) client.ThirdPartyResourceInterface {
return &FakeThirdPartyResources{Fake: c, Namespace: namespace}
}

View File

@ -0,0 +1,20 @@
/*
Copyright 2015 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package configmap provides Registry interface
// and its REST implementation for storing
// ConfigMap API objects.
package configmap

View File

@ -0,0 +1,79 @@
/*
Copyright 2015 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package etcd
import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/registry/configmap"
"k8s.io/kubernetes/pkg/registry/generic"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/storage"
etcdgeneric "k8s.io/kubernetes/pkg/registry/generic/etcd"
)
// REST implements a RESTStorage for ConfigMap against etcd
type REST struct {
*etcdgeneric.Etcd
}
// NewREST returns a RESTStorage object that will work with ConfigMap objects.
func NewREST(s storage.Interface, storageDecorator generic.StorageDecorator) *REST {
prefix := "/configmaps"
newListFunc := func() runtime.Object { return &extensions.ConfigMapList{} }
storageInterface := storageDecorator(
s, 100, &extensions.ConfigMap{}, prefix, false, newListFunc)
store := &etcdgeneric.Etcd{
NewFunc: func() runtime.Object {
return &extensions.ConfigMap{}
},
// NewListFunc returns an object to store results of an etcd list.
NewListFunc: newListFunc,
// Produces a path that etcd understands, to the root of the resource
// by combining the namespace in the context with the given prefix.
KeyRootFunc: func(ctx api.Context) string {
return etcdgeneric.NamespaceKeyRootFunc(ctx, prefix)
},
// Produces a path that etcd understands, to the resource by combining
// the namespace in the context with the given prefix
KeyFunc: func(ctx api.Context, name string) (string, error) {
return etcdgeneric.NamespaceKeyFunc(ctx, prefix, name)
},
// Retrieves the name field of a ConfigMap object.
ObjectNameFunc: func(obj runtime.Object) (string, error) {
return obj.(*extensions.ConfigMap).Name, nil
},
// Matches objects based on labels/fields for list and watch
PredicateFunc: configmap.MatchConfigMap,
EndpointName: "configmaps",
CreateStrategy: configmap.Strategy,
UpdateStrategy: configmap.Strategy,
Storage: storageInterface,
}
return &REST{store}
}

View File

@ -0,0 +1,149 @@
/*
Copyright 2015 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package etcd
import (
"testing"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/registry/generic"
"k8s.io/kubernetes/pkg/registry/registrytest"
"k8s.io/kubernetes/pkg/runtime"
etcdtesting "k8s.io/kubernetes/pkg/storage/etcd/testing"
)
func newStorage(t *testing.T) (*REST, *etcdtesting.EtcdTestServer) {
etcdStorage, server := registrytest.NewEtcdStorage(t, "extensions")
return NewREST(etcdStorage, generic.UndecoratedStorage), server
}
func validNewConfigMap() *extensions.ConfigMap {
return &extensions.ConfigMap{
ObjectMeta: api.ObjectMeta{
Name: "foo",
Namespace: "default",
Labels: map[string]string{
"label-1": "value-1",
"label-2": "value-2",
},
},
Data: map[string]string{
"test": "data",
},
}
}
func TestCreate(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
test := registrytest.New(t, storage.Etcd)
validConfigMap := validNewConfigMap()
validConfigMap.ObjectMeta = api.ObjectMeta{
GenerateName: "foo-",
}
test.TestCreate(
validConfigMap,
&extensions.ConfigMap{
ObjectMeta: api.ObjectMeta{Name: "badName"},
Data: map[string]string{
"key": "value",
},
},
&extensions.ConfigMap{
ObjectMeta: api.ObjectMeta{Name: "name-2"},
Data: map[string]string{
"..dotfile": "do: nothing\n",
},
},
)
}
func TestUpdate(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
test := registrytest.New(t, storage.Etcd)
test.TestUpdate(
// valid
validNewConfigMap(),
// updateFunc
func(obj runtime.Object) runtime.Object {
cfg := obj.(*extensions.ConfigMap)
cfg.Data["update-test"] = "value"
return cfg
},
// invalid updateFunc
func(obj runtime.Object) runtime.Object {
cfg := obj.(*extensions.ConfigMap)
cfg.Data["badKey"] = "value"
return cfg
},
)
}
func TestDelete(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
test := registrytest.New(t, storage.Etcd)
test.TestDelete(validNewConfigMap())
}
func TestGet(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
test := registrytest.New(t, storage.Etcd)
test.TestGet(validNewConfigMap())
}
func TestList(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
test := registrytest.New(t, storage.Etcd)
test.TestList(validNewConfigMap())
}
func TestWatch(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
test := registrytest.New(t, storage.Etcd)
test.TestWatch(
validNewConfigMap(),
// matching labels
[]labels.Set{
{"label-1": "value-1"},
{"label-2": "value-2"},
},
// not matching labels
[]labels.Set{
{"foo": "bar"},
},
// matching fields
[]fields.Set{
{"metadata.namespace": "default"},
{"metadata.name": "foo"},
},
// not matching fields
[]fields.Set{
{"metadata.name": "bar"},
{"name": "foo"},
},
)
}

View File

@ -0,0 +1,92 @@
/*
Copyright 2015 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package configmap
import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/rest"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/watch"
)
// Registry is an interface for things that know how to store ConfigMaps.
type Registry interface {
ListConfigMaps(ctx api.Context, options *unversioned.ListOptions) (*extensions.ConfigMapList, error)
WatchConfigMaps(ctx api.Context, options *unversioned.ListOptions) (watch.Interface, error)
GetConfigMap(ctx api.Context, name string) (*extensions.ConfigMap, error)
CreateConfigMap(ctx api.Context, cfg *extensions.ConfigMap) (*extensions.ConfigMap, error)
UpdateConfigMap(ctx api.Context, cfg *extensions.ConfigMap) (*extensions.ConfigMap, error)
DeleteConfigMap(ctx api.Context, name string) error
}
// storage puts strong typing around storage calls
type storage struct {
rest.StandardStorage
}
// NewRegistry returns a new Registry interface for the given Storage. Any mismatched
// types will panic.
func NewRegistry(s rest.StandardStorage) Registry {
return &storage{s}
}
func (s *storage) ListConfigMaps(ctx api.Context, options *unversioned.ListOptions) (*extensions.ConfigMapList, error) {
obj, err := s.List(ctx, options)
if err != nil {
return nil, err
}
return obj.(*extensions.ConfigMapList), err
}
func (s *storage) WatchConfigMaps(ctx api.Context, options *unversioned.ListOptions) (watch.Interface, error) {
return s.Watch(ctx, options)
}
func (s *storage) GetConfigMap(ctx api.Context, name string) (*extensions.ConfigMap, error) {
obj, err := s.Get(ctx, name)
if err != nil {
return nil, err
}
return obj.(*extensions.ConfigMap), nil
}
func (s *storage) CreateConfigMap(ctx api.Context, cfg *extensions.ConfigMap) (*extensions.ConfigMap, error) {
obj, err := s.Create(ctx, cfg)
if err != nil {
return nil, err
}
return obj.(*extensions.ConfigMap), nil
}
func (s *storage) UpdateConfigMap(ctx api.Context, cfg *extensions.ConfigMap) (*extensions.ConfigMap, error) {
obj, _, err := s.Update(ctx, cfg)
if err != nil {
return nil, err
}
return obj.(*extensions.ConfigMap), nil
}
func (s *storage) DeleteConfigMap(ctx api.Context, name string) error {
_, err := s.Delete(ctx, name, nil)
return err
}

View File

@ -0,0 +1,105 @@
/*
Copyright 2015 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package configmap
import (
"fmt"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/rest"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/apis/extensions/validation"
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/registry/generic"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/util/validation/field"
)
// strategy implements behavior for ConfigMap objects
type strategy struct {
runtime.ObjectTyper
api.NameGenerator
}
// Strategy is the default logic that applies when creating and updating ConfigMap
// objects via the REST API.
var Strategy = strategy{api.Scheme, api.SimpleNameGenerator}
// Strategy should implement rest.RESTCreateStrategy
var _ rest.RESTCreateStrategy = Strategy
// Strategy should implement rest.RESTUpdateStrategy
var _ rest.RESTUpdateStrategy = Strategy
func (strategy) NamespaceScoped() bool {
return true
}
func (strategy) PrepareForCreate(obj runtime.Object) {
_ = obj.(*extensions.ConfigMap)
}
func (strategy) Validate(ctx api.Context, obj runtime.Object) field.ErrorList {
cfg := obj.(*extensions.ConfigMap)
return validation.ValidateConfigMap(cfg)
}
// Canonicalize normalizes the object after validation.
func (strategy) Canonicalize(obj runtime.Object) {
}
func (strategy) AllowCreateOnUpdate() bool {
return false
}
func (strategy) PrepareForUpdate(newObj, oldObj runtime.Object) {
_ = oldObj.(*extensions.ConfigMap)
_ = newObj.(*extensions.ConfigMap)
}
func (strategy) AllowUnconditionalUpdate() bool {
return true
}
func (strategy) ValidateUpdate(ctx api.Context, newObj, oldObj runtime.Object) field.ErrorList {
oldCfg, newCfg := oldObj.(*extensions.ConfigMap), newObj.(*extensions.ConfigMap)
return validation.ValidateConfigMapUpdate(newCfg, oldCfg)
}
// ConfigMapToSelectableFields returns a field set that represents the object for matching purposes.
func ConfigMapToSelectableFields(cfg *extensions.ConfigMap) fields.Set {
return generic.ObjectMetaFieldsSet(cfg.ObjectMeta, true)
}
// MatchConfigMap returns a generic matcher for a given label and field selector.
func MatchConfigMap(label labels.Selector, field fields.Selector) generic.Matcher {
return &generic.SelectionPredicate{
Label: label,
Field: field,
GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) {
cfg, ok := obj.(*extensions.ConfigMap)
if !ok {
return nil, nil, fmt.Errorf("given object is not of type ConfigMap")
}
return labels.Set(cfg.ObjectMeta.Labels), ConfigMapToSelectableFields(cfg), nil
},
}
}

View File

@ -0,0 +1,69 @@
/*
Copyright 2015 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package configmap
import (
"testing"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/extensions"
)
func TestConfigMapStrategy(t *testing.T) {
ctx := api.NewDefaultContext()
if !Strategy.NamespaceScoped() {
t.Errorf("ConfigMap must be namespace scoped")
}
if Strategy.AllowCreateOnUpdate() {
t.Errorf("ConfigMap should not allow create on update")
}
cfg := &extensions.ConfigMap{
ObjectMeta: api.ObjectMeta{
Name: "valid-config-data",
Namespace: api.NamespaceDefault,
},
Data: map[string]string{
"foo": "bar",
},
}
Strategy.PrepareForCreate(cfg)
errs := Strategy.Validate(ctx, cfg)
if len(errs) != 0 {
t.Errorf("unexpected error validating %v", errs)
}
newCfg := &extensions.ConfigMap{
ObjectMeta: api.ObjectMeta{
Name: "valid-config-data-2",
Namespace: api.NamespaceDefault,
ResourceVersion: "4",
},
Data: map[string]string{
"invalidKey": "updatedValue",
},
}
Strategy.PrepareForUpdate(newCfg, cfg)
errs = Strategy.ValidateUpdate(ctx, newCfg, cfg)
if len(errs) == 0 {
t.Errorf("Expected a validation error")
}
}