Skip to content

Commit

Permalink
admission controller: annotates pod network-readiness=false by default (
Browse files Browse the repository at this point in the history
CentaurusInfra#363)

* admission control: annotate pod as network-readiness=false by default

* renamed admission controller to DefaultPodNetworkReadiness

* minor: import corrected

* minor: referenced package name corrected

* minor: reformtted code

* update BUILD to include renamed package
  • Loading branch information
hwchen authored Jun 17, 2020
1 parent 6130e03 commit 5457cf7
Show file tree
Hide file tree
Showing 6 changed files with 226 additions and 0 deletions.
1 change: 1 addition & 0 deletions pkg/kubeapiserver/options/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ go_library(
"//plugin/pkg/admission/admit:go_default_library",
"//plugin/pkg/admission/alwayspullimages:go_default_library",
"//plugin/pkg/admission/antiaffinity:go_default_library",
"//plugin/pkg/admission/defaultpodnetworkreadiness:go_default_library",
"//plugin/pkg/admission/defaulttolerationseconds:go_default_library",
"//plugin/pkg/admission/deny:go_default_library",
"//plugin/pkg/admission/eventratelimit:go_default_library",
Expand Down
3 changes: 3 additions & 0 deletions pkg/kubeapiserver/options/plugins.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"k8s.io/kubernetes/plugin/pkg/admission/admit"
"k8s.io/kubernetes/plugin/pkg/admission/alwayspullimages"
"k8s.io/kubernetes/plugin/pkg/admission/antiaffinity"
"k8s.io/kubernetes/plugin/pkg/admission/defaultpodnetworkreadiness"
"k8s.io/kubernetes/plugin/pkg/admission/defaulttolerationseconds"
"k8s.io/kubernetes/plugin/pkg/admission/deny"
"k8s.io/kubernetes/plugin/pkg/admission/eventratelimit"
Expand Down Expand Up @@ -64,6 +65,7 @@ import (
// AllOrderedPlugins is the list of all the plugins in order.
var AllOrderedPlugins = []string{
admit.PluginName, // AlwaysAdmit
defaultpodnetworkreadiness.PluginName, // DefaultPodNetworkReadiness
autoprovision.PluginName, // NamespaceAutoProvision
lifecycle.PluginName, // NamespaceLifecycle
nsexists.PluginName, // NamespaceExists
Expand Down Expand Up @@ -130,6 +132,7 @@ func RegisterAllAdmissionPlugins(plugins *admission.Plugins) {
setdefault.Register(plugins)
resize.Register(plugins)
storageobjectinuseprotection.Register(plugins)
defaultpodnetworkreadiness.Register(plugins)
}

// DefaultOffAdmissionPlugins get admission plugins off by default for kube-apiserver.
Expand Down
1 change: 1 addition & 0 deletions plugin/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ filegroup(
"//plugin/pkg/admission/admit:all-srcs",
"//plugin/pkg/admission/alwayspullimages:all-srcs",
"//plugin/pkg/admission/antiaffinity:all-srcs",
"//plugin/pkg/admission/defaultpodnetworkreadiness:all-srcs",
"//plugin/pkg/admission/defaulttolerationseconds:all-srcs",
"//plugin/pkg/admission/deny:all-srcs",
"//plugin/pkg/admission/eventratelimit:all-srcs",
Expand Down
39 changes: 39 additions & 0 deletions plugin/pkg/admission/defaultpodnetworkreadiness/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")

go_library(
name = "go_default_library",
srcs = ["admission.go"],
importpath = "k8s.io/kubernetes/plugin/pkg/admission/defaultpodnetworkreadiness",
visibility = ["//visibility:public"],
deps = [
"//pkg/apis/core:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/admission:go_default_library",
],
)

go_test(
name = "go_default_test",
srcs = ["admission_test.go"],
embed = [":go_default_library"],
deps = [
"//pkg/apis/core:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/admission:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/admission/testing:go_default_library",
],
)

filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)

filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
86 changes: 86 additions & 0 deletions plugin/pkg/admission/defaultpodnetworkreadiness/admission.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
Copyright 2020 Authors of Arktos.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package defaultpodnetworkreadiness

import (
"io"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apiserver/pkg/admission"
api "k8s.io/kubernetes/pkg/apis/core"
)

const (
// PluginName indicates name of admission plugin.
PluginName = "DefaultPodNetworkReadiness"

networkReadiness = "arktos.futurewei.com/network-readiness"
)

// Register registers a plugin
func Register(plugins *admission.Plugins) {
plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) {
return new(), nil
})
}

var _ admission.MutationInterface = &plugin{}

type plugin struct {
*admission.Handler
}

// Admit makes an admission decision based on the request attributes
func (p plugin) Admit(attributes admission.Attributes, o admission.ObjectInterfaces) (err error) {
if shouldIgnore(attributes) {
return nil
}

pod, ok := attributes.GetObject().(*api.Pod)
if !ok {
return apierrors.NewBadRequest("Resource was marked with kind Pod but was unable to be converted")
}

if pod.Spec.SecurityContext != nil && pod.Spec.SecurityContext.HostNetwork {
return nil
}

if _, readinessExists := pod.Annotations[networkReadiness]; readinessExists {
return nil
}

// no network-readiness yet; ensure annotation network-readiness=false is set by default
if pod.ObjectMeta.Annotations == nil {
pod.ObjectMeta.Annotations = map[string]string{}
}
pod.ObjectMeta.Annotations[networkReadiness] = "false"
return nil
}

func shouldIgnore(attributes admission.Attributes) bool {
// ignore all calls to subresources or resources other than pods.
if len(attributes.GetSubresource()) != 0 || attributes.GetResource().GroupResource() != api.Resource("pods") {
return true
}

return false
}

func new() *plugin {
return &plugin{
Handler: admission.NewHandler(admission.Create, admission.Update),
}
}
96 changes: 96 additions & 0 deletions plugin/pkg/admission/defaultpodnetworkreadiness/admission_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/*
Copyright 2020 Authors of Arktos.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package defaultpodnetworkreadiness

import (
"testing"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apiserver/pkg/admission"
admissiontesting "k8s.io/apiserver/pkg/admission/testing"
api "k8s.io/kubernetes/pkg/apis/core"
)

func TestAdmit(t *testing.T) {
tcs := []struct {
desc string
pod *api.Pod
notExpectingReadiness bool
expectedReadinessValue string
}{
{
desc: "annotate false value to applicable pod",
pod: &api.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "emptyPod", Namespace: "test-ne", Tenant: "test-te"},
Spec: api.PodSpec{
Containers: []api.Container{
{Name: "ctr1", Image: "image"},
},
},
},
expectedReadinessValue: "false",
},
{
desc: "not to change pod already has readiness annotated",
pod: &api.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "readyPod", Namespace: "test-ne", Tenant: "test-te", Annotations: map[string]string{"arktos.futurewei.com/network-readiness": "true"}},
Spec: api.PodSpec{
Containers: []api.Container{
{Name: "ctr1", Image: "image"},
},
},
},
expectedReadinessValue: "true",
},
{
desc: "not to annotate pod of host network",
pod: &api.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "readyPod", Namespace: "test-ne", Tenant: "test-te"},
Spec: api.PodSpec{
SecurityContext: &api.PodSecurityContext{
HostNetwork: true,
},
Containers: []api.Container{
{Name: "ctr1", Image: "image"},
},
},
},
notExpectingReadiness: true,
},
}

handler := admissiontesting.WithReinvocationTesting(t, &plugin{})

for _, tc := range tcs {
t.Run(tc.desc, func(t *testing.T) {
pod := tc.pod
attributes := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), pod.Tenant, pod.Namespace, pod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)
err := handler.Admit(attributes, nil)
if err != nil {
t.Fatalf("Unexpected error returned from admission handler: %v", err)
}

v, ok := pod.Annotations[networkReadiness]
if tc.notExpectingReadiness && ok {
t.Fatalf("should not have added readiness annotation")
}
if v != tc.expectedReadinessValue {
t.Fatalf("unexpected readiness annotation: expected %q, got %q", tc.expectedReadinessValue, v)
}
})
}
}

0 comments on commit 5457cf7

Please sign in to comment.