diff --git a/test/assets/multus/bridge-nad.yaml b/test/assets/multus/bridge-nad.yaml new file mode 100644 index 0000000000..1025a132b1 --- /dev/null +++ b/test/assets/multus/bridge-nad.yaml @@ -0,0 +1,25 @@ +apiVersion: "k8s.cni.cncf.io/v1" +kind: NetworkAttachmentDefinition +metadata: + name: bridge-conf +spec: + config: '{ + "cniVersion": "0.4.0", + "type": "bridge", + "bridge": "br-test", + "mode": "bridge", + "ipam": { + "type": "host-local", + "ranges": [ + [ + { + "subnet": "10.10.0.0/24", + "rangeStart": "10.10.0.20", + "rangeEnd": "10.10.0.50", + "gateway": "10.10.0.254" + } + ] + ], + "dataDir": "/var/lib/cni/br-test" + } + }' diff --git a/test/assets/multus/bridge-pod.yaml b/test/assets/multus/bridge-pod.yaml new file mode 100644 index 0000000000..59e3832636 --- /dev/null +++ b/test/assets/multus/bridge-pod.yaml @@ -0,0 +1,28 @@ +kind: Pod +apiVersion: v1 +metadata: + name: test-bridge + annotations: + k8s.v1.cni.cncf.io/networks: bridge-conf + labels: + app: test-bridge +spec: + terminationGracePeriodSeconds: 0 + containers: + - name: hello-microshift + image: quay.io/microshift/busybox:1.36 + command: ["/bin/sh"] + args: ["-c", "while true; do echo -ne \"HTTP/1.0 200 OK\r\nContent-Length: 16\r\n\r\nHello MicroShift\" | nc -l -p 8080 ; done"] + ports: + - containerPort: 8080 + protocol: TCP + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + runAsNonRoot: true + runAsUser: 1001 + runAsGroup: 1001 + seccompProfile: + type: RuntimeDefault diff --git a/test/assets/multus/bridge-preexisting-nad.yaml b/test/assets/multus/bridge-preexisting-nad.yaml new file mode 100644 index 0000000000..0fdda25c9a --- /dev/null +++ b/test/assets/multus/bridge-preexisting-nad.yaml @@ -0,0 +1,25 @@ +apiVersion: "k8s.cni.cncf.io/v1" +kind: NetworkAttachmentDefinition +metadata: + name: bridge-preexisting-conf +spec: + config: '{ + "cniVersion": "0.4.0", + "type": "bridge", + "bridge": "br-preexisting", + "mode": "bridge", + "ipam": { + "type": "host-local", + "ranges": [ + [ + { + "subnet": "10.10.1.0/24", + "rangeStart": "10.10.1.20", + "rangeEnd": "10.10.1.50", + "gateway": "10.10.1.254" + } + ] + ], + "dataDir": "/var/lib/cni/br-preexisting" + } + }' diff --git a/test/assets/multus/bridge-preexisting-pod.yaml b/test/assets/multus/bridge-preexisting-pod.yaml new file mode 100644 index 0000000000..4ffd6e4e49 --- /dev/null +++ b/test/assets/multus/bridge-preexisting-pod.yaml @@ -0,0 +1,28 @@ +kind: Pod +apiVersion: v1 +metadata: + name: test-bridge-preexisting + annotations: + k8s.v1.cni.cncf.io/networks: bridge-preexisting-conf + labels: + app: test-bridge-preexisting +spec: + terminationGracePeriodSeconds: 0 + containers: + - name: hello-microshift + image: quay.io/microshift/busybox:1.36 + command: ["/bin/sh"] + args: ["-c", "while true; do echo -ne \"HTTP/1.0 200 OK\r\nContent-Length: 16\r\n\r\nHello MicroShift\" | nc -l -p 8080 ; done"] + ports: + - containerPort: 8080 + protocol: TCP + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + runAsNonRoot: true + runAsUser: 1001 + runAsGroup: 1001 + seccompProfile: + type: RuntimeDefault diff --git a/test/image-blueprints/layer3-periodic/group1/rhel92-source-with-optionals.toml b/test/image-blueprints/layer3-periodic/group1/rhel92-source-with-optionals.toml index cf1d7197d7..a3a292785c 100644 --- a/test/image-blueprints/layer3-periodic/group1/rhel92-source-with-optionals.toml +++ b/test/image-blueprints/layer3-periodic/group1/rhel92-source-with-optionals.toml @@ -25,6 +25,10 @@ version = "{{ .Env.SOURCE_VERSION }}" name = "microshift-olm" version = "{{ .Env.SOURCE_VERSION }}" +[[packages]] +name = "microshift-multus" +version = "{{ .Env.SOURCE_VERSION }}" + [[packages]] name = "microshift-test-agent" version = "*" diff --git a/test/resources/DataFormats.py b/test/resources/DataFormats.py index 3448dc43b9..1b3fd3732d 100644 --- a/test/resources/DataFormats.py +++ b/test/resources/DataFormats.py @@ -14,6 +14,8 @@ def json_parse(data): if not data: return DotDict() parsed = json.loads(data) + if type(parsed) == list: + return DotDict({"list": parsed})["list"] return DotDict(parsed) diff --git a/test/resources/microshift-host.resource b/test/resources/microshift-host.resource index ec596f35b6..408ab725d8 100644 --- a/test/resources/microshift-host.resource +++ b/test/resources/microshift-host.resource @@ -122,3 +122,14 @@ Command Should Work Log ${stderr} Should Be Equal As Integers 0 ${rc} RETURN ${stdout} + +Command Should Fail + [Documentation] Run a command remotely, expect command to fail + [Arguments] ${command} + ${stdout} ${stderr} ${rc}= SSHLibrary.Execute Command + ... ${command} + ... sudo=True + ... return_rc=True + ... return_stderr=True + ... return_stdout=True + Should Not Be Equal As Integers 0 ${rc} diff --git a/test/resources/oc.resource b/test/resources/oc.resource index 459d6eb11c..a4d843de04 100644 --- a/test/resources/oc.resource +++ b/test/resources/oc.resource @@ -46,8 +46,8 @@ Oc Delete Oc Exec [Documentation] Run 'oc exec' on a specific pod in the curret test namespace ... Returns the command's combined STDOUT/STDER - [Arguments] ${pod} ${cmd} - ${output}= Run With Kubeconfig oc exec -n ${NAMESPACE} pod/${pod} -- /bin/bash -c '${cmd}' + [Arguments] ${pod} ${cmd} ${ns}=${NAMESPACE} + ${output}= Run With Kubeconfig oc exec -n ${ns} pod/${pod} -- /bin/bash -c '${cmd}' RETURN ${output} Named Pod Should Be Ready @@ -57,7 +57,7 @@ Named Pod Should Be Ready ... ${timeout} Period of time to wait for pod to reach ready status. Default 30s. [Arguments] ${name} ${ns}=${NAMESPACE} ${timeout}=30s - Run With Kubeconfig oc wait -n ${NAMESPACE} pod/${name} --for="condition=Ready" --timeout=${timeout} + Run With Kubeconfig oc wait -n ${ns} pod/${name} --for="condition=Ready" --timeout=${timeout} Named Pod Should Be Deleted [Documentation] Wait for pod with ${name} to be deleted @@ -65,7 +65,7 @@ Named Pod Should Be Deleted ... ${ns} Namespace of named pod. Defaults to NAMESPACE suite variable ... ${timeout} Period of time to wait for pod to reach ready status. Default 30s. [Arguments] ${name} ${ns}=${NAMESPACE} ${timeout}=30s - Run With Kubeconfig oc wait -n ${NAMESPACE} pod/${name} --for=delete --timeout=${timeout} + Run With Kubeconfig oc wait -n ${ns} pod/${name} --for=delete --timeout=${timeout} Labeled Pod Should Be Ready [Documentation] Wait for pod(s) ready by ${label} to become "Ready" diff --git a/test/suites/optional/multus.robot b/test/suites/optional/multus.robot new file mode 100644 index 0000000000..0ba5ecff1d --- /dev/null +++ b/test/suites/optional/multus.robot @@ -0,0 +1,135 @@ +*** Settings *** +Documentation Tests for Multus and Bridge plugin on MicroShift + +Resource ../../resources/common.resource +Resource ../../resources/microshift-process.resource + +Suite Setup Setup Suite With Namespace +Suite Teardown Teardown Suite With Namespace + + +*** Variables *** +${BRIDGE_INTERFACE} br-test +${BRIDGE_NAD_YAML} ./assets/multus/bridge-nad.yaml +${BRIDGE_POD_YAML} ./assets/multus/bridge-pod.yaml +${BRIDGE_POD_NAME} test-bridge +${BRIDGE_IP} 10.10.0.10/24 + +${PE_BRIDGE_INTERFACE} br-preexisting +${PE_BRIDGE_NAD_YAML} ./assets/multus/bridge-preexisting-nad.yaml +${PE_BRIDGE_POD_YAML} ./assets/multus/bridge-preexisting-pod.yaml +${PE_BRIDGE_POD_NAME} test-bridge-preexisting +${PE_BRIDGE_IP} 10.10.1.10/24 + + +*** Test Cases *** +Pre-Existing Bridge Interface + [Documentation] Test verifies if Bridge CNI plugin will work correctly with pre-existing interface. + [Setup] Run Keywords + ... Interface Should Not Exist ${PE_BRIDGE_INTERFACE} + ... AND + ... Create Interface ${PE_BRIDGE_INTERFACE} + ... AND + ... Create NAD And Pod ${PE_BRIDGE_NAD_YAML} ${PE_BRIDGE_POD_YAML} + ... AND + ... Named Pod Should Be Ready ${PE_BRIDGE_POD_NAME} ${NAMESPACE} + ... AND + ... Interface Should Exist ${PE_BRIDGE_INTERFACE} + ... AND + ... Set IP For Host Interface ${PE_BRIDGE_INTERFACE} ${PE_BRIDGE_IP} + + Connect To Pod Over Local Interface ${PE_BRIDGE_POD_NAME} ${NAMESPACE} ${PE_BRIDGE_INTERFACE} + + [Teardown] Cleanup Bridge Test + ... ${PE_BRIDGE_NAD_YAML} + ... ${PE_BRIDGE_POD_YAML} + ... ${PE_BRIDGE_INTERFACE} + +No Pre-Existing Bridge Interface + [Documentation] Test verifies if Bridge CNI plugin will + ... work correctly if there is no pre-existing bridge + ... interface - it needs to be created. + [Setup] Run Keywords + ... Interface Should Not Exist ${BRIDGE_INTERFACE} + ... AND + ... Create NAD And Pod ${BRIDGE_NAD_YAML} ${BRIDGE_POD_YAML} + ... AND + ... Named Pod Should Be Ready ${BRIDGE_POD_NAME} ${NAMESPACE} + ... AND + ... Interface Should Exist ${BRIDGE_INTERFACE} + ... AND + ... Set IP For Host Interface ${BRIDGE_INTERFACE} ${BRIDGE_IP} + + Connect To Pod Over Local Interface ${BRIDGE_POD_NAME} ${NAMESPACE} ${BRIDGE_INTERFACE} + + [Teardown] Cleanup Bridge Test + ... ${BRIDGE_NAD_YAML} + ... ${BRIDGE_POD_YAML} + ... ${BRIDGE_INTERFACE} + + +*** Keywords *** +Create NAD And Pod + [Documentation] Creates provided NetworkAttachmentDefinition and Pod. + [Arguments] ${nad} ${pod} + Oc Create -n ${NAMESPACE} -f ${nad} + Oc Create -n ${NAMESPACE} -f ${pod} + +Cleanup Bridge Test + [Documentation] Removes provided NetworkAttachmentDefinition, Pod and network interface to allow for test rerun. + [Arguments] ${nad} ${pod} ${if} + Run Keyword And Continue On Failure + ... Oc Delete -n ${NAMESPACE} -f ${pod} + Run Keyword And Continue On Failure + ... Oc Delete -n ${NAMESPACE} -f ${nad} + Command Should Work ip link delete ${if} + +Connect To Pod Over Local Interface + [Documentation] Makes a HTTP request to 8080 for a given Pod over given interface. + [Arguments] ${pod} ${ns} ${if} + + ${networks}= Get And Verify Pod Networks ${pod} ${ns} + ${extra_ip}= Set Variable ${networks}[1][ips][0] + + ${stdout}= Command Should Work curl -v --interface ${if} ${extra_ip}:8080 + Should Contain ${stdout} Hello MicroShift + +Interface Should Not Exist + [Documentation] Verifies that network interface does not exist. + [Arguments] ${if} + Command Should Fail ip link show ${if} + +Create Interface + [Documentation] Creates network interface. + [Arguments] ${if} ${type}=bridge + Command Should Work ip link add dev ${if} type ${type} + +Interface Should Exist + [Documentation] Verifies that interface exists on the host. + [Arguments] ${if} + Command Should Work ip link show ${if} + +Set IP For Host Interface + [Documentation] Sets IP address for the interface. + [Arguments] ${if} ${cidr} + Command Should Work ip addr add ${cidr} dev ${if} + +Get And Verify Pod Networks + [Documentation] Obtains interfaces of the Pod from its annotation. + ... The annotation is managed by Multus. + [Arguments] ${pod} ${ns} + + ${networks_str}= Oc Get JsonPath + ... pod + ... ${ns} + ... ${pod} + ... .metadata.annotations.k8s\\.v1\\.cni\\.cncf\\.io/network-status + Should Not Be Empty ${networks_str} + + ${networks}= Json Parse ${networks_str} + ${n}= Get Length ${networks} + Should Be Equal As Integers ${n} 2 + Should Be Equal As Strings ${networks}[0][name] ovn-kubernetes + Should Match ${networks}[1][name] ${NAMESPACE}/bridge*-conf + + RETURN ${networks}