Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
K
k3s
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Jacklull
k3s
Commits
0339ef79
Commit
0339ef79
authored
Jul 26, 2016
by
derekwaynecarr
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Fix usage of shared informer in namespace admission controllers
parent
2af3f03f
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
275 additions
and
101 deletions
+275
-101
handler.go
pkg/admission/handler.go
+41
-0
types.go
pkg/admission/types.go
+1
-1
admission.go
plugin/pkg/admission/namespace/autoprovision/admission.go
+13
-7
admission_test.go
...n/pkg/admission/namespace/autoprovision/admission_test.go
+106
-87
admission.go
plugin/pkg/admission/namespace/exists/admission.go
+13
-6
admission_test.go
plugin/pkg/admission/namespace/exists/admission_test.go
+101
-0
No files found.
pkg/admission/handler.go
View file @
0339ef79
...
@@ -17,13 +17,25 @@ limitations under the License.
...
@@ -17,13 +17,25 @@ limitations under the License.
package
admission
package
admission
import
(
import
(
"time"
"k8s.io/kubernetes/pkg/util/sets"
"k8s.io/kubernetes/pkg/util/sets"
)
)
const
(
// timeToWaitForReady is the amount of time to wait to let an admission controller to be ready to satisfy a request.
// this is useful when admission controllers need to warm their caches before letting requests through.
timeToWaitForReady
=
10
*
time
.
Second
)
// ReadyFunc is a function that returns true if the admission controller is ready to handle requests.
type
ReadyFunc
func
()
bool
// Handler is a base for admission control handlers that
// Handler is a base for admission control handlers that
// support a predefined set of operations
// support a predefined set of operations
type
Handler
struct
{
type
Handler
struct
{
operations
sets
.
String
operations
sets
.
String
readyFunc
ReadyFunc
}
}
// Handles returns true for methods that this handler supports
// Handles returns true for methods that this handler supports
...
@@ -42,3 +54,32 @@ func NewHandler(ops ...Operation) *Handler {
...
@@ -42,3 +54,32 @@ func NewHandler(ops ...Operation) *Handler {
operations
:
operations
,
operations
:
operations
,
}
}
}
}
// SetReadyFunc allows late registration of a ReadyFunc to know if the handler is ready to process requests.
func
(
h
*
Handler
)
SetReadyFunc
(
readyFunc
ReadyFunc
)
{
h
.
readyFunc
=
readyFunc
}
// WaitForReady will wait for the readyFunc (if registered) to return ready, and in case of timeout, will return false.
func
(
h
*
Handler
)
WaitForReady
()
bool
{
// there is no ready func configured, so we return immediately
if
h
.
readyFunc
==
nil
{
return
true
}
return
h
.
waitForReadyInternal
(
time
.
After
(
timeToWaitForReady
))
}
func
(
h
*
Handler
)
waitForReadyInternal
(
timeout
<-
chan
time
.
Time
)
bool
{
// there is no configured ready func, so return immediately
if
h
.
readyFunc
==
nil
{
return
true
}
for
!
h
.
readyFunc
()
{
select
{
case
<-
time
.
After
(
100
*
time
.
Millisecond
)
:
case
<-
timeout
:
return
h
.
readyFunc
()
}
}
return
true
}
pkg/admission/types.go
View file @
0339ef79
...
@@ -26,7 +26,7 @@ type Validator interface {
...
@@ -26,7 +26,7 @@ type Validator interface {
Validate
()
error
Validate
()
error
}
}
// Wants
NamespaceInformer defines a function which sets NamespaceInformer
for admission plugins that need it
// Wants
InformerFactory defines a function which sets InformerFactory
for admission plugins that need it
type
WantsInformerFactory
interface
{
type
WantsInformerFactory
interface
{
SetInformerFactory
(
informers
.
SharedInformerFactory
)
SetInformerFactory
(
informers
.
SharedInformerFactory
)
Validator
Validator
...
...
plugin/pkg/admission/namespace/autoprovision/admission.go
View file @
0339ef79
...
@@ -22,9 +22,11 @@ import (
...
@@ -22,9 +22,11 @@ import (
clientset
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
clientset
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"fmt"
"fmt"
"k8s.io/kubernetes/pkg/admission"
"k8s.io/kubernetes/pkg/admission"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/controller/framework"
"k8s.io/kubernetes/pkg/controller/framework/informers"
"k8s.io/kubernetes/pkg/controller/framework/informers"
)
)
...
@@ -39,8 +41,8 @@ func init() {
...
@@ -39,8 +41,8 @@ func init() {
// It is useful in deployments that do not want to restrict creation of a namespace prior to its usage.
// It is useful in deployments that do not want to restrict creation of a namespace prior to its usage.
type
provision
struct
{
type
provision
struct
{
*
admission
.
Handler
*
admission
.
Handler
client
clientset
.
Interface
client
clientset
.
Interface
informerFactory
informers
.
SharedInformerFactory
namespaceInformer
framework
.
SharedIndexInformer
}
}
var
_
=
admission
.
WantsInformerFactory
(
&
provision
{})
var
_
=
admission
.
WantsInformerFactory
(
&
provision
{})
...
@@ -52,7 +54,10 @@ func (p *provision) Admit(a admission.Attributes) (err error) {
...
@@ -52,7 +54,10 @@ func (p *provision) Admit(a admission.Attributes) (err error) {
if
len
(
a
.
GetNamespace
())
==
0
||
a
.
GetKind
()
.
GroupKind
()
==
api
.
Kind
(
"Namespace"
)
{
if
len
(
a
.
GetNamespace
())
==
0
||
a
.
GetKind
()
.
GroupKind
()
==
api
.
Kind
(
"Namespace"
)
{
return
nil
return
nil
}
}
// we need to wait for our caches to warm
if
!
p
.
WaitForReady
()
{
return
admission
.
NewForbidden
(
a
,
fmt
.
Errorf
(
"not yet ready to handle request"
))
}
namespace
:=
&
api
.
Namespace
{
namespace
:=
&
api
.
Namespace
{
ObjectMeta
:
api
.
ObjectMeta
{
ObjectMeta
:
api
.
ObjectMeta
{
Name
:
a
.
GetNamespace
(),
Name
:
a
.
GetNamespace
(),
...
@@ -60,7 +65,7 @@ func (p *provision) Admit(a admission.Attributes) (err error) {
...
@@ -60,7 +65,7 @@ func (p *provision) Admit(a admission.Attributes) (err error) {
},
},
Status
:
api
.
NamespaceStatus
{},
Status
:
api
.
NamespaceStatus
{},
}
}
_
,
exists
,
err
:=
p
.
informerFactory
.
Namespaces
()
.
Informer
()
.
GetStore
()
.
Get
(
namespace
)
_
,
exists
,
err
:=
p
.
namespaceInformer
.
GetStore
()
.
Get
(
namespace
)
if
err
!=
nil
{
if
err
!=
nil
{
return
admission
.
NewForbidden
(
a
,
err
)
return
admission
.
NewForbidden
(
a
,
err
)
}
}
...
@@ -83,12 +88,13 @@ func NewProvision(c clientset.Interface) admission.Interface {
...
@@ -83,12 +88,13 @@ func NewProvision(c clientset.Interface) admission.Interface {
}
}
func
(
p
*
provision
)
SetInformerFactory
(
f
informers
.
SharedInformerFactory
)
{
func
(
p
*
provision
)
SetInformerFactory
(
f
informers
.
SharedInformerFactory
)
{
p
.
informerFactory
=
f
p
.
namespaceInformer
=
f
.
Namespaces
()
.
Informer
()
p
.
SetReadyFunc
(
p
.
namespaceInformer
.
HasSynced
)
}
}
func
(
p
*
provision
)
Validate
()
error
{
func
(
p
*
provision
)
Validate
()
error
{
if
p
.
informerFactory
==
nil
{
if
p
.
namespaceInformer
==
nil
{
return
fmt
.
Errorf
(
"
namespace autoprovision plugin needs SharedInformerFactory
"
)
return
fmt
.
Errorf
(
"
missing namespaceInformer
"
)
}
}
return
nil
return
nil
}
}
plugin/pkg/admission/namespace/autoprovision/admission_test.go
View file @
0339ef79
...
@@ -17,12 +17,15 @@ limitations under the License.
...
@@ -17,12 +17,15 @@ limitations under the License.
package
autoprovision
package
autoprovision
import
(
import
(
"fmt"
"testing"
"testing"
"time"
"time"
"k8s.io/kubernetes/pkg/admission"
"k8s.io/kubernetes/pkg/admission"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/api/unversioned"
clientset
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake"
"k8s.io/kubernetes/pkg/client/testing/core"
"k8s.io/kubernetes/pkg/client/testing/core"
"k8s.io/kubernetes/pkg/controller/framework/informers"
"k8s.io/kubernetes/pkg/controller/framework/informers"
...
@@ -30,124 +33,140 @@ import (
...
@@ -30,124 +33,140 @@ import (
"k8s.io/kubernetes/pkg/util/wait"
"k8s.io/kubernetes/pkg/util/wait"
)
)
// TestAdmission verifies a namespace is created on create requests for namespace managed resources
// newHandlerForTest returns the admission controller configured for testing.
func
TestAdmission
(
t
*
testing
.
T
)
{
func
newHandlerForTest
(
c
clientset
.
Interface
)
(
admission
.
Interface
,
informers
.
SharedInformerFactory
,
error
)
{
namespace
:=
"test"
f
:=
informers
.
NewSharedInformerFactory
(
c
,
5
*
time
.
Minute
)
handler
:=
NewProvision
(
c
)
plugins
:=
[]
admission
.
Interface
{
handler
}
pluginInitializer
:=
admission
.
NewPluginInitializer
(
f
)
pluginInitializer
.
Initialize
(
plugins
)
err
:=
admission
.
Validate
(
plugins
)
return
handler
,
f
,
err
}
// newMockClientForTest creates a mock client that returns a client configured for the specified list of namespaces.
func
newMockClientForTest
(
namespaces
[]
string
)
*
fake
.
Clientset
{
mockClient
:=
&
fake
.
Clientset
{}
mockClient
:=
&
fake
.
Clientset
{}
informerFactory
:=
informers
.
NewSharedInformerFactory
(
mockClient
,
5
*
time
.
Minute
)
mockClient
.
AddReactor
(
"list"
,
"namespaces"
,
func
(
action
core
.
Action
)
(
bool
,
runtime
.
Object
,
error
)
{
informerFactory
.
Namespaces
()
namespaceList
:=
&
api
.
NamespaceList
{
informerFactory
.
Start
(
wait
.
NeverStop
)
ListMeta
:
unversioned
.
ListMeta
{
handler
:=
&
provision
{
ResourceVersion
:
fmt
.
Sprintf
(
"%d"
,
len
(
namespaces
)),
client
:
mockClient
,
},
informerFactory
:
informerFactory
,
}
}
for
i
,
ns
:=
range
namespaces
{
pod
:=
api
.
Pod
{
namespaceList
.
Items
=
append
(
namespaceList
.
Items
,
api
.
Namespace
{
ObjectMeta
:
api
.
ObjectMeta
{
Name
:
ns
,
ResourceVersion
:
fmt
.
Sprintf
(
"%d"
,
i
),
},
})
}
return
true
,
namespaceList
,
nil
})
return
mockClient
}
// newPod returns a new pod for the specified namespace
func
newPod
(
namespace
string
)
api
.
Pod
{
return
api
.
Pod
{
ObjectMeta
:
api
.
ObjectMeta
{
Name
:
"123"
,
Namespace
:
namespace
},
ObjectMeta
:
api
.
ObjectMeta
{
Name
:
"123"
,
Namespace
:
namespace
},
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"
}},
},
},
}
}
err
:=
handler
.
Admit
(
admission
.
NewAttributesRecord
(
&
pod
,
nil
,
api
.
Kind
(
"Pod"
)
.
WithVersion
(
"version"
),
pod
.
Namespace
,
pod
.
Name
,
api
.
Resource
(
"pods"
)
.
WithVersion
(
"version"
),
""
,
admission
.
Create
,
nil
))
}
// hasCreateNamespaceAction returns true if it has the create namespace action
func
hasCreateNamespaceAction
(
mockClient
*
fake
.
Clientset
)
bool
{
for
_
,
action
:=
range
mockClient
.
Actions
()
{
if
action
.
GetVerb
()
==
"create"
&&
action
.
GetResource
()
.
Resource
==
"namespaces"
{
return
true
}
}
return
false
}
// TestAdmission verifies a namespace is created on create requests for namespace managed resources
func
TestAdmission
(
t
*
testing
.
T
)
{
namespace
:=
"test"
mockClient
:=
newMockClientForTest
([]
string
{})
handler
,
informerFactory
,
err
:=
newHandlerForTest
(
mockClient
)
if
err
!=
nil
{
if
err
!=
nil
{
t
.
Errorf
(
"
Unexpected error returned from admission handler"
)
t
.
Errorf
(
"
unexpected error initializing handler: %v"
,
err
)
}
}
actions
:=
mockClient
.
Actions
()
informerFactory
.
Start
(
wait
.
NeverStop
)
if
len
(
actions
)
!=
1
{
t
.
Errorf
(
"Expected a create-namespace request"
)
pod
:=
newPod
(
namespace
)
err
=
handler
.
Admit
(
admission
.
NewAttributesRecord
(
&
pod
,
nil
,
api
.
Kind
(
"Pod"
)
.
WithVersion
(
"version"
),
pod
.
Namespace
,
pod
.
Name
,
api
.
Resource
(
"pods"
)
.
WithVersion
(
"version"
),
""
,
admission
.
Create
,
nil
))
if
err
!=
nil
{
t
.
Errorf
(
"unexpected error returned from admission handler"
)
}
}
if
!
actions
[
0
]
.
Matches
(
"create"
,
"namespaces"
)
{
if
!
hasCreateNamespaceAction
(
mockClient
)
{
t
.
Errorf
(
"
Expected a create-namespace request to be made via the client
"
)
t
.
Errorf
(
"
expected create namespace action
"
)
}
}
}
}
// TestAdmissionNamespaceExists verifies that no client call is made when a namespace already exists
// TestAdmissionNamespaceExists verifies that no client call is made when a namespace already exists
// func TestAdmissionNamespaceExists(t *testing.T) {
func
TestAdmissionNamespaceExists
(
t
*
testing
.
T
)
{
// namespace := "test"
namespace
:=
"test"
// mockClient := &fake.Clientset{}
mockClient
:=
newMockClientForTest
([]
string
{
namespace
})
// informerFactory := informers.NewSharedInformerFactory(mockClient, 5*time.Minute)
handler
,
informerFactory
,
err
:=
newHandlerForTest
(
mockClient
)
// informerFactory.Namespaces().Informer().GetStore().Add(&api.Namespace{
if
err
!=
nil
{
// ObjectMeta: api.ObjectMeta{Name: namespace},
t
.
Errorf
(
"unexpected error initializing handler: %v"
,
err
)
// })
}
// informerFactory.Start(wait.NeverStop)
informerFactory
.
Start
(
wait
.
NeverStop
)
// handler := &provision{
// client: mockClient,
pod
:=
newPod
(
namespace
)
// informerFactory: informerFactory,
err
=
handler
.
Admit
(
admission
.
NewAttributesRecord
(
&
pod
,
nil
,
api
.
Kind
(
"Pod"
)
.
WithVersion
(
"version"
),
pod
.
Namespace
,
pod
.
Name
,
api
.
Resource
(
"pods"
)
.
WithVersion
(
"version"
),
""
,
admission
.
Create
,
nil
))
// }
if
err
!=
nil
{
// pod := api.Pod{
t
.
Errorf
(
"unexpected error returned from admission handler"
)
// ObjectMeta: api.ObjectMeta{Name: "123", Namespace: namespace},
}
// Spec: api.PodSpec{
if
hasCreateNamespaceAction
(
mockClient
)
{
// Volumes: []api.Volume{{Name: "vol"}},
t
.
Errorf
(
"unexpected create namespace action"
)
// Containers: []api.Container{{Name: "ctr", Image: "image"}},
}
// },
}
// }
// err := handler.Admit(admission.NewAttributesRecord(&pod, nil, api.Kind("Pod").WithVersion("version"), pod.Namespace, pod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, nil))
// if err != nil {
// t.Errorf("Unexpected error returned from admission handler")
// }
// if len(mockClient.Actions()) != 0 {
// t.Errorf("No client request should have been made")
// }
// }
// TestIgnoreAdmission validates that a request is ignored if its not a create
// TestIgnoreAdmission validates that a request is ignored if its not a create
func
TestIgnoreAdmission
(
t
*
testing
.
T
)
{
func
TestIgnoreAdmission
(
t
*
testing
.
T
)
{
namespace
:=
"test"
namespace
:=
"test"
mockClient
:=
&
fake
.
Clientset
{}
mockClient
:=
newMockClientForTest
([]
string
{})
handler
:=
admission
.
NewChainHandler
(
NewProvision
(
mockClient
))
handler
,
informerFactory
,
err
:=
newHandlerForTest
(
mockClient
)
pod
:=
api
.
Pod
{
if
err
!=
nil
{
ObjectMeta
:
api
.
ObjectMeta
{
Name
:
"123"
,
Namespace
:
namespace
},
t
.
Errorf
(
"unexpected error initializing handler: %v"
,
err
)
Spec
:
api
.
PodSpec
{
Volumes
:
[]
api
.
Volume
{{
Name
:
"vol"
}},
Containers
:
[]
api
.
Container
{{
Name
:
"ctr"
,
Image
:
"image"
}},
},
}
}
err
:=
handler
.
Admit
(
admission
.
NewAttributesRecord
(
&
pod
,
nil
,
api
.
Kind
(
"Pod"
)
.
WithVersion
(
"version"
),
pod
.
Namespace
,
pod
.
Name
,
api
.
Resource
(
"pods"
)
.
WithVersion
(
"version"
),
""
,
admission
.
Update
,
nil
))
informerFactory
.
Start
(
wait
.
NeverStop
)
chainHandler
:=
admission
.
NewChainHandler
(
handler
)
pod
:=
newPod
(
namespace
)
err
=
chainHandler
.
Admit
(
admission
.
NewAttributesRecord
(
&
pod
,
nil
,
api
.
Kind
(
"Pod"
)
.
WithVersion
(
"version"
),
pod
.
Namespace
,
pod
.
Name
,
api
.
Resource
(
"pods"
)
.
WithVersion
(
"version"
),
""
,
admission
.
Update
,
nil
))
if
err
!=
nil
{
if
err
!=
nil
{
t
.
Errorf
(
"
U
nexpected error returned from admission handler"
)
t
.
Errorf
(
"
u
nexpected error returned from admission handler"
)
}
}
if
len
(
mockClient
.
Actions
())
!=
0
{
if
hasCreateNamespaceAction
(
mockClient
)
{
t
.
Errorf
(
"
No client request should have been made
"
)
t
.
Errorf
(
"
unexpected create namespace action
"
)
}
}
}
}
// TestAdmissionNamespaceExistsUnknownToHandler
func
TestAdmissionWithLatentCache
(
t
*
testing
.
T
)
{
func
TestAdmissionNamespaceExistsUnknownToHandler
(
t
*
testing
.
T
)
{
namespace
:=
"test"
namespace
:=
"test"
mockClient
:=
&
fake
.
Clientset
{}
mockClient
:=
newMockClientForTest
([]
string
{})
mockClient
.
AddReactor
(
"create"
,
"namespaces"
,
func
(
action
core
.
Action
)
(
bool
,
runtime
.
Object
,
error
)
{
mockClient
.
AddReactor
(
"create"
,
"namespaces"
,
func
(
action
core
.
Action
)
(
bool
,
runtime
.
Object
,
error
)
{
return
true
,
nil
,
errors
.
NewAlreadyExists
(
api
.
Resource
(
"namespaces"
),
namespace
)
return
true
,
nil
,
errors
.
NewAlreadyExists
(
api
.
Resource
(
"namespaces"
),
namespace
)
})
})
informerFactory
:=
informers
.
NewSharedInformerFactory
(
mockClient
,
5
*
time
.
Minute
)
handler
,
informerFactory
,
err
:=
newHandlerForTest
(
mockClient
)
informerFactory
.
Namespaces
()
informerFactory
.
Start
(
wait
.
NeverStop
)
handler
:=
&
provision
{
client
:
mockClient
,
informerFactory
:
informerFactory
,
}
pod
:=
api
.
Pod
{
ObjectMeta
:
api
.
ObjectMeta
{
Name
:
"123"
,
Namespace
:
namespace
},
Spec
:
api
.
PodSpec
{
Volumes
:
[]
api
.
Volume
{{
Name
:
"vol"
}},
Containers
:
[]
api
.
Container
{{
Name
:
"ctr"
,
Image
:
"image"
}},
},
}
err
:=
handler
.
Admit
(
admission
.
NewAttributesRecord
(
&
pod
,
nil
,
api
.
Kind
(
"Pod"
)
.
WithVersion
(
"version"
),
pod
.
Namespace
,
pod
.
Name
,
api
.
Resource
(
"pods"
)
.
WithVersion
(
"version"
),
""
,
admission
.
Create
,
nil
))
if
err
!=
nil
{
if
err
!=
nil
{
t
.
Errorf
(
"
Unexpected error returned from admission handler"
)
t
.
Errorf
(
"
unexpected error initializing handler: %v"
,
err
)
}
}
}
informerFactory
.
Start
(
wait
.
NeverStop
)
// TestAdmissionNamespaceValidate
pod
:=
newPod
(
namespace
)
func
TestAdmissionNamespaceValidate
(
t
*
testing
.
T
)
{
err
=
handler
.
Admit
(
admission
.
NewAttributesRecord
(
&
pod
,
nil
,
api
.
Kind
(
"Pod"
)
.
WithVersion
(
"version"
),
pod
.
Namespace
,
pod
.
Name
,
api
.
Resource
(
"pods"
)
.
WithVersion
(
"version"
),
""
,
admission
.
Create
,
nil
))
mockClient
:=
&
fake
.
Clientset
{}
informerFactory
:=
informers
.
NewSharedInformerFactory
(
mockClient
,
5
*
time
.
Minute
)
handler
:=
&
provision
{
client
:
mockClient
,
}
handler
.
SetInformerFactory
(
informerFactory
)
err
:=
handler
.
Validate
()
if
err
!=
nil
{
if
err
!=
nil
{
t
.
Errorf
(
"Failed to initialize informer"
)
t
.
Errorf
(
"unexpected error returned from admission handler"
)
}
if
!
hasCreateNamespaceAction
(
mockClient
)
{
t
.
Errorf
(
"expected create namespace action"
)
}
}
}
}
plugin/pkg/admission/namespace/exists/admission.go
View file @
0339ef79
...
@@ -22,9 +22,11 @@ import (
...
@@ -22,9 +22,11 @@ import (
clientset
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
clientset
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"fmt"
"fmt"
"k8s.io/kubernetes/pkg/admission"
"k8s.io/kubernetes/pkg/admission"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/controller/framework"
"k8s.io/kubernetes/pkg/controller/framework/informers"
"k8s.io/kubernetes/pkg/controller/framework/informers"
)
)
...
@@ -39,8 +41,8 @@ func init() {
...
@@ -39,8 +41,8 @@ func init() {
// It is useful in deployments that want to enforce pre-declaration of a Namespace resource.
// It is useful in deployments that want to enforce pre-declaration of a Namespace resource.
type
exists
struct
{
type
exists
struct
{
*
admission
.
Handler
*
admission
.
Handler
client
clientset
.
Interface
client
clientset
.
Interface
informerFactory
informers
.
SharedInformerFactory
namespaceInformer
framework
.
SharedIndexInformer
}
}
var
_
=
admission
.
WantsInformerFactory
(
&
exists
{})
var
_
=
admission
.
WantsInformerFactory
(
&
exists
{})
...
@@ -53,6 +55,10 @@ func (e *exists) Admit(a admission.Attributes) (err error) {
...
@@ -53,6 +55,10 @@ func (e *exists) Admit(a admission.Attributes) (err error) {
return
nil
return
nil
}
}
// we need to wait for our caches to warm
if
!
e
.
WaitForReady
()
{
return
admission
.
NewForbidden
(
a
,
fmt
.
Errorf
(
"not yet ready to handle request"
))
}
namespace
:=
&
api
.
Namespace
{
namespace
:=
&
api
.
Namespace
{
ObjectMeta
:
api
.
ObjectMeta
{
ObjectMeta
:
api
.
ObjectMeta
{
Name
:
a
.
GetNamespace
(),
Name
:
a
.
GetNamespace
(),
...
@@ -60,7 +66,7 @@ func (e *exists) Admit(a admission.Attributes) (err error) {
...
@@ -60,7 +66,7 @@ func (e *exists) Admit(a admission.Attributes) (err error) {
},
},
Status
:
api
.
NamespaceStatus
{},
Status
:
api
.
NamespaceStatus
{},
}
}
_
,
exists
,
err
:=
e
.
informerFactory
.
Namespaces
()
.
Informer
()
.
GetStore
()
.
Get
(
namespace
)
_
,
exists
,
err
:=
e
.
namespaceInformer
.
GetStore
()
.
Get
(
namespace
)
if
err
!=
nil
{
if
err
!=
nil
{
return
errors
.
NewInternalError
(
err
)
return
errors
.
NewInternalError
(
err
)
}
}
...
@@ -89,12 +95,13 @@ func NewExists(c clientset.Interface) admission.Interface {
...
@@ -89,12 +95,13 @@ func NewExists(c clientset.Interface) admission.Interface {
}
}
func
(
e
*
exists
)
SetInformerFactory
(
f
informers
.
SharedInformerFactory
)
{
func
(
e
*
exists
)
SetInformerFactory
(
f
informers
.
SharedInformerFactory
)
{
e
.
informerFactory
=
f
e
.
namespaceInformer
=
f
.
Namespaces
()
.
Informer
()
e
.
SetReadyFunc
(
e
.
namespaceInformer
.
HasSynced
)
}
}
func
(
e
*
exists
)
Validate
()
error
{
func
(
e
*
exists
)
Validate
()
error
{
if
e
.
informerFactory
==
nil
{
if
e
.
namespaceInformer
==
nil
{
return
fmt
.
Errorf
(
"
namespace exists plugin needs a namespace i
nformer"
)
return
fmt
.
Errorf
(
"
missing namespaceI
nformer"
)
}
}
return
nil
return
nil
}
}
plugin/pkg/admission/namespace/exists/admission_test.go
View file @
0339ef79
...
@@ -15,3 +15,104 @@ limitations under the License.
...
@@ -15,3 +15,104 @@ limitations under the License.
*/
*/
package
exists
package
exists
import
(
"fmt"
"testing"
"time"
"k8s.io/kubernetes/pkg/admission"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/unversioned"
clientset
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake"
"k8s.io/kubernetes/pkg/client/testing/core"
"k8s.io/kubernetes/pkg/controller/framework/informers"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/util/wait"
)
// newHandlerForTest returns the admission controller configured for testing.
func
newHandlerForTest
(
c
clientset
.
Interface
)
(
admission
.
Interface
,
informers
.
SharedInformerFactory
,
error
)
{
f
:=
informers
.
NewSharedInformerFactory
(
c
,
5
*
time
.
Minute
)
handler
:=
NewExists
(
c
)
plugins
:=
[]
admission
.
Interface
{
handler
}
pluginInitializer
:=
admission
.
NewPluginInitializer
(
f
)
pluginInitializer
.
Initialize
(
plugins
)
err
:=
admission
.
Validate
(
plugins
)
return
handler
,
f
,
err
}
// newMockClientForTest creates a mock client that returns a client configured for the specified list of namespaces.
func
newMockClientForTest
(
namespaces
[]
string
)
*
fake
.
Clientset
{
mockClient
:=
&
fake
.
Clientset
{}
mockClient
.
AddReactor
(
"list"
,
"namespaces"
,
func
(
action
core
.
Action
)
(
bool
,
runtime
.
Object
,
error
)
{
namespaceList
:=
&
api
.
NamespaceList
{
ListMeta
:
unversioned
.
ListMeta
{
ResourceVersion
:
fmt
.
Sprintf
(
"%d"
,
len
(
namespaces
)),
},
}
for
i
,
ns
:=
range
namespaces
{
namespaceList
.
Items
=
append
(
namespaceList
.
Items
,
api
.
Namespace
{
ObjectMeta
:
api
.
ObjectMeta
{
Name
:
ns
,
ResourceVersion
:
fmt
.
Sprintf
(
"%d"
,
i
),
},
})
}
return
true
,
namespaceList
,
nil
})
return
mockClient
}
// newPod returns a new pod for the specified namespace
func
newPod
(
namespace
string
)
api
.
Pod
{
return
api
.
Pod
{
ObjectMeta
:
api
.
ObjectMeta
{
Name
:
"123"
,
Namespace
:
namespace
},
Spec
:
api
.
PodSpec
{
Volumes
:
[]
api
.
Volume
{{
Name
:
"vol"
}},
Containers
:
[]
api
.
Container
{{
Name
:
"ctr"
,
Image
:
"image"
}},
},
}
}
// TestAdmissionNamespaceExists verifies pod is admitted only if namespace exists.
func
TestAdmissionNamespaceExists
(
t
*
testing
.
T
)
{
namespace
:=
"test"
mockClient
:=
newMockClientForTest
([]
string
{
namespace
})
handler
,
informerFactory
,
err
:=
newHandlerForTest
(
mockClient
)
if
err
!=
nil
{
t
.
Errorf
(
"unexpected error initializing handler: %v"
,
err
)
}
informerFactory
.
Start
(
wait
.
NeverStop
)
pod
:=
newPod
(
namespace
)
err
=
handler
.
Admit
(
admission
.
NewAttributesRecord
(
&
pod
,
nil
,
api
.
Kind
(
"Pod"
)
.
WithVersion
(
"version"
),
pod
.
Namespace
,
pod
.
Name
,
api
.
Resource
(
"pods"
)
.
WithVersion
(
"version"
),
""
,
admission
.
Create
,
nil
))
if
err
!=
nil
{
t
.
Errorf
(
"unexpected error returned from admission handler"
)
}
}
// TestAdmissionNamespaceDoesNotExist verifies pod is not admitted if namespace does not exist.
func
TestAdmissionNamespaceDoesNotExist
(
t
*
testing
.
T
)
{
namespace
:=
"test"
mockClient
:=
newMockClientForTest
([]
string
{})
mockClient
.
AddReactor
(
"get"
,
"namespaces"
,
func
(
action
core
.
Action
)
(
bool
,
runtime
.
Object
,
error
)
{
return
true
,
nil
,
fmt
.
Errorf
(
"nope, out of luck"
)
})
handler
,
informerFactory
,
err
:=
newHandlerForTest
(
mockClient
)
if
err
!=
nil
{
t
.
Errorf
(
"unexpected error initializing handler: %v"
,
err
)
}
informerFactory
.
Start
(
wait
.
NeverStop
)
pod
:=
newPod
(
namespace
)
err
=
handler
.
Admit
(
admission
.
NewAttributesRecord
(
&
pod
,
nil
,
api
.
Kind
(
"Pod"
)
.
WithVersion
(
"version"
),
pod
.
Namespace
,
pod
.
Name
,
api
.
Resource
(
"pods"
)
.
WithVersion
(
"version"
),
""
,
admission
.
Create
,
nil
))
if
err
==
nil
{
actions
:=
""
for
_
,
action
:=
range
mockClient
.
Actions
()
{
actions
=
actions
+
action
.
GetVerb
()
+
":"
+
action
.
GetResource
()
.
Resource
+
":"
+
action
.
GetSubresource
()
+
", "
}
t
.
Errorf
(
"expected error returned from admission handler: %v"
,
actions
)
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment