mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-25 04:33:26 +00:00
Merge pull request #12605 from DirectXMan12/refactor/merge-namespace-admission-controllers
Auto commit by PR queue bot
This commit is contained in:
commit
3b2d73e914
@ -48,7 +48,7 @@ Documentation for other releases can be found at
|
|||||||
- [SecurityContextDeny](#securitycontextdeny)
|
- [SecurityContextDeny](#securitycontextdeny)
|
||||||
- [ResourceQuota](#resourcequota)
|
- [ResourceQuota](#resourcequota)
|
||||||
- [LimitRanger](#limitranger)
|
- [LimitRanger](#limitranger)
|
||||||
- [NamespaceExists](#namespaceexists)
|
- [NamespaceExists (deprecated)](#namespaceexists-deprecated)
|
||||||
- [NamespaceAutoProvision (deprecated)](#namespaceautoprovision-deprecated)
|
- [NamespaceAutoProvision (deprecated)](#namespaceautoprovision-deprecated)
|
||||||
- [NamespaceLifecycle](#namespacelifecycle)
|
- [NamespaceLifecycle](#namespacelifecycle)
|
||||||
- [Is there a recommended set of plug-ins to use?](#is-there-a-recommended-set-of-plug-ins-to-use)
|
- [Is there a recommended set of plug-ins to use?](#is-there-a-recommended-set-of-plug-ins-to-use)
|
||||||
@ -129,29 +129,29 @@ applies a 0.1 CPU requirement to all Pods in the `default` namespace.
|
|||||||
|
|
||||||
See the [limitRange design doc](../design/admission_control_limit_range.md) and the [example of Limit Range](limitrange/) for more details.
|
See the [limitRange design doc](../design/admission_control_limit_range.md) and the [example of Limit Range](limitrange/) for more details.
|
||||||
|
|
||||||
### NamespaceExists
|
### NamespaceExists (deprecated)
|
||||||
|
|
||||||
This plug-in will observe all incoming requests that attempt to create a resource in a Kubernetes `Namespace`
|
This plug-in will observe all incoming requests that attempt to create a resource in a Kubernetes `Namespace`
|
||||||
and reject the request if the `Namespace` was not previously created. We strongly recommend running
|
and reject the request if the `Namespace` was not previously created. We strongly recommend running
|
||||||
this plug-in to ensure integrity of your data.
|
this plug-in to ensure integrity of your data.
|
||||||
|
|
||||||
|
The functionality of this admission controller has been merged into `NamespaceLifecycle`
|
||||||
|
|
||||||
### NamespaceAutoProvision (deprecated)
|
### NamespaceAutoProvision (deprecated)
|
||||||
|
|
||||||
This plug-in will observe all incoming requests that attempt to create a resource in a Kubernetes `Namespace`
|
This plug-in will observe all incoming requests that attempt to create a resource in a Kubernetes `Namespace`
|
||||||
and create a new `Namespace` if one did not already exist previously.
|
and create a new `Namespace` if one did not already exist previously.
|
||||||
|
|
||||||
We strongly recommend `NamespaceExists` over `NamespaceAutoProvision`.
|
We strongly recommend `NamespaceLifecycle` over `NamespaceAutoProvision`.
|
||||||
|
|
||||||
### NamespaceLifecycle
|
### NamespaceLifecycle
|
||||||
|
|
||||||
This plug-in enforces that a `Namespace` that is undergoing termination cannot have new objects created in it.
|
This plug-in enforces that a `Namespace` that is undergoing termination cannot have new objects created in it,
|
||||||
|
and ensures that requests in a non-existant `Namespace` are rejected.
|
||||||
|
|
||||||
A `Namespace` deletion kicks off a sequence of operations that remove all objects (pods, services, etc.) in that
|
A `Namespace` deletion kicks off a sequence of operations that remove all objects (pods, services, etc.) in that
|
||||||
namespace. In order to enforce integrity of that process, we strongly recommend running this plug-in.
|
namespace. In order to enforce integrity of that process, we strongly recommend running this plug-in.
|
||||||
|
|
||||||
Once `NamespaceAutoProvision` is deprecated, we anticipate `NamespaceLifecycle` and `NamespaceExists` will
|
|
||||||
be merged into a single plug-in that enforces the life-cycle of a `Namespace` in Kubernetes.
|
|
||||||
|
|
||||||
## Is there a recommended set of plug-ins to use?
|
## Is there a recommended set of plug-ins to use?
|
||||||
|
|
||||||
Yes.
|
Yes.
|
||||||
@ -159,7 +159,7 @@ Yes.
|
|||||||
For Kubernetes 1.0, we strongly recommend running the following set of admission control plug-ins (order matters):
|
For Kubernetes 1.0, we strongly recommend running the following set of admission control plug-ins (order matters):
|
||||||
|
|
||||||
```
|
```
|
||||||
--admission-control=NamespaceLifecycle,NamespaceExists,LimitRanger,SecurityContextDeny,ServiceAccount,ResourceQuota
|
--admission-control=NamespaceLifecycle,LimitRanger,SecurityContextDeny,ServiceAccount,ResourceQuota
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@ package lifecycle
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"time"
|
||||||
|
|
||||||
"k8s.io/kubernetes/pkg/admission"
|
"k8s.io/kubernetes/pkg/admission"
|
||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
@ -51,11 +52,8 @@ type lifecycle struct {
|
|||||||
func (l *lifecycle) Admit(a admission.Attributes) (err error) {
|
func (l *lifecycle) Admit(a admission.Attributes) (err error) {
|
||||||
|
|
||||||
// prevent deletion of immortal namespaces
|
// prevent deletion of immortal namespaces
|
||||||
if a.GetOperation() == admission.Delete {
|
if a.GetOperation() == admission.Delete && a.GetKind() == "Namespace" && l.immortalNamespaces.Has(a.GetName()) {
|
||||||
if a.GetKind() == "Namespace" && l.immortalNamespaces.Has(a.GetName()) {
|
return errors.NewForbidden(a.GetKind(), a.GetName(), fmt.Errorf("namespace can never be deleted"))
|
||||||
return errors.NewForbidden(a.GetKind(), a.GetName(), fmt.Errorf("namespace can never be deleted"))
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultVersion, kind, err := api.RESTMapper.VersionAndKindForResource(a.GetResource())
|
defaultVersion, kind, err := api.RESTMapper.VersionAndKindForResource(a.GetResource())
|
||||||
@ -78,15 +76,27 @@ func (l *lifecycle) Admit(a admission.Attributes) (err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return admission.NewForbidden(a, err)
|
return admission.NewForbidden(a, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// refuse to operate on non-existent namespaces
|
||||||
if !exists {
|
if !exists {
|
||||||
return nil
|
// in case of latency in our caches, make a call direct to storage to verify that it truly exists or not
|
||||||
}
|
namespaceObj, err = l.client.Namespaces().Get(a.GetNamespace())
|
||||||
namespace := namespaceObj.(*api.Namespace)
|
if err != nil {
|
||||||
if namespace.Status.Phase != api.NamespaceTerminating {
|
return admission.NewForbidden(a, fmt.Errorf("Namespace %s does not exist", a.GetNamespace()))
|
||||||
return nil
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return admission.NewForbidden(a, fmt.Errorf("Unable to create new content in namespace %s because it is being terminated.", a.GetNamespace()))
|
// ensure that we're not trying to create objects in terminating namespaces
|
||||||
|
if a.GetOperation() == admission.Create {
|
||||||
|
namespace := namespaceObj.(*api.Namespace)
|
||||||
|
if namespace.Status.Phase != api.NamespaceTerminating {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return admission.NewForbidden(a, fmt.Errorf("Unable to create new content in namespace %s because it is being terminated.", a.GetNamespace()))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewLifecycle creates a new namespace lifecycle admission control handler
|
// NewLifecycle creates a new namespace lifecycle admission control handler
|
||||||
@ -103,11 +113,11 @@ func NewLifecycle(c client.Interface) admission.Interface {
|
|||||||
},
|
},
|
||||||
&api.Namespace{},
|
&api.Namespace{},
|
||||||
store,
|
store,
|
||||||
0,
|
5*time.Minute,
|
||||||
)
|
)
|
||||||
reflector.Run()
|
reflector.Run()
|
||||||
return &lifecycle{
|
return &lifecycle{
|
||||||
Handler: admission.NewHandler(admission.Create, admission.Delete),
|
Handler: admission.NewHandler(admission.Create, admission.Update, admission.Delete),
|
||||||
client: c,
|
client: c,
|
||||||
store: store,
|
store: store,
|
||||||
immortalNamespaces: util.NewStringSet(api.NamespaceDefault),
|
immortalNamespaces: util.NewStringSet(api.NamespaceDefault),
|
||||||
|
@ -17,12 +17,15 @@ limitations under the License.
|
|||||||
package lifecycle
|
package lifecycle
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"k8s.io/kubernetes/pkg/admission"
|
"k8s.io/kubernetes/pkg/admission"
|
||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
"k8s.io/kubernetes/pkg/client/unversioned/cache"
|
"k8s.io/kubernetes/pkg/client/unversioned/cache"
|
||||||
"k8s.io/kubernetes/pkg/client/unversioned/testclient"
|
"k8s.io/kubernetes/pkg/client/unversioned/testclient"
|
||||||
|
"k8s.io/kubernetes/pkg/runtime"
|
||||||
|
"k8s.io/kubernetes/pkg/watch"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestAdmission
|
// TestAdmission
|
||||||
@ -36,14 +39,37 @@ func TestAdmission(t *testing.T) {
|
|||||||
Phase: api.NamespaceActive,
|
Phase: api.NamespaceActive,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
store := cache.NewStore(cache.IndexFuncToKeyFuncAdapter(cache.MetaNamespaceIndexFunc))
|
|
||||||
|
reactFunc := func(action testclient.Action) (runtime.Object, error) {
|
||||||
|
switch {
|
||||||
|
case action.Matches("get", "namespaces"):
|
||||||
|
if getAction, ok := action.(testclient.GetAction); ok && getAction.GetName() == namespaceObj.Name {
|
||||||
|
return namespaceObj, nil
|
||||||
|
}
|
||||||
|
case action.Matches("list", "namespaces"):
|
||||||
|
return &api.NamespaceList{Items: []api.Namespace{*namespaceObj}}, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("No result for action %v", action)
|
||||||
|
}
|
||||||
|
|
||||||
|
store := cache.NewStore(cache.MetaNamespaceKeyFunc)
|
||||||
store.Add(namespaceObj)
|
store.Add(namespaceObj)
|
||||||
mockClient := &testclient.Fake{}
|
fakeWatch := watch.NewFake()
|
||||||
|
mockClient := &testclient.Fake{Watch: fakeWatch, ReactFn: reactFunc}
|
||||||
lfhandler := NewLifecycle(mockClient).(*lifecycle)
|
lfhandler := NewLifecycle(mockClient).(*lifecycle)
|
||||||
lfhandler.store = store
|
lfhandler.store = store
|
||||||
handler := admission.NewChainHandler(lfhandler)
|
handler := admission.NewChainHandler(lfhandler)
|
||||||
pod := api.Pod{
|
pod := api.Pod{
|
||||||
ObjectMeta: api.ObjectMeta{Name: "123", Namespace: namespaceObj.Namespace},
|
ObjectMeta: api.ObjectMeta{Name: "123", Namespace: namespaceObj.Name},
|
||||||
|
Spec: api.PodSpec{
|
||||||
|
Volumes: []api.Volume{{Name: "vol"}},
|
||||||
|
Containers: []api.Container{{Name: "ctr", Image: "image"}},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
badPod := api.Pod{
|
||||||
|
ObjectMeta: api.ObjectMeta{Name: "456", Namespace: "doesnotexist"},
|
||||||
Spec: api.PodSpec{
|
Spec: api.PodSpec{
|
||||||
Volumes: []api.Volume{{Name: "vol"}},
|
Volumes: []api.Volume{{Name: "vol"}},
|
||||||
Containers: []api.Container{{Name: "ctr", Image: "image"}},
|
Containers: []api.Container{{Name: "ctr", Image: "image"}},
|
||||||
@ -88,4 +114,19 @@ func TestAdmission(t *testing.T) {
|
|||||||
t.Errorf("Did not expect an error %v", err)
|
t.Errorf("Did not expect an error %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// verify create/update/delete of object in non-existant namespace throws error
|
||||||
|
err = handler.Admit(admission.NewAttributesRecord(&badPod, "Pod", badPod.Namespace, badPod.Name, "pods", "", admission.Create, nil))
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("Expected an aerror that objects cannot be created in non-existant namespaces", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = handler.Admit(admission.NewAttributesRecord(&badPod, "Pod", badPod.Namespace, badPod.Name, "pods", "", admission.Update, nil))
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("Expected an aerror that objects cannot be updated in non-existant namespaces", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = handler.Admit(admission.NewAttributesRecord(&badPod, "Pod", badPod.Namespace, badPod.Name, "pods", "", admission.Delete, nil))
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("Expected an aerror that objects cannot be deleted in non-existant namespaces", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user