Skip to content

Commit

Permalink
Extra validation rules are added to block deletion, and modification …
Browse files Browse the repository at this point in the history
…of networks which would result in invalid cluster state.
  • Loading branch information
Levovar committed Aug 2, 2019
1 parent 44b3b3e commit 26a2980
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 24 deletions.
8 changes: 4 additions & 4 deletions pkg/admit/netadmit.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ func (validator *Validator) ValidateNetwork(responseWriter http.ResponseWriter,
return
}
origNewManifest := *newManifest
isManifestValid, err := validateNetworkByType(oldManifest, newManifest, admissionReview.Request.Operation)
isManifestValid, err := validateNetworkByType(oldManifest, newManifest, admissionReview.Request.Operation, validator.Client)
if !isManifestValid {
SendErroneousAdmissionResponse(responseWriter, admissionReview.Request, err)
return
Expand Down Expand Up @@ -99,13 +99,13 @@ func getNetworkManifest(objectToReview []byte) (*danmtypes.DanmNet,error) {
return &networkManifest, nil
}

func validateNetworkByType(oldManifest, newManifest *danmtypes.DanmNet, opType v1beta1.Operation) (bool,error) {
func validateNetworkByType(oldManifest, newManifest *danmtypes.DanmNet, opType v1beta1.Operation, client danmclientset.Interface) (bool,error) {
validatorMapping, isTypeHandled := danmValidationConfig[newManifest.TypeMeta.Kind]
if !isTypeHandled {
return false, errors.New("K8s API type:" + newManifest.TypeMeta.Kind + " is not handled by DANM webhook")
}
for _, validator := range validatorMapping {
err := validator(oldManifest,newManifest,opType)
err := validator(oldManifest,newManifest,opType,client)
if err != nil {
return false, err
}
Expand Down Expand Up @@ -133,7 +133,7 @@ func mutateNetManifest(danmClient danmclientset.Interface, dnet *danmtypes.DanmN
//Example is NetworkID related validations for TenantNetworks
//TODO: make this also fancy when more post validation needs surface
func postValidateManifest(dnet *danmtypes.DanmNet) error {
return validateNetworkId(nil, dnet, "")
return validateNetworkId(nil, dnet, "", nil)
}

func CreateAllocationArray(dnet *danmtypes.DanmNet) {
Expand Down
12 changes: 12 additions & 0 deletions pkg/admit/netdel.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"net/http"
"k8s.io/api/admission/v1beta1"
"github.com/nokia/danm/pkg/confman"
"github.com/nokia/danm/pkg/danmep"
)

//A GIGANTIC DISCLAIMER: THIS DOES NOT WORK BEFORE K8S 1.15!
Expand All @@ -21,6 +22,17 @@ func (validator *Validator) DeleteNetwork(responseWriter http.ResponseWriter, re
SendErroneousAdmissionResponse(responseWriter, admissionReview.Request, err)
return
}
isAnyPodConnectedToNetwork, connectedEp, err := danmep.ArePodsConnectedToNetwork(validator.Client, oldManifest)
if err != nil {
SendErroneousAdmissionResponse(responseWriter, admissionReview.Request,
errors.New("Network cannot be deleted because there is no way to tell if Pods are still using it due to:" + err.Error()))
return
}
if isAnyPodConnectedToNetwork {
SendErroneousAdmissionResponse(responseWriter, admissionReview.Request,
errors.New("Network cannot be deleted because there are Pods still connected to it e.g. Pod:" + connectedEp.Spec.Pod + " in namespace:" + connectedEp.ObjectMeta.Namespace))
return
}
if oldManifest.TypeMeta.Kind == "TenantNetwork" && IsTypeDynamic(oldManifest.Spec.NetworkType) {
tconf, err := confman.GetTenantConfig(validator.Client)
if err != nil {
Expand Down
42 changes: 31 additions & 11 deletions pkg/admit/validators.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
"encoding/binary"
admissionv1 "k8s.io/api/admission/v1beta1"
danmtypes "github.com/nokia/danm/crd/apis/danm/v1"
danmclientset "github.com/nokia/danm/crd/client/clientset/versioned"
"github.com/nokia/danm/pkg/danmep"
"github.com/nokia/danm/pkg/ipam"
"k8s.io/kubernetes/pkg/kubelet/cm/cpuset"
)
Expand All @@ -16,8 +18,8 @@ const (
)

var (
DanmNetMapping = []ValidatorFunc{validateIpv4Fields,validateIpv6Fields,validateAllocationPool,validateVids,validateNetworkId,validateAbsenceOfAllowedTenants,validateNeType}
ClusterNetMapping = []ValidatorFunc{validateIpv4Fields,validateIpv6Fields,validateAllocationPool,validateVids,validateNetworkId,validateNeType}
DanmNetMapping = []ValidatorFunc{validateIpv4Fields,validateIpv6Fields,validateAllocationPool,validateVids,validateNetworkId,validateAbsenceOfAllowedTenants,validateNeType,validateVniChange}
ClusterNetMapping = []ValidatorFunc{validateIpv4Fields,validateIpv6Fields,validateAllocationPool,validateVids,validateNetworkId,validateNeType,validateVniChange}
TenantNetMapping = []ValidatorFunc{validateIpv4Fields,validateIpv6Fields,validateAllocationPool,validateAbsenceOfAllowedTenants,validateTenantNetRules,validateNeType}
danmValidationConfig = map[string]ValidatorMapping {
"DanmNet": DanmNetMapping,
Expand All @@ -26,14 +28,14 @@ var (
}
)

type ValidatorFunc func(oldManifest, newManifest *danmtypes.DanmNet, opType admissionv1.Operation) error
type ValidatorFunc func(oldManifest, newManifest *danmtypes.DanmNet, opType admissionv1.Operation, client danmclientset.Interface) error
type ValidatorMapping []ValidatorFunc

func validateIpv4Fields(oldManifest, newManifest *danmtypes.DanmNet, opType admissionv1.Operation) error {
func validateIpv4Fields(oldManifest, newManifest *danmtypes.DanmNet, opType admissionv1.Operation, client danmclientset.Interface) error {
return validateIpFields(newManifest.Spec.Options.Cidr, newManifest.Spec.Options.Routes)
}

func validateIpv6Fields(oldManifest, newManifest *danmtypes.DanmNet, opType admissionv1.Operation) error {
func validateIpv6Fields(oldManifest, newManifest *danmtypes.DanmNet, opType admissionv1.Operation, client danmclientset.Interface) error {
return validateIpFields(newManifest.Spec.Options.Net6, newManifest.Spec.Options.Routes6)
}

Expand All @@ -56,7 +58,7 @@ func validateIpFields(cidr string, routes map[string]string) error {
return nil
}

func validateAllocationPool(oldManifest, newManifest *danmtypes.DanmNet, opType admissionv1.Operation) error {
func validateAllocationPool(oldManifest, newManifest *danmtypes.DanmNet, opType admissionv1.Operation, client danmclientset.Interface) error {
if opType == admissionv1.Create && newManifest.Spec.Options.Alloc != "" {
return errors.New("Allocation bitmask shall not be manually defined upon creation!")
}
Expand Down Expand Up @@ -90,7 +92,7 @@ func GetBroadcastAddress(subnet *net.IPNet) (net.IP) {
return ip
}

func validateVids(oldManifest, newManifest *danmtypes.DanmNet, opType admissionv1.Operation) error {
func validateVids(oldManifest, newManifest *danmtypes.DanmNet, opType admissionv1.Operation, client danmclientset.Interface) error {
isVlanDefined := (newManifest.Spec.Options.Vlan!=0)
isVxlanDefined := (newManifest.Spec.Options.Vxlan!=0)
if isVlanDefined && isVxlanDefined {
Expand All @@ -99,7 +101,7 @@ func validateVids(oldManifest, newManifest *danmtypes.DanmNet, opType admissionv
return nil
}

func validateNetworkId(oldManifest, newManifest *danmtypes.DanmNet, opType admissionv1.Operation) error {
func validateNetworkId(oldManifest, newManifest *danmtypes.DanmNet, opType admissionv1.Operation, client danmclientset.Interface) error {
if newManifest.Spec.NetworkID == "" {
return errors.New("Spec.NetworkID mandatory parameter is missing!")
}
Expand All @@ -109,14 +111,14 @@ func validateNetworkId(oldManifest, newManifest *danmtypes.DanmNet, opType admis
return nil
}

func validateAbsenceOfAllowedTenants(oldManifest, newManifest *danmtypes.DanmNet, opType admissionv1.Operation) error {
func validateAbsenceOfAllowedTenants(oldManifest, newManifest *danmtypes.DanmNet, opType admissionv1.Operation, client danmclientset.Interface) error {
if newManifest.Spec.AllowedTenants != nil {
return errors.New("AllowedTenants attribute is only valid for the ClusterNetwork API!")
}
return nil
}

func validateTenantNetRules(oldManifest, newManifest *danmtypes.DanmNet, opType admissionv1.Operation) error {
func validateTenantNetRules(oldManifest, newManifest *danmtypes.DanmNet, opType admissionv1.Operation, client danmclientset.Interface) error {
if opType == admissionv1.Create &&
(newManifest.Spec.Options.Vxlan != 0 ||
newManifest.Spec.Options.Vlan != 0) {
Expand Down Expand Up @@ -182,7 +184,7 @@ func validateIfaceConfig(ifaceConf danmtypes.IfaceProfile, opType admissionv1.Op
return nil
}

func validateNeType(oldManifest, newManifest *danmtypes.DanmNet, opType admissionv1.Operation) error {
func validateNeType(oldManifest, newManifest *danmtypes.DanmNet, opType admissionv1.Operation, client danmclientset.Interface) error {
if newManifest.Spec.NetworkType == "sriov" {
if newManifest.Spec.Options.DevicePool == "" || newManifest.Spec.Options.Device != "" {
return errors.New("Spec.Options.device_pool must, and Spec.Options.host_device cannot be provided for SR-IOV networks!")
Expand All @@ -191,4 +193,22 @@ func validateNeType(oldManifest, newManifest *danmtypes.DanmNet, opType admissio
return errors.New("Spec.Options.device_pool and Spec.Options.host_device cannot be provided together!")
}
return nil
}

func validateVniChange(oldManifest, newManifest *danmtypes.DanmNet, opType admissionv1.Operation, client danmclientset.Interface) error {
if opType != admissionv1.Update {
return nil
}
isAnyPodConnectedToNetwork, connectedEp, err := danmep.ArePodsConnectedToNetwork(client, newManifest)
if err != nil {
return errors.New("no way to tell if Pods are still using the network due to:" + err.Error())
}
if !isAnyPodConnectedToNetwork {
return nil
}
if (oldManifest.Spec.Options.Vlan != 0 && (oldManifest.Spec.Options.Vlan != newManifest.Spec.Options.Vlan || oldManifest.Spec.Options.Device != newManifest.Spec.Options.Device)) ||
(oldManifest.Spec.Options.Vxlan != 0 && (oldManifest.Spec.Options.Vxlan != newManifest.Spec.Options.Vxlan || oldManifest.Spec.Options.Device != newManifest.Spec.Options.Device)) {
return errors.New("cannot change VNI/host_device of a network which having any Pods connected to it e.g. Pod:" + connectedEp.Spec.Pod + " in namespace:" + connectedEp.ObjectMeta.Namespace)
}
return nil
}
37 changes: 30 additions & 7 deletions pkg/danmep/danmep.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,13 @@ func DeleteIpvlanInterface(ep danmtypes.DanmEp) (error) {
func FindByCid(client danmclientset.Interface, cid string)([]danmtypes.DanmEp, error) {
result, err := client.DanmV1().DanmEps("").List(meta_v1.ListOptions{})
if err != nil {
log.Println("cannot get list of eps:" + err.Error())
return nil, err
return nil, errors.New("cannot list DanmEps because:" + err.Error())
}
ret := make([]danmtypes.DanmEp, 0)
if result == nil {
return ret, nil
}
eplist := result.Items
var ret = make([]danmtypes.DanmEp, 0)
for _, ep := range eplist {
if ep.Spec.CID == cid {
ret = append(ret, ep)
Expand All @@ -68,11 +70,13 @@ func FindByCid(client danmclientset.Interface, cid string)([]danmtypes.DanmEp, e
func CidsByHost(client danmclientset.Interface, host string)(map[string]danmtypes.DanmEp, error) {
result, err := client.DanmV1().DanmEps("").List(meta_v1.ListOptions{})
if err != nil {
log.Println("cannot get list of eps")
return nil, err
return nil, errors.New("cannot list DanmEps because:" + err.Error())
}
ret := make(map[string]danmtypes.DanmEp, 0)
if result == nil {
return ret, nil
}
eplist := result.Items
var ret = make(map[string]danmtypes.DanmEp, 0)
for _, ep := range eplist {
if ep.Spec.Host == host {
ret[ep.Spec.CID] = ep
Expand Down Expand Up @@ -103,7 +107,7 @@ func DetermineHostDeviceName(dnet *danmtypes.DanmNet) string {
return device
}

func CreateRoutesInNetNs(ep danmtypes.DanmEp, dnet *danmtypes.DanmNet, ) error {
func CreateRoutesInNetNs(ep danmtypes.DanmEp, dnet *danmtypes.DanmNet) error {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
origNs, err := ns.GetCurrentNS()
Expand Down Expand Up @@ -196,3 +200,22 @@ func PutDanmEp(danmClient danmclientset.Interface, ep danmtypes.DanmEp) error {
}
return errors.New("DanmEp creation was supposedly successful, but the object hasn't really appeared within 1 sec")
}

// ArePodsConnectedToNetwork checks if there are any Pods currently in the system using the particular network.
// If there is at least, it returns true, and the spec of the first matching DanmEp.
func ArePodsConnectedToNetwork(client danmclientset.Interface, dnet *danmtypes.DanmNet)(bool, danmtypes.DanmEp, error) {
result, err := client.DanmV1().DanmEps("").List(meta_v1.ListOptions{})
if err != nil {
return false, danmtypes.DanmEp{}, errors.New("cannot list DanmEps because:" + err.Error())
}
if result == nil {
return false, danmtypes.DanmEp{}, nil
}
eplist := result.Items
for _, ep := range eplist {
if ep.Spec.ApiType == dnet.TypeMeta.Kind && ep.Spec.NetworkName == dnet.ObjectMeta.Name {
return true, ep, nil
}
}
return false, danmtypes.DanmEp{}, nil
}
4 changes: 2 additions & 2 deletions scm/build/build.sh
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/bin/sh -ex
export GOOS=linux
cd $GOPATH/src/github.com/nokia/danm
glide install --strip-vendor
#glide install --strip-vendor
go get -d github.com/vishvananda/netlink
go get github.com/containernetworking/plugins/pkg/ns
go get github.com/golang/groupcache/lru
Expand All @@ -20,4 +20,4 @@ go install -a -ldflags '-extldflags "-static"' github.com/nokia/danm/cmd/netwatc
go install -a -ldflags '-extldflags "-static"' github.com/nokia/danm/cmd/fakeipam
go install -a -ldflags '-extldflags "-static"' github.com/nokia/danm/cmd/svcwatcher
go install -a -ldflags '-extldflags "-static"' github.com/nokia/danm/cmd/cnitest
go install -a -ldflags '-extldflags "-static"' github.com/nokia/danm/cmd/webhook
go install -a -ldflags '-extldflags "-static"' github.com/nokia/danm/cmd/webhook

0 comments on commit 26a2980

Please sign in to comment.