mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 11:50:44 +00:00
Namespace.Spec.Finalizer support
This commit is contained in:
parent
cb7c781da5
commit
29c491ef2e
@ -95,3 +95,10 @@ func IsServiceIPSet(service *Service) bool {
|
|||||||
func IsServiceIPRequested(service *Service) bool {
|
func IsServiceIPRequested(service *Service) bool {
|
||||||
return service.Spec.PortalIP == ""
|
return service.Spec.PortalIP == ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var standardFinalizers = util.NewStringSet(
|
||||||
|
string(FinalizerKubernetes))
|
||||||
|
|
||||||
|
func IsStandardFinalizerName(str string) bool {
|
||||||
|
return standardFinalizers.Has(str)
|
||||||
|
}
|
||||||
|
@ -198,6 +198,9 @@ func FuzzerFor(t *testing.T, version string, src rand.Source) *fuzz.Fuzzer {
|
|||||||
c.FuzzNoCustom(s) // fuzz self without calling this function again
|
c.FuzzNoCustom(s) // fuzz self without calling this function again
|
||||||
s.Type = api.SecretTypeOpaque
|
s.Type = api.SecretTypeOpaque
|
||||||
},
|
},
|
||||||
|
func(s *api.NamespaceSpec, c fuzz.Continue) {
|
||||||
|
s.Finalizers = []api.FinalizerName{api.FinalizerKubernetes}
|
||||||
|
},
|
||||||
func(s *api.NamespaceStatus, c fuzz.Continue) {
|
func(s *api.NamespaceStatus, c fuzz.Continue) {
|
||||||
s.Phase = api.NamespaceActive
|
s.Phase = api.NamespaceActive
|
||||||
},
|
},
|
||||||
|
@ -966,8 +966,17 @@ type NodeList struct {
|
|||||||
|
|
||||||
// NamespaceSpec describes the attributes on a Namespace
|
// NamespaceSpec describes the attributes on a Namespace
|
||||||
type NamespaceSpec struct {
|
type NamespaceSpec struct {
|
||||||
|
// Finalizers is an opaque list of values that must be empty to permanently remove object from storage
|
||||||
|
Finalizers []FinalizerName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type FinalizerName string
|
||||||
|
|
||||||
|
// These are internal finalizer values to Kubernetes, must be qualified name unless defined here
|
||||||
|
const (
|
||||||
|
FinalizerKubernetes FinalizerName = "kubernetes"
|
||||||
|
)
|
||||||
|
|
||||||
// NamespaceStatus is information about the current status of a Namespace.
|
// NamespaceStatus is information about the current status of a Namespace.
|
||||||
type NamespaceStatus struct {
|
type NamespaceStatus struct {
|
||||||
// Phase is the current lifecycle phase of the namespace.
|
// Phase is the current lifecycle phase of the namespace.
|
||||||
|
@ -1426,4 +1426,17 @@ func init() {
|
|||||||
// If one of the conversion functions is malformed, detect it immediately.
|
// If one of the conversion functions is malformed, detect it immediately.
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
err = newer.Scheme.AddFieldLabelConversionFunc("v1beta1", "namespaces",
|
||||||
|
func(label, value string) (string, string, error) {
|
||||||
|
switch label {
|
||||||
|
case "status.phase":
|
||||||
|
return label, value, nil
|
||||||
|
default:
|
||||||
|
return "", "", fmt.Errorf("field label not supported: %s", label)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
// If one of the conversion functions is malformed, detect it immediately.
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -782,8 +782,17 @@ type MinionList struct {
|
|||||||
Items []Minion `json:"items" description:"list of nodes"`
|
Items []Minion `json:"items" description:"list of nodes"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type FinalizerName string
|
||||||
|
|
||||||
|
// These are internal finalizer values to Kubernetes, must be qualified name unless defined here
|
||||||
|
const (
|
||||||
|
FinalizerKubernetes FinalizerName = "kubernetes"
|
||||||
|
)
|
||||||
|
|
||||||
// NamespaceSpec describes the attributes on a Namespace
|
// NamespaceSpec describes the attributes on a Namespace
|
||||||
type NamespaceSpec struct {
|
type NamespaceSpec struct {
|
||||||
|
// Finalizers is an opaque list of values that must be empty to permanently remove object from storage
|
||||||
|
Finalizers []FinalizerName `json:"finalizers,omitempty" description:"an opaque list of values that must be empty to permanently remove object from storage"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NamespaceStatus is information about the current status of a Namespace.
|
// NamespaceStatus is information about the current status of a Namespace.
|
||||||
|
@ -1354,4 +1354,17 @@ func init() {
|
|||||||
// If one of the conversion functions is malformed, detect it immediately.
|
// If one of the conversion functions is malformed, detect it immediately.
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
err = newer.Scheme.AddFieldLabelConversionFunc("v1beta1", "namespaces",
|
||||||
|
func(label, value string) (string, string, error) {
|
||||||
|
switch label {
|
||||||
|
case "status.phase":
|
||||||
|
return label, value, nil
|
||||||
|
default:
|
||||||
|
return "", "", fmt.Errorf("field label not supported: %s", label)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
// If one of the conversion functions is malformed, detect it immediately.
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -798,8 +798,17 @@ type MinionList struct {
|
|||||||
Items []Minion `json:"items" description:"list of nodes"`
|
Items []Minion `json:"items" description:"list of nodes"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type FinalizerName string
|
||||||
|
|
||||||
|
// These are internal finalizer values to Kubernetes, must be qualified name unless defined here
|
||||||
|
const (
|
||||||
|
FinalizerKubernetes FinalizerName = "kubernetes"
|
||||||
|
)
|
||||||
|
|
||||||
// NamespaceSpec describes the attributes on a Namespace
|
// NamespaceSpec describes the attributes on a Namespace
|
||||||
type NamespaceSpec struct {
|
type NamespaceSpec struct {
|
||||||
|
// Finalizers is an opaque list of values that must be empty to permanently remove object from storage
|
||||||
|
Finalizers []FinalizerName `json:"finalizers,omitempty" description:"an opaque list of values that must be empty to permanently remove object from storage"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NamespaceStatus is information about the current status of a Namespace.
|
// NamespaceStatus is information about the current status of a Namespace.
|
||||||
|
@ -60,4 +60,17 @@ func init() {
|
|||||||
// If one of the conversion functions is malformed, detect it immediately.
|
// If one of the conversion functions is malformed, detect it immediately.
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
err = newer.Scheme.AddFieldLabelConversionFunc("v1beta1", "namespaces",
|
||||||
|
func(label, value string) (string, string, error) {
|
||||||
|
switch label {
|
||||||
|
case "status.phase":
|
||||||
|
return label, value, nil
|
||||||
|
default:
|
||||||
|
return "", "", fmt.Errorf("field label not supported: %s", label)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
// If one of the conversion functions is malformed, detect it immediately.
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -950,8 +950,17 @@ type NodeList struct {
|
|||||||
Items []Node `json:"items" description:"list of nodes"`
|
Items []Node `json:"items" description:"list of nodes"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type FinalizerName string
|
||||||
|
|
||||||
|
// These are internal finalizer values to Kubernetes, must be qualified name unless defined here
|
||||||
|
const (
|
||||||
|
FinalizerKubernetes FinalizerName = "kubernetes"
|
||||||
|
)
|
||||||
|
|
||||||
// NamespaceSpec describes the attributes on a Namespace
|
// NamespaceSpec describes the attributes on a Namespace
|
||||||
type NamespaceSpec struct {
|
type NamespaceSpec struct {
|
||||||
|
// Finalizers is an opaque list of values that must be empty to permanently remove object from storage
|
||||||
|
Finalizers []FinalizerName `json:"finalizers,omitempty" description:"an opaque list of values that must be empty to permanently remove object from storage"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NamespaceStatus is information about the current status of a Namespace.
|
// NamespaceStatus is information about the current status of a Namespace.
|
||||||
|
@ -1011,9 +1011,28 @@ func ValidateResourceQuotaStatusUpdate(newResourceQuota, oldResourceQuota *api.R
|
|||||||
func ValidateNamespace(namespace *api.Namespace) errs.ValidationErrorList {
|
func ValidateNamespace(namespace *api.Namespace) errs.ValidationErrorList {
|
||||||
allErrs := errs.ValidationErrorList{}
|
allErrs := errs.ValidationErrorList{}
|
||||||
allErrs = append(allErrs, ValidateObjectMeta(&namespace.ObjectMeta, false, ValidateNamespaceName).Prefix("metadata")...)
|
allErrs = append(allErrs, ValidateObjectMeta(&namespace.ObjectMeta, false, ValidateNamespaceName).Prefix("metadata")...)
|
||||||
|
for i := range namespace.Spec.Finalizers {
|
||||||
|
allErrs = append(allErrs, validateFinalizerName(string(namespace.Spec.Finalizers[i]))...)
|
||||||
|
}
|
||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validate finalizer names
|
||||||
|
func validateFinalizerName(stringValue string) errs.ValidationErrorList {
|
||||||
|
allErrs := errs.ValidationErrorList{}
|
||||||
|
if !util.IsQualifiedName(stringValue) {
|
||||||
|
return append(allErrs, fmt.Errorf("finalizer name: %v, %v", stringValue, qualifiedNameErrorMsg))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(strings.Split(stringValue, "/")) == 1 {
|
||||||
|
if !api.IsStandardFinalizerName(stringValue) {
|
||||||
|
return append(allErrs, fmt.Errorf("finalizer name: %v is neither a standard finalizer name nor is it fully qualified", stringValue))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return errs.ValidationErrorList{}
|
||||||
|
}
|
||||||
|
|
||||||
// ValidateNamespaceUpdate tests to make sure a mamespace update can be applied. Modifies oldNamespace.
|
// ValidateNamespaceUpdate tests to make sure a mamespace update can be applied. Modifies oldNamespace.
|
||||||
func ValidateNamespaceUpdate(oldNamespace *api.Namespace, namespace *api.Namespace) errs.ValidationErrorList {
|
func ValidateNamespaceUpdate(oldNamespace *api.Namespace, namespace *api.Namespace) errs.ValidationErrorList {
|
||||||
allErrs := errs.ValidationErrorList{}
|
allErrs := errs.ValidationErrorList{}
|
||||||
@ -1022,6 +1041,7 @@ func ValidateNamespaceUpdate(oldNamespace *api.Namespace, namespace *api.Namespa
|
|||||||
// TODO: move reset function to its own location
|
// TODO: move reset function to its own location
|
||||||
// Ignore metadata changes now that they have been tested
|
// Ignore metadata changes now that they have been tested
|
||||||
oldNamespace.ObjectMeta = namespace.ObjectMeta
|
oldNamespace.ObjectMeta = namespace.ObjectMeta
|
||||||
|
oldNamespace.Spec.Finalizers = namespace.Spec.Finalizers
|
||||||
|
|
||||||
// TODO: Add a 'real' ValidationError type for this error and provide print actual diffs.
|
// TODO: Add a 'real' ValidationError type for this error and provide print actual diffs.
|
||||||
if !api.Semantic.DeepEqual(oldNamespace, namespace) {
|
if !api.Semantic.DeepEqual(oldNamespace, namespace) {
|
||||||
@ -1036,9 +1056,20 @@ func ValidateNamespaceUpdate(oldNamespace *api.Namespace, namespace *api.Namespa
|
|||||||
func ValidateNamespaceStatusUpdate(newNamespace, oldNamespace *api.Namespace) errs.ValidationErrorList {
|
func ValidateNamespaceStatusUpdate(newNamespace, oldNamespace *api.Namespace) errs.ValidationErrorList {
|
||||||
allErrs := errs.ValidationErrorList{}
|
allErrs := errs.ValidationErrorList{}
|
||||||
allErrs = append(allErrs, ValidateObjectMetaUpdate(&oldNamespace.ObjectMeta, &newNamespace.ObjectMeta).Prefix("metadata")...)
|
allErrs = append(allErrs, ValidateObjectMetaUpdate(&oldNamespace.ObjectMeta, &newNamespace.ObjectMeta).Prefix("metadata")...)
|
||||||
if newNamespace.Status.Phase != oldNamespace.Status.Phase {
|
|
||||||
allErrs = append(allErrs, errs.NewFieldInvalid("status.phase", newNamespace.Status.Phase, "namespace phase cannot be changed directly"))
|
|
||||||
}
|
|
||||||
newNamespace.Spec = oldNamespace.Spec
|
newNamespace.Spec = oldNamespace.Spec
|
||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ValidateNamespaceFinalizeUpdate tests to see if the update is legal for an end user to make. newNamespace is updated with fields
|
||||||
|
// that cannot be changed.
|
||||||
|
func ValidateNamespaceFinalizeUpdate(newNamespace, oldNamespace *api.Namespace) errs.ValidationErrorList {
|
||||||
|
allErrs := errs.ValidationErrorList{}
|
||||||
|
allErrs = append(allErrs, ValidateObjectMetaUpdate(&oldNamespace.ObjectMeta, &newNamespace.ObjectMeta).Prefix("metadata")...)
|
||||||
|
for i := range newNamespace.Spec.Finalizers {
|
||||||
|
allErrs = append(allErrs, validateFinalizerName(string(newNamespace.Spec.Finalizers[i]))...)
|
||||||
|
}
|
||||||
|
newNamespace.ObjectMeta = oldNamespace.ObjectMeta
|
||||||
|
newNamespace.Status = oldNamespace.Status
|
||||||
|
fmt.Printf("NEW NAMESPACE FINALIZERS : %v\n", newNamespace.Spec.Finalizers)
|
||||||
|
return allErrs
|
||||||
|
}
|
||||||
|
@ -2206,6 +2206,90 @@ func TestValidateNamespace(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestValidateNamespaceFinalizeUpdate(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: "foo"},
|
||||||
|
Spec: api.NamespaceSpec{
|
||||||
|
Finalizers: []api.FinalizerName{"Foo"},
|
||||||
|
},
|
||||||
|
}, false},
|
||||||
|
{api.Namespace{
|
||||||
|
ObjectMeta: api.ObjectMeta{
|
||||||
|
Name: "foo"},
|
||||||
|
Spec: api.NamespaceSpec{
|
||||||
|
Finalizers: []api.FinalizerName{"foo.com/bar"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
api.Namespace{
|
||||||
|
ObjectMeta: api.ObjectMeta{
|
||||||
|
Name: "foo"},
|
||||||
|
Spec: api.NamespaceSpec{
|
||||||
|
Finalizers: []api.FinalizerName{"foo.com/bar", "what.com/bar"},
|
||||||
|
},
|
||||||
|
}, true},
|
||||||
|
}
|
||||||
|
for i, test := range tests {
|
||||||
|
errs := ValidateNamespaceFinalizeUpdate(&test.namespace, &test.oldNamespace)
|
||||||
|
if test.valid && len(errs) > 0 {
|
||||||
|
t.Errorf("%d: Unexpected error: %v", i, errs)
|
||||||
|
t.Logf("%#v vs %#v", test.oldNamespace, test.namespace)
|
||||||
|
}
|
||||||
|
if !test.valid && len(errs) == 0 {
|
||||||
|
t.Errorf("%d: Unexpected non-error", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValidateNamespaceStatusUpdate(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: "foo"},
|
||||||
|
Status: api.NamespaceStatus{
|
||||||
|
Phase: api.NamespaceTerminating,
|
||||||
|
},
|
||||||
|
}, true},
|
||||||
|
{api.Namespace{
|
||||||
|
ObjectMeta: api.ObjectMeta{
|
||||||
|
Name: "foo"}},
|
||||||
|
api.Namespace{
|
||||||
|
ObjectMeta: api.ObjectMeta{
|
||||||
|
Name: "bar"},
|
||||||
|
Status: api.NamespaceStatus{
|
||||||
|
Phase: api.NamespaceTerminating,
|
||||||
|
},
|
||||||
|
}, false},
|
||||||
|
}
|
||||||
|
for i, test := range tests {
|
||||||
|
errs := ValidateNamespaceStatusUpdate(&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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestValidateNamespaceUpdate(t *testing.T) {
|
func TestValidateNamespaceUpdate(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
oldNamespace api.Namespace
|
oldNamespace api.Namespace
|
||||||
|
@ -29,7 +29,7 @@ type FakeNamespaces struct {
|
|||||||
Fake *Fake
|
Fake *Fake
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *FakeNamespaces) List(selector labels.Selector) (*api.NamespaceList, error) {
|
func (c *FakeNamespaces) List(labels labels.Selector, field fields.Selector) (*api.NamespaceList, error) {
|
||||||
c.Fake.Actions = append(c.Fake.Actions, FakeAction{Action: "list-namespaces"})
|
c.Fake.Actions = append(c.Fake.Actions, FakeAction{Action: "list-namespaces"})
|
||||||
return api.Scheme.CopyOrDie(&c.Fake.NamespacesList).(*api.NamespaceList), nil
|
return api.Scheme.CopyOrDie(&c.Fake.NamespacesList).(*api.NamespaceList), nil
|
||||||
}
|
}
|
||||||
@ -58,3 +58,13 @@ func (c *FakeNamespaces) Watch(label labels.Selector, field fields.Selector, res
|
|||||||
c.Fake.Actions = append(c.Fake.Actions, FakeAction{Action: "watch-namespaces", Value: resourceVersion})
|
c.Fake.Actions = append(c.Fake.Actions, FakeAction{Action: "watch-namespaces", Value: resourceVersion})
|
||||||
return c.Fake.Watch, nil
|
return c.Fake.Watch, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *FakeNamespaces) Finalize(namespace *api.Namespace) (*api.Namespace, error) {
|
||||||
|
c.Fake.Actions = append(c.Fake.Actions, FakeAction{Action: "finalize-namespace", Value: namespace.Name})
|
||||||
|
return &api.Namespace{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *FakeNamespaces) Status(namespace *api.Namespace) (*api.Namespace, error) {
|
||||||
|
c.Fake.Actions = append(c.Fake.Actions, FakeAction{Action: "status-namespace", Value: namespace.Name})
|
||||||
|
return &api.Namespace{}, nil
|
||||||
|
}
|
||||||
|
@ -33,10 +33,12 @@ type NamespacesInterface interface {
|
|||||||
type NamespaceInterface interface {
|
type NamespaceInterface interface {
|
||||||
Create(item *api.Namespace) (*api.Namespace, error)
|
Create(item *api.Namespace) (*api.Namespace, error)
|
||||||
Get(name string) (result *api.Namespace, err error)
|
Get(name string) (result *api.Namespace, err error)
|
||||||
List(selector labels.Selector) (*api.NamespaceList, error)
|
List(label labels.Selector, field fields.Selector) (*api.NamespaceList, error)
|
||||||
Delete(name string) error
|
Delete(name string) error
|
||||||
Update(item *api.Namespace) (*api.Namespace, error)
|
Update(item *api.Namespace) (*api.Namespace, error)
|
||||||
Watch(label labels.Selector, field fields.Selector, resourceVersion string) (watch.Interface, error)
|
Watch(label labels.Selector, field fields.Selector, resourceVersion string) (watch.Interface, error)
|
||||||
|
Finalize(item *api.Namespace) (*api.Namespace, error)
|
||||||
|
Status(item *api.Namespace) (*api.Namespace, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// namespaces implements NamespacesInterface
|
// namespaces implements NamespacesInterface
|
||||||
@ -57,9 +59,13 @@ func (c *namespaces) Create(namespace *api.Namespace) (*api.Namespace, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// List lists all the namespaces in the cluster.
|
// List lists all the namespaces in the cluster.
|
||||||
func (c *namespaces) List(selector labels.Selector) (*api.NamespaceList, error) {
|
func (c *namespaces) List(label labels.Selector, field fields.Selector) (*api.NamespaceList, error) {
|
||||||
result := &api.NamespaceList{}
|
result := &api.NamespaceList{}
|
||||||
err := c.r.Get().Resource("namespaces").LabelsSelectorParam(api.LabelSelectorQueryParam(c.r.APIVersion()), selector).Do().Into(result)
|
err := c.r.Get().
|
||||||
|
Resource("namespaces").
|
||||||
|
LabelsSelectorParam(api.LabelSelectorQueryParam(c.r.APIVersion()), label).
|
||||||
|
FieldsSelectorParam(api.FieldSelectorQueryParam(c.r.APIVersion()), field).
|
||||||
|
Do().Into(result)
|
||||||
return result, err
|
return result, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,6 +80,28 @@ func (c *namespaces) Update(namespace *api.Namespace) (result *api.Namespace, er
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Finalize takes the representation of a namespace to update. Returns the server's representation of the namespace, and an error, if it occurs.
|
||||||
|
func (c *namespaces) Finalize(namespace *api.Namespace) (result *api.Namespace, err error) {
|
||||||
|
result = &api.Namespace{}
|
||||||
|
if len(namespace.ResourceVersion) == 0 {
|
||||||
|
err = fmt.Errorf("invalid update object, missing resource version: %v", namespace)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = c.r.Put().Resource("namespaces").Name(namespace.Name).SubResource("finalize").Body(namespace).Do().Into(result)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Status takes the representation of a namespace to update. Returns the server's representation of the namespace, and an error, if it occurs.
|
||||||
|
func (c *namespaces) Status(namespace *api.Namespace) (result *api.Namespace, err error) {
|
||||||
|
result = &api.Namespace{}
|
||||||
|
if len(namespace.ResourceVersion) == 0 {
|
||||||
|
err = fmt.Errorf("invalid update object, missing resource version: %v", namespace)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = c.r.Put().Resource("namespaces").Name(namespace.Name).SubResource("status").Body(namespace).Do().Into(result)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Get gets an existing namespace
|
// Get gets an existing namespace
|
||||||
func (c *namespaces) Get(name string) (*api.Namespace, error) {
|
func (c *namespaces) Get(name string) (*api.Namespace, error) {
|
||||||
if len(name) == 0 {
|
if len(name) == 0 {
|
||||||
|
@ -91,7 +91,7 @@ func TestNamespaceList(t *testing.T) {
|
|||||||
},
|
},
|
||||||
Response: Response{StatusCode: 200, Body: namespaceList},
|
Response: Response{StatusCode: 200, Body: namespaceList},
|
||||||
}
|
}
|
||||||
response, err := c.Setup().Namespaces().List(labels.Everything())
|
response, err := c.Setup().Namespaces().List(labels.Everything(), fields.Everything())
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("%#v should be nil.", err)
|
t.Errorf("%#v should be nil.", err)
|
||||||
@ -117,6 +117,9 @@ func TestNamespaceUpdate(t *testing.T) {
|
|||||||
"name": "baz",
|
"name": "baz",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
Spec: api.NamespaceSpec{
|
||||||
|
Finalizers: []api.FinalizerName{api.FinalizerKubernetes},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
c := &testClient{
|
c := &testClient{
|
||||||
Request: testRequest{Method: "PUT", Path: "/namespaces/foo"},
|
Request: testRequest{Method: "PUT", Path: "/namespaces/foo"},
|
||||||
@ -126,6 +129,28 @@ func TestNamespaceUpdate(t *testing.T) {
|
|||||||
c.Validate(t, receivedNamespace, err)
|
c.Validate(t, receivedNamespace, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNamespaceFinalize(t *testing.T) {
|
||||||
|
requestNamespace := &api.Namespace{
|
||||||
|
ObjectMeta: api.ObjectMeta{
|
||||||
|
Name: "foo",
|
||||||
|
ResourceVersion: "1",
|
||||||
|
Labels: map[string]string{
|
||||||
|
"foo": "bar",
|
||||||
|
"name": "baz",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Spec: api.NamespaceSpec{
|
||||||
|
Finalizers: []api.FinalizerName{api.FinalizerKubernetes},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
c := &testClient{
|
||||||
|
Request: testRequest{Method: "PUT", Path: "/namespaces/foo/finalize"},
|
||||||
|
Response: Response{StatusCode: 200, Body: requestNamespace},
|
||||||
|
}
|
||||||
|
receivedNamespace, err := c.Setup().Namespaces().Finalize(requestNamespace)
|
||||||
|
c.Validate(t, receivedNamespace, err)
|
||||||
|
}
|
||||||
|
|
||||||
func TestNamespaceDelete(t *testing.T) {
|
func TestNamespaceDelete(t *testing.T) {
|
||||||
c := &testClient{
|
c := &testClient{
|
||||||
Request: testRequest{Method: "DELETE", Path: "/namespaces/foo"},
|
Request: testRequest{Method: "DELETE", Path: "/namespaces/foo"},
|
||||||
|
@ -358,7 +358,7 @@ func (m *Master) init(c *Config) {
|
|||||||
resourceQuotaStorage, resourceQuotaStatusStorage := resourcequotaetcd.NewStorage(c.EtcdHelper)
|
resourceQuotaStorage, resourceQuotaStatusStorage := resourcequotaetcd.NewStorage(c.EtcdHelper)
|
||||||
secretRegistry := secret.NewEtcdRegistry(c.EtcdHelper)
|
secretRegistry := secret.NewEtcdRegistry(c.EtcdHelper)
|
||||||
|
|
||||||
namespaceStorage := namespaceetcd.NewStorage(c.EtcdHelper)
|
namespaceStorage, namespaceStatusStorage, namespaceFinalizeStorage := namespaceetcd.NewStorage(c.EtcdHelper)
|
||||||
m.namespaceRegistry = namespace.NewRegistry(namespaceStorage)
|
m.namespaceRegistry = namespace.NewRegistry(namespaceStorage)
|
||||||
|
|
||||||
// TODO: split me up into distinct storage registries
|
// TODO: split me up into distinct storage registries
|
||||||
@ -402,6 +402,8 @@ func (m *Master) init(c *Config) {
|
|||||||
"resourceQuotas": resourceQuotaStorage,
|
"resourceQuotas": resourceQuotaStorage,
|
||||||
"resourceQuotas/status": resourceQuotaStatusStorage,
|
"resourceQuotas/status": resourceQuotaStatusStorage,
|
||||||
"namespaces": namespaceStorage,
|
"namespaces": namespaceStorage,
|
||||||
|
"namespaces/status": namespaceStatusStorage,
|
||||||
|
"namespaces/finalize": namespaceFinalizeStorage,
|
||||||
"secrets": secret.NewStorage(secretRegistry),
|
"secrets": secret.NewStorage(secretRegistry),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,6 +17,8 @@ limitations under the License.
|
|||||||
package etcd
|
package etcd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/fields"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/fields"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
|
||||||
@ -25,15 +27,27 @@ import (
|
|||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/registry/namespace"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/registry/namespace"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/tools"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/tools"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// rest implements a RESTStorage for namespaces against etcd
|
// rest implements a RESTStorage for namespaces against etcd
|
||||||
type REST struct {
|
type REST struct {
|
||||||
*etcdgeneric.Etcd
|
*etcdgeneric.Etcd
|
||||||
|
status *etcdgeneric.Etcd
|
||||||
|
}
|
||||||
|
|
||||||
|
// StatusREST implements the REST endpoint for changing the status of a namespace.
|
||||||
|
type StatusREST struct {
|
||||||
|
store *etcdgeneric.Etcd
|
||||||
|
}
|
||||||
|
|
||||||
|
// FinalizeREST implements the REST endpoint for finalizing a namespace.
|
||||||
|
type FinalizeREST struct {
|
||||||
|
store *etcdgeneric.Etcd
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewStorage returns a RESTStorage object that will work against namespaces
|
// NewStorage returns a RESTStorage object that will work against namespaces
|
||||||
func NewStorage(h tools.EtcdHelper) *REST {
|
func NewStorage(h tools.EtcdHelper) (*REST, *StatusREST, *FinalizeREST) {
|
||||||
store := &etcdgeneric.Etcd{
|
store := &etcdgeneric.Etcd{
|
||||||
NewFunc: func() runtime.Object { return &api.Namespace{} },
|
NewFunc: func() runtime.Object { return &api.Namespace{} },
|
||||||
NewListFunc: func() runtime.Object { return &api.NamespaceList{} },
|
NewListFunc: func() runtime.Object { return &api.NamespaceList{} },
|
||||||
@ -56,5 +70,54 @@ func NewStorage(h tools.EtcdHelper) *REST {
|
|||||||
store.UpdateStrategy = namespace.Strategy
|
store.UpdateStrategy = namespace.Strategy
|
||||||
store.ReturnDeletedObject = true
|
store.ReturnDeletedObject = true
|
||||||
|
|
||||||
return &REST{Etcd: store}
|
statusStore := *store
|
||||||
|
statusStore.UpdateStrategy = namespace.StatusStrategy
|
||||||
|
|
||||||
|
finalizeStore := *store
|
||||||
|
finalizeStore.UpdateStrategy = namespace.FinalizeStrategy
|
||||||
|
|
||||||
|
return &REST{Etcd: store, status: &statusStore}, &StatusREST{store: &statusStore}, &FinalizeREST{store: &finalizeStore}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete enforces life-cycle rules for namespace termination
|
||||||
|
func (r *REST) Delete(ctx api.Context, name string, options *api.DeleteOptions) (runtime.Object, error) {
|
||||||
|
nsObj, err := r.Get(ctx, name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace := nsObj.(*api.Namespace)
|
||||||
|
|
||||||
|
// upon first request to delete, we switch the phase to start namespace termination
|
||||||
|
if namespace.DeletionTimestamp == nil {
|
||||||
|
now := util.Now()
|
||||||
|
namespace.DeletionTimestamp = &now
|
||||||
|
namespace.Status.Phase = api.NamespaceTerminating
|
||||||
|
result, _, err := r.status.Update(ctx, namespace)
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// prior to final deletion, we must ensure that finalizers is empty
|
||||||
|
if len(namespace.Spec.Finalizers) != 0 {
|
||||||
|
err = fmt.Errorf("Unable to delete namespace %v because finalizers is not empty %v", namespace.Name, namespace.Spec.Finalizers)
|
||||||
|
}
|
||||||
|
return r.Etcd.Delete(ctx, name, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *StatusREST) New() runtime.Object {
|
||||||
|
return &api.Namespace{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update alters the status subset of an object.
|
||||||
|
func (r *StatusREST) Update(ctx api.Context, obj runtime.Object) (runtime.Object, bool, error) {
|
||||||
|
return r.store.Update(ctx, obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *FinalizeREST) New() runtime.Object {
|
||||||
|
return &api.Namespace{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update alters the status finalizers subset of an object.
|
||||||
|
func (r *FinalizeREST) Update(ctx api.Context, obj runtime.Object) (runtime.Object, bool, error) {
|
||||||
|
return r.store.Update(ctx, obj)
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ func newHelper(t *testing.T) (*tools.FakeEtcdClient, tools.EtcdHelper) {
|
|||||||
|
|
||||||
func newStorage(t *testing.T) (*REST, *tools.FakeEtcdClient, tools.EtcdHelper) {
|
func newStorage(t *testing.T) (*REST, *tools.FakeEtcdClient, tools.EtcdHelper) {
|
||||||
fakeEtcdClient, h := newHelper(t)
|
fakeEtcdClient, h := newHelper(t)
|
||||||
storage := NewStorage(h)
|
storage, _, _ := NewStorage(h)
|
||||||
return storage, fakeEtcdClient, h
|
return storage, fakeEtcdClient, h
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,7 +69,7 @@ func TestStorage(t *testing.T) {
|
|||||||
|
|
||||||
func TestCreate(t *testing.T) {
|
func TestCreate(t *testing.T) {
|
||||||
fakeEtcdClient, helper := newHelper(t)
|
fakeEtcdClient, helper := newHelper(t)
|
||||||
storage := NewStorage(helper)
|
storage, _, _ := NewStorage(helper)
|
||||||
test := resttest.New(t, storage, fakeEtcdClient.SetError)
|
test := resttest.New(t, storage, fakeEtcdClient.SetError)
|
||||||
namespace := validNewNamespace()
|
namespace := validNewNamespace()
|
||||||
namespace.ObjectMeta = api.ObjectMeta{}
|
namespace.ObjectMeta = api.ObjectMeta{}
|
||||||
@ -94,7 +94,7 @@ func expectNamespace(t *testing.T, out runtime.Object) (*api.Namespace, bool) {
|
|||||||
|
|
||||||
func TestCreateSetsFields(t *testing.T) {
|
func TestCreateSetsFields(t *testing.T) {
|
||||||
fakeEtcdClient, helper := newHelper(t)
|
fakeEtcdClient, helper := newHelper(t)
|
||||||
storage := NewStorage(helper)
|
storage, _, _ := NewStorage(helper)
|
||||||
namespace := validNewNamespace()
|
namespace := validNewNamespace()
|
||||||
_, err := storage.Create(api.NewDefaultContext(), namespace)
|
_, err := storage.Create(api.NewDefaultContext(), namespace)
|
||||||
if err != fakeEtcdClient.Err {
|
if err != fakeEtcdClient.Err {
|
||||||
@ -124,7 +124,7 @@ func TestListEmptyNamespaceList(t *testing.T) {
|
|||||||
E: fakeEtcdClient.NewError(tools.EtcdErrorCodeNotFound),
|
E: fakeEtcdClient.NewError(tools.EtcdErrorCodeNotFound),
|
||||||
}
|
}
|
||||||
|
|
||||||
storage := NewStorage(helper)
|
storage, _, _ := NewStorage(helper)
|
||||||
namespaces, err := storage.List(api.NewContext(), labels.Everything(), fields.Everything())
|
namespaces, err := storage.List(api.NewContext(), labels.Everything(), fields.Everything())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Unexpected error: %v", err)
|
t.Errorf("Unexpected error: %v", err)
|
||||||
@ -157,7 +157,7 @@ func TestListNamespaceList(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
storage := NewStorage(helper)
|
storage, _, _ := NewStorage(helper)
|
||||||
namespacesObj, err := storage.List(api.NewDefaultContext(), labels.Everything(), fields.Everything())
|
namespacesObj, err := storage.List(api.NewDefaultContext(), labels.Everything(), fields.Everything())
|
||||||
namespaces := namespacesObj.(*api.NamespaceList)
|
namespaces := namespacesObj.(*api.NamespaceList)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -204,7 +204,7 @@ func TestListNamespaceListSelection(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
storage := NewStorage(helper)
|
storage, _, _ := NewStorage(helper)
|
||||||
ctx := api.NewDefaultContext()
|
ctx := api.NewDefaultContext()
|
||||||
table := []struct {
|
table := []struct {
|
||||||
label, field string
|
label, field string
|
||||||
@ -252,9 +252,11 @@ func TestListNamespaceListSelection(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestNamespaceDecode(t *testing.T) {
|
func TestNamespaceDecode(t *testing.T) {
|
||||||
storage := NewStorage(tools.EtcdHelper{})
|
_, helper := newHelper(t)
|
||||||
|
storage, _, _ := NewStorage(helper)
|
||||||
expected := validNewNamespace()
|
expected := validNewNamespace()
|
||||||
expected.Status.Phase = api.NamespaceActive
|
expected.Status.Phase = api.NamespaceActive
|
||||||
|
expected.Spec.Finalizers = []api.FinalizerName{api.FinalizerKubernetes}
|
||||||
body, err := latest.Codec.Encode(expected)
|
body, err := latest.Codec.Encode(expected)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
@ -281,7 +283,7 @@ func TestGet(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
storage := NewStorage(helper)
|
storage, _, _ := NewStorage(helper)
|
||||||
obj, err := storage.Get(api.NewContext(), "foo")
|
obj, err := storage.Get(api.NewContext(), "foo")
|
||||||
namespace := obj.(*api.Namespace)
|
namespace := obj.(*api.Namespace)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -311,8 +313,9 @@ func TestDeleteNamespace(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
storage := NewStorage(helper)
|
storage, _, _ := NewStorage(helper)
|
||||||
_, err := storage.Delete(api.NewDefaultContext(), "foo", nil)
|
_, err := storage.Delete(api.NewDefaultContext(), "foo", nil)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -45,10 +45,27 @@ func (namespaceStrategy) NamespaceScoped() bool {
|
|||||||
|
|
||||||
// ResetBeforeCreate clears fields that are not allowed to be set by end users on creation.
|
// ResetBeforeCreate clears fields that are not allowed to be set by end users on creation.
|
||||||
func (namespaceStrategy) ResetBeforeCreate(obj runtime.Object) {
|
func (namespaceStrategy) ResetBeforeCreate(obj runtime.Object) {
|
||||||
|
// on create, status is active
|
||||||
namespace := obj.(*api.Namespace)
|
namespace := obj.(*api.Namespace)
|
||||||
namespace.Status = api.NamespaceStatus{
|
namespace.Status = api.NamespaceStatus{
|
||||||
Phase: api.NamespaceActive,
|
Phase: api.NamespaceActive,
|
||||||
}
|
}
|
||||||
|
// on create, we require the kubernetes value
|
||||||
|
// we cannot use this in defaults conversion because we let it get removed over life of object
|
||||||
|
hasKubeFinalizer := false
|
||||||
|
for i := range namespace.Spec.Finalizers {
|
||||||
|
if namespace.Spec.Finalizers[i] == api.FinalizerKubernetes {
|
||||||
|
hasKubeFinalizer = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !hasKubeFinalizer {
|
||||||
|
if len(namespace.Spec.Finalizers) == 0 {
|
||||||
|
namespace.Spec.Finalizers = []api.FinalizerName{api.FinalizerKubernetes}
|
||||||
|
} else {
|
||||||
|
namespace.Spec.Finalizers = append(namespace.Spec.Finalizers, api.FinalizerKubernetes)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates a new namespace.
|
// Validate validates a new namespace.
|
||||||
@ -74,10 +91,19 @@ type namespaceStatusStrategy struct {
|
|||||||
var StatusStrategy = namespaceStatusStrategy{Strategy}
|
var StatusStrategy = namespaceStatusStrategy{Strategy}
|
||||||
|
|
||||||
func (namespaceStatusStrategy) ValidateUpdate(obj, old runtime.Object) fielderrors.ValidationErrorList {
|
func (namespaceStatusStrategy) ValidateUpdate(obj, old runtime.Object) fielderrors.ValidationErrorList {
|
||||||
// TODO: merge valid fields after update
|
|
||||||
return validation.ValidateNamespaceStatusUpdate(obj.(*api.Namespace), old.(*api.Namespace))
|
return validation.ValidateNamespaceStatusUpdate(obj.(*api.Namespace), old.(*api.Namespace))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type namespaceFinalizeStrategy struct {
|
||||||
|
namespaceStrategy
|
||||||
|
}
|
||||||
|
|
||||||
|
var FinalizeStrategy = namespaceFinalizeStrategy{Strategy}
|
||||||
|
|
||||||
|
func (namespaceFinalizeStrategy) ValidateUpdate(obj, old runtime.Object) fielderrors.ValidationErrorList {
|
||||||
|
return validation.ValidateNamespaceFinalizeUpdate(obj.(*api.Namespace), old.(*api.Namespace))
|
||||||
|
}
|
||||||
|
|
||||||
// MatchNamespace returns a generic matcher for a given label and field selector.
|
// MatchNamespace returns a generic matcher for a given label and field selector.
|
||||||
func MatchNamespace(label labels.Selector, field fields.Selector) generic.Matcher {
|
func MatchNamespace(label labels.Selector, field fields.Selector) generic.Matcher {
|
||||||
return generic.MatcherFunc(func(obj runtime.Object) (bool, error) {
|
return generic.MatcherFunc(func(obj runtime.Object) (bool, error) {
|
||||||
|
@ -87,7 +87,7 @@ func NewProvision(c client.Interface) admission.Interface {
|
|||||||
reflector := cache.NewReflector(
|
reflector := cache.NewReflector(
|
||||||
&cache.ListWatch{
|
&cache.ListWatch{
|
||||||
ListFunc: func() (runtime.Object, error) {
|
ListFunc: func() (runtime.Object, error) {
|
||||||
return c.Namespaces().List(labels.Everything())
|
return c.Namespaces().List(labels.Everything(), fields.Everything())
|
||||||
},
|
},
|
||||||
WatchFunc: func(resourceVersion string) (watch.Interface, error) {
|
WatchFunc: func(resourceVersion string) (watch.Interface, error) {
|
||||||
return c.Namespaces().Watch(labels.Everything(), fields.Everything(), resourceVersion)
|
return c.Namespaces().Watch(labels.Everything(), fields.Everything(), resourceVersion)
|
||||||
|
@ -86,7 +86,7 @@ func NewExists(c client.Interface) admission.Interface {
|
|||||||
reflector := cache.NewReflector(
|
reflector := cache.NewReflector(
|
||||||
&cache.ListWatch{
|
&cache.ListWatch{
|
||||||
ListFunc: func() (runtime.Object, error) {
|
ListFunc: func() (runtime.Object, error) {
|
||||||
return c.Namespaces().List(labels.Everything())
|
return c.Namespaces().List(labels.Everything(), fields.Everything())
|
||||||
},
|
},
|
||||||
WatchFunc: func(resourceVersion string) (watch.Interface, error) {
|
WatchFunc: func(resourceVersion string) (watch.Interface, error) {
|
||||||
return c.Namespaces().Watch(labels.Everything(), fields.Everything(), resourceVersion)
|
return c.Namespaces().Watch(labels.Everything(), fields.Everything(), resourceVersion)
|
||||||
|
@ -91,7 +91,7 @@ func NewLifecycle(c client.Interface) admission.Interface {
|
|||||||
reflector := cache.NewReflector(
|
reflector := cache.NewReflector(
|
||||||
&cache.ListWatch{
|
&cache.ListWatch{
|
||||||
ListFunc: func() (runtime.Object, error) {
|
ListFunc: func() (runtime.Object, error) {
|
||||||
return c.Namespaces().List(labels.Everything())
|
return c.Namespaces().List(labels.Everything(), fields.Everything())
|
||||||
},
|
},
|
||||||
WatchFunc: func(resourceVersion string) (watch.Interface, error) {
|
WatchFunc: func(resourceVersion string) (watch.Interface, error) {
|
||||||
return c.Namespaces().Watch(labels.Everything(), fields.Everything(), resourceVersion)
|
return c.Namespaces().Watch(labels.Everything(), fields.Everything(), resourceVersion)
|
||||||
|
Loading…
Reference in New Issue
Block a user