mirror of
https://github.com/k3s-io/kubernetes.git
synced 2026-01-05 15:37:24 +00:00
Add support for Namespace as Kind
Add example for using namespaces
This commit is contained in:
@@ -61,7 +61,7 @@ func TestValidNamespace(t *testing.T) {
|
||||
}
|
||||
|
||||
ctx = api.NewContext()
|
||||
ns := api.Namespace(ctx)
|
||||
ns := api.NamespaceValue(ctx)
|
||||
if ns != "" {
|
||||
t.Errorf("Expected the empty string")
|
||||
}
|
||||
|
||||
@@ -122,8 +122,9 @@ func init() {
|
||||
// the list of kinds that are scoped at the root of the api hierarchy
|
||||
// if a kind is not enumerated here, it is assumed to have a namespace scope
|
||||
kindToRootScope := map[string]bool{
|
||||
"Node": true,
|
||||
"Minion": true,
|
||||
"Node": true,
|
||||
"Minion": true,
|
||||
"Namespace": true,
|
||||
}
|
||||
|
||||
// enumerate all supported versions, get the kinds, and register with the mapper how to address our resources
|
||||
|
||||
@@ -51,7 +51,7 @@ var RESTScopeNamespaceLegacy = &restScope{
|
||||
|
||||
var RESTScopeNamespace = &restScope{
|
||||
name: RESTScopeNameNamespace,
|
||||
paramName: "ns",
|
||||
paramName: "namespaces",
|
||||
paramPath: true,
|
||||
paramDescription: "object name and auth scope, such as for teams and projects",
|
||||
}
|
||||
|
||||
@@ -50,6 +50,8 @@ func init() {
|
||||
&ResourceQuota{},
|
||||
&ResourceQuotaList{},
|
||||
&ResourceQuotaUsage{},
|
||||
&Namespace{},
|
||||
&NamespaceList{},
|
||||
)
|
||||
// Legacy names are supported
|
||||
Scheme.AddKnownTypeWithName("", "Minion", &Node{})
|
||||
@@ -81,3 +83,5 @@ func (*LimitRangeList) IsAnAPIObject() {}
|
||||
func (*ResourceQuota) IsAnAPIObject() {}
|
||||
func (*ResourceQuotaList) IsAnAPIObject() {}
|
||||
func (*ResourceQuotaUsage) IsAnAPIObject() {}
|
||||
func (*Namespace) IsAnAPIObject() {}
|
||||
func (*NamespaceList) IsAnAPIObject() {}
|
||||
|
||||
@@ -118,7 +118,7 @@ type nodeStrategy struct {
|
||||
// objects.
|
||||
var Nodes RESTCreateStrategy = nodeStrategy{api.Scheme, api.SimpleNameGenerator}
|
||||
|
||||
// NamespaceScoped is false for services.
|
||||
// NamespaceScoped is false for nodes.
|
||||
func (nodeStrategy) NamespaceScoped() bool {
|
||||
return false
|
||||
}
|
||||
@@ -134,3 +134,30 @@ func (nodeStrategy) Validate(obj runtime.Object) errors.ValidationErrorList {
|
||||
node := obj.(*api.Node)
|
||||
return validation.ValidateMinion(node)
|
||||
}
|
||||
|
||||
// namespaceStrategy implements behavior for nodes
|
||||
type namespaceStrategy struct {
|
||||
runtime.ObjectTyper
|
||||
api.NameGenerator
|
||||
}
|
||||
|
||||
// Namespaces is the default logic that applies when creating and updating Namespace
|
||||
// objects.
|
||||
var Namespaces RESTCreateStrategy = namespaceStrategy{api.Scheme, api.SimpleNameGenerator}
|
||||
|
||||
// NamespaceScoped is false for namespaces.
|
||||
func (namespaceStrategy) NamespaceScoped() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// ResetBeforeCreate clears fields that are not allowed to be set by end users on creation.
|
||||
func (namespaceStrategy) ResetBeforeCreate(obj runtime.Object) {
|
||||
_ = obj.(*api.Namespace)
|
||||
// Namespace allow *all* fields, including status, to be set.
|
||||
}
|
||||
|
||||
// Validate validates a new namespace.
|
||||
func (namespaceStrategy) Validate(obj runtime.Object) errors.ValidationErrorList {
|
||||
namespace := obj.(*api.Namespace)
|
||||
return validation.ValidateNamespace(namespace)
|
||||
}
|
||||
|
||||
@@ -833,6 +833,35 @@ type NodeList struct {
|
||||
Items []Node `json:"items"`
|
||||
}
|
||||
|
||||
// NamespaceSpec describes the attributes on a Namespace
|
||||
type NamespaceSpec struct {
|
||||
}
|
||||
|
||||
// NamespaceStatus is information about the current status of a Namespace.
|
||||
type NamespaceStatus struct {
|
||||
}
|
||||
|
||||
// A namespace provides a scope for Names.
|
||||
// Use of multiple namespaces is optional
|
||||
type Namespace struct {
|
||||
TypeMeta `json:",inline"`
|
||||
ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
// Spec defines the behavior of the Namespace.
|
||||
Spec NamespaceSpec `json:"spec,omitempty"`
|
||||
|
||||
// Status describes the current status of a Namespace
|
||||
Status NamespaceStatus `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
// NamespaceList is a list of Namespaces.
|
||||
type NamespaceList struct {
|
||||
TypeMeta `json:",inline"`
|
||||
ListMeta `json:"metadata,omitempty"`
|
||||
|
||||
Items []Namespace `json:"items"`
|
||||
}
|
||||
|
||||
// Binding is written by a scheduler to cause a pod to be bound to a host.
|
||||
type Binding struct {
|
||||
TypeMeta `json:",inline"`
|
||||
|
||||
@@ -731,6 +731,21 @@ func init() {
|
||||
}
|
||||
return nil
|
||||
},
|
||||
func(in *Namespace, out *newer.Namespace, s conversion.Scope) error {
|
||||
if err := s.Convert(&in.TypeMeta, &out.TypeMeta, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.Convert(&in.TypeMeta, &out.ObjectMeta, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.Convert(&in.Spec, &out.Spec, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.Convert(&in.Labels, &out.ObjectMeta.Labels, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
func(in *newer.LimitRangeSpec, out *LimitRangeSpec, s conversion.Scope) error {
|
||||
*out = LimitRangeSpec{}
|
||||
out.Limits = make([]LimitRangeItem, len(in.Limits), len(in.Limits))
|
||||
|
||||
@@ -51,6 +51,8 @@ func init() {
|
||||
&ResourceQuota{},
|
||||
&ResourceQuotaList{},
|
||||
&ResourceQuotaUsage{},
|
||||
&Namespace{},
|
||||
&NamespaceList{},
|
||||
)
|
||||
// Future names are supported
|
||||
api.Scheme.AddKnownTypeWithName("v1beta1", "Node", &Minion{})
|
||||
@@ -82,3 +84,5 @@ func (*LimitRangeList) IsAnAPIObject() {}
|
||||
func (*ResourceQuota) IsAnAPIObject() {}
|
||||
func (*ResourceQuotaList) IsAnAPIObject() {}
|
||||
func (*ResourceQuotaUsage) IsAnAPIObject() {}
|
||||
func (*Namespace) IsAnAPIObject() {}
|
||||
func (*NamespaceList) IsAnAPIObject() {}
|
||||
|
||||
@@ -666,6 +666,36 @@ type MinionList struct {
|
||||
Items []Minion `json:"items" description:"list of nodes"`
|
||||
}
|
||||
|
||||
// NamespaceSpec describes the attributes on a Namespace
|
||||
type NamespaceSpec struct {
|
||||
}
|
||||
|
||||
// NamespaceStatus is information about the current status of a Namespace.
|
||||
type NamespaceStatus struct {
|
||||
}
|
||||
|
||||
// A namespace provides a scope for Names.
|
||||
// Use of multiple namespaces is optional
|
||||
type Namespace struct {
|
||||
TypeMeta `json:",inline"`
|
||||
|
||||
// Labels
|
||||
Labels map[string]string `json:"labels,omitempty" description:"map of string keys and values that can be used to organize and categorize namespaces"`
|
||||
|
||||
// Spec defines the behavior of the Namespace.
|
||||
Spec NamespaceSpec `json:"spec,omitempty"`
|
||||
|
||||
// Status describes the current status of a Namespace
|
||||
Status NamespaceStatus `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
// NamespaceList is a list of Namespaces.
|
||||
type NamespaceList struct {
|
||||
TypeMeta `json:",inline"`
|
||||
|
||||
Items []Namespace `json:"items"`
|
||||
}
|
||||
|
||||
// Binding is written by a scheduler to cause a pod to be bound to a host.
|
||||
type Binding struct {
|
||||
TypeMeta `json:",inline"`
|
||||
|
||||
@@ -651,6 +651,21 @@ func init() {
|
||||
}
|
||||
return nil
|
||||
},
|
||||
func(in *Namespace, out *newer.Namespace, s conversion.Scope) error {
|
||||
if err := s.Convert(&in.TypeMeta, &out.TypeMeta, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.Convert(&in.TypeMeta, &out.ObjectMeta, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.Convert(&in.Spec, &out.Spec, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.Convert(&in.Labels, &out.ObjectMeta.Labels, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
func(in *newer.LimitRangeSpec, out *LimitRangeSpec, s conversion.Scope) error {
|
||||
*out = LimitRangeSpec{}
|
||||
out.Limits = make([]LimitRangeItem, len(in.Limits), len(in.Limits))
|
||||
|
||||
@@ -51,6 +51,8 @@ func init() {
|
||||
&ResourceQuota{},
|
||||
&ResourceQuotaList{},
|
||||
&ResourceQuotaUsage{},
|
||||
&Namespace{},
|
||||
&NamespaceList{},
|
||||
)
|
||||
// Future names are supported
|
||||
api.Scheme.AddKnownTypeWithName("v1beta2", "Node", &Minion{})
|
||||
@@ -82,3 +84,5 @@ func (*LimitRangeList) IsAnAPIObject() {}
|
||||
func (*ResourceQuota) IsAnAPIObject() {}
|
||||
func (*ResourceQuotaList) IsAnAPIObject() {}
|
||||
func (*ResourceQuotaUsage) IsAnAPIObject() {}
|
||||
func (*Namespace) IsAnAPIObject() {}
|
||||
func (*NamespaceList) IsAnAPIObject() {}
|
||||
|
||||
@@ -627,6 +627,36 @@ type MinionList struct {
|
||||
Items []Minion `json:"items" description:"list of nodes"`
|
||||
}
|
||||
|
||||
// NamespaceSpec describes the attributes on a Namespace
|
||||
type NamespaceSpec struct {
|
||||
}
|
||||
|
||||
// NamespaceStatus is information about the current status of a Namespace.
|
||||
type NamespaceStatus struct {
|
||||
}
|
||||
|
||||
// A namespace provides a scope for Names.
|
||||
// Use of multiple namespaces is optional
|
||||
type Namespace struct {
|
||||
TypeMeta `json:",inline"`
|
||||
|
||||
// Labels
|
||||
Labels map[string]string `json:"labels,omitempty" description:"map of string keys and values that can be used to organize and categorize namespaces"`
|
||||
|
||||
// Spec defines the behavior of the Namespace.
|
||||
Spec NamespaceSpec `json:"spec,omitempty"`
|
||||
|
||||
// Status describes the current status of a Namespace
|
||||
Status NamespaceStatus `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
// NamespaceList is a list of Namespaces.
|
||||
type NamespaceList struct {
|
||||
TypeMeta `json:",inline"`
|
||||
|
||||
Items []Namespace `json:"items"`
|
||||
}
|
||||
|
||||
// Binding is written by a scheduler to cause a pod to be bound to a host.
|
||||
type Binding struct {
|
||||
TypeMeta `json:",inline"`
|
||||
|
||||
@@ -51,6 +51,8 @@ func init() {
|
||||
&ResourceQuota{},
|
||||
&ResourceQuotaList{},
|
||||
&ResourceQuotaUsage{},
|
||||
&Namespace{},
|
||||
&NamespaceList{},
|
||||
)
|
||||
// Legacy names are supported
|
||||
api.Scheme.AddKnownTypeWithName("v1beta3", "Minion", &Node{})
|
||||
@@ -82,3 +84,5 @@ func (*LimitRangeList) IsAnAPIObject() {}
|
||||
func (*ResourceQuota) IsAnAPIObject() {}
|
||||
func (*ResourceQuotaList) IsAnAPIObject() {}
|
||||
func (*ResourceQuotaUsage) IsAnAPIObject() {}
|
||||
func (*Namespace) IsAnAPIObject() {}
|
||||
func (*NamespaceList) IsAnAPIObject() {}
|
||||
|
||||
@@ -848,6 +848,35 @@ type NodeList struct {
|
||||
Items []Node `json:"items"`
|
||||
}
|
||||
|
||||
// NamespaceSpec describes the attributes on a Namespace
|
||||
type NamespaceSpec struct {
|
||||
}
|
||||
|
||||
// NamespaceStatus is information about the current status of a Namespace.
|
||||
type NamespaceStatus struct {
|
||||
}
|
||||
|
||||
// A namespace provides a scope for Names.
|
||||
// Use of multiple namespaces is optional
|
||||
type Namespace struct {
|
||||
TypeMeta `json:",inline"`
|
||||
ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
// Spec defines the behavior of the Namespace.
|
||||
Spec NamespaceSpec `json:"spec,omitempty"`
|
||||
|
||||
// Status describes the current status of a Namespace
|
||||
Status NamespaceStatus `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
// NamespaceList is a list of Namespaces.
|
||||
type NamespaceList struct {
|
||||
TypeMeta `json:",inline"`
|
||||
ListMeta `json:"metadata,omitempty"`
|
||||
|
||||
Items []Namespace `json:"items"`
|
||||
}
|
||||
|
||||
// Binding is written by a scheduler to cause a pod to be bound to a node. Name is not
|
||||
// required for Bindings.
|
||||
type Binding struct {
|
||||
|
||||
@@ -109,6 +109,13 @@ func ValidateNodeName(name string, prefix bool) (bool, string) {
|
||||
return nameIsDNSSubdomain(name, prefix)
|
||||
}
|
||||
|
||||
// ValidateNamespaceName can be used to check whether the given namespace name is valid.
|
||||
// Prefix indicates this name will be used as part of generation, in which case
|
||||
// trailing dashes are allowed.
|
||||
func ValidateNamespaceName(name string, prefix bool) (bool, string) {
|
||||
return nameIsDNSSubdomain(name, prefix)
|
||||
}
|
||||
|
||||
// nameIsDNSSubdomain is a ValidateNameFunc for names that must be a DNS subdomain.
|
||||
func nameIsDNSSubdomain(name string, prefix bool) (bool, string) {
|
||||
if prefix {
|
||||
@@ -839,3 +846,27 @@ func ValidateResourceQuota(resourceQuota *api.ResourceQuota) errs.ValidationErro
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// ValidateNamespace tests if required fields are set.
|
||||
func ValidateNamespace(namespace *api.Namespace) errs.ValidationErrorList {
|
||||
allErrs := errs.ValidationErrorList{}
|
||||
allErrs = append(allErrs, ValidateObjectMeta(&namespace.ObjectMeta, false, ValidateNamespaceName).Prefix("metadata")...)
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// ValidateNamespaceUpdate tests to make sure a mamespace update can be applied. Modifies oldNamespace.
|
||||
func ValidateNamespaceUpdate(oldNamespace *api.Namespace, namespace *api.Namespace) errs.ValidationErrorList {
|
||||
allErrs := errs.ValidationErrorList{}
|
||||
allErrs = append(allErrs, ValidateObjectMetaUpdate(&oldNamespace.ObjectMeta, &namespace.ObjectMeta).Prefix("metadata")...)
|
||||
|
||||
// TODO: move reset function to its own location
|
||||
// Ignore metadata changes now that they have been tested
|
||||
oldNamespace.ObjectMeta = namespace.ObjectMeta
|
||||
|
||||
// TODO: Add a 'real' ValidationError type for this error and provide print actual diffs.
|
||||
if !api.Semantic.DeepEqual(oldNamespace, namespace) {
|
||||
glog.V(4).Infof("Update failed validation %#v vs %#v", oldNamespace, namespace)
|
||||
allErrs = append(allErrs, fmt.Errorf("update contains more than labels or annotation changes"))
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
@@ -2353,3 +2353,114 @@ func TestValidateResourceQuota(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateNamespace(t *testing.T) {
|
||||
validLabels := map[string]string{"a": "b"}
|
||||
invalidLabels := map[string]string{"NoUppercaseOrSpecialCharsLike=Equals": "b"}
|
||||
successCases := []api.Namespace{
|
||||
{
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc", Labels: validLabels},
|
||||
},
|
||||
{
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc-123"},
|
||||
},
|
||||
}
|
||||
for _, successCase := range successCases {
|
||||
if errs := ValidateNamespace(&successCase); len(errs) != 0 {
|
||||
t.Errorf("expected success: %v", errs)
|
||||
}
|
||||
}
|
||||
errorCases := map[string]struct {
|
||||
R api.Namespace
|
||||
D string
|
||||
}{
|
||||
"zero-length name": {
|
||||
api.Namespace{ObjectMeta: api.ObjectMeta{Name: ""}},
|
||||
"",
|
||||
},
|
||||
"defined-namespace": {
|
||||
api.Namespace{ObjectMeta: api.ObjectMeta{Name: "abc-123", Namespace: "makesnosense"}},
|
||||
"",
|
||||
},
|
||||
"invalid-labels": {
|
||||
api.Namespace{ObjectMeta: api.ObjectMeta{Name: "abc", Labels: invalidLabels}},
|
||||
"",
|
||||
},
|
||||
}
|
||||
for k, v := range errorCases {
|
||||
errs := ValidateNamespace(&v.R)
|
||||
if len(errs) == 0 {
|
||||
t.Errorf("expected failure for %s", k)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateNamespaceUpdate(t *testing.T) {
|
||||
tests := []struct {
|
||||
oldNamespace api.Namespace
|
||||
namespace api.Namespace
|
||||
valid bool
|
||||
}{
|
||||
{api.Namespace{}, api.Namespace{}, true},
|
||||
{api.Namespace{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "foo"}},
|
||||
api.Namespace{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "bar"},
|
||||
}, false},
|
||||
{api.Namespace{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "foo",
|
||||
Labels: map[string]string{"foo": "bar"},
|
||||
},
|
||||
}, api.Namespace{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "foo",
|
||||
Labels: map[string]string{"foo": "baz"},
|
||||
},
|
||||
}, true},
|
||||
{api.Namespace{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "foo",
|
||||
},
|
||||
}, api.Namespace{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "foo",
|
||||
Labels: map[string]string{"foo": "baz"},
|
||||
},
|
||||
}, true},
|
||||
{api.Namespace{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "foo",
|
||||
Labels: map[string]string{"bar": "foo"},
|
||||
},
|
||||
}, api.Namespace{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "foo",
|
||||
Labels: map[string]string{"foo": "baz"},
|
||||
},
|
||||
}, true},
|
||||
{api.Namespace{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "foo",
|
||||
Labels: map[string]string{"foo": "baz"},
|
||||
},
|
||||
}, api.Namespace{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "foo",
|
||||
Labels: map[string]string{"Foo": "baz"},
|
||||
},
|
||||
}, false},
|
||||
}
|
||||
for i, test := range tests {
|
||||
errs := ValidateNamespaceUpdate(&test.oldNamespace, &test.namespace)
|
||||
if test.valid && len(errs) > 0 {
|
||||
t.Errorf("%d: Unexpected error: %v", i, errs)
|
||||
t.Logf("%#v vs %#v", test.oldNamespace.ObjectMeta, test.namespace.ObjectMeta)
|
||||
}
|
||||
if !test.valid && len(errs) == 0 {
|
||||
t.Errorf("%d: Unexpected non-error", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user