Compare commits
31 Commits
Author | SHA1 | Date |
---|---|---|
|
ee7c96f201 | |
|
d0bdb01ee9 | |
|
8338cebec2 | |
|
44a77d77e9 | |
|
277757c333 | |
|
0b7ae2f1b1 | |
|
40b0a08c95 | |
|
7f701df6ab | |
|
a28faab926 | |
|
3f7369aeb9 | |
|
097592d34b | |
|
d2f3f46deb | |
|
3578abe586 | |
|
5f84615a5d | |
|
3b4dfc5dff | |
|
a845cc88d2 | |
|
4c9ae43c0e | |
|
be3f5a9b2d | |
|
f4f2dc7430 | |
|
81ed2d06d6 | |
|
00f4094a12 | |
|
f0289faa47 | |
|
81d15e90c3 | |
|
309b6bbc17 | |
|
692efbd2a7 | |
|
6a68851f26 | |
|
d5c71ad528 | |
|
587837eeda | |
|
425425837e | |
|
c0cc785dbc | |
|
9f73d4da82 |
|
@ -9,7 +9,7 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Check out code
|
- name: Check out code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||||
|
|
||||||
- name: Re-Test Action
|
- name: Re-Test Action
|
||||||
uses: ./.github/actions/retest-action
|
uses: ./.github/actions/retest-action
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
name: Scorecard supply-chain security
|
||||||
|
on:
|
||||||
|
branch_protection_rule:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
schedule:
|
||||||
|
- cron: 29 15 * * 0
|
||||||
|
permissions: read-all
|
||||||
|
jobs:
|
||||||
|
analysis:
|
||||||
|
name: Scorecard analysis
|
||||||
|
permissions:
|
||||||
|
id-token: write
|
||||||
|
security-events: write
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
|
- name: Run analysis
|
||||||
|
uses: ossf/scorecard-action@0864cf19026789058feabb7e87baa5f140aac736 # v2.3.1
|
||||||
|
with:
|
||||||
|
results_file: results.sarif
|
||||||
|
results_format: sarif
|
||||||
|
publish_results: true
|
||||||
|
|
||||||
|
- name: Upload artifact
|
||||||
|
uses: actions/upload-artifact@97a0fba1372883ab732affbe8f94b823f91727db # v3.pre.node20
|
||||||
|
with:
|
||||||
|
name: SARIF file
|
||||||
|
path: results.sarif
|
||||||
|
retention-days: 5
|
||||||
|
|
||||||
|
- name: Upload to code-scanning
|
||||||
|
uses: github/codeql-action/upload-sarif@8214744c546c1e5c8f03dde8fab3a7353211988d # v3.26.7
|
||||||
|
with:
|
||||||
|
sarif_file: results.sarif
|
|
@ -10,30 +10,39 @@ env:
|
||||||
jobs:
|
jobs:
|
||||||
lint:
|
lint:
|
||||||
name: Lint
|
name: Lint
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pull-requests: read
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: setup go
|
- name: setup go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2
|
||||||
with:
|
with:
|
||||||
go-version: ${{ env.GO_VERSION }}
|
go-version: ${{ env.GO_VERSION }}
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: ibiqlik/action-yamllint@v3
|
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||||
|
|
||||||
|
- uses: ibiqlik/action-yamllint@2576378a8e339169678f9939646ee3ee325e845c # v3.1.1
|
||||||
with:
|
with:
|
||||||
format: auto
|
format: auto
|
||||||
- uses: golangci/golangci-lint-action@v6
|
config_file: .yamllint.yaml
|
||||||
|
|
||||||
|
- uses: golangci/golangci-lint-action@aaa42aa0628b4ae2578232a66b541047968fac86 # v6.1.0
|
||||||
with:
|
with:
|
||||||
args: --verbose
|
args: --verbose
|
||||||
version: v1.57.1
|
version: v1.57.1
|
||||||
|
|
||||||
build:
|
build:
|
||||||
name: Build all linux architectures
|
name: Build all linux architectures
|
||||||
needs: lint
|
needs: lint
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: setup go
|
- name: setup go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2
|
||||||
with:
|
with:
|
||||||
go-version: ${{ env.GO_VERSION }}
|
go-version: ${{ env.GO_VERSION }}
|
||||||
- uses: actions/checkout@v4
|
|
||||||
|
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||||
|
|
||||||
- name: Build on all supported architectures
|
- name: Build on all supported architectures
|
||||||
run: |
|
run: |
|
||||||
|
@ -49,10 +58,11 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: setup go
|
- name: setup go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2
|
||||||
with:
|
with:
|
||||||
go-version: ${{ env.GO_VERSION }}
|
go-version: ${{ env.GO_VERSION }}
|
||||||
- uses: actions/checkout@v4
|
|
||||||
|
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||||
|
|
||||||
- name: Install test binaries
|
- name: Install test binaries
|
||||||
run: |
|
run: |
|
||||||
|
@ -62,9 +72,9 @@ jobs:
|
||||||
- name: test
|
- name: test
|
||||||
run: COVERALLS=1 ./test.sh
|
run: COVERALLS=1 ./test.sh
|
||||||
|
|
||||||
- name: Send coverage to coveralls
|
- env:
|
||||||
env:
|
|
||||||
COVERALLS_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
COVERALLS_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
name: Send coverage to coveralls
|
||||||
run: |
|
run: |
|
||||||
PATH=$PATH:$(go env GOPATH)/bin
|
PATH=$PATH:$(go env GOPATH)/bin
|
||||||
gover
|
gover
|
||||||
|
@ -76,9 +86,11 @@ jobs:
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
steps:
|
steps:
|
||||||
- name: setup go
|
- name: setup go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2
|
||||||
with:
|
with:
|
||||||
go-version: ${{ env.GO_VERSION }}
|
go-version: ${{ env.GO_VERSION }}
|
||||||
- uses: actions/checkout@v4
|
|
||||||
|
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||||
|
|
||||||
- name: test
|
- name: test
|
||||||
run: bash ./test.sh
|
run: bash ./test.sh
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
.idea/
|
.idea/
|
||||||
bin/
|
|
||||||
gopath/
|
|
||||||
*.sw[ponm]
|
*.sw[ponm]
|
||||||
.vagrant
|
.vagrant
|
||||||
release-*
|
release-*
|
||||||
|
cnitool/cnitool
|
|
@ -3,6 +3,7 @@ extends: default
|
||||||
|
|
||||||
rules:
|
rules:
|
||||||
document-start: disable
|
document-start: disable
|
||||||
|
line-length: disable
|
||||||
truthy:
|
truthy:
|
||||||
ignore: |
|
ignore: |
|
||||||
.github/workflows/*.yml
|
.github/workflows/*.yml
|
14
MAINTAINERS
14
MAINTAINERS
|
@ -1,8 +1,14 @@
|
||||||
Bruce Ma <brucema19901024@gmail.com> (@mars1024)
|
Bruce Ma <brucema19901024@gmail.com> (@mars1024)
|
||||||
Casey Callendrello <cdc@isovalent.com> (@squeed)
|
Casey Callendrello <cdc@isovalent.com> (@squeed)
|
||||||
Dan Williams <dcbw@redhat.com> (@dcbw)
|
|
||||||
Matt Dupre <matt@tigera.io> (@matthewdupre)
|
|
||||||
Michael Cambria <mcambria@redhat.com> (@mccv1r0)
|
Michael Cambria <mcambria@redhat.com> (@mccv1r0)
|
||||||
Michael Zappa <Michael.Zappa@gmail.com> (@MikeZappa87)
|
Michael Zappa <Michael.Zappa@gmail.com> (@MikeZappa87)
|
||||||
Piotr Skamruk <piotr.skamruk@gmail.com> (@jellonek)
|
Tomofumi Hayashi <s1061123@gmail.com> (@s1061123)
|
||||||
Tomofumi Hayashi <tohayash@redhat.com> (@s1061123)
|
Lionel Jouin <lionel.jouin@est.tech> (@LionelJouin)
|
||||||
|
Ben Leggett <benjamin@edera.dev> (@bleggett)
|
||||||
|
Marcelo Guerrero <guerrero.viveros@gmail.com> (@mlguerrero12)
|
||||||
|
Doug Smith <douglas.kipp.smith@gmail.com> (@dougbtv)
|
||||||
|
|
||||||
|
Emeritus:
|
||||||
|
Dan Williams <dcbw@redhat.com> (@dcbw)
|
||||||
|
Matt Dupre <matt@tigera.io> (@matthewdupre)
|
||||||
|
Piotr Skamruk <piotr.skamruk@gmail.com> (@jellonek)
|
11
README.md
11
README.md
|
@ -4,6 +4,9 @@
|
||||||
|
|
||||||
# CNI - the Container Network Interface
|
# CNI - the Container Network Interface
|
||||||
|
|
||||||
|
[](https://bestpractices.coreinfrastructure.org/projects/2446)
|
||||||
|
[](https://securityscorecards.dev/viewer/?uri=github.com/containernetworking/cni)
|
||||||
|
|
||||||
## What is CNI?
|
## What is CNI?
|
||||||
|
|
||||||
CNI (_Container Network Interface_), a [Cloud Native Computing Foundation](https://cncf.io) project, consists of a specification and libraries for writing plugins to configure network interfaces in Linux containers, along with a number of supported plugins.
|
CNI (_Container Network Interface_), a [Cloud Native Computing Foundation](https://cncf.io) project, consists of a specification and libraries for writing plugins to configure network interfaces in Linux containers, along with a number of supported plugins.
|
||||||
|
@ -37,7 +40,6 @@ To avoid duplication, we think it is prudent to define a common interface betwee
|
||||||
|
|
||||||
## Who is using CNI?
|
## Who is using CNI?
|
||||||
### Container runtimes
|
### Container runtimes
|
||||||
- [rkt - container engine](https://coreos.com/blog/rkt-cni-networking.html)
|
|
||||||
- [Kubernetes - a system to simplify container operations](https://kubernetes.io/docs/concepts/extend-kubernetes/compute-storage-net/network-plugins/)
|
- [Kubernetes - a system to simplify container operations](https://kubernetes.io/docs/concepts/extend-kubernetes/compute-storage-net/network-plugins/)
|
||||||
- [OpenShift - Kubernetes with additional enterprise features](https://github.com/openshift/origin/blob/master/docs/openshift_networking_requirements.md)
|
- [OpenShift - Kubernetes with additional enterprise features](https://github.com/openshift/origin/blob/master/docs/openshift_networking_requirements.md)
|
||||||
- [Cloud Foundry - a platform for cloud applications](https://github.com/cloudfoundry-incubator/cf-networking-release)
|
- [Cloud Foundry - a platform for cloud applications](https://github.com/cloudfoundry-incubator/cf-networking-release)
|
||||||
|
@ -48,16 +50,13 @@ To avoid duplication, we think it is prudent to define a common interface betwee
|
||||||
|
|
||||||
### 3rd party plugins
|
### 3rd party plugins
|
||||||
- [Project Calico - a layer 3 virtual network](https://github.com/projectcalico/calico)
|
- [Project Calico - a layer 3 virtual network](https://github.com/projectcalico/calico)
|
||||||
- [Weave - a multi-host Docker network](https://github.com/weaveworks/weave)
|
|
||||||
- [Contiv Networking - policy networking for various use cases](https://github.com/contiv/netplugin)
|
- [Contiv Networking - policy networking for various use cases](https://github.com/contiv/netplugin)
|
||||||
- [SR-IOV](https://github.com/hustcat/sriov-cni)
|
- [SR-IOV](https://github.com/hustcat/sriov-cni)
|
||||||
- [Cilium - BPF & XDP for containers](https://github.com/cilium/cilium)
|
- [Cilium - eBPF & XDP for containers](https://github.com/cilium/cilium)
|
||||||
- [Infoblox - enterprise IP address management for containers](https://github.com/infobloxopen/cni-infoblox)
|
|
||||||
- [Multus - a Multi plugin](https://github.com/k8snetworkplumbingwg/multus-cni)
|
- [Multus - a Multi plugin](https://github.com/k8snetworkplumbingwg/multus-cni)
|
||||||
- [Romana - Layer 3 CNI plugin supporting network policy for Kubernetes](https://github.com/romana/kube)
|
- [Romana - Layer 3 CNI plugin supporting network policy for Kubernetes](https://github.com/romana/kube)
|
||||||
- [CNI-Genie - generic CNI network plugin](https://github.com/Huawei-PaaS/CNI-Genie)
|
- [CNI-Genie - generic CNI network plugin](https://github.com/Huawei-PaaS/CNI-Genie)
|
||||||
- [Nuage CNI - Nuage Networks SDN plugin for network policy kubernetes support ](https://github.com/nuagenetworks/nuage-cni)
|
- [Nuage CNI - Nuage Networks SDN plugin for network policy kubernetes support ](https://github.com/nuagenetworks/nuage-cni)
|
||||||
- [Silk - a CNI plugin designed for Cloud Foundry](https://github.com/cloudfoundry-incubator/silk)
|
|
||||||
- [Linen - a CNI plugin designed for overlay networks with Open vSwitch and fit in SDN/OpenFlow network environment](https://github.com/John-Lin/linen-cni)
|
- [Linen - a CNI plugin designed for overlay networks with Open vSwitch and fit in SDN/OpenFlow network environment](https://github.com/John-Lin/linen-cni)
|
||||||
- [Vhostuser - a Dataplane network plugin - Supports OVS-DPDK & VPP](https://github.com/intel/vhost-user-net-plugin)
|
- [Vhostuser - a Dataplane network plugin - Supports OVS-DPDK & VPP](https://github.com/intel/vhost-user-net-plugin)
|
||||||
- [Amazon ECS CNI Plugins - a collection of CNI Plugins to configure containers with Amazon EC2 elastic network interfaces (ENIs)](https://github.com/aws/amazon-ecs-cni-plugins)
|
- [Amazon ECS CNI Plugins - a collection of CNI Plugins to configure containers with Amazon EC2 elastic network interfaces (ENIs)](https://github.com/aws/amazon-ecs-cni-plugins)
|
||||||
|
@ -66,13 +65,11 @@ To avoid duplication, we think it is prudent to define a common interface betwee
|
||||||
- [Juniper Contrail](https://www.juniper.net/cloud) / [TungstenFabric](https://tungstenfabric.io) - Provides overlay SDN solution, delivering multicloud networking, hybrid cloud networking, simultaneous overlay-underlay support, network policy enforcement, network isolation, service chaining and flexible load balancing
|
- [Juniper Contrail](https://www.juniper.net/cloud) / [TungstenFabric](https://tungstenfabric.io) - Provides overlay SDN solution, delivering multicloud networking, hybrid cloud networking, simultaneous overlay-underlay support, network policy enforcement, network isolation, service chaining and flexible load balancing
|
||||||
- [Knitter - a CNI plugin supporting multiple networking for Kubernetes](https://github.com/ZTE/Knitter)
|
- [Knitter - a CNI plugin supporting multiple networking for Kubernetes](https://github.com/ZTE/Knitter)
|
||||||
- [DANM - a CNI-compliant networking solution for TelCo workloads running on Kubernetes](https://github.com/nokia/danm)
|
- [DANM - a CNI-compliant networking solution for TelCo workloads running on Kubernetes](https://github.com/nokia/danm)
|
||||||
- [VMware NSX – a CNI plugin that enables automated NSX L2/L3 networking and L4/L7 Load Balancing; network isolation at the pod, node, and cluster level; and zero-trust security policy for your Kubernetes cluster.](https://docs.vmware.com/en/VMware-NSX-T/2.2/com.vmware.nsxt.ncp_kubernetes.doc/GUID-6AFA724E-BB62-4693-B95C-321E8DDEA7E1.html)
|
|
||||||
- [cni-route-override - a meta CNI plugin that override route information](https://github.com/redhat-nfvpe/cni-route-override)
|
- [cni-route-override - a meta CNI plugin that override route information](https://github.com/redhat-nfvpe/cni-route-override)
|
||||||
- [Terway - a collection of CNI Plugins based on alibaba cloud VPC/ECS network product](https://github.com/AliyunContainerService/terway)
|
- [Terway - a collection of CNI Plugins based on alibaba cloud VPC/ECS network product](https://github.com/AliyunContainerService/terway)
|
||||||
- [Cisco ACI CNI - for on-prem and cloud container networking with consistent policy and security model.](https://github.com/noironetworks/aci-containers)
|
- [Cisco ACI CNI - for on-prem and cloud container networking with consistent policy and security model.](https://github.com/noironetworks/aci-containers)
|
||||||
- [Kube-OVN - a CNI plugin that bases on OVN/OVS and provides advanced features like subnet, static ip, ACL, QoS, etc.](https://github.com/kubeovn/kube-ovn)
|
- [Kube-OVN - a CNI plugin that bases on OVN/OVS and provides advanced features like subnet, static ip, ACL, QoS, etc.](https://github.com/kubeovn/kube-ovn)
|
||||||
- [Project Antrea - an Open vSwitch k8s CNI](https://github.com/vmware-tanzu/antrea)
|
- [Project Antrea - an Open vSwitch k8s CNI](https://github.com/vmware-tanzu/antrea)
|
||||||
- [OVN4NFV-K8S-Plugin - a OVN based CNI controller plugin to provide cloud native based Service function chaining (SFC), Multiple OVN overlay networking](https://github.com/opnfv/ovn4nfv-k8s-plugin)
|
|
||||||
- [Azure CNI - a CNI plugin that natively extends Azure Virtual Networks to containers](https://github.com/Azure/azure-container-networking)
|
- [Azure CNI - a CNI plugin that natively extends Azure Virtual Networks to containers](https://github.com/Azure/azure-container-networking)
|
||||||
- [Hybridnet - a CNI plugin designed for hybrid clouds which provides both overlay and underlay networking for containers in one or more clusters. Overlay and underlay containers can run on the same node and have cluster-wide bidirectional network connectivity.](https://github.com/alibaba/hybridnet)
|
- [Hybridnet - a CNI plugin designed for hybrid clouds which provides both overlay and underlay networking for containers in one or more clusters. Overlay and underlay containers can run on the same node and have cluster-wide bidirectional network connectivity.](https://github.com/alibaba/hybridnet)
|
||||||
- [Spiderpool - An IP Address Management (IPAM) CNI plugin of Kubernetes for managing static ip for underlay network](https://github.com/spidernet-io/spiderpool)
|
- [Spiderpool - An IP Address Management (IPAM) CNI plugin of Kubernetes for managing static ip for underlay network](https://github.com/spidernet-io/spiderpool)
|
||||||
|
|
26
SPEC.md
26
SPEC.md
|
@ -109,14 +109,16 @@ A network configuration consists of a JSON object with the following keys:
|
||||||
|
|
||||||
- `cniVersion` (string): [Semantic Version 2.0](https://semver.org) of CNI specification to which this configuration list and all the individual configurations conform. Currently "1.1.0"
|
- `cniVersion` (string): [Semantic Version 2.0](https://semver.org) of CNI specification to which this configuration list and all the individual configurations conform. Currently "1.1.0"
|
||||||
- `cniVersions` (string list): List of all CNI versions which this configuration supports. See [version selection](#version-selection) below.
|
- `cniVersions` (string list): List of all CNI versions which this configuration supports. See [version selection](#version-selection) below.
|
||||||
- `name` (string): Network name. This should be unique across all network configurations on a host (or other administrative domain). Must start with an alphanumeric character, optionally followed by any combination of one or more alphanumeric characters, underscore, dot (.) or hyphen (-).
|
- `name` (string): Network name. This should be unique across all network configurations on a host (or other administrative domain). Must start with an alphanumeric character, optionally followed by any combination of one or more alphanumeric characters, underscore, dot (.) or hyphen (-). Must not contain characters disallowed in file paths.
|
||||||
- `disableCheck` (boolean): Either `true` or `false`. If `disableCheck` is `true`, runtimes must not call `CHECK` for this network configuration list. This allows an administrator to prevent `CHECK`ing where a combination of plugins is known to return spurious errors.
|
- `disableCheck` (boolean): Either `true` or `false`. If `disableCheck` is `true`, runtimes must not call `CHECK` for this network configuration list. This allows an administrator to prevent `CHECK`ing where a combination of plugins is known to return spurious errors.
|
||||||
- `disableGC` (boolean): Either `true` or `false`. If `disableGC` is `true`, runtimes must not call `GC` for this network configuration list. This allows an administrator to prevent `GC`ing when it is known that garbage collection may have undesired effects (e.g. shared configuration between multiple runtimes).
|
- `disableGC` (boolean): Either `true` or `false`. If `disableGC` is `true`, runtimes must not call `GC` for this network configuration list. This allows an administrator to prevent `GC`ing when it is known that garbage collection may have undesired effects (e.g. shared configuration between multiple runtimes).
|
||||||
- `plugins` (list): A list of CNI plugins and their configuration, which is a list of plugin configuration objects.
|
- `loadOnlyInlinedPlugins` (boolean): Either `true` or `false`. If `false` (default), indicates [plugin configuration objects](#plugin-configuration-objects) can be aggregated from multiple sources. Any valid plugin configuration objects aggregated from other sources must be appended to the final list of `plugins` for that network name. If set to `true`, indicates that valid plugin configuration objects aggregated from sources other than the main network configuration will be ignored. If `plugins` is not present in the network configuration, `loadOnlyInlinedPlugins` cannot be set to `true`.
|
||||||
|
- `plugins` (list): A list of inlined [plugin configuration objects](#plugin-configuration-objects). If this key is populated with inlined plugin objects, and `loadOnlyInlinedPlugins` is true, the final set of plugins for a network must consist of all the plugin objects in this list, merged with all the plugins loaded from the sibling folder with the same name as the network.
|
||||||
|
|
||||||
#### Plugin configuration objects:
|
#### Plugin configuration objects:
|
||||||
Plugin configuration objects may contain additional fields than the ones defined here.
|
Runtimes may aggregate plugin configuration objects from multiple sources, and must unambiguously associate each loaded plugin configuration object with a single, valid network configuration. All aggregated plugin configuration objects must be validated, and each plugin with a valid configuration object must be invoked.
|
||||||
The runtime MUST pass through these fields, unchanged, to the plugin, as defined in section 3.
|
|
||||||
|
Plugin configuration objects may contain additional fields beyond the ones defined here. The runtime MUST pass through these fields, unchanged, to the invoked plugin, as defined in section 3.
|
||||||
|
|
||||||
**Required keys:**
|
**Required keys:**
|
||||||
- `type` (string): Matches the name of the CNI plugin binary on disk. Must not contain characters disallowed in file paths for the system (e.g. / or \\).
|
- `type` (string): Matches the name of the CNI plugin binary on disk. Must not contain characters disallowed in file paths for the system (e.g. / or \\).
|
||||||
|
@ -147,6 +149,7 @@ Plugins that consume any of these configuration keys should respect their intend
|
||||||
Plugins may define additional fields that they accept and may generate an error if called with unknown fields. Runtimes must preserve unknown fields in plugin configuration objects when transforming for execution.
|
Plugins may define additional fields that they accept and may generate an error if called with unknown fields. Runtimes must preserve unknown fields in plugin configuration objects when transforming for execution.
|
||||||
|
|
||||||
#### Example configuration
|
#### Example configuration
|
||||||
|
The following is an example JSON representation of a network configuration `dbnet` with three plugin configurations (`bridge`, `tuning`, and `portmap`).
|
||||||
```jsonc
|
```jsonc
|
||||||
{
|
{
|
||||||
"cniVersion": "1.1.0",
|
"cniVersion": "1.1.0",
|
||||||
|
@ -158,7 +161,7 @@ Plugins may define additional fields that they accept and may generate an error
|
||||||
// plugin specific parameters
|
// plugin specific parameters
|
||||||
"bridge": "cni0",
|
"bridge": "cni0",
|
||||||
"keyA": ["some more", "plugin specific", "configuration"],
|
"keyA": ["some more", "plugin specific", "configuration"],
|
||||||
|
|
||||||
"ipam": {
|
"ipam": {
|
||||||
"type": "host-local",
|
"type": "host-local",
|
||||||
// ipam specific
|
// ipam specific
|
||||||
|
@ -265,7 +268,10 @@ A CNI plugin, upon receiving a `DEL` command, should either
|
||||||
- delete the interface defined by `CNI_IFNAME` inside the container at `CNI_NETNS`, or
|
- delete the interface defined by `CNI_IFNAME` inside the container at `CNI_NETNS`, or
|
||||||
- undo any modifications applied in the plugin's `ADD` functionality
|
- undo any modifications applied in the plugin's `ADD` functionality
|
||||||
|
|
||||||
Plugins should generally complete a `DEL` action without error even if some resources are missing. For example, an IPAM plugin should generally release an IP allocation and return success even if the container network namespace no longer exists, unless that network namespace is critical for IPAM management. While DHCP may usually send a 'release' message on the container network interface, since DHCP leases have a lifetime this release action would not be considered critical and no error should be returned if this action fails. For another example, the `bridge` plugin should delegate the DEL action to the IPAM plugin and clean up its own resources even if the container network namespace and/or container network interface no longer exist.
|
A `prevResult` must be supplied to CNI plugins as part of a `DEL` command. For the first plugin in the `DEL` command plugin chain, this `prevResult` will be the final result of the previous `ADD` command.
|
||||||
|
Plugins should still return without error if `prevResult` is empty for a `DEL` command, however.
|
||||||
|
|
||||||
|
`DEL` command invocations are always considered best-effort - plugins should always complete a `DEL` action without error to the fullest extent possible, even if some resources or state are missing. For example, an IPAM plugin should generally release an IP allocation and return success even if the container network namespace no longer exists, unless that network namespace is critical for IPAM management. While DHCP may usually send a 'release' message on the container network interface, since DHCP leases have a lifetime this release action would not be considered critical and no error should be returned if this action fails. For another example, the `bridge` plugin should delegate the DEL action to the IPAM plugin and clean up its own resources even if the container network namespace and/or container network interface no longer exist.
|
||||||
|
|
||||||
Plugins MUST accept multiple `DEL` calls for the same (`CNI_CONTAINERID`, `CNI_IFNAME`) pair, and return success if the interface in question, or any modifications added, are missing.
|
Plugins MUST accept multiple `DEL` calls for the same (`CNI_CONTAINERID`, `CNI_IFNAME`) pair, and return success if the interface in question, or any modifications added, are missing.
|
||||||
|
|
||||||
|
@ -375,8 +381,6 @@ Resources may, for example, include:
|
||||||
|
|
||||||
A plugin SHOULD remove as many stale resources as possible. For example, a plugin should remove any IPAM reservations associated with attachments not in the provided list. The plugin MAY assume that the isolation domain (e.g. network namespace) has been deleted, and thus any resources (e.g. network interfaces) therein have been removed.
|
A plugin SHOULD remove as many stale resources as possible. For example, a plugin should remove any IPAM reservations associated with attachments not in the provided list. The plugin MAY assume that the isolation domain (e.g. network namespace) has been deleted, and thus any resources (e.g. network interfaces) therein have been removed.
|
||||||
|
|
||||||
Garbage collection is a per-network operation. If a plugin manages resources shared across multiple networks, it must only remove stale resources known to belong to the network provided in the `GC `action.
|
|
||||||
|
|
||||||
Plugins should generally complete a `GC` action without error. If an error is encountered, a plugin should continue; removing as many resources as possible, and report the errors back to the runtime.
|
Plugins should generally complete a `GC` action without error. If an error is encountered, a plugin should continue; removing as many resources as possible, and report the errors back to the runtime.
|
||||||
|
|
||||||
Plugins MUST, additionally, forward any GC calls to delegated plugins they are configured to use (see section 4).
|
Plugins MUST, additionally, forward any GC calls to delegated plugins they are configured to use (see section 4).
|
||||||
|
@ -387,7 +391,7 @@ The runtime MUST NOT use GC as a substitute for DEL. Plugins may be unable to cl
|
||||||
|
|
||||||
The runtime must provide a JSON-serialized plugin configuration object (defined below) on standard in. It contains an additional key;
|
The runtime must provide a JSON-serialized plugin configuration object (defined below) on standard in. It contains an additional key;
|
||||||
|
|
||||||
- `cni.dev/attachments` (array of objects): The list of **still valid** attachments to this network:
|
- `cni.dev/valid-attachments` (array of objects): The list of **still valid** attachments to this network:
|
||||||
- `containerID` (string): the value of CNI_CONTAINERID as provided during the CNI ADD operation
|
- `containerID` (string): the value of CNI_CONTAINERID as provided during the CNI ADD operation
|
||||||
- `ifname` (string): the value of CNI_IFNAME as provided during the CNI ADD operation
|
- `ifname` (string): the value of CNI_IFNAME as provided during the CNI ADD operation
|
||||||
|
|
||||||
|
@ -496,7 +500,7 @@ For attachment-specific operations (ADD, DEL, CHECK), additional field requireme
|
||||||
- `capabilities`: must not be set
|
- `capabilities`: must not be set
|
||||||
|
|
||||||
For GC operations:
|
For GC operations:
|
||||||
- `cni.dev/attachments`: as specified in section 2.
|
- `cni.dev/valid-attachments`: as specified in section 2.
|
||||||
|
|
||||||
All other fields not prefixed with `cni.dev/` should be passed through unaltered.
|
All other fields not prefixed with `cni.dev/` should be passed through unaltered.
|
||||||
|
|
||||||
|
@ -647,6 +651,8 @@ Error Code|Error Description
|
||||||
`6`|Failed to decode content. For example, failed to unmarshal network config from bytes or failed to decode version info from string.
|
`6`|Failed to decode content. For example, failed to unmarshal network config from bytes or failed to decode version info from string.
|
||||||
`7`|Invalid network config. If some validations on network configs do not pass, this error will be raised.
|
`7`|Invalid network config. If some validations on network configs do not pass, this error will be raised.
|
||||||
`11`|Try again later. If the plugin detects some transient condition that should clear up, it can use this code to notify the runtime it should re-try the operation later.
|
`11`|Try again later. If the plugin detects some transient condition that should clear up, it can use this code to notify the runtime it should re-try the operation later.
|
||||||
|
`50`|The plugin is not available (i.e. cannot service `ADD` requests)
|
||||||
|
`51`|The plugin is not available, and existing containers in the network may have limited connectivity.
|
||||||
|
|
||||||
In addition, stderr can be used for unstructured output such as logs.
|
In addition, stderr can be used for unstructured output such as logs.
|
||||||
|
|
||||||
|
|
BIN
cnitool/cnitool
BIN
cnitool/cnitool
Binary file not shown.
|
@ -68,7 +68,7 @@ func main() {
|
||||||
if netdir == "" {
|
if netdir == "" {
|
||||||
netdir = DefaultNetDir
|
netdir = DefaultNetDir
|
||||||
}
|
}
|
||||||
netconf, err := libcni.LoadConfList(netdir, os.Args[2])
|
netconf, err := libcni.LoadNetworkConf(netdir, os.Args[2])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
exit(err)
|
exit(err)
|
||||||
}
|
}
|
||||||
|
|
18
go.mod
18
go.mod
|
@ -3,20 +3,20 @@ module github.com/containernetworking/cni
|
||||||
go 1.21
|
go 1.21
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Masterminds/semver/v3 v3.2.1
|
github.com/onsi/ginkgo/v2 v2.20.1
|
||||||
github.com/onsi/ginkgo/v2 v2.19.0
|
github.com/onsi/gomega v1.34.1
|
||||||
github.com/onsi/gomega v1.33.1
|
|
||||||
github.com/vishvananda/netns v0.0.4
|
github.com/vishvananda/netns v0.0.4
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/go-logr/logr v1.4.1 // indirect
|
github.com/go-logr/logr v1.4.2 // indirect
|
||||||
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
||||||
github.com/google/go-cmp v0.6.0 // indirect
|
github.com/google/go-cmp v0.6.0 // indirect
|
||||||
github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 // indirect
|
github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 // indirect
|
||||||
golang.org/x/net v0.25.0 // indirect
|
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
|
||||||
golang.org/x/sys v0.20.0 // indirect
|
golang.org/x/net v0.28.0 // indirect
|
||||||
golang.org/x/text v0.15.0 // indirect
|
golang.org/x/sys v0.23.0 // indirect
|
||||||
golang.org/x/tools v0.21.0 // indirect
|
golang.org/x/text v0.17.0 // indirect
|
||||||
|
golang.org/x/tools v0.24.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
|
40
go.sum
40
go.sum
|
@ -1,35 +1,35 @@
|
||||||
github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
|
|
||||||
github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
|
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
|
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||||
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||||
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 h1:k7nVchz72niMH6YLQNvHSdIE7iqsQxK1P41mySCvssg=
|
github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 h1:FKHo8hFI3A+7w0aUQuYXQ+6EN5stWmeY/AZqtM8xk9k=
|
||||||
github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
|
github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo=
|
||||||
github.com/onsi/ginkgo/v2 v2.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA=
|
github.com/onsi/ginkgo/v2 v2.20.1 h1:YlVIbqct+ZmnEph770q9Q7NVAz4wwIiVNahee6JyUzo=
|
||||||
github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To=
|
github.com/onsi/ginkgo/v2 v2.20.1/go.mod h1:lG9ey2Z29hR41WMVthyJBGUBcBhGOtoPF2VFMvBXFCI=
|
||||||
github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk=
|
github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
|
||||||
github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0=
|
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8=
|
github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8=
|
||||||
github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
|
github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
|
||||||
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
|
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
|
||||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
|
||||||
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
|
golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
|
||||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
|
||||||
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
|
golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM=
|
||||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw=
|
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
|
||||||
golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||||
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
|
||||||
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
|
||||||
|
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
|
||||||
|
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
|
|
@ -67,18 +67,23 @@ type RuntimeConf struct {
|
||||||
CacheDir string
|
CacheDir string
|
||||||
}
|
}
|
||||||
|
|
||||||
type NetworkConfig struct {
|
// Use PluginConfig instead of NetworkConfig, the NetworkConfig
|
||||||
Network *types.NetConf
|
// backwards-compat alias will be removed in a future release.
|
||||||
|
type NetworkConfig = PluginConfig
|
||||||
|
|
||||||
|
type PluginConfig struct {
|
||||||
|
Network *types.PluginConf
|
||||||
Bytes []byte
|
Bytes []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
type NetworkConfigList struct {
|
type NetworkConfigList struct {
|
||||||
Name string
|
Name string
|
||||||
CNIVersion string
|
CNIVersion string
|
||||||
DisableCheck bool
|
DisableCheck bool
|
||||||
DisableGC bool
|
DisableGC bool
|
||||||
Plugins []*NetworkConfig
|
LoadOnlyInlinedPlugins bool
|
||||||
Bytes []byte
|
Plugins []*PluginConfig
|
||||||
|
Bytes []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
type NetworkAttachment struct {
|
type NetworkAttachment struct {
|
||||||
|
@ -102,14 +107,14 @@ type CNI interface {
|
||||||
GetNetworkListCachedResult(net *NetworkConfigList, rt *RuntimeConf) (types.Result, error)
|
GetNetworkListCachedResult(net *NetworkConfigList, rt *RuntimeConf) (types.Result, error)
|
||||||
GetNetworkListCachedConfig(net *NetworkConfigList, rt *RuntimeConf) ([]byte, *RuntimeConf, error)
|
GetNetworkListCachedConfig(net *NetworkConfigList, rt *RuntimeConf) ([]byte, *RuntimeConf, error)
|
||||||
|
|
||||||
AddNetwork(ctx context.Context, net *NetworkConfig, rt *RuntimeConf) (types.Result, error)
|
AddNetwork(ctx context.Context, net *PluginConfig, rt *RuntimeConf) (types.Result, error)
|
||||||
CheckNetwork(ctx context.Context, net *NetworkConfig, rt *RuntimeConf) error
|
CheckNetwork(ctx context.Context, net *PluginConfig, rt *RuntimeConf) error
|
||||||
DelNetwork(ctx context.Context, net *NetworkConfig, rt *RuntimeConf) error
|
DelNetwork(ctx context.Context, net *PluginConfig, rt *RuntimeConf) error
|
||||||
GetNetworkCachedResult(net *NetworkConfig, rt *RuntimeConf) (types.Result, error)
|
GetNetworkCachedResult(net *PluginConfig, rt *RuntimeConf) (types.Result, error)
|
||||||
GetNetworkCachedConfig(net *NetworkConfig, rt *RuntimeConf) ([]byte, *RuntimeConf, error)
|
GetNetworkCachedConfig(net *PluginConfig, rt *RuntimeConf) ([]byte, *RuntimeConf, error)
|
||||||
|
|
||||||
ValidateNetworkList(ctx context.Context, net *NetworkConfigList) ([]string, error)
|
ValidateNetworkList(ctx context.Context, net *NetworkConfigList) ([]string, error)
|
||||||
ValidateNetwork(ctx context.Context, net *NetworkConfig) ([]string, error)
|
ValidateNetwork(ctx context.Context, net *PluginConfig) ([]string, error)
|
||||||
|
|
||||||
GCNetworkList(ctx context.Context, net *NetworkConfigList, args *GCArgs) error
|
GCNetworkList(ctx context.Context, net *NetworkConfigList, args *GCArgs) error
|
||||||
GetStatusNetworkList(ctx context.Context, net *NetworkConfigList) error
|
GetStatusNetworkList(ctx context.Context, net *NetworkConfigList) error
|
||||||
|
@ -147,7 +152,7 @@ func NewCNIConfigWithCacheDir(path []string, cacheDir string, exec invoke.Exec)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildOneConfig(name, cniVersion string, orig *NetworkConfig, prevResult types.Result, rt *RuntimeConf) (*NetworkConfig, error) {
|
func buildOneConfig(name, cniVersion string, orig *PluginConfig, prevResult types.Result, rt *RuntimeConf) (*PluginConfig, error) {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
inject := map[string]interface{}{
|
inject := map[string]interface{}{
|
||||||
|
@ -183,7 +188,7 @@ func buildOneConfig(name, cniVersion string, orig *NetworkConfig, prevResult typ
|
||||||
// capabilities include "portMappings", and the CapabilityArgs map includes a
|
// capabilities include "portMappings", and the CapabilityArgs map includes a
|
||||||
// "portMappings" key, that key and its value are added to the "runtimeConfig"
|
// "portMappings" key, that key and its value are added to the "runtimeConfig"
|
||||||
// dictionary to be passed to the plugin's stdin.
|
// dictionary to be passed to the plugin's stdin.
|
||||||
func injectRuntimeConfig(orig *NetworkConfig, rt *RuntimeConf) (*NetworkConfig, error) {
|
func injectRuntimeConfig(orig *PluginConfig, rt *RuntimeConf) (*PluginConfig, error) {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
rc := make(map[string]interface{})
|
rc := make(map[string]interface{})
|
||||||
|
@ -404,7 +409,7 @@ func (c *CNIConfig) GetNetworkListCachedResult(list *NetworkConfigList, rt *Runt
|
||||||
|
|
||||||
// GetNetworkCachedResult returns the cached Result of the previous
|
// GetNetworkCachedResult returns the cached Result of the previous
|
||||||
// AddNetwork() operation for a network, or an error.
|
// AddNetwork() operation for a network, or an error.
|
||||||
func (c *CNIConfig) GetNetworkCachedResult(net *NetworkConfig, rt *RuntimeConf) (types.Result, error) {
|
func (c *CNIConfig) GetNetworkCachedResult(net *PluginConfig, rt *RuntimeConf) (types.Result, error) {
|
||||||
return c.getCachedResult(net.Network.Name, net.Network.CNIVersion, rt)
|
return c.getCachedResult(net.Network.Name, net.Network.CNIVersion, rt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -416,7 +421,7 @@ func (c *CNIConfig) GetNetworkListCachedConfig(list *NetworkConfigList, rt *Runt
|
||||||
|
|
||||||
// GetNetworkCachedConfig copies the input RuntimeConf to output
|
// GetNetworkCachedConfig copies the input RuntimeConf to output
|
||||||
// RuntimeConf with fields updated with info from the cached Config.
|
// RuntimeConf with fields updated with info from the cached Config.
|
||||||
func (c *CNIConfig) GetNetworkCachedConfig(net *NetworkConfig, rt *RuntimeConf) ([]byte, *RuntimeConf, error) {
|
func (c *CNIConfig) GetNetworkCachedConfig(net *PluginConfig, rt *RuntimeConf) ([]byte, *RuntimeConf, error) {
|
||||||
return c.getCachedConfig(net.Network.Name, rt)
|
return c.getCachedConfig(net.Network.Name, rt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -482,7 +487,7 @@ func (c *CNIConfig) GetCachedAttachments(containerID string) ([]*NetworkAttachme
|
||||||
return attachments, nil
|
return attachments, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CNIConfig) addNetwork(ctx context.Context, name, cniVersion string, net *NetworkConfig, prevResult types.Result, rt *RuntimeConf) (types.Result, error) {
|
func (c *CNIConfig) addNetwork(ctx context.Context, name, cniVersion string, net *PluginConfig, prevResult types.Result, rt *RuntimeConf) (types.Result, error) {
|
||||||
c.ensureExec()
|
c.ensureExec()
|
||||||
pluginPath, err := c.exec.FindInPath(net.Network.Type, c.Path)
|
pluginPath, err := c.exec.FindInPath(net.Network.Type, c.Path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -524,7 +529,7 @@ func (c *CNIConfig) AddNetworkList(ctx context.Context, list *NetworkConfigList,
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CNIConfig) checkNetwork(ctx context.Context, name, cniVersion string, net *NetworkConfig, prevResult types.Result, rt *RuntimeConf) error {
|
func (c *CNIConfig) checkNetwork(ctx context.Context, name, cniVersion string, net *PluginConfig, prevResult types.Result, rt *RuntimeConf) error {
|
||||||
c.ensureExec()
|
c.ensureExec()
|
||||||
pluginPath, err := c.exec.FindInPath(net.Network.Type, c.Path)
|
pluginPath, err := c.exec.FindInPath(net.Network.Type, c.Path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -566,7 +571,7 @@ func (c *CNIConfig) CheckNetworkList(ctx context.Context, list *NetworkConfigLis
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CNIConfig) delNetwork(ctx context.Context, name, cniVersion string, net *NetworkConfig, prevResult types.Result, rt *RuntimeConf) error {
|
func (c *CNIConfig) delNetwork(ctx context.Context, name, cniVersion string, net *PluginConfig, prevResult types.Result, rt *RuntimeConf) error {
|
||||||
c.ensureExec()
|
c.ensureExec()
|
||||||
pluginPath, err := c.exec.FindInPath(net.Network.Type, c.Path)
|
pluginPath, err := c.exec.FindInPath(net.Network.Type, c.Path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -607,7 +612,7 @@ func (c *CNIConfig) DelNetworkList(ctx context.Context, list *NetworkConfigList,
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func pluginDescription(net *types.NetConf) string {
|
func pluginDescription(net *types.PluginConf) string {
|
||||||
if net == nil {
|
if net == nil {
|
||||||
return "<missing>"
|
return "<missing>"
|
||||||
}
|
}
|
||||||
|
@ -621,7 +626,7 @@ func pluginDescription(net *types.NetConf) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddNetwork executes the plugin with the ADD command
|
// AddNetwork executes the plugin with the ADD command
|
||||||
func (c *CNIConfig) AddNetwork(ctx context.Context, net *NetworkConfig, rt *RuntimeConf) (types.Result, error) {
|
func (c *CNIConfig) AddNetwork(ctx context.Context, net *PluginConfig, rt *RuntimeConf) (types.Result, error) {
|
||||||
result, err := c.addNetwork(ctx, net.Network.Name, net.Network.CNIVersion, net, nil, rt)
|
result, err := c.addNetwork(ctx, net.Network.Name, net.Network.CNIVersion, net, nil, rt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -635,7 +640,7 @@ func (c *CNIConfig) AddNetwork(ctx context.Context, net *NetworkConfig, rt *Runt
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckNetwork executes the plugin with the CHECK command
|
// CheckNetwork executes the plugin with the CHECK command
|
||||||
func (c *CNIConfig) CheckNetwork(ctx context.Context, net *NetworkConfig, rt *RuntimeConf) error {
|
func (c *CNIConfig) CheckNetwork(ctx context.Context, net *PluginConfig, rt *RuntimeConf) error {
|
||||||
// CHECK was added in CNI spec version 0.4.0 and higher
|
// CHECK was added in CNI spec version 0.4.0 and higher
|
||||||
if gtet, err := version.GreaterThanOrEqualTo(net.Network.CNIVersion, "0.4.0"); err != nil {
|
if gtet, err := version.GreaterThanOrEqualTo(net.Network.CNIVersion, "0.4.0"); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -651,7 +656,7 @@ func (c *CNIConfig) CheckNetwork(ctx context.Context, net *NetworkConfig, rt *Ru
|
||||||
}
|
}
|
||||||
|
|
||||||
// DelNetwork executes the plugin with the DEL command
|
// DelNetwork executes the plugin with the DEL command
|
||||||
func (c *CNIConfig) DelNetwork(ctx context.Context, net *NetworkConfig, rt *RuntimeConf) error {
|
func (c *CNIConfig) DelNetwork(ctx context.Context, net *PluginConfig, rt *RuntimeConf) error {
|
||||||
var cachedResult types.Result
|
var cachedResult types.Result
|
||||||
|
|
||||||
// Cached result on DEL was added in CNI spec version 0.4.0 and higher
|
// Cached result on DEL was added in CNI spec version 0.4.0 and higher
|
||||||
|
@ -711,7 +716,7 @@ func (c *CNIConfig) ValidateNetworkList(ctx context.Context, list *NetworkConfig
|
||||||
// ValidateNetwork checks that a configuration is reasonably valid.
|
// ValidateNetwork checks that a configuration is reasonably valid.
|
||||||
// It uses the same logic as ValidateNetworkList)
|
// It uses the same logic as ValidateNetworkList)
|
||||||
// Returns a list of capabilities
|
// Returns a list of capabilities
|
||||||
func (c *CNIConfig) ValidateNetwork(ctx context.Context, net *NetworkConfig) ([]string, error) {
|
func (c *CNIConfig) ValidateNetwork(ctx context.Context, net *PluginConfig) ([]string, error) {
|
||||||
caps := []string{}
|
caps := []string{}
|
||||||
for c, ok := range net.Network.Capabilities {
|
for c, ok := range net.Network.Capabilities {
|
||||||
if ok {
|
if ok {
|
||||||
|
@ -817,6 +822,8 @@ func (c *CNIConfig) GCNetworkList(ctx context.Context, list *NetworkConfigList,
|
||||||
}
|
}
|
||||||
if args != nil {
|
if args != nil {
|
||||||
inject["cni.dev/valid-attachments"] = args.ValidAttachments
|
inject["cni.dev/valid-attachments"] = args.ValidAttachments
|
||||||
|
// #1101: spec used incorrect variable name
|
||||||
|
inject["cni.dev/attachments"] = args.ValidAttachments
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, plugin := range list.Plugins {
|
for _, plugin := range list.Plugins {
|
||||||
|
@ -834,7 +841,7 @@ func (c *CNIConfig) GCNetworkList(ctx context.Context, list *NetworkConfigList,
|
||||||
return errors.Join(errs...)
|
return errors.Join(errs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CNIConfig) gcNetwork(ctx context.Context, net *NetworkConfig) error {
|
func (c *CNIConfig) gcNetwork(ctx context.Context, net *PluginConfig) error {
|
||||||
c.ensureExec()
|
c.ensureExec()
|
||||||
pluginPath, err := c.exec.FindInPath(net.Network.Type, c.Path)
|
pluginPath, err := c.exec.FindInPath(net.Network.Type, c.Path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -869,7 +876,7 @@ func (c *CNIConfig) GetStatusNetworkList(ctx context.Context, list *NetworkConfi
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CNIConfig) getStatusNetwork(ctx context.Context, net *NetworkConfig) error {
|
func (c *CNIConfig) getStatusNetwork(ctx context.Context, net *PluginConfig) error {
|
||||||
c.ensureExec()
|
c.ensureExec()
|
||||||
pluginPath, err := c.exec.FindInPath(net.Network.Type, c.Path)
|
pluginPath, err := c.exec.FindInPath(net.Network.Type, c.Path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -176,7 +176,7 @@ var _ = Describe("Invoking plugins", func() {
|
||||||
pluginConfig []byte
|
pluginConfig []byte
|
||||||
cniConfig *libcni.CNIConfig
|
cniConfig *libcni.CNIConfig
|
||||||
runtimeConfig *libcni.RuntimeConf
|
runtimeConfig *libcni.RuntimeConf
|
||||||
netConfig *libcni.NetworkConfig
|
netConfig *libcni.PluginConfig
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -295,7 +295,7 @@ var _ = Describe("Invoking plugins", func() {
|
||||||
debug *noop_debug.Debug
|
debug *noop_debug.Debug
|
||||||
pluginConfig string
|
pluginConfig string
|
||||||
cniConfig *libcni.CNIConfig
|
cniConfig *libcni.CNIConfig
|
||||||
netConfig *libcni.NetworkConfig
|
netConfig *libcni.PluginConfig
|
||||||
runtimeConfig *libcni.RuntimeConf
|
runtimeConfig *libcni.RuntimeConf
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
|
|
||||||
|
@ -1636,7 +1636,7 @@ var _ = Describe("Invoking plugins", func() {
|
||||||
cniBinPath string
|
cniBinPath string
|
||||||
pluginConfig string
|
pluginConfig string
|
||||||
cniConfig *libcni.CNIConfig
|
cniConfig *libcni.CNIConfig
|
||||||
netConfig *libcni.NetworkConfig
|
netConfig *libcni.PluginConfig
|
||||||
runtimeConfig *libcni.RuntimeConf
|
runtimeConfig *libcni.RuntimeConf
|
||||||
netConfigList *libcni.NetworkConfigList
|
netConfigList *libcni.NetworkConfigList
|
||||||
)
|
)
|
||||||
|
@ -1809,7 +1809,7 @@ var _ = Describe("Invoking plugins", func() {
|
||||||
cniBinPath string
|
cniBinPath string
|
||||||
pluginConfig string
|
pluginConfig string
|
||||||
cniConfig *libcni.CNIConfig
|
cniConfig *libcni.CNIConfig
|
||||||
netConfig *libcni.NetworkConfig
|
netConfig *libcni.PluginConfig
|
||||||
runtimeConfig *libcni.RuntimeConf
|
runtimeConfig *libcni.RuntimeConf
|
||||||
|
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
|
@ -1931,14 +1931,14 @@ var _ = Describe("Invoking plugins", func() {
|
||||||
Context("when the RuntimeConf is incomplete", func() {
|
Context("when the RuntimeConf is incomplete", func() {
|
||||||
var (
|
var (
|
||||||
testRt *libcni.RuntimeConf
|
testRt *libcni.RuntimeConf
|
||||||
testNetConf *libcni.NetworkConfig
|
testNetConf *libcni.PluginConfig
|
||||||
testNetConfList *libcni.NetworkConfigList
|
testNetConfList *libcni.NetworkConfigList
|
||||||
)
|
)
|
||||||
|
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
testRt = &libcni.RuntimeConf{}
|
testRt = &libcni.RuntimeConf{}
|
||||||
testNetConf = &libcni.NetworkConfig{
|
testNetConf = &libcni.PluginConfig{
|
||||||
Network: &types.NetConf{},
|
Network: &types.PluginConf{},
|
||||||
}
|
}
|
||||||
testNetConfList = &libcni.NetworkConfigList{}
|
testNetConfList = &libcni.NetworkConfigList{}
|
||||||
})
|
})
|
||||||
|
|
181
libcni/conf.go
181
libcni/conf.go
|
@ -20,11 +20,10 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"slices"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/Masterminds/semver/v3"
|
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/types"
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
"github.com/containernetworking/cni/pkg/version"
|
"github.com/containernetworking/cni/pkg/version"
|
||||||
)
|
)
|
||||||
|
@ -46,9 +45,16 @@ func (e NoConfigsFoundError) Error() string {
|
||||||
return fmt.Sprintf(`no net configurations found in %s`, e.Dir)
|
return fmt.Sprintf(`no net configurations found in %s`, e.Dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ConfFromBytes(bytes []byte) (*NetworkConfig, error) {
|
// This will not validate that the plugins actually belong to the netconfig by ensuring
|
||||||
conf := &NetworkConfig{Bytes: bytes, Network: &types.NetConf{}}
|
// that they are loaded from a directory named after the networkName, relative to the network config.
|
||||||
if err := json.Unmarshal(bytes, conf.Network); err != nil {
|
//
|
||||||
|
// Since here we are just accepting raw bytes, the caller is responsible for ensuring that the plugin
|
||||||
|
// config provided here actually "belongs" to the networkconfig in question.
|
||||||
|
func NetworkPluginConfFromBytes(pluginConfBytes []byte) (*PluginConfig, error) {
|
||||||
|
// TODO why are we creating a struct that holds both the byte representation and the deserialized
|
||||||
|
// representation, and returning that, instead of just returning the deserialized representation?
|
||||||
|
conf := &PluginConfig{Bytes: pluginConfBytes, Network: &types.PluginConf{}}
|
||||||
|
if err := json.Unmarshal(pluginConfBytes, conf.Network); err != nil {
|
||||||
return nil, fmt.Errorf("error parsing configuration: %w", err)
|
return nil, fmt.Errorf("error parsing configuration: %w", err)
|
||||||
}
|
}
|
||||||
if conf.Network.Type == "" {
|
if conf.Network.Type == "" {
|
||||||
|
@ -57,17 +63,35 @@ func ConfFromBytes(bytes []byte) (*NetworkConfig, error) {
|
||||||
return conf, nil
|
return conf, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ConfFromFile(filename string) (*NetworkConfig, error) {
|
// Given a path to a directory containing a network configuration, and the name of a network,
|
||||||
bytes, err := os.ReadFile(filename)
|
// loads all plugin definitions found at path `networkConfPath/networkName/*.conf`
|
||||||
|
func NetworkPluginConfsFromFiles(networkConfPath, networkName string) ([]*PluginConfig, error) {
|
||||||
|
var pConfs []*PluginConfig
|
||||||
|
|
||||||
|
pluginConfPath := filepath.Join(networkConfPath, networkName)
|
||||||
|
|
||||||
|
pluginConfFiles, err := ConfFiles(pluginConfPath, []string{".conf"})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error reading %s: %w", filename, err)
|
return nil, fmt.Errorf("failed to read plugin config files in %s: %w", pluginConfPath, err)
|
||||||
}
|
}
|
||||||
return ConfFromBytes(bytes)
|
|
||||||
|
for _, pluginConfFile := range pluginConfFiles {
|
||||||
|
pluginConfBytes, err := os.ReadFile(pluginConfFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error reading %s: %w", pluginConfFile, err)
|
||||||
|
}
|
||||||
|
pluginConf, err := NetworkPluginConfFromBytes(pluginConfBytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
pConfs = append(pConfs, pluginConf)
|
||||||
|
}
|
||||||
|
return pConfs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ConfListFromBytes(bytes []byte) (*NetworkConfigList, error) {
|
func NetworkConfFromBytes(confBytes []byte) (*NetworkConfigList, error) {
|
||||||
rawList := make(map[string]interface{})
|
rawList := make(map[string]interface{})
|
||||||
if err := json.Unmarshal(bytes, &rawList); err != nil {
|
if err := json.Unmarshal(confBytes, &rawList); err != nil {
|
||||||
return nil, fmt.Errorf("error parsing configuration list: %w", err)
|
return nil, fmt.Errorf("error parsing configuration list: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,24 +116,20 @@ func ConfListFromBytes(bytes []byte) (*NetworkConfigList, error) {
|
||||||
rawVersions, ok := rawList["cniVersions"]
|
rawVersions, ok := rawList["cniVersions"]
|
||||||
if ok {
|
if ok {
|
||||||
// Parse the current package CNI version
|
// Parse the current package CNI version
|
||||||
currentVersion, err := semver.NewVersion(version.Current())
|
|
||||||
if err != nil {
|
|
||||||
panic("CNI version is invalid semver!")
|
|
||||||
}
|
|
||||||
|
|
||||||
rvs, ok := rawVersions.([]interface{})
|
rvs, ok := rawVersions.([]interface{})
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("error parsing configuration list: invalid type for cniVersions: %T", rvs)
|
return nil, fmt.Errorf("error parsing configuration list: invalid type for cniVersions: %T", rvs)
|
||||||
}
|
}
|
||||||
vs := make([]*semver.Version, 0, len(rvs))
|
vs := make([]string, 0, len(rvs))
|
||||||
for i, rv := range rvs {
|
for i, rv := range rvs {
|
||||||
v, ok := rv.(string)
|
v, ok := rv.(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("error parsing configuration list: invalid type for cniVersions index %d: %T", i, rv)
|
return nil, fmt.Errorf("error parsing configuration list: invalid type for cniVersions index %d: %T", i, rv)
|
||||||
}
|
}
|
||||||
if v, err := semver.NewVersion(v); err != nil {
|
gt, err := version.GreaterThan(v, version.Current())
|
||||||
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error parsing configuration list: invalid cniVersions entry %s at index %d: %w", v, i, err)
|
return nil, fmt.Errorf("error parsing configuration list: invalid cniVersions entry %s at index %d: %w", v, i, err)
|
||||||
} else if !v.GreaterThan(currentVersion) {
|
} else if !gt {
|
||||||
// Skip versions "greater" than this implementation of the spec
|
// Skip versions "greater" than this implementation of the spec
|
||||||
vs = append(vs, v)
|
vs = append(vs, v)
|
||||||
}
|
}
|
||||||
|
@ -117,16 +137,25 @@ func ConfListFromBytes(bytes []byte) (*NetworkConfigList, error) {
|
||||||
|
|
||||||
// if cniVersion was already set, append it to the list for sorting.
|
// if cniVersion was already set, append it to the list for sorting.
|
||||||
if cniVersion != "" {
|
if cniVersion != "" {
|
||||||
if v, err := semver.NewVersion(cniVersion); err != nil {
|
gt, err := version.GreaterThan(cniVersion, version.Current())
|
||||||
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error parsing configuration list: invalid cniVersion %s: %w", cniVersion, err)
|
return nil, fmt.Errorf("error parsing configuration list: invalid cniVersion %s: %w", cniVersion, err)
|
||||||
} else if !v.GreaterThan(currentVersion) {
|
} else if !gt {
|
||||||
// ignore any versions higher than the current implemented spec version
|
// ignore any versions higher than the current implemented spec version
|
||||||
vs = append(vs, v)
|
vs = append(vs, cniVersion)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sort.Sort(semver.Collection(vs))
|
slices.SortFunc[[]string](vs, func(v1, v2 string) int {
|
||||||
|
if v1 == v2 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
if gt, _ := version.GreaterThan(v1, v2); gt {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
})
|
||||||
if len(vs) > 0 {
|
if len(vs) > 0 {
|
||||||
cniVersion = vs[len(vs)-1].String()
|
cniVersion = vs[len(vs)-1]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,19 +192,36 @@ func ConfListFromBytes(bytes []byte) (*NetworkConfigList, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
loadOnlyInlinedPlugins, err := readBool("loadOnlyInlinedPlugins")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
list := &NetworkConfigList{
|
list := &NetworkConfigList{
|
||||||
Name: name,
|
Name: name,
|
||||||
DisableCheck: disableCheck,
|
DisableCheck: disableCheck,
|
||||||
DisableGC: disableGC,
|
DisableGC: disableGC,
|
||||||
CNIVersion: cniVersion,
|
LoadOnlyInlinedPlugins: loadOnlyInlinedPlugins,
|
||||||
Bytes: bytes,
|
CNIVersion: cniVersion,
|
||||||
|
Bytes: confBytes,
|
||||||
}
|
}
|
||||||
|
|
||||||
var plugins []interface{}
|
var plugins []interface{}
|
||||||
plug, ok := rawList["plugins"]
|
plug, ok := rawList["plugins"]
|
||||||
if !ok {
|
// We can have a `plugins` list key in the main conf,
|
||||||
return nil, fmt.Errorf("error parsing configuration list: no 'plugins' key")
|
// We can also have `loadOnlyInlinedPlugins == true`
|
||||||
|
//
|
||||||
|
// If `plugins` is there, then `loadOnlyInlinedPlugins` can be true
|
||||||
|
//
|
||||||
|
// If plugins is NOT there, then `loadOnlyInlinedPlugins` cannot be true
|
||||||
|
//
|
||||||
|
// We have to have at least some plugins.
|
||||||
|
if !ok && loadOnlyInlinedPlugins {
|
||||||
|
return nil, fmt.Errorf("error parsing configuration list: `loadOnlyInlinedPlugins` is true, and no 'plugins' key")
|
||||||
|
} else if !ok && !loadOnlyInlinedPlugins {
|
||||||
|
return list, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
plugins, ok = plug.([]interface{})
|
plugins, ok = plug.([]interface{})
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("error parsing configuration list: invalid 'plugins' type %T", plug)
|
return nil, fmt.Errorf("error parsing configuration list: invalid 'plugins' type %T", plug)
|
||||||
|
@ -195,24 +241,68 @@ func ConfListFromBytes(bytes []byte) (*NetworkConfigList, error) {
|
||||||
}
|
}
|
||||||
list.Plugins = append(list.Plugins, netConf)
|
list.Plugins = append(list.Plugins, netConf)
|
||||||
}
|
}
|
||||||
|
|
||||||
return list, nil
|
return list, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ConfListFromFile(filename string) (*NetworkConfigList, error) {
|
func NetworkConfFromFile(filename string) (*NetworkConfigList, error) {
|
||||||
bytes, err := os.ReadFile(filename)
|
bytes, err := os.ReadFile(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error reading %s: %w", filename, err)
|
return nil, fmt.Errorf("error reading %s: %w", filename, err)
|
||||||
}
|
}
|
||||||
return ConfListFromBytes(bytes)
|
|
||||||
|
conf, err := NetworkConfFromBytes(bytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !conf.LoadOnlyInlinedPlugins {
|
||||||
|
plugins, err := NetworkPluginConfsFromFiles(filepath.Dir(filename), conf.Name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
conf.Plugins = append(conf.Plugins, plugins...)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(conf.Plugins) == 0 {
|
||||||
|
// Having 0 plugins for a given network is not necessarily a problem,
|
||||||
|
// but return as error for caller to decide, since they tried to load
|
||||||
|
return nil, fmt.Errorf("no plugin configs found")
|
||||||
|
}
|
||||||
|
return conf, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deprecated: This file format is no longer supported, use NetworkConfXXX and NetworkPluginXXX functions
|
||||||
|
func ConfFromBytes(bytes []byte) (*NetworkConfig, error) {
|
||||||
|
return NetworkPluginConfFromBytes(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: This file format is no longer supported, use NetworkConfXXX and NetworkPluginXXX functions
|
||||||
|
func ConfFromFile(filename string) (*NetworkConfig, error) {
|
||||||
|
bytes, err := os.ReadFile(filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error reading %s: %w", filename, err)
|
||||||
|
}
|
||||||
|
return ConfFromBytes(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ConfListFromBytes(bytes []byte) (*NetworkConfigList, error) {
|
||||||
|
return NetworkConfFromBytes(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ConfListFromFile(filename string) (*NetworkConfigList, error) {
|
||||||
|
return NetworkConfFromFile(filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConfFiles simply returns a slice of all files in the provided directory
|
||||||
|
// with extensions matching the provided set.
|
||||||
func ConfFiles(dir string, extensions []string) ([]string, error) {
|
func ConfFiles(dir string, extensions []string) ([]string, error) {
|
||||||
// In part, adapted from rkt/networking/podenv.go#listFiles
|
// In part, adapted from rkt/networking/podenv.go#listFiles
|
||||||
files, err := os.ReadDir(dir)
|
files, err := os.ReadDir(dir)
|
||||||
switch {
|
switch {
|
||||||
case err == nil: // break
|
case err == nil: // break
|
||||||
case os.IsNotExist(err):
|
case os.IsNotExist(err):
|
||||||
|
// If folder not there, return no error - only return an
|
||||||
|
// error if we cannot read contents or there are no contents.
|
||||||
return nil, nil
|
return nil, nil
|
||||||
default:
|
default:
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -233,6 +323,7 @@ func ConfFiles(dir string, extensions []string) ([]string, error) {
|
||||||
return confFiles, nil
|
return confFiles, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deprecated: This file format is no longer supported, use NetworkConfXXX and NetworkPluginXXX functions
|
||||||
func LoadConf(dir, name string) (*NetworkConfig, error) {
|
func LoadConf(dir, name string) (*NetworkConfig, error) {
|
||||||
files, err := ConfFiles(dir, []string{".conf", ".json"})
|
files, err := ConfFiles(dir, []string{".conf", ".json"})
|
||||||
switch {
|
switch {
|
||||||
|
@ -256,6 +347,15 @@ func LoadConf(dir, name string) (*NetworkConfig, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadConfList(dir, name string) (*NetworkConfigList, error) {
|
func LoadConfList(dir, name string) (*NetworkConfigList, error) {
|
||||||
|
return LoadNetworkConf(dir, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadNetworkConf looks at all the network configs in a given dir,
|
||||||
|
// loads and parses them all, and returns the first one with an extension of `.conf`
|
||||||
|
// that matches the provided network name predicate.
|
||||||
|
func LoadNetworkConf(dir, name string) (*NetworkConfigList, error) {
|
||||||
|
// TODO this .conflist/.conf extension thing is confusing and inexact
|
||||||
|
// for implementors. We should pick one extension for everything and stick with it.
|
||||||
files, err := ConfFiles(dir, []string{".conflist"})
|
files, err := ConfFiles(dir, []string{".conflist"})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -263,7 +363,7 @@ func LoadConfList(dir, name string) (*NetworkConfigList, error) {
|
||||||
sort.Strings(files)
|
sort.Strings(files)
|
||||||
|
|
||||||
for _, confFile := range files {
|
for _, confFile := range files {
|
||||||
conf, err := ConfListFromFile(confFile)
|
conf, err := NetworkConfFromFile(confFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -272,7 +372,7 @@ func LoadConfList(dir, name string) (*NetworkConfigList, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try and load a network configuration file (instead of list)
|
// Deprecated: Try and load a network configuration file (instead of list)
|
||||||
// from the same name, then upconvert.
|
// from the same name, then upconvert.
|
||||||
singleConf, err := LoadConf(dir, name)
|
singleConf, err := LoadConf(dir, name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -288,7 +388,8 @@ func LoadConfList(dir, name string) (*NetworkConfigList, error) {
|
||||||
return ConfListFromConf(singleConf)
|
return ConfListFromConf(singleConf)
|
||||||
}
|
}
|
||||||
|
|
||||||
func InjectConf(original *NetworkConfig, newValues map[string]interface{}) (*NetworkConfig, error) {
|
// InjectConf takes a PluginConfig and inserts additional values into it, ensuring the result is serializable.
|
||||||
|
func InjectConf(original *PluginConfig, newValues map[string]interface{}) (*PluginConfig, error) {
|
||||||
config := make(map[string]interface{})
|
config := make(map[string]interface{})
|
||||||
err := json.Unmarshal(original.Bytes, &config)
|
err := json.Unmarshal(original.Bytes, &config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -312,12 +413,14 @@ func InjectConf(original *NetworkConfig, newValues map[string]interface{}) (*Net
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return ConfFromBytes(newBytes)
|
return NetworkPluginConfFromBytes(newBytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConfListFromConf "upconverts" a network config in to a NetworkConfigList,
|
// ConfListFromConf "upconverts" a network config in to a NetworkConfigList,
|
||||||
// with the single network as the only entry in the list.
|
// with the single network as the only entry in the list.
|
||||||
func ConfListFromConf(original *NetworkConfig) (*NetworkConfigList, error) {
|
//
|
||||||
|
// Deprecated: Non-conflist file formats are unsupported, use NetworkConfXXX and NetworkPluginXXX functions
|
||||||
|
func ConfListFromConf(original *PluginConfig) (*NetworkConfigList, error) {
|
||||||
// Re-deserialize the config's json, then make a raw map configlist.
|
// Re-deserialize the config's json, then make a raw map configlist.
|
||||||
// This may seem a bit strange, but it's to make the Bytes fields
|
// This may seem a bit strange, but it's to make the Bytes fields
|
||||||
// actually make sense. Otherwise, the generated json is littered with
|
// actually make sense. Otherwise, the generated json is littered with
|
||||||
|
|
|
@ -50,8 +50,8 @@ var _ = Describe("Loading configuration from disk", func() {
|
||||||
It("finds the network config file for the plugin of the given type", func() {
|
It("finds the network config file for the plugin of the given type", func() {
|
||||||
netConfig, err := libcni.LoadConf(configDir, "some-plugin")
|
netConfig, err := libcni.LoadConf(configDir, "some-plugin")
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(netConfig).To(Equal(&libcni.NetworkConfig{
|
Expect(netConfig).To(Equal(&libcni.PluginConfig{
|
||||||
Network: &types.NetConf{
|
Network: &types.PluginConf{
|
||||||
Name: "some-plugin",
|
Name: "some-plugin",
|
||||||
Type: "foobar",
|
Type: "foobar",
|
||||||
},
|
},
|
||||||
|
@ -79,8 +79,8 @@ var _ = Describe("Loading configuration from disk", func() {
|
||||||
It("finds the network config file for the plugin of the given type", func() {
|
It("finds the network config file for the plugin of the given type", func() {
|
||||||
netConfig, err := libcni.LoadConf(configDir, "some-plugin")
|
netConfig, err := libcni.LoadConf(configDir, "some-plugin")
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(netConfig).To(Equal(&libcni.NetworkConfig{
|
Expect(netConfig).To(Equal(&libcni.PluginConfig{
|
||||||
Network: &types.NetConf{
|
Network: &types.PluginConf{
|
||||||
Name: "some-plugin",
|
Name: "some-plugin",
|
||||||
Type: "foobar",
|
Type: "foobar",
|
||||||
},
|
},
|
||||||
|
@ -181,7 +181,7 @@ var _ = Describe("Loading configuration from disk", func() {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
Describe("ConfFromBytes", func() {
|
Describe("NetworkPluginConfFromBytes", func() {
|
||||||
Context("when the config is missing 'type'", func() {
|
Context("when the config is missing 'type'", func() {
|
||||||
It("returns a useful error", func() {
|
It("returns a useful error", func() {
|
||||||
_, err := libcni.ConfFromBytes([]byte(`{ "name": "some-plugin", "some-key": "some-value" }`))
|
_, err := libcni.ConfFromBytes([]byte(`{ "name": "some-plugin", "some-key": "some-value" }`))
|
||||||
|
@ -190,7 +190,7 @@ var _ = Describe("Loading configuration from disk", func() {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
Describe("LoadConfList", func() {
|
Describe("LoadNetworkConf", func() {
|
||||||
var (
|
var (
|
||||||
configDir string
|
configDir string
|
||||||
configList []byte
|
configList []byte
|
||||||
|
@ -202,7 +202,7 @@ var _ = Describe("Loading configuration from disk", func() {
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
configList = []byte(`{
|
configList = []byte(`{
|
||||||
"name": "some-list",
|
"name": "some-network",
|
||||||
"cniVersion": "0.2.0",
|
"cniVersion": "0.2.0",
|
||||||
"disableCheck": true,
|
"disableCheck": true,
|
||||||
"plugins": [
|
"plugins": [
|
||||||
|
@ -228,23 +228,23 @@ var _ = Describe("Loading configuration from disk", func() {
|
||||||
})
|
})
|
||||||
|
|
||||||
It("finds the network config file for the plugin of the given type", func() {
|
It("finds the network config file for the plugin of the given type", func() {
|
||||||
netConfigList, err := libcni.LoadConfList(configDir, "some-list")
|
netConfigList, err := libcni.LoadNetworkConf(configDir, "some-network")
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(netConfigList).To(Equal(&libcni.NetworkConfigList{
|
Expect(netConfigList).To(Equal(&libcni.NetworkConfigList{
|
||||||
Name: "some-list",
|
Name: "some-network",
|
||||||
CNIVersion: "0.2.0",
|
CNIVersion: "0.2.0",
|
||||||
DisableCheck: true,
|
DisableCheck: true,
|
||||||
Plugins: []*libcni.NetworkConfig{
|
Plugins: []*libcni.PluginConfig{
|
||||||
{
|
{
|
||||||
Network: &types.NetConf{Type: "host-local"},
|
Network: &types.PluginConf{Type: "host-local"},
|
||||||
Bytes: []byte(`{"subnet":"10.0.0.1/24","type":"host-local"}`),
|
Bytes: []byte(`{"subnet":"10.0.0.1/24","type":"host-local"}`),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Network: &types.NetConf{Type: "bridge"},
|
Network: &types.PluginConf{Type: "bridge"},
|
||||||
Bytes: []byte(`{"mtu":1400,"type":"bridge"}`),
|
Bytes: []byte(`{"mtu":1400,"type":"bridge"}`),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Network: &types.NetConf{Type: "port-forwarding"},
|
Network: &types.PluginConf{Type: "port-forwarding"},
|
||||||
Bytes: []byte(`{"ports":{"20.0.0.1:8080":"80"},"type":"port-forwarding"}`),
|
Bytes: []byte(`{"ports":{"20.0.0.1:8080":"80"},"type":"port-forwarding"}`),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -255,7 +255,7 @@ var _ = Describe("Loading configuration from disk", func() {
|
||||||
Context("when there is a config file with the same name as the list", func() {
|
Context("when there is a config file with the same name as the list", func() {
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
configFile := []byte(`{
|
configFile := []byte(`{
|
||||||
"name": "some-list",
|
"name": "some-network",
|
||||||
"cniVersion": "0.2.0",
|
"cniVersion": "0.2.0",
|
||||||
"type": "bridge"
|
"type": "bridge"
|
||||||
}`)
|
}`)
|
||||||
|
@ -263,7 +263,7 @@ var _ = Describe("Loading configuration from disk", func() {
|
||||||
})
|
})
|
||||||
|
|
||||||
It("Loads the config list first", func() {
|
It("Loads the config list first", func() {
|
||||||
netConfigList, err := libcni.LoadConfList(configDir, "some-list")
|
netConfigList, err := libcni.LoadNetworkConf(configDir, "some-network")
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(netConfigList.Plugins).To(HaveLen(3))
|
Expect(netConfigList.Plugins).To(HaveLen(3))
|
||||||
})
|
})
|
||||||
|
@ -271,7 +271,7 @@ var _ = Describe("Loading configuration from disk", func() {
|
||||||
It("falls back to the config file", func() {
|
It("falls back to the config file", func() {
|
||||||
Expect(os.Remove(filepath.Join(configDir, "50-whatever.conflist"))).To(Succeed())
|
Expect(os.Remove(filepath.Join(configDir, "50-whatever.conflist"))).To(Succeed())
|
||||||
|
|
||||||
netConfigList, err := libcni.LoadConfList(configDir, "some-list")
|
netConfigList, err := libcni.LoadNetworkConf(configDir, "some-network")
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(netConfigList.Plugins).To(HaveLen(1))
|
Expect(netConfigList.Plugins).To(HaveLen(1))
|
||||||
Expect(netConfigList.Plugins[0].Network.Type).To(Equal("bridge"))
|
Expect(netConfigList.Plugins[0].Network.Type).To(Equal("bridge"))
|
||||||
|
@ -284,15 +284,15 @@ var _ = Describe("Loading configuration from disk", func() {
|
||||||
})
|
})
|
||||||
|
|
||||||
It("returns a useful error", func() {
|
It("returns a useful error", func() {
|
||||||
_, err := libcni.LoadConfList(configDir, "some-plugin")
|
_, err := libcni.LoadNetworkConf(configDir, "some-network")
|
||||||
Expect(err).To(MatchError(libcni.NoConfigsFoundError{Dir: configDir}))
|
Expect(err).To(MatchError(libcni.NoConfigsFoundError{Dir: configDir}))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
Context("when there is no config for the desired plugin list", func() {
|
Context("when there is no config for the desired network name", func() {
|
||||||
It("returns a useful error", func() {
|
It("returns a useful error", func() {
|
||||||
_, err := libcni.LoadConfList(configDir, "some-other-plugin")
|
_, err := libcni.LoadNetworkConf(configDir, "some-other-network")
|
||||||
Expect(err).To(MatchError(libcni.NotFoundError{Dir: configDir, Name: "some-other-plugin"}))
|
Expect(err).To(MatchError(libcni.NotFoundError{Dir: configDir, Name: "some-other-network"}))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -302,7 +302,7 @@ var _ = Describe("Loading configuration from disk", func() {
|
||||||
})
|
})
|
||||||
|
|
||||||
It("returns a useful error", func() {
|
It("returns a useful error", func() {
|
||||||
_, err := libcni.LoadConfList(configDir, "some-plugin")
|
_, err := libcni.LoadNetworkConf(configDir, "some-plugin")
|
||||||
Expect(err).To(MatchError(`error parsing configuration list: unexpected end of JSON input`))
|
Expect(err).To(MatchError(`error parsing configuration list: unexpected end of JSON input`))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -326,7 +326,7 @@ var _ = Describe("Loading configuration from disk", func() {
|
||||||
})
|
})
|
||||||
|
|
||||||
It("will not find the config", func() {
|
It("will not find the config", func() {
|
||||||
_, err := libcni.LoadConfList(configDir, "deep")
|
_, err := libcni.LoadNetworkConf(configDir, "deep")
|
||||||
Expect(err).To(MatchError(HavePrefix("no net configuration with name")))
|
Expect(err).To(MatchError(HavePrefix("no net configuration with name")))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -334,7 +334,7 @@ var _ = Describe("Loading configuration from disk", func() {
|
||||||
Context("when disableCheck is a string not a boolean", func() {
|
Context("when disableCheck is a string not a boolean", func() {
|
||||||
It("will read a 'true' value and convert to boolean", func() {
|
It("will read a 'true' value and convert to boolean", func() {
|
||||||
configList = []byte(`{
|
configList = []byte(`{
|
||||||
"name": "some-list",
|
"name": "some-network",
|
||||||
"cniVersion": "0.4.0",
|
"cniVersion": "0.4.0",
|
||||||
"disableCheck": "true",
|
"disableCheck": "true",
|
||||||
"plugins": [
|
"plugins": [
|
||||||
|
@ -346,14 +346,14 @@ var _ = Describe("Loading configuration from disk", func() {
|
||||||
}`)
|
}`)
|
||||||
Expect(os.WriteFile(filepath.Join(configDir, "50-whatever.conflist"), configList, 0o600)).To(Succeed())
|
Expect(os.WriteFile(filepath.Join(configDir, "50-whatever.conflist"), configList, 0o600)).To(Succeed())
|
||||||
|
|
||||||
netConfigList, err := libcni.LoadConfList(configDir, "some-list")
|
netConfigList, err := libcni.LoadNetworkConf(configDir, "some-network")
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(netConfigList.DisableCheck).To(BeTrue())
|
Expect(netConfigList.DisableCheck).To(BeTrue())
|
||||||
})
|
})
|
||||||
|
|
||||||
It("will read a 'false' value and convert to boolean", func() {
|
It("will read a 'false' value and convert to boolean", func() {
|
||||||
configList = []byte(`{
|
configList = []byte(`{
|
||||||
"name": "some-list",
|
"name": "some-network",
|
||||||
"cniVersion": "0.4.0",
|
"cniVersion": "0.4.0",
|
||||||
"disableCheck": "false",
|
"disableCheck": "false",
|
||||||
"plugins": [
|
"plugins": [
|
||||||
|
@ -365,7 +365,7 @@ var _ = Describe("Loading configuration from disk", func() {
|
||||||
}`)
|
}`)
|
||||||
Expect(os.WriteFile(filepath.Join(configDir, "50-whatever.conflist"), configList, 0o600)).To(Succeed())
|
Expect(os.WriteFile(filepath.Join(configDir, "50-whatever.conflist"), configList, 0o600)).To(Succeed())
|
||||||
|
|
||||||
netConfigList, err := libcni.LoadConfList(configDir, "some-list")
|
netConfigList, err := libcni.LoadNetworkConf(configDir, "some-network")
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(netConfigList.DisableCheck).To(BeFalse())
|
Expect(netConfigList.DisableCheck).To(BeFalse())
|
||||||
})
|
})
|
||||||
|
@ -373,7 +373,7 @@ var _ = Describe("Loading configuration from disk", func() {
|
||||||
It("will return an error on an unrecognized value", func() {
|
It("will return an error on an unrecognized value", func() {
|
||||||
const badValue string = "adsfasdfasf"
|
const badValue string = "adsfasdfasf"
|
||||||
configList = []byte(fmt.Sprintf(`{
|
configList = []byte(fmt.Sprintf(`{
|
||||||
"name": "some-list",
|
"name": "some-network",
|
||||||
"cniVersion": "0.4.0",
|
"cniVersion": "0.4.0",
|
||||||
"disableCheck": "%s",
|
"disableCheck": "%s",
|
||||||
"plugins": [
|
"plugins": [
|
||||||
|
@ -385,35 +385,244 @@ var _ = Describe("Loading configuration from disk", func() {
|
||||||
}`, badValue))
|
}`, badValue))
|
||||||
Expect(os.WriteFile(filepath.Join(configDir, "50-whatever.conflist"), configList, 0o600)).To(Succeed())
|
Expect(os.WriteFile(filepath.Join(configDir, "50-whatever.conflist"), configList, 0o600)).To(Succeed())
|
||||||
|
|
||||||
_, err := libcni.LoadConfList(configDir, "some-list")
|
_, err := libcni.LoadNetworkConf(configDir, "some-network")
|
||||||
Expect(err).To(MatchError(`error parsing configuration list: invalid value "adsfasdfasf" for disableCheck`))
|
Expect(err).To(MatchError(fmt.Sprintf("error parsing configuration list: invalid value \"%s\" for disableCheck", badValue)))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
Context("for loadOnlyInlinedPlugins", func() {
|
||||||
|
It("the value will be parsed", func() {
|
||||||
|
configList = []byte(`{
|
||||||
|
"name": "some-network",
|
||||||
|
"cniVersion": "0.4.0",
|
||||||
|
"loadOnlyInlinedPlugins": true,
|
||||||
|
"plugins": [
|
||||||
|
{
|
||||||
|
"type": "host-local",
|
||||||
|
"subnet": "10.0.0.1/24"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}`)
|
||||||
|
Expect(os.WriteFile(filepath.Join(configDir, "50-whatever.conflist"), configList, 0o600)).To(Succeed())
|
||||||
|
|
||||||
|
dirPluginConf := []byte(`{
|
||||||
|
"type": "bro-check-out-my-plugin",
|
||||||
|
"subnet": "10.0.0.1/24"
|
||||||
|
}`)
|
||||||
|
|
||||||
|
subDir := filepath.Join(configDir, "some-network")
|
||||||
|
Expect(os.MkdirAll(subDir, 0o700)).To(Succeed())
|
||||||
|
Expect(os.WriteFile(filepath.Join(subDir, "funky-second-plugin.conf"), dirPluginConf, 0o600)).To(Succeed())
|
||||||
|
|
||||||
|
netConfigList, err := libcni.LoadNetworkConf(configDir, "some-network")
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(netConfigList.LoadOnlyInlinedPlugins).To(BeTrue())
|
||||||
|
})
|
||||||
|
|
||||||
|
It("the value will be false if not in config", func() {
|
||||||
|
configList = []byte(`{
|
||||||
|
"name": "some-network",
|
||||||
|
"cniVersion": "0.4.0",
|
||||||
|
"plugins": [
|
||||||
|
{
|
||||||
|
"type": "host-local",
|
||||||
|
"subnet": "10.0.0.1/24"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}`)
|
||||||
|
Expect(os.WriteFile(filepath.Join(configDir, "50-whatever.conflist"), configList, 0o600)).To(Succeed())
|
||||||
|
|
||||||
|
netConfigList, err := libcni.LoadNetworkConf(configDir, "some-network")
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(netConfigList.LoadOnlyInlinedPlugins).To(BeFalse())
|
||||||
|
})
|
||||||
|
|
||||||
|
It("will return an error on an unrecognized value", func() {
|
||||||
|
const badValue string = "sphagnum"
|
||||||
|
configList = []byte(fmt.Sprintf(`{
|
||||||
|
"name": "some-network",
|
||||||
|
"cniVersion": "0.4.0",
|
||||||
|
"loadOnlyInlinedPlugins": "%s",
|
||||||
|
"plugins": [
|
||||||
|
{
|
||||||
|
"type": "host-local",
|
||||||
|
"subnet": "10.0.0.1/24"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}`, badValue))
|
||||||
|
Expect(os.WriteFile(filepath.Join(configDir, "50-whatever.conflist"), configList, 0o600)).To(Succeed())
|
||||||
|
|
||||||
|
_, err := libcni.LoadNetworkConf(configDir, "some-network")
|
||||||
|
Expect(err).To(MatchError(fmt.Sprintf(`error parsing configuration list: invalid value "%s" for loadOnlyInlinedPlugins`, badValue)))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("will return an error if `plugins` is missing and `loadOnlyInlinedPlugins` is `true`", func() {
|
||||||
|
configList = []byte(`{
|
||||||
|
"name": "some-network",
|
||||||
|
"cniVersion": "0.4.0",
|
||||||
|
"loadOnlyInlinedPlugins": true
|
||||||
|
}`)
|
||||||
|
Expect(os.WriteFile(filepath.Join(configDir, "50-whatever.conflist"), configList, 0o600)).To(Succeed())
|
||||||
|
|
||||||
|
_, err := libcni.LoadNetworkConf(configDir, "some-network")
|
||||||
|
Expect(err).To(MatchError("error parsing configuration list: `loadOnlyInlinedPlugins` is true, and no 'plugins' key"))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("will return no error if `plugins` is missing and `loadOnlyInlinedPlugins` is false", func() {
|
||||||
|
configList = []byte(`{
|
||||||
|
"name": "some-network",
|
||||||
|
"cniVersion": "0.4.0",
|
||||||
|
"loadOnlyInlinedPlugins": false
|
||||||
|
}`)
|
||||||
|
Expect(os.WriteFile(filepath.Join(configDir, "50-whatever.conflist"), configList, 0o600)).To(Succeed())
|
||||||
|
|
||||||
|
dirPluginConf := []byte(`{
|
||||||
|
"type": "bro-check-out-my-plugin",
|
||||||
|
"subnet": "10.0.0.1/24"
|
||||||
|
}`)
|
||||||
|
|
||||||
|
subDir := filepath.Join(configDir, "some-network")
|
||||||
|
Expect(os.MkdirAll(subDir, 0o700)).To(Succeed())
|
||||||
|
Expect(os.WriteFile(filepath.Join(subDir, "funky-second-plugin.conf"), dirPluginConf, 0o600)).To(Succeed())
|
||||||
|
|
||||||
|
netConfigList, err := libcni.LoadNetworkConf(configDir, "some-network")
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(netConfigList.LoadOnlyInlinedPlugins).To(BeFalse())
|
||||||
|
Expect(netConfigList.Plugins).To(HaveLen(1))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("will return error if `loadOnlyInlinedPlugins` is implicitly false + no conf plugin is defined, but no plugins subfolder with network name exists", func() {
|
||||||
|
configList = []byte(`{
|
||||||
|
"name": "some-network",
|
||||||
|
"cniVersion": "0.4.0"
|
||||||
|
}`)
|
||||||
|
Expect(os.WriteFile(filepath.Join(configDir, "50-whatever.conflist"), configList, 0o600)).To(Succeed())
|
||||||
|
|
||||||
|
_, err := libcni.LoadNetworkConf(configDir, "some-network")
|
||||||
|
Expect(err).To(MatchError("no plugin configs found"))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("will return NO error if `loadOnlyInlinedPlugins` is implicitly false + at least 1 conf plugin is defined, but no plugins subfolder with network name exists", func() {
|
||||||
|
configList = []byte(`{
|
||||||
|
"name": "some-network",
|
||||||
|
"cniVersion": "0.4.0",
|
||||||
|
"plugins": [
|
||||||
|
{
|
||||||
|
"type": "host-local",
|
||||||
|
"subnet": "10.0.0.1/24"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}`)
|
||||||
|
Expect(os.WriteFile(filepath.Join(configDir, "50-whatever.conflist"), configList, 0o600)).To(Succeed())
|
||||||
|
|
||||||
|
_, err := libcni.LoadNetworkConf(configDir, "some-network")
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
})
|
||||||
|
|
||||||
|
It("will return NO error if `loadOnlyInlinedPlugins` is implicitly false + at least 1 conf plugin is defined and network name subfolder exists, but is empty/unreadable", func() {
|
||||||
|
configList = []byte(`{
|
||||||
|
"name": "some-network",
|
||||||
|
"cniVersion": "0.4.0",
|
||||||
|
"plugins": [
|
||||||
|
{
|
||||||
|
"type": "host-local",
|
||||||
|
"subnet": "10.0.0.1/24"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}`)
|
||||||
|
Expect(os.WriteFile(filepath.Join(configDir, "50-whatever.conflist"), configList, 0o600)).To(Succeed())
|
||||||
|
|
||||||
|
subDir := filepath.Join(configDir, "some-network")
|
||||||
|
Expect(os.MkdirAll(subDir, 0o700)).To(Succeed())
|
||||||
|
|
||||||
|
_, err := libcni.LoadNetworkConf(configDir, "some-network")
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
})
|
||||||
|
|
||||||
|
It("will merge loaded and inlined plugin lists if both `plugins` is set and `loadOnlyInlinedPlugins` is false", func() {
|
||||||
|
configList = []byte(`{
|
||||||
|
"name": "some-network",
|
||||||
|
"cniVersion": "0.4.0",
|
||||||
|
"plugins": [
|
||||||
|
{
|
||||||
|
"type": "host-local",
|
||||||
|
"subnet": "10.0.0.1/24"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}`)
|
||||||
|
|
||||||
|
dirPluginConf := []byte(`{
|
||||||
|
"type": "bro-check-out-my-plugin",
|
||||||
|
"subnet": "10.0.0.1/24"
|
||||||
|
}`)
|
||||||
|
|
||||||
|
Expect(os.WriteFile(filepath.Join(configDir, "50-whatever.conflist"), configList, 0o600)).To(Succeed())
|
||||||
|
|
||||||
|
subDir := filepath.Join(configDir, "some-network")
|
||||||
|
Expect(os.MkdirAll(subDir, 0o700)).To(Succeed())
|
||||||
|
Expect(os.WriteFile(filepath.Join(subDir, "funky-second-plugin.conf"), dirPluginConf, 0o600)).To(Succeed())
|
||||||
|
|
||||||
|
netConfigList, err := libcni.LoadNetworkConf(configDir, "some-network")
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(netConfigList.LoadOnlyInlinedPlugins).To(BeFalse())
|
||||||
|
Expect(netConfigList.Plugins).To(HaveLen(2))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("will ignore loaded plugins if `plugins` is set and `loadOnlyInlinedPlugins` is true", func() {
|
||||||
|
configList = []byte(`{
|
||||||
|
"name": "some-network",
|
||||||
|
"cniVersion": "0.4.0",
|
||||||
|
"loadOnlyInlinedPlugins": true,
|
||||||
|
"plugins": [
|
||||||
|
{
|
||||||
|
"type": "host-local",
|
||||||
|
"subnet": "10.0.0.1/24"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}`)
|
||||||
|
|
||||||
|
dirPluginConf := []byte(`{
|
||||||
|
"type": "bro-check-out-my-plugin",
|
||||||
|
"subnet": "10.0.0.1/24"
|
||||||
|
}`)
|
||||||
|
|
||||||
|
Expect(os.WriteFile(filepath.Join(configDir, "50-whatever.conflist"), configList, 0o600)).To(Succeed())
|
||||||
|
subDir := filepath.Join(configDir, "some-network")
|
||||||
|
Expect(os.MkdirAll(subDir, 0o700)).To(Succeed())
|
||||||
|
Expect(os.WriteFile(filepath.Join(subDir, "funky-second-plugin.conf"), dirPluginConf, 0o600)).To(Succeed())
|
||||||
|
|
||||||
|
netConfigList, err := libcni.LoadNetworkConf(configDir, "some-network")
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(netConfigList.LoadOnlyInlinedPlugins).To(BeTrue())
|
||||||
|
Expect(netConfigList.Plugins).To(HaveLen(1))
|
||||||
|
Expect(netConfigList.Plugins[0].Network.Type).To(Equal("host-local"))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
Describe("ConfListFromFile", func() {
|
Describe("NetworkConfFromFile", func() {
|
||||||
Context("when the file cannot be opened", func() {
|
Context("when the file cannot be opened", func() {
|
||||||
It("returns a useful error", func() {
|
It("returns a useful error", func() {
|
||||||
_, err := libcni.ConfListFromFile("/tmp/nope/not-here")
|
_, err := libcni.NetworkConfFromFile("/tmp/nope/not-here")
|
||||||
Expect(err).To(MatchError(HavePrefix(`error reading /tmp/nope/not-here: open /tmp/nope/not-here`)))
|
Expect(err).To(MatchError(HavePrefix(`error reading /tmp/nope/not-here: open /tmp/nope/not-here`)))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
Describe("InjectConf", func() {
|
Describe("InjectConf", func() {
|
||||||
var testNetConfig *libcni.NetworkConfig
|
var testNetConfig *libcni.PluginConfig
|
||||||
|
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
testNetConfig = &libcni.NetworkConfig{
|
testNetConfig = &libcni.PluginConfig{
|
||||||
Network: &types.NetConf{Name: "some-plugin", Type: "foobar"},
|
Network: &types.PluginConf{Name: "some-plugin", Type: "foobar"},
|
||||||
Bytes: []byte(`{ "name": "some-plugin", "type": "foobar" }`),
|
Bytes: []byte(`{ "name": "some-plugin", "type": "foobar" }`),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
Context("when function parameters are incorrect", func() {
|
Context("when function parameters are incorrect", func() {
|
||||||
It("returns unmarshal error", func() {
|
It("returns unmarshal error", func() {
|
||||||
conf := &libcni.NetworkConfig{
|
conf := &libcni.PluginConfig{
|
||||||
Network: &types.NetConf{Name: "some-plugin"},
|
Network: &types.PluginConf{Name: "some-plugin"},
|
||||||
Bytes: []byte(`{ cc cc cc}`),
|
Bytes: []byte(`{ cc cc cc}`),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -438,8 +647,8 @@ var _ = Describe("Loading configuration from disk", func() {
|
||||||
|
|
||||||
resultConfig, err := libcni.InjectConf(testNetConfig, map[string]interface{}{"test": "test"})
|
resultConfig, err := libcni.InjectConf(testNetConfig, map[string]interface{}{"test": "test"})
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(resultConfig).To(Equal(&libcni.NetworkConfig{
|
Expect(resultConfig).To(Equal(&libcni.PluginConfig{
|
||||||
Network: &types.NetConf{
|
Network: &types.PluginConf{
|
||||||
Name: "some-plugin",
|
Name: "some-plugin",
|
||||||
Type: "foobar",
|
Type: "foobar",
|
||||||
},
|
},
|
||||||
|
@ -456,8 +665,8 @@ var _ = Describe("Loading configuration from disk", func() {
|
||||||
resultConfig, err = libcni.InjectConf(resultConfig, map[string]interface{}{"test": "changedValue"})
|
resultConfig, err = libcni.InjectConf(resultConfig, map[string]interface{}{"test": "changedValue"})
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
Expect(resultConfig).To(Equal(&libcni.NetworkConfig{
|
Expect(resultConfig).To(Equal(&libcni.PluginConfig{
|
||||||
Network: &types.NetConf{
|
Network: &types.PluginConf{
|
||||||
Name: "some-plugin",
|
Name: "some-plugin",
|
||||||
Type: "foobar",
|
Type: "foobar",
|
||||||
},
|
},
|
||||||
|
@ -474,8 +683,8 @@ var _ = Describe("Loading configuration from disk", func() {
|
||||||
resultConfig, err = libcni.InjectConf(resultConfig, map[string]interface{}{"test": "test"})
|
resultConfig, err = libcni.InjectConf(resultConfig, map[string]interface{}{"test": "test"})
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
Expect(resultConfig).To(Equal(&libcni.NetworkConfig{
|
Expect(resultConfig).To(Equal(&libcni.PluginConfig{
|
||||||
Network: &types.NetConf{
|
Network: &types.PluginConf{
|
||||||
Name: "some-plugin",
|
Name: "some-plugin",
|
||||||
Type: "foobar",
|
Type: "foobar",
|
||||||
},
|
},
|
||||||
|
@ -496,8 +705,8 @@ var _ = Describe("Loading configuration from disk", func() {
|
||||||
resultConfig, err = libcni.InjectConf(resultConfig, map[string]interface{}{"type": "bridge"})
|
resultConfig, err = libcni.InjectConf(resultConfig, map[string]interface{}{"type": "bridge"})
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
Expect(resultConfig).To(Equal(&libcni.NetworkConfig{
|
Expect(resultConfig).To(Equal(&libcni.PluginConfig{
|
||||||
Network: &types.NetConf{Name: "some-plugin", Type: "bridge", DNS: types.DNS{Nameservers: servers, Domain: "local"}},
|
Network: &types.PluginConf{Name: "some-plugin", Type: "bridge", DNS: types.DNS{Nameservers: servers, Domain: "local"}},
|
||||||
Bytes: expectedPluginConfig,
|
Bytes: expectedPluginConfig,
|
||||||
}))
|
}))
|
||||||
})
|
})
|
||||||
|
@ -505,7 +714,7 @@ var _ = Describe("Loading configuration from disk", func() {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
var _ = Describe("ConfListFromBytes", func() {
|
var _ = Describe("NetworkConfFromBytes", func() {
|
||||||
Describe("Version selection", func() {
|
Describe("Version selection", func() {
|
||||||
makeConfig := func(versions ...string) []byte {
|
makeConfig := func(versions ...string) []byte {
|
||||||
// ugly fake json encoding, but whatever
|
// ugly fake json encoding, but whatever
|
||||||
|
@ -516,36 +725,36 @@ var _ = Describe("ConfListFromBytes", func() {
|
||||||
return []byte(fmt.Sprintf(`{"name": "test", "cniVersions": [%s], "plugins": [{"type": "foo"}]}`, strings.Join(vs, ",")))
|
return []byte(fmt.Sprintf(`{"name": "test", "cniVersions": [%s], "plugins": [{"type": "foo"}]}`, strings.Join(vs, ",")))
|
||||||
}
|
}
|
||||||
It("correctly selects the maximum version", func() {
|
It("correctly selects the maximum version", func() {
|
||||||
conf, err := libcni.ConfListFromBytes(makeConfig("1.1.0", "0.4.0", "1.0.0"))
|
conf, err := libcni.NetworkConfFromBytes(makeConfig("1.1.0", "0.4.0", "1.0.0"))
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(conf.CNIVersion).To(Equal("1.1.0"))
|
Expect(conf.CNIVersion).To(Equal("1.1.0"))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("selects the highest version supported by libcni", func() {
|
It("selects the highest version supported by libcni", func() {
|
||||||
conf, err := libcni.ConfListFromBytes(makeConfig("99.0.0", "1.1.0", "0.4.0", "1.0.0"))
|
conf, err := libcni.NetworkConfFromBytes(makeConfig("99.0.0", "1.1.0", "0.4.0", "1.0.0"))
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(conf.CNIVersion).To(Equal("1.1.0"))
|
Expect(conf.CNIVersion).To(Equal("1.1.0"))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("fails when invalid versions are specified", func() {
|
It("fails when invalid versions are specified", func() {
|
||||||
_, err := libcni.ConfListFromBytes(makeConfig("1.1.0", "0.4.0", "1.0.f"))
|
_, err := libcni.NetworkConfFromBytes(makeConfig("1.1.0", "0.4.0", "1.0.f"))
|
||||||
Expect(err).To(HaveOccurred())
|
Expect(err).To(HaveOccurred())
|
||||||
})
|
})
|
||||||
|
|
||||||
It("falls back to cniVersion", func() {
|
It("falls back to cniVersion", func() {
|
||||||
conf, err := libcni.ConfListFromBytes([]byte(`{"name": "test", "cniVersion": "1.2.3", "plugins": [{"type": "foo"}]}`))
|
conf, err := libcni.NetworkConfFromBytes([]byte(`{"name": "test", "cniVersion": "1.2.3", "plugins": [{"type": "foo"}]}`))
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(conf.CNIVersion).To(Equal("1.2.3"))
|
Expect(conf.CNIVersion).To(Equal("1.2.3"))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("merges cniVersions and cniVersion", func() {
|
It("merges cniVersions and cniVersion", func() {
|
||||||
conf, err := libcni.ConfListFromBytes([]byte(`{"name": "test", "cniVersion": "1.0.0", "cniVersions": ["0.1.0", "0.4.0"], "plugins": [{"type": "foo"}]}`))
|
conf, err := libcni.NetworkConfFromBytes([]byte(`{"name": "test", "cniVersion": "1.0.0", "cniVersions": ["0.1.0", "0.4.0"], "plugins": [{"type": "foo"}]}`))
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(conf.CNIVersion).To(Equal("1.0.0"))
|
Expect(conf.CNIVersion).To(Equal("1.0.0"))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("handles an empty cniVersions array", func() {
|
It("handles an empty cniVersions array", func() {
|
||||||
conf, err := libcni.ConfListFromBytes([]byte(`{"name": "test", "cniVersions": [], "plugins": [{"type": "foo"}]}`))
|
conf, err := libcni.NetworkConfFromBytes([]byte(`{"name": "test", "cniVersions": [], "plugins": [{"type": "foo"}]}`))
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(conf.CNIVersion).To(Equal(""))
|
Expect(conf.CNIVersion).To(Equal(""))
|
||||||
})
|
})
|
||||||
|
@ -553,7 +762,7 @@ var _ = Describe("ConfListFromBytes", func() {
|
||||||
})
|
})
|
||||||
|
|
||||||
var _ = Describe("ConfListFromConf", func() {
|
var _ = Describe("ConfListFromConf", func() {
|
||||||
var testNetConfig *libcni.NetworkConfig
|
var testNetConfig *libcni.PluginConfig
|
||||||
|
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
pb := []byte(`{"name":"some-plugin","cniVersion":"0.3.1", "type":"foobar"}`)
|
pb := []byte(`{"name":"some-plugin","cniVersion":"0.3.1", "type":"foobar"}`)
|
||||||
|
@ -575,11 +784,11 @@ var _ = Describe("ConfListFromConf", func() {
|
||||||
Expect(ncl).To(Equal(&libcni.NetworkConfigList{
|
Expect(ncl).To(Equal(&libcni.NetworkConfigList{
|
||||||
Name: "some-plugin",
|
Name: "some-plugin",
|
||||||
CNIVersion: "0.3.1",
|
CNIVersion: "0.3.1",
|
||||||
Plugins: []*libcni.NetworkConfig{testNetConfig},
|
Plugins: []*libcni.PluginConfig{testNetConfig},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
// Test that the json unmarshals to the same data
|
// Test that the json unmarshals to the same data
|
||||||
ncl2, err := libcni.ConfListFromBytes(bytes)
|
ncl2, err := libcni.NetworkConfFromBytes(bytes)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
ncl2.Bytes = nil
|
ncl2.Bytes = nil
|
||||||
ncl2.Plugins[0].Bytes = nil
|
ncl2.Plugins[0].Bytes = nil
|
||||||
|
|
|
@ -56,8 +56,12 @@ func (n *IPNet) UnmarshalJSON(data []byte) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NetConfType describes a network.
|
// Use PluginConf instead of NetConf, the NetConf
|
||||||
type NetConfType struct {
|
// backwards-compat alias will be removed in a future release.
|
||||||
|
type NetConf = PluginConf
|
||||||
|
|
||||||
|
// PluginConf describes a plugin configuration for a specific network.
|
||||||
|
type PluginConf struct {
|
||||||
CNIVersion string `json:"cniVersion,omitempty"`
|
CNIVersion string `json:"cniVersion,omitempty"`
|
||||||
|
|
||||||
Name string `json:"name,omitempty"`
|
Name string `json:"name,omitempty"`
|
||||||
|
@ -73,9 +77,6 @@ type NetConfType struct {
|
||||||
ValidAttachments []GCAttachment `json:"cni.dev/valid-attachments,omitempty"`
|
ValidAttachments []GCAttachment `json:"cni.dev/valid-attachments,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NetConf is defined as different type as custom MarshalJSON() and issue #1096
|
|
||||||
type NetConf NetConfType
|
|
||||||
|
|
||||||
// GCAttachment is the parameters to a GC call -- namely,
|
// GCAttachment is the parameters to a GC call -- namely,
|
||||||
// the container ID and ifname pair that represents a
|
// the container ID and ifname pair that represents a
|
||||||
// still-valid attachment.
|
// still-valid attachment.
|
||||||
|
@ -86,11 +87,8 @@ type GCAttachment struct {
|
||||||
|
|
||||||
// Note: DNS should be omit if DNS is empty but default Marshal function
|
// Note: DNS should be omit if DNS is empty but default Marshal function
|
||||||
// will output empty structure hence need to write a Marshal function
|
// will output empty structure hence need to write a Marshal function
|
||||||
func (n *NetConfType) MarshalJSON() ([]byte, error) {
|
func (n *PluginConf) MarshalJSON() ([]byte, error) {
|
||||||
// use type alias to escape recursion for json.Marshal() to MarshalJSON()
|
bytes, err := json.Marshal(*n)
|
||||||
type fixObjType = NetConf
|
|
||||||
|
|
||||||
bytes, err := json.Marshal(fixObjType(*n))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -120,10 +118,10 @@ func (i *IPAM) IsEmpty() bool {
|
||||||
type NetConfList struct {
|
type NetConfList struct {
|
||||||
CNIVersion string `json:"cniVersion,omitempty"`
|
CNIVersion string `json:"cniVersion,omitempty"`
|
||||||
|
|
||||||
Name string `json:"name,omitempty"`
|
Name string `json:"name,omitempty"`
|
||||||
DisableCheck bool `json:"disableCheck,omitempty"`
|
DisableCheck bool `json:"disableCheck,omitempty"`
|
||||||
DisableGC bool `json:"disableGC,omitempty"`
|
DisableGC bool `json:"disableGC,omitempty"`
|
||||||
Plugins []*NetConf `json:"plugins,omitempty"`
|
Plugins []*PluginConf `json:"plugins,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Result is an interface that provides the result of plugin execution
|
// Result is an interface that provides the result of plugin execution
|
||||||
|
@ -231,7 +229,7 @@ func (r *Route) Copy() *Route {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Well known error codes
|
// Well known error codes
|
||||||
// see https://github.com/containernetworking/cni/blob/main/SPEC.md#well-known-error-codes
|
// see https://github.com/containernetworking/cni/blob/main/SPEC.md#error
|
||||||
const (
|
const (
|
||||||
ErrUnknown uint = iota // 0
|
ErrUnknown uint = iota // 0
|
||||||
ErrIncompatibleCNIVersion // 1
|
ErrIncompatibleCNIVersion // 1
|
||||||
|
@ -243,6 +241,8 @@ const (
|
||||||
ErrInvalidNetworkConfig // 7
|
ErrInvalidNetworkConfig // 7
|
||||||
ErrInvalidNetNS // 8
|
ErrInvalidNetNS // 8
|
||||||
ErrTryAgainLater uint = 11
|
ErrTryAgainLater uint = 11
|
||||||
|
ErrPluginNotAvailable uint = 50
|
||||||
|
ErrLimitedConnectivity uint = 51
|
||||||
ErrInternal uint = 999
|
ErrInternal uint = 999
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -142,3 +142,27 @@ func GreaterThanOrEqualTo(version, otherVersion string) (bool, error) {
|
||||||
}
|
}
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GreaterThan returns true if the first version is greater than the second
|
||||||
|
func GreaterThan(version, otherVersion string) (bool, error) {
|
||||||
|
firstMajor, firstMinor, firstMicro, err := ParseVersion(version)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
secondMajor, secondMinor, secondMicro, err := ParseVersion(otherVersion)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if firstMajor > secondMajor {
|
||||||
|
return true, nil
|
||||||
|
} else if firstMajor == secondMajor {
|
||||||
|
if firstMinor > secondMinor {
|
||||||
|
return true, nil
|
||||||
|
} else if firstMinor == secondMinor && firstMicro > secondMicro {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
|
@ -63,7 +63,7 @@ func NewResult(version string, resultBytes []byte) (types.Result, error) {
|
||||||
|
|
||||||
// ParsePrevResult parses a prevResult in a NetConf structure and sets
|
// ParsePrevResult parses a prevResult in a NetConf structure and sets
|
||||||
// the NetConf's PrevResult member to the parsed Result object.
|
// the NetConf's PrevResult member to the parsed Result object.
|
||||||
func ParsePrevResult(conf *types.NetConf) error {
|
func ParsePrevResult(conf *types.PluginConf) error {
|
||||||
if conf.RawPrevResult == nil {
|
if conf.RawPrevResult == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,7 +59,7 @@ var _ = Describe("Version operations", func() {
|
||||||
err := json.Unmarshal(rawBytes, &raw)
|
err := json.Unmarshal(rawBytes, &raw)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
conf := &types.NetConf{
|
conf := &types.PluginConf{
|
||||||
CNIVersion: "1.0.0",
|
CNIVersion: "1.0.0",
|
||||||
Name: "foobar",
|
Name: "foobar",
|
||||||
Type: "baz",
|
Type: "baz",
|
||||||
|
@ -94,7 +94,7 @@ var _ = Describe("Version operations", func() {
|
||||||
})
|
})
|
||||||
|
|
||||||
It("fails if the prevResult version is unknown", func() {
|
It("fails if the prevResult version is unknown", func() {
|
||||||
conf := &types.NetConf{
|
conf := &types.PluginConf{
|
||||||
CNIVersion: version.Current(),
|
CNIVersion: version.Current(),
|
||||||
Name: "foobar",
|
Name: "foobar",
|
||||||
Type: "baz",
|
Type: "baz",
|
||||||
|
@ -108,7 +108,7 @@ var _ = Describe("Version operations", func() {
|
||||||
})
|
})
|
||||||
|
|
||||||
It("fails if the prevResult version does not match the prevResult version", func() {
|
It("fails if the prevResult version does not match the prevResult version", func() {
|
||||||
conf := &types.NetConf{
|
conf := &types.PluginConf{
|
||||||
CNIVersion: version.Current(),
|
CNIVersion: version.Current(),
|
||||||
Name: "foobar",
|
Name: "foobar",
|
||||||
Type: "baz",
|
Type: "baz",
|
||||||
|
@ -128,7 +128,7 @@ var _ = Describe("Version operations", func() {
|
||||||
|
|
||||||
Context("when a prevResult is not available", func() {
|
Context("when a prevResult is not available", func() {
|
||||||
It("does not fail", func() {
|
It("does not fail", func() {
|
||||||
conf := &types.NetConf{
|
conf := &types.PluginConf{
|
||||||
CNIVersion: version.Current(),
|
CNIVersion: version.Current(),
|
||||||
Name: "foobar",
|
Name: "foobar",
|
||||||
Type: "baz",
|
Type: "baz",
|
||||||
|
@ -139,4 +139,22 @@ var _ = Describe("Version operations", func() {
|
||||||
Expect(conf.PrevResult).To(BeNil())
|
Expect(conf.PrevResult).To(BeNil())
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Context("version parsing", func() {
|
||||||
|
It("parses versions correctly", func() {
|
||||||
|
v1 := "1.1.0"
|
||||||
|
v2 := "1.1.1"
|
||||||
|
|
||||||
|
check := func(a, b string, want bool) {
|
||||||
|
GinkgoHelper()
|
||||||
|
gt, err := version.GreaterThan(a, b)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(gt).To(Equal(want))
|
||||||
|
}
|
||||||
|
|
||||||
|
check(v1, v2, false)
|
||||||
|
check(v2, v1, true)
|
||||||
|
check(v2, v2, false)
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -36,7 +36,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type NetConf struct {
|
type NetConf struct {
|
||||||
types.NetConf
|
types.PluginConf
|
||||||
DebugFile string `json:"debugFile"`
|
DebugFile string `json:"debugFile"`
|
||||||
CommandLog string `json:"commandLog"`
|
CommandLog string `json:"commandLog"`
|
||||||
}
|
}
|
||||||
|
@ -46,7 +46,7 @@ func loadConf(bytes []byte) (*NetConf, error) {
|
||||||
if err := json.Unmarshal(bytes, n); err != nil {
|
if err := json.Unmarshal(bytes, n); err != nil {
|
||||||
return nil, fmt.Errorf("failed to load netconf: %w %q", err, string(bytes))
|
return nil, fmt.Errorf("failed to load netconf: %w %q", err, string(bytes))
|
||||||
}
|
}
|
||||||
if err := version.ParsePrevResult(&n.NetConf); err != nil {
|
if err := version.ParsePrevResult(&n.PluginConf); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return n, nil
|
return n, nil
|
||||||
|
|
Loading…
Reference in New Issue