Skip to content

Commit

Permalink
UPSTREAM: <carry>: openshift-kube-apiserver: Add custom resource vali…
Browse files Browse the repository at this point in the history
…dation for network spec
  • Loading branch information
abhat authored and soltysh committed Sep 8, 2021
1 parent b0481fa commit e2d42e7
Show file tree
Hide file tree
Showing 3 changed files with 163 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"k8s.io/kubernetes/openshift-kube-apiserver/admission/customresourcevalidation/console"
"k8s.io/kubernetes/openshift-kube-apiserver/admission/customresourcevalidation/features"
"k8s.io/kubernetes/openshift-kube-apiserver/admission/customresourcevalidation/image"
"k8s.io/kubernetes/openshift-kube-apiserver/admission/customresourcevalidation/network"
"k8s.io/kubernetes/openshift-kube-apiserver/admission/customresourcevalidation/oauth"
"k8s.io/kubernetes/openshift-kube-apiserver/admission/customresourcevalidation/project"
"k8s.io/kubernetes/openshift-kube-apiserver/admission/customresourcevalidation/rolebindingrestriction"
Expand All @@ -31,6 +32,7 @@ var AllCustomResourceValidators = []string{
clusterresourcequota.PluginName,
securitycontextconstraints.PluginName,
rolebindingrestriction.PluginName,
network.PluginName,

// this one is special because we don't work without it.
securitycontextconstraints.DefaultingPluginName,
Expand All @@ -54,7 +56,8 @@ func RegisterCustomResourceValidation(plugins *admission.Plugins) {
securitycontextconstraints.Register(plugins)
// This plugin validates the authorization.openshift.io/v1 RoleBindingRestriction resources.
rolebindingrestriction.Register(plugins)

// This plugin validates the network.config.openshift.io object for service node port range changes
network.Register(plugins)
// this one is special because we don't work without it.
securitycontextconstraints.RegisterDefaulting(plugins)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")

go_library(
name = "go_default_library",
srcs = ["validate_network_config.go"],
importpath = "k8s.io/kubernetes/openshift-kube-apiserver/admission/customresourcevalidation/network",
visibility = ["//visibility:public"],
deps = [
"//openshift-kube-apiserver/admission/customresourcevalidation:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/api/validation:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/validation:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/admission:go_default_library",
"//vendor/github.com/openshift/api/config/v1: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"],
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
package network

import (
"fmt"
"io"

"k8s.io/apimachinery/pkg/api/validation"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
utilnet "k8s.io/apimachinery/pkg/util/net"
"k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/apiserver/pkg/admission"
kubeoptions "k8s.io/kubernetes/pkg/kubeapiserver/options"

configv1 "github.com/openshift/api/config/v1"
"k8s.io/kubernetes/openshift-kube-apiserver/admission/customresourcevalidation"
)

const PluginName = "config.openshift.io/ValidateNetwork"

// Register registers a plugin
func Register(plugins *admission.Plugins) {
plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) {
return customresourcevalidation.NewValidator(
map[schema.GroupResource]bool{
configv1.Resource("networks"): true,
},
map[schema.GroupVersionKind]customresourcevalidation.ObjectValidator{
configv1.GroupVersion.WithKind("Network"): networkV1{},
})
})
}

func toNetworkV1(uncastObj runtime.Object) (*configv1.Network, field.ErrorList) {
if uncastObj == nil {
return nil, nil
}

allErrs := field.ErrorList{}

obj, ok := uncastObj.(*configv1.Network)
if !ok {
return nil, append(allErrs,
field.NotSupported(field.NewPath("kind"), fmt.Sprintf("%T", uncastObj), []string{"Network"}),
field.NotSupported(field.NewPath("apiVersion"), fmt.Sprintf("%T", uncastObj), []string{"config.openshift.io/v1"}))
}

return obj, nil
}

type networkV1 struct {
}

func validateNetworkServiceNodePortRangeUpdate(obj, oldObj *configv1.Network) *field.Error {
var err error
defaultRange := kubeoptions.DefaultServiceNodePortRange
oldRange := &defaultRange
newRange := &defaultRange

oldRangeStr := oldObj.Spec.ServiceNodePortRange
if oldRangeStr != "" {
if oldRange, err = utilnet.ParsePortRange(oldRangeStr); err != nil {
return field.Invalid(field.NewPath("spec", "serviceNodePortRange"),
oldRangeStr,
fmt.Sprintf("failed to parse the old port range: %v", err))
}
}
newRangeStr := obj.Spec.ServiceNodePortRange
if newRangeStr != "" {
if newRange, err = utilnet.ParsePortRange(newRangeStr); err != nil {
return field.Invalid(field.NewPath("spec", "serviceNodePortRange"),
newRangeStr,
fmt.Sprintf("failed to parse the new port range: %v", err))
}
}
if !newRange.Contains(oldRange.Base) || !newRange.Contains(oldRange.Base+oldRange.Size-1) {
return field.Invalid(field.NewPath("spec", "serviceNodePortRange"),
newRangeStr,
fmt.Sprintf("new service node port range %s does not completely cover the previous range %s", newRange, oldRange))
}
return nil
}

func (networkV1) ValidateCreate(uncastObj runtime.Object) field.ErrorList {
obj, allErrs := toNetworkV1(uncastObj)
if len(allErrs) > 0 {
return allErrs
}

allErrs = append(allErrs, validation.ValidateObjectMeta(&obj.ObjectMeta, false, customresourcevalidation.RequireNameCluster, field.NewPath("metadata"))...)

return allErrs
}

func (networkV1) ValidateUpdate(uncastObj runtime.Object, uncastOldObj runtime.Object) field.ErrorList {
obj, allErrs := toNetworkV1(uncastObj)
if len(allErrs) > 0 {
return allErrs
}
oldObj, allErrs := toNetworkV1(uncastOldObj)
if len(allErrs) > 0 {
return allErrs
}

allErrs = append(allErrs, validation.ValidateObjectMetaUpdate(&obj.ObjectMeta, &oldObj.ObjectMeta, field.NewPath("metadata"))...)
if err := validateNetworkServiceNodePortRangeUpdate(obj, oldObj); err != nil {
allErrs = append(allErrs, err)
}

return allErrs
}

func (networkV1) ValidateStatusUpdate(uncastObj runtime.Object, uncastOldObj runtime.Object) field.ErrorList {
obj, errs := toNetworkV1(uncastObj)
if len(errs) > 0 {
return errs
}
oldObj, errs := toNetworkV1(uncastOldObj)
if len(errs) > 0 {
return errs
}

// TODO validate the obj. remember that status validation should *never* fail on spec validation errors.
errs = append(errs, validation.ValidateObjectMetaUpdate(&obj.ObjectMeta, &oldObj.ObjectMeta, field.NewPath("metadata"))...)

return errs
}

0 comments on commit e2d42e7

Please sign in to comment.