Remove validating admission controller

Remove validating admission controller to complete transfer of this feature to new repository at https://github.com/K8sNetworkPlumbingWG/net-attach-def-admission-controller
This commit is contained in:
Przemyslaw Lal
2018-12-11 09:15:08 +00:00
committed by Tomofumi Hayashi
parent 6a46d54161
commit abdfc70c0d
11 changed files with 0 additions and 794 deletions

View File

@@ -1,95 +0,0 @@
#!/bin/bash
# Copyright (c) 2018 Intel Corporation
#
# 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.
# create temp dir to store intermediate files
tmp=$(mktemp -d)
# generate private key
echo "Generating private RSA key..."
openssl genrsa -out ${tmp}/webhook-key.pem 2048 >/dev/null 2>&1
# generate CSR
echo "Generating CSR configuration file..."
cat <<EOF >> ${tmp}/webhook.conf
[req]
req_extensions = v3_req
distinguished_name = req_distinguished_name
[req_distinguished_name]
[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = multus-webhook-service
DNS.2 = multus-webhook-service.default
DNS.3 = multus-webhook-service.default.svc
EOF
openssl req -new -key ${tmp}/webhook-key.pem -subj "/CN=multus-webhook-service.default.svc" -out ${tmp}/server.csr -config ${tmp}/webhook.conf
# push CSR to Kubernetes API server
echo "Sending CSR to Kubernetes..."
csr_name="multus-webhook-service.default"
kubectl delete csr ${csr_name} >/dev/null 2>&1
cat <<EOF | kubectl create -f -
apiVersion: certificates.k8s.io/v1beta1
kind: CertificateSigningRequest
metadata:
name: ${csr_name}
spec:
request: $(cat ${tmp}/server.csr | base64 -w0)
groups:
- system:authenticated
usages:
- digital signature
- key encipherment
- server auth
EOF
# approve certificate
echo "Approving CSR..."
kubectl certificate approve ${csr_name}
# wait for the cert to be issued
echo -n "Waiting for the certificate to be issued..."
cert=""
for sec in $(seq 15); do
cert=$(kubectl get csr ${csr_name} -o jsonpath='{.status.certificate}')
if [[ $cert != "" ]]; then
echo -e "\nCertificate issued succesfully."
echo $cert | base64 --decode > ${tmp}/webhook-cert.pem
break
fi
echo -n "."; sleep 1
done
if [[ $cert == "" ]]; then
echo -e "\nError: certificate not issued. Verify that the API for signing certificates is enabled."
exit
fi
# create secret
echo "Creating secret..."
kubectl delete secret "multus-webhook-secret"
kubectl create secret generic --from-file=key.pem=${tmp}/webhook-key.pem --from-file=cert.pem=${tmp}/webhook-cert.pem "multus-webhook-secret"
# set cert in webhook configuration
echo "Patching configuration file with certificate..."
if [[ -f configuration-template.yaml ]]; then
sed "s/__CERT__/${cert}/" configuration-template.yaml > configuration.yaml
echo "File configuration.yaml patched."
else
echo -e "Error: validating configuration template file 'configuration-template.yaml' is missing. Please update it with cert.pem value from the secret manually."
fi

View File

@@ -1,38 +0,0 @@
# Copyright (c) 2018 Intel Corporation
#
# 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.
apiVersion: admissionregistration.k8s.io/v1beta1
kind: ValidatingWebhookConfiguration
metadata:
labels:
app: multus-webhook
name: multus-webhook-config
webhooks:
- clientConfig:
caBundle: __CERT__
service:
name: multus-webhook-service
namespace: default
path: /validate
failurePolicy: Fail
name: multus-webhook.k8s.cni.cncf.io
rules:
- apiGroups:
- k8s.cni.cncf.io
apiVersions:
- v1
resources:
- network-attachment-definitions
operations:
- CREATE

View File

@@ -1,50 +0,0 @@
# Copyright (c) 2018 Intel Corporation
#
# 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.
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: multus-webhook
name: multus-webhook-deployment
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: multus-webhook
template:
metadata:
labels:
app: multus-webhook
spec:
containers:
- name: multus-webhook
image: multus-webhook
command:
- /webhook/webhook
args:
- --bind-address=0.0.0.0
- --port=443
- --tls-private-key-file=/webhook/tls/key.pem
- --tls-cert-file=/webhook/tls/cert.pem
volumeMounts:
- mountPath: /webhook/tls
name: multus-webhook-secret
readOnly: True
imagePullPolicy: IfNotPresent
volumes:
- name: multus-webhook-secret
secret:
secretName: multus-webhook-secret

View File

@@ -1,27 +0,0 @@
# Copyright (c) 2018 Intel Corporation
#
# 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.
apiVersion: v1
kind: Service
metadata:
name: multus-webhook-service
labels:
app: multus-webhook
namespace: default
spec:
ports:
- port: 443
targetPort: 443
selector:
app: multus-webhook

View File

@@ -1,112 +0,0 @@
# Validating admission webhook
## Building Docker image
From the root directory of Multus execute:
```
cd webhook
./build
```
## Deploying webhook application
Change working directory. From the root directory of Multus execute:
```
cd deployment/webhook
```
Create key and certificate pair and patch configuration-template.yaml file with base64-encoded certificate file. Run:
```
./certs.sh
```
*Note: Verify that Kubernetes controller manager has --cluster-signing-cert-file and --cluster-signing-key-file parameters set to paths to your CA keypair,
to make sure that Certificates API is enabled in order to generate certificate signed by cluster CA.
Script generates private key and certificate signing request, which is then pushed to the Kubernetes API server.
Then script approves that CSR and API server issues the certificate. Certificate is obtained from the API server and used to create a secret.
Script also patches `configuration-template.yaml` file with base64-encoded certificate and creates `configuration.yaml` file containing
Validating Webhook Configuration specification, which is deployed in one of the next steps.
More details about TLS certificates management in a cluster available [here](https://kubernetes.io/docs/tasks/tls/managing-tls-in-a-cluster/).*
Create service:
```
kubectl create -f service.yaml
```
Run deployment:
```
kubectl create -f deployment.yaml
```
Create Validating Webhook Configuration:
```
kubectl create -f configuration.yaml
```
## Verifying installation
Try to create invalid Network Attachment Definition resource:
```
cat <<EOF | kubectl create -f -
apiVersion: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
metadata:
name: invalid-net-attach-def
spec:
config: '{
"invalid": "config"
}'
EOF
```
Webhook should deny the request:
```
Error from server: error when creating "STDIN": admission webhook "multus-webhook.k8s.cni.cncf.io" denied the request: Invalid network config spec
```
Now, try to create correctly defined one:
```
cat <<EOF | kubectl create -f -
apiVersion: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
metadata:
name: correct-net-attach-def
spec:
config: '{
"cniVersion": "0.3.0",
"name": "a-bridge-network",
"type": "bridge",
"bridge": "br0",
"isGateway": true,
"ipam": {
"type": "host-local",
"subnet": "192.168.5.0/24",
"dataDir": "/mnt/cluster-ipam"
}
}'
EOF
```
Resource should be allowed and created:
```
networkattachmentdefinition.k8s.cni.cncf.io/correct-net-attach-def created
```
## Troubleshooting
Webhook server prints a lot of debug messages that could help to find the root cause of an issue.
To display logs run:
```
kubectl logs -l app=multus-webhook
```
Example output showing logs for handling requests generated in the "Verifying installation section":
```
# kubectl logs multus-webhook-pod
2018-08-22T13:33:09Z [debug] Starting Multus webhook server
2018-08-22T13:33:32Z [debug] Validating network config spec: { "invalid": "config" }
2018-08-22T13:33:32Z [debug] Spec is not a valid network config: error parsing configuration list: no name. Trying to parse into config list
2018-08-22T13:33:32Z [debug] Spec is not a valid network config list: error parsing configuration: missing 'type'
2018-08-22T13:33:32Z [error] Invalid config: error parsing configuration: missing 'type'
2018-08-22T13:33:32Z [debug] Sending response to the API server
2018-08-22T13:35:29Z [debug] Validating network config spec: { "cniVersion": "0.3.0", "name": "a-bridge-network", "type": "bridge", "bridge": "br0", "isGateway": true, "ipam": { "type": "host-local", "subnet": "192.168.5.0/24", "dataDir": "/mnt/cluster-ipam" } }
2018-08-22T13:35:29Z [debug] Spec is not a valid network config: error parsing configuration list: no 'plugins' key. Trying to parse into config list
2018-08-22T13:35:29Z [debug] Network Attachment Defintion is valid. Admission Review request allowed
2018-08-22T13:35:29Z [debug] Sending response to the API server
```

View File

@@ -1,19 +0,0 @@
FROM golang:1.10 as builder
RUN go get github.com/Masterminds/glide
ENV PKG_NAME=github.com/intel/multus-cni/webhook
ENV PKG_PATH=$GOPATH/src/$PKG_NAME
WORKDIR $PKG_PATH
COPY *.go glide.yaml $PKG_PATH/
RUN glide install -v
RUN go build -v -o webhook
FROM golang:1.10
ENV PKG_NAME=github.com/intel/multus-cni/webhook
ENV PKG_PATH=$GOPATH/src/$PKG_NAME
RUN mkdir /webhook
COPY --from=builder $PKG_PATH/webhook /webhook/
WORKDIR /webhook
CMD ["./webhook"]

View File

@@ -1,10 +0,0 @@
#!/usr/bin/env bash
set -e
docker build --build-arg http_proxy=${http_proxy} \
--build-arg HTTP_PROXY=${HTTP_PROXY} \
--build-arg https_proxy=${https_proxy} \
--build-arg HTTPS_PROXY=${HTTPS_PROXY} \
--build-arg no_proxy=${no_proxy} \
--build-arg NO_PROXY=${NO_PROXY} \
-t multus-webhook .

View File

@@ -1,26 +0,0 @@
package: github.com/intel/multus-cni/webhook
import:
- package: github.com/containernetworking/cni
version: ~0.7.0-alpha1
subpackages:
- libcni
- package: github.com/intel/multus-cni
version: ~3.1.0
subpackages:
- logging
- types
- package: k8s.io/api
subpackages:
- admission/v1beta1
- package: k8s.io/apimachinery
subpackages:
- pkg/apis/meta/v1
- pkg/runtime
- pkg/runtime/serializer
testImport:
- package: github.com/onsi/ginkgo
version: ~1.6.0
subpackages:
- extensions/table
- package: github.com/onsi/gomega
version: ~1.4.1

View File

@@ -1,203 +0,0 @@
// Copyright (c) 2018 Intel Corporation
//
// 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 main
import (
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"net/http"
"regexp"
"github.com/intel/multus-cni/logging"
"github.com/intel/multus-cni/types"
"github.com/containernetworking/cni/libcni"
"k8s.io/api/admission/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/serializer"
)
func validateNetworkAttachmentDefinition(netAttachDef types.NetworkAttachmentDefinition) (bool, error) {
nameRegex := `^[a-z0-9]([-a-z0-9]*[a-z0-9])?$`
isNameCorrect, err := regexp.MatchString(nameRegex, netAttachDef.Metadata.Name)
if !isNameCorrect {
logging.Errorf("Invalid name.")
return false, fmt.Errorf("Invalid name")
}
if err != nil {
logging.Errorf("Error validating name: %s.", err)
return false, err
}
if netAttachDef.Spec.Config == "" {
logging.Errorf("Network Config is empty.")
return false, fmt.Errorf("Network Config is empty")
}
logging.Printf(logging.DebugLevel, "Validating network config spec: %s", netAttachDef.Spec.Config)
/* try to unmarshal config into NetworkConfig or NetworkConfigList
using actual code from libcni - if succesful, it means that the config
will be accepted by CNI itseld as well */
confBytes := []byte(netAttachDef.Spec.Config)
_, err = libcni.ConfListFromBytes(confBytes)
if err != nil {
logging.Printf(logging.DebugLevel, "Spec is not a valid network config: %s. Trying to parse into config list", err)
_, err = libcni.ConfFromBytes(confBytes)
if err != nil {
logging.Printf(logging.DebugLevel, "Spec is not a valid network config list: %s", err)
logging.Errorf("Invalid config: %s", err)
return false, fmt.Errorf("Invalid network config spec")
}
}
logging.Printf(logging.DebugLevel, "Network Attachment Defintion is valid. Admission Review request allowed")
return true, nil
}
func prepareAdmissionReviewResponse(allowed bool, message string, ar *v1beta1.AdmissionReview) error {
if ar.Request != nil {
ar.Response = &v1beta1.AdmissionResponse{
UID: ar.Request.UID,
Allowed: allowed,
}
if message != "" {
ar.Response.Result = &metav1.Status{
Message: message,
}
}
return nil
} else {
return fmt.Errorf("AdmissionReview request empty")
}
}
func deserializeAdmissionReview(body []byte) (v1beta1.AdmissionReview, error) {
ar := v1beta1.AdmissionReview{}
runtimeScheme := runtime.NewScheme()
codecs := serializer.NewCodecFactory(runtimeScheme)
deserializer := codecs.UniversalDeserializer()
_, _, err := deserializer.Decode(body, nil, &ar)
/* Decode() won't return an error if the data wasn't actual AdmissionReview */
if err == nil && ar.TypeMeta.Kind != "AdmissionReview" {
err = fmt.Errorf("Object is not an AdmissionReview")
}
return ar, err
}
func deserializeNetworkAttachmentDefinition(ar v1beta1.AdmissionReview) (types.NetworkAttachmentDefinition, error) {
/* unmarshal NetworkAttachmentDefinition from AdmissionReview request */
netAttachDef := types.NetworkAttachmentDefinition{}
err := json.Unmarshal(ar.Request.Object.Raw, &netAttachDef)
return netAttachDef, err
}
func handleValidationError(w http.ResponseWriter, ar v1beta1.AdmissionReview, orgErr error) {
err := prepareAdmissionReviewResponse(false, orgErr.Error(), &ar)
if err != nil {
logging.Errorf("Error preparing AdmissionResponse: %s", err.Error())
http.Error(w, fmt.Sprintf("Error preparing AdmissionResponse: %s", err.Error()), http.StatusBadRequest)
return
}
writeResponse(w, ar)
}
func writeResponse(w http.ResponseWriter, ar v1beta1.AdmissionReview) {
logging.Printf(logging.DebugLevel, "Sending response to the API server")
resp, _ := json.Marshal(ar)
w.Write(resp)
}
func validateHandler(w http.ResponseWriter, req *http.Request) {
var body []byte
if req.Body != nil {
if data, err := ioutil.ReadAll(req.Body); err == nil {
body = data
}
}
if len(body) == 0 {
logging.Errorf("Error reading HTTP request: empty body")
http.Error(w, "Error reading HTTP request: empty body", http.StatusBadRequest)
return
}
/* validate HTTP request headers */
contentType := req.Header.Get("Content-Type")
if contentType != "application/json" {
logging.Errorf("Invalid Content-Type='%s', expected 'application/json'", contentType)
http.Error(w, "Invalid Content-Type='%s', expected 'application/json'", http.StatusUnsupportedMediaType)
return
}
/* read AdmissionReview from the request body */
ar, err := deserializeAdmissionReview(body)
if err != nil {
logging.Errorf("Error deserializing AdmissionReview: %s", err.Error())
http.Error(w, fmt.Sprintf("Error deserializing AdmissionReview: %s", err.Error()), http.StatusBadRequest)
return
}
netAttachDef, err := deserializeNetworkAttachmentDefinition(ar)
if err != nil {
handleValidationError(w, ar, err)
return
}
/* perform actual object validation */
allowed, err := validateNetworkAttachmentDefinition(netAttachDef)
if err != nil {
handleValidationError(w, ar, err)
return
}
err = prepareAdmissionReviewResponse(allowed, "", &ar)
if err != nil {
logging.Errorf(err.Error())
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
writeResponse(w, ar)
}
func main() {
/* load configuration */
port := flag.Int("port", 443, "The port on which to serve.")
address := flag.String("bind-address", "0.0.0.0", "The IP address on which to listen for the --port port.")
cert := flag.String("tls-cert-file", "cert.pem", "File containing the default x509 Certificate for HTTPS.")
key := flag.String("tls-private-key-file", "key.pem", "File containing the default x509 private key matching --tls-cert-file.")
flag.Parse()
/* enable logging */
logging.SetLogLevel("debug")
logging.Printf(logging.DebugLevel, "Starting Multus webhook server")
/* register handlers */
http.HandleFunc("/validate", validateHandler)
/* start serving */
err := http.ListenAndServeTLS(fmt.Sprintf("%s:%d", *address, *port), *cert, *key, nil)
if err != nil {
logging.Errorf("Error starting web server: %s", err.Error())
return
}
}

View File

@@ -1,27 +0,0 @@
// Copyright (c) 2017 Intel Corporation
//
// 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 main
import (
"testing"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
func TestWebhook(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Webhook Suite")
}

View File

@@ -1,187 +0,0 @@
// Copyright (c) 2017 Intel Corporation
//
// 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 main
import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/ginkgo/extensions/table"
. "github.com/onsi/gomega"
"bytes"
"net/http"
"net/http/httptest"
"k8s.io/api/admission/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/intel/multus-cni/types"
)
var _ = Describe("Webhook", func() {
Describe("Preparing Admission Review Response", func() {
Context("Admission Review Request is nil", func() {
It("should return error", func() {
ar := &v1beta1.AdmissionReview{}
ar.Request = nil
Expect(prepareAdmissionReviewResponse(false, "", ar)).To(HaveOccurred())
})
})
Context("Message is not empty", func() {
It("should set message in the response", func() {
ar := &v1beta1.AdmissionReview{}
ar.Request = &v1beta1.AdmissionRequest{
UID: "fake-uid",
}
err := prepareAdmissionReviewResponse(false, "some message", ar)
Expect(err).NotTo(HaveOccurred())
Expect(ar.Response.Result.Message).To(Equal("some message"))
})
})
})
Describe("Deserializing Admission Review", func() {
Context("It's not an Admission Review", func() {
It("should return an error", func() {
body := []byte("some-invalid-body")
_, err := deserializeAdmissionReview(body)
Expect(err).To(HaveOccurred())
})
})
})
Describe("Deserializing Network Attachment Definition", func() {
Context("It's not an Network Attachment Definition", func() {
It("should return an error", func() {
ar := &v1beta1.AdmissionReview{}
ar.Request = &v1beta1.AdmissionRequest{}
_, err := deserializeNetworkAttachmentDefinition(*ar)
Expect(err).To(HaveOccurred())
})
})
})
Describe("Handling validation request", func() {
Context("Request body is empty", func() {
It("should return an error", func() {
req := httptest.NewRequest("POST", "https://fakewebhook/validate", nil)
w := httptest.NewRecorder()
validateHandler(w, req)
resp := w.Result()
Expect(resp.StatusCode).To(Equal(http.StatusBadRequest))
})
})
Context("Content type is not application/json", func() {
It("should return an error", func() {
req := httptest.NewRequest("POST", "https://fakewebhook/validate", bytes.NewBufferString("fake-body"))
req.Header.Set("Content-Type", "invalid-type")
w := httptest.NewRecorder()
validateHandler(w, req)
resp := w.Result()
Expect(resp.StatusCode).To(Equal(http.StatusUnsupportedMediaType))
})
})
})
DescribeTable("Network Attachment Definition validation",
func(in types.NetworkAttachmentDefinition, out bool, shouldFail bool) {
actualOut, err := validateNetworkAttachmentDefinition(in)
Expect(actualOut).To(Equal(out))
if shouldFail {
Expect(err).To(HaveOccurred())
}
},
Entry(
"empty config",
types.NetworkAttachmentDefinition{
Metadata: metav1.ObjectMeta{
Name: "some-valid-name",
},
},
false, true,
),
Entry(
"invalid name",
types.NetworkAttachmentDefinition{
Metadata: metav1.ObjectMeta{
Name: "some_invalid_name",
},
},
false, true,
),
Entry(
"invalid network config",
types.NetworkAttachmentDefinition{
Metadata: metav1.ObjectMeta{
Name: "some-valid-name",
},
Spec: types.NetworkAttachmentDefinitionSpec{
Config: `{"some-invalid": "config"}`,
},
},
false, true,
),
Entry(
"valid network config",
types.NetworkAttachmentDefinition{
Metadata: metav1.ObjectMeta{
Name: "some-valid-name",
},
Spec: types.NetworkAttachmentDefinitionSpec{
Config: `{
"cniVersion": "0.3.0",
"type": "some-plugin"
}`,
},
},
true, false,
),
Entry(
"valid network config list",
types.NetworkAttachmentDefinition{
Metadata: metav1.ObjectMeta{
Name: "some-valid-name",
},
Spec: types.NetworkAttachmentDefinitionSpec{
Config: `{
"cniVersion": "0.3.0",
"name": "some-bridge-network",
"plugins": [
{
"type": "bridge",
"bridge": "br0",
"ipam": {
"type": "host-local",
"subnet": "192.168.1.0/24"
}
},
{
"type": "some-plugin"
},
{
"type": "another-plugin",
"sysctl": {
"net.ipv4.conf.all.log_martians": "1"
}
}
]
}`,
},
},
true, false,
),
)
})