Compare commits

...

992 Commits

Author SHA1 Message Date
Casey Callendrello ee7c96f201
Merge pull request #1152 from architkulkarni/status-codes
Add error codes for STATUS verb to `types.go` and `SPEC.md`
2025-06-02 17:11:33 +02:00
Casey Callendrello d0bdb01ee9
Merge pull request #1153 from xmulligan/patch-1
docs: remove archived 3rd party plugins
2025-05-16 10:30:26 +02:00
Lionel Jouin 8338cebec2
Merge pull request #1161 from bleggett/bleggett/update-email
Update my email
2025-04-22 23:46:17 +02:00
Benjamin Leggett 44a77d77e9
Update my email
Signed-off-by: Benjamin Leggett <benjamin@edera.io>
2025-04-22 11:20:07 -04:00
Tomofumi Hayashi 277757c333
Merge pull request #1157 from squeed/update-maintainers
update MAINTAINERS
2025-04-15 00:40:04 +09:00
Casey Callendrello 0b7ae2f1b1
Merge pull request #1156 from bleggett/bleggett/clarify-del
SPEC: Clarify some language around `DEL` and `prevResult`
2025-04-14 17:31:05 +02:00
Casey Callendrello 40b0a08c95 update MAINTAINERS
Add some new ones, mark others as emeritus.

Signed-off-by: Casey Callendrello <c1@caseyc.net>
2025-04-14 17:26:51 +02:00
Benjamin Leggett 7f701df6ab
Clarify some language around `DEL` and `prevResult`
Signed-off-by: Benjamin Leggett <benjamin@edera.io>
2025-04-09 18:07:38 -04:00
Casey Callendrello a28faab926
Merge pull request #1155 from squeed/remove-deprecated
libcni: remove some deprecation warnings
2025-04-07 17:37:53 +02:00
Casey Callendrello 3f7369aeb9 libcni: remove some deprecation warnings
We were a bit over-eager with the deprecations; these types and aliases
are doing no harm :-).

Signed-off-by: Casey Callendrello <c1@caseyc.net>
2025-04-07 17:34:18 +02:00
Mike Zappa 097592d34b
Merge pull request #1154 from LionelJouin/maintainers
Update MAINTAINERS
2025-03-31 08:15:00 -06:00
Lionel Jouin d2f3f46deb Update MAINTAINERS
Signed-off-by: Lionel Jouin <lionel.jouin@est.tech>
2025-03-27 15:07:21 +01:00
Bill Mulligan 3578abe586 remove archived 3rd party plugins
Signed-off-by: Bill Mulligan <billmulligan516@gmail.com>
2025-03-16 03:48:34 +01:00
Archit Kulkarni 5f84615a5d
Add error codes for STATUS verb to `types.go` and `SPEC.md`
Signed-off-by: Archit Kulkarni <architkulkarni@google.com>
2025-01-16 00:33:03 +00:00
Casey Callendrello 3b4dfc5dff
Merge pull request #1119 from containernetworking/dependabot/github_actions/github/codeql-action-3.26.7
build(deps): bump github/codeql-action from 3.26.4 to 3.26.7
2024-09-17 16:14:21 +02:00
dependabot[bot] a845cc88d2
build(deps): bump github/codeql-action from 3.26.4 to 3.26.7
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.26.4 to 3.26.7.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](f0f3afee80...8214744c54)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-16 00:12:22 +00:00
Casey Callendrello 4c9ae43c0e
Merge pull request #1109 from containernetworking/dependabot/go_modules/golang-53495c1738
build(deps): bump the golang group across 1 directory with 2 updates
2024-08-29 10:01:03 +02:00
Casey Callendrello be3f5a9b2d
Merge pull request #1108 from mmorel-35/scorecard
Setup scorecard workflow
2024-08-29 10:00:34 +02:00
Matthieu MOREL f4f2dc7430 Setup scorecard workflow
Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>
2024-08-28 15:01:51 +00:00
Casey Callendrello 81ed2d06d6
Merge pull request #1110 from danwinship/cnitool-binary
Remove accidentally-committed cnitool binary
2024-08-28 15:30:50 +02:00
Dan Winship 00f4094a12 Remove accidentally-committed cnitool binary
Signed-off-by: Dan Winship <danwinship@redhat.com>
2024-08-28 09:04:46 -04:00
dependabot[bot] f0289faa47
build(deps): bump the golang group across 1 directory with 2 updates
Bumps the golang group with 1 update in the / directory: [github.com/onsi/ginkgo/v2](https://github.com/onsi/ginkgo).


Updates `github.com/onsi/ginkgo/v2` from 2.19.0 to 2.20.1
- [Release notes](https://github.com/onsi/ginkgo/releases)
- [Changelog](https://github.com/onsi/ginkgo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/ginkgo/compare/v2.19.0...v2.20.1)

Updates `github.com/onsi/gomega` from 1.33.1 to 1.34.1
- [Release notes](https://github.com/onsi/gomega/releases)
- [Changelog](https://github.com/onsi/gomega/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/gomega/compare/v1.33.1...v1.34.1)

---
updated-dependencies:
- dependency-name: github.com/onsi/ginkgo/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: golang
- dependency-name: github.com/onsi/gomega
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: golang
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-26 00:39:46 +00:00
Casey Callendrello 81d15e90c3
Merge pull request #1052 from bleggett/bleggett/multi-dir-pluginconfig
RFC - Support safe subdirectory-based plugin conf loading
2024-07-22 11:14:15 -04:00
Casey Callendrello 309b6bbc17
Merge pull request #1103 from squeed/fix-gc-key-name
SPEC, libcni: harmonize GC valid-attachment key
2024-07-22 11:10:34 -04:00
Casey Callendrello 692efbd2a7 libcni: set both GC valid attachment keys
I made an (embarassing) error where SPEC.md and libcni disagreed on the
key for valid attachments for a GC operation. The spec has been
updated, but libcni should set both keys as a transition mechanism.

Signed-off-by: Casey Callendrello <c1@caseyc.net>
2024-07-22 10:57:12 -04:00
Casey Callendrello 6a68851f26 SPEC: use correct GC field name.
The SPEC and libcni disagreed on the name to use for still-valid
attachments. Change the spec to be aligned with libcni.

Signed-off-by: Casey Callendrello <c1@caseyc.net>
2024-07-01 17:28:26 +02:00
Mike Zappa d5c71ad528
Merge pull request #1100 from squeed/parse-semver 2024-06-24 13:59:30 -06:00
Casey Callendrello 587837eeda libcni: remove use of Masterminds/semver
We didn't really need it, and it's yet another dep to update.

Signed-off-by: Casey Callendrello <c1@caseyc.net>
2024-06-24 18:32:57 +02:00
Benjamin Leggett 425425837e
libcni: Support subdirectory-based plugin loading (#928)
Signed-off-by: Benjamin Leggett <benjamin.leggett@solo.io>
2024-06-17 12:22:05 -04:00
Benjamin Leggett c0cc785dbc
Use type aliases to hint deprecation for old API types (#928)
Signed-off-by: Benjamin Leggett <benjamin.leggett@solo.io>
2024-06-17 11:45:13 -04:00
Benjamin Leggett 9f73d4da82
SPEC: #928 support non-inlined plugin loading
Signed-off-by: Benjamin Leggett <benjamin.leggett@solo.io>
2024-06-17 11:19:33 -04:00
Tomofumi Hayashi e82d9969cf
Merge pull request #1098 from squeed/gc-shared-warning
SPEC: add warning about preserving shared resources for GC
2024-06-18 00:15:44 +09:00
Casey Callendrello 2c926b5f17 SPEC: add warning about preserving shared resources for GC
For plugins that may share resources or resource pools across networks,
we should make it explicitly clear that GC must only clean up resources
known to be owned by the calling network.

Signed-off-by: Casey Callendrello <c1@caseyc.net>
2024-06-10 18:06:29 +02:00
Casey Callendrello ed014fe9dc
Merge pull request #1089 from Icarus9913/chore/darwin-ns
supplement ns building files for darwin OS
2024-06-10 17:23:41 +02:00
Casey Callendrello db70b6d343
Merge pull request #1094 from containernetworking/dependabot/go_modules/golang-a90ed34d00
build(deps): bump github.com/onsi/ginkgo/v2 from 2.17.3 to 2.19.0 in the golang group
2024-06-10 17:23:26 +02:00
Casey Callendrello fac588ba52
Merge pull request #1093 from containernetworking/dependabot/docker/dot-github/actions/retest-action/alpine-3.20
build(deps): bump alpine from 3.19 to 3.20 in /.github/actions/retest-action
2024-06-10 17:23:04 +02:00
Casey Callendrello 6e57c2ee5d
Merge pull request #1097 from s1061123/fix/1096
Fix faulty json.Marshal behavior for embeds types.NetConf
2024-06-10 17:20:05 +02:00
Tomofumi Hayashi 499596af1b
Merge branch 'main' into fix/1096 2024-06-11 00:06:55 +09:00
Casey Callendrello 73debca5ce
Merge pull request #1090 from squeed/gc-improvements
Spec, libcni: add disableGC flag
2024-06-10 17:06:14 +02:00
Tomofumi Hayashi 9d422db72c Fix faulty json.Marshal behavior for embeds types.NetConf
Signed-off-by: Tomofumi Hayashi <tohayash@redhat.com>
2024-06-04 03:31:55 +09:00
Casey Callendrello c28331408d libcni: implement disableGC
This allows administrators to disable GC behavior.

Signed-off-by: Casey Callendrello <c1@caseyc.net>
2024-06-03 16:58:20 +02:00
Casey Callendrello 1125b83816 Spec: add disableGC flag
This flag allows administrators to disable garbage collection for
exceptional circumstances.

Signed-off-by: Casey Callendrello <c1@caseyc.net>
2024-06-03 16:51:40 +02:00
Casey Callendrello 529bccb346 GetCachedAttachments should ignore missing directories
If there's no cache dir, there are no cached attachments to return. No
sense in propagating that error.

Signed-off-by: Casey Callendrello <c1@caseyc.net>
2024-06-03 16:51:40 +02:00
dependabot[bot] 267db7092b
build(deps): bump github.com/onsi/ginkgo/v2 in the golang group
Bumps the golang group with 1 update: [github.com/onsi/ginkgo/v2](https://github.com/onsi/ginkgo).


Updates `github.com/onsi/ginkgo/v2` from 2.17.3 to 2.19.0
- [Release notes](https://github.com/onsi/ginkgo/releases)
- [Changelog](https://github.com/onsi/ginkgo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/ginkgo/compare/v2.17.3...v2.19.0)

---
updated-dependencies:
- dependency-name: github.com/onsi/ginkgo/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: golang
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-03 00:57:15 +00:00
Casey Callendrello cebd7dff3a
Merge pull request #1095 from maiqueb/always-attempt-to-cleanup-cache-on-list-delete
libcni: always delete the cache on conflist for CNI DEL
2024-05-28 10:26:17 +02:00
Miguel Duarte Barroso 6ef571535d libcni: always delete the cache on conflist for CNI DEL
This aligns the call with `DelNetwork`, and allows CRIO tests to bump -
[0].

[0] - https://github.com/cri-o/cri-o/pull/8207#issuecomment-2129937741

Signed-off-by: Miguel Duarte Barroso <mdbarroso@redhat.com>
2024-05-27 17:52:09 +02:00
dependabot[bot] 02ffd013fe
build(deps): bump alpine in /.github/actions/retest-action
Bumps alpine from 3.19 to 3.20.

---
updated-dependencies:
- dependency-name: alpine
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-27 01:00:56 +00:00
Icarus Wu 1e7858f987
supplement ns building files for darwin os
Signed-off-by: Icarus Wu <icaruswu66@qq.com>
2024-05-13 22:43:34 +08:00
Casey Callendrello c04330ee9e
Merge pull request #1063 from austinvazquez/update-golangci-lint-action-package
Update golangci-lint to v1.57.1
2024-05-13 16:41:31 +02:00
Austin Vazquez 52babb0dd3 Update golangci-lint to v1.57.1
Signed-off-by: Austin Vazquez <macedonv@amazon.com>
2024-05-13 16:40:38 +02:00
Casey Callendrello 9f32fa36f7
Merge pull request #1087 from containernetworking/dependabot/github_actions/golangci/golangci-lint-action-6
build(deps): bump golangci/golangci-lint-action from 4 to 6
2024-05-13 16:37:27 +02:00
dependabot[bot] f5e23f52c0 build(deps): bump golangci/golangci-lint-action from 4 to 6
Bumps [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action) from 4 to 6.
- [Release notes](https://github.com/golangci/golangci-lint-action/releases)
- [Commits](https://github.com/golangci/golangci-lint-action/compare/v4...v6)

---
updated-dependencies:
- dependency-name: golangci/golangci-lint-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-13 16:37:19 +02:00
Casey Callendrello 85241009c7
Merge pull request #1088 from containernetworking/dependabot/go_modules/golang-4bdc54f992
build(deps): bump the golang group with 2 updates
2024-05-13 16:36:35 +02:00
dependabot[bot] e34763a7ec
build(deps): bump the golang group with 2 updates
Bumps the golang group with 2 updates: [github.com/onsi/ginkgo/v2](https://github.com/onsi/ginkgo) and [github.com/onsi/gomega](https://github.com/onsi/gomega).


Updates `github.com/onsi/ginkgo/v2` from 2.17.2 to 2.17.3
- [Release notes](https://github.com/onsi/ginkgo/releases)
- [Changelog](https://github.com/onsi/ginkgo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/ginkgo/compare/v2.17.2...v2.17.3)

Updates `github.com/onsi/gomega` from 1.33.0 to 1.33.1
- [Release notes](https://github.com/onsi/gomega/releases)
- [Changelog](https://github.com/onsi/gomega/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/gomega/compare/v1.33.0...v1.33.1)

---
updated-dependencies:
- dependency-name: github.com/onsi/ginkgo/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: golang
- dependency-name: github.com/onsi/gomega
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: golang
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-13 01:01:31 +00:00
Casey Callendrello f85b8fe31f
Merge pull request #1084 from containernetworking/dependabot/go_modules/golang-7f8ea5be5c
build(deps): bump the golang group across 1 directory with 2 updates
2024-05-06 16:48:11 +02:00
dependabot[bot] 3d7be1bae9
build(deps): bump the golang group across 1 directory with 2 updates
Bumps the golang group with 1 update in the / directory: [github.com/onsi/ginkgo/v2](https://github.com/onsi/ginkgo).


Updates `github.com/onsi/ginkgo/v2` from 2.17.1 to 2.17.2
- [Release notes](https://github.com/onsi/ginkgo/releases)
- [Changelog](https://github.com/onsi/ginkgo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/ginkgo/compare/v2.17.1...v2.17.2)

Updates `github.com/onsi/gomega` from 1.32.0 to 1.33.0
- [Release notes](https://github.com/onsi/gomega/releases)
- [Changelog](https://github.com/onsi/gomega/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/gomega/compare/v1.32.0...v1.33.0)

---
updated-dependencies:
- dependency-name: github.com/onsi/ginkgo/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: golang
- dependency-name: github.com/onsi/gomega
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: golang
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-29 00:39:11 +00:00
Casey Callendrello df52e9460d
Merge pull request #1082 from squeed/v1.2-release
spec: remove "-dev" tag
2024-04-15 17:47:08 +02:00
Casey Callendrello 047255eeed spec: remove "-dev" tag
This is ready for release :-)

Signed-off-by: Casey Callendrello <c1@caseyc.net>
2024-04-15 17:39:18 +02:00
Casey Callendrello 0137b32398
Merge pull request #1054 from MikeZappa87/feature/addversiontocni
Add Version to CNI interface
2024-04-08 17:16:56 +02:00
Michael Zappa dd65e2d527 add version to cni interface
Signed-off-by: Michael Zappa <michael.zappa@gmail.com>
2024-04-08 17:09:10 +02:00
Casey Callendrello bbae2202a6
Merge pull request #1080 from s1061123/cnitool-1.1.0-support
cnitool CNI 1.1.0 support
2024-04-05 14:00:25 +02:00
Tomofumi Hayashi 0036b628e1 cnitool CNI 1.1.0 support
This change make cnitool CNI v1.1.0 support. This also addresses
to fix error crash in case of GC with empty attachments.

Signed-off-by: Tomofumi Hayashi <tohayash@redhat.com>
2024-04-03 23:49:46 +09:00
Casey Callendrello 83287db830
Merge pull request #1078 from henry118/multierror
use errors package in golang v20 to join multiple errors
2024-03-27 16:40:23 +01:00
Henry Wang a67dabb3d0 use errors package in golang v20 to join multiple errors
Signed-off-by: Henry Wang <henwang@amazon.com>
2024-03-26 16:57:40 +00:00
Casey Callendrello 951d8d0e05
Merge pull request #1077 from containernetworking/dependabot/go_modules/golang-74105ccef4
build(deps): bump the golang group with 2 updates
2024-03-25 15:35:32 +01:00
dependabot[bot] f846cb72b0
build(deps): bump the golang group with 2 updates
Bumps the golang group with 2 updates: [github.com/onsi/ginkgo/v2](https://github.com/onsi/ginkgo) and [github.com/onsi/gomega](https://github.com/onsi/gomega).


Updates `github.com/onsi/ginkgo/v2` from 2.13.2 to 2.17.1
- [Release notes](https://github.com/onsi/ginkgo/releases)
- [Changelog](https://github.com/onsi/ginkgo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/ginkgo/compare/v2.13.2...v2.17.1)

Updates `github.com/onsi/gomega` from 1.30.0 to 1.32.0
- [Release notes](https://github.com/onsi/gomega/releases)
- [Changelog](https://github.com/onsi/gomega/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/gomega/compare/v1.30.0...v1.32.0)

---
updated-dependencies:
- dependency-name: github.com/onsi/ginkgo/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: golang
- dependency-name: github.com/onsi/gomega
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: golang
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-25 14:28:03 +00:00
Casey Callendrello e4b261e1a2
Merge pull request #1073 from henry118/updatego
Update golang to active supported version
2024-03-25 15:27:11 +01:00
Henry Wang 829ae61c58 Update golang to active supported version
Signed-off-by: Henry Wang <henwang@amazon.com>
2024-03-19 20:24:14 +00:00
Tomofumi Hayashi 86c37cb0a2
Merge pull request #1071 from squeed/minor-api-fixes
Minor CNI v1.1 api fixes
2024-03-19 11:36:33 +01:00
Casey Callendrello 56f11c6c0c types: add ValidAttachments to "base" NetConf struct
Since every plugin needs to deserialize this, it should be in the
standard network configuration struct.

Signed-off-by: Casey Callendrello <c1@caseyc.net>
2024-03-18 12:22:01 +01:00
Casey Callendrello 8eb1091d78 pkg/invoke: add DelegateStatus, DelegateGC methods
Signed-off-by: Casey Callendrello <c1@caseyc.net>
2024-03-18 12:22:01 +01:00
Casey Callendrello a448e71e98
Merge pull request #961 from squeed/cleanup-releasing
Docs: remove obsolete information about building release artifacts
2024-03-17 21:37:38 +01:00
Casey Callendrello 2e9688f3b4 Docs: remove obsolete information about building release artifacts
Now that plugins live in a different repository, we don't need these old
files. We don't actuall build any release binaries.

Signed-off-by: Casey Callendrello <c1@caseyc.net>
2024-03-17 21:32:20 +01:00
Casey Callendrello 1c8da137b2
Merge pull request #1060 from SchSeba/add_mtu
Add MTU to CNI result
2024-03-15 16:11:10 +01:00
Sebastian Sch c45adb66fe Add MTU to CNI result
This commit allow CNIs to expose the MTU of interface

Signed-off-by: Sebastian Sch <sebassch@gmail.com>
2024-03-14 14:19:38 +02:00
Casey Callendrello 563cf56c27
Merge pull request #1072 from henry118/invalidcache
tolerate invalid cni caches for deletion
2024-03-12 13:27:38 +01:00
Henry Wang 2d04079cfe tolerate invalid cni caches for deletion
Signed-off-by: Henry Wang <henwang@amazon.com>
2024-03-07 23:12:45 +00:00
Casey Callendrello 845a73789a
Merge pull request #1068 from LionelJouin/scope
Add Scope property for routes
2024-03-05 10:53:32 +01:00
Lionel Jouin 00576a2e55 Scope property as int pointer
Signed-off-by: Lionel Jouin <lionel.jouin@est.tech>
2024-03-04 18:34:55 +01:00
Lionel Jouin a4d4a0d1ff Add Scope property for routes
Fixes: #598
Signed-off-by: Lionel Jouin <lionel.jouin@est.tech>
2024-03-04 18:32:23 +01:00
Casey Callendrello 06d9dec402
Merge pull request #1062 from LionelJouin/tableid
Add table ID property for routes
2024-03-04 17:53:01 +01:00
Tomofumi Hayashi d9f3a7bb2b
Merge branch 'main' into tableid 2024-03-05 01:45:57 +09:00
Casey Callendrello c822f1331b
Merge pull request #1069 from MikeZappa87/spec/socketpci
Add SocketPath/PciID to Interface struct
2024-03-04 17:41:10 +01:00
Michael Zappa 163db33480 Add socket path and pciid to CNI interface result
Signed-off-by: Michael Zappa <michaelzappa@microsoft.com>
2024-03-04 09:23:25 -07:00
Lionel Jouin 94fc2b1545 Table ID property as int pointer
Signed-off-by: Lionel Jouin <lionel.jouin@est.tech>
2024-02-19 18:58:35 +01:00
Lionel Jouin 3c4079b3db Add table ID property for routes
Fixes #1061

Signed-off-by: Lionel Jouin <lionel.jouin@est.tech>
2024-02-02 15:49:20 +01:00
Casey Callendrello b62753aa2b
Merge pull request #1039 from s1061123/json-remove-dns
Add Marshal function in Result/NetConf to omit empty value
2024-01-08 17:47:25 +01:00
Casey Callendrello 1557aeb57c
Merge pull request #1047 from containernetworking/dependabot/github_actions/actions/setup-go-5
build(deps): bump actions/setup-go from 4 to 5
2024-01-08 17:00:11 +01:00
Casey Callendrello dc24f70105
Merge pull request #1048 from containernetworking/dependabot/docker/dot-github/actions/retest-action/alpine-3.19
build(deps): bump alpine from 3.18 to 3.19 in /.github/actions/retest-action
2024-01-08 16:59:21 +01:00
Casey Callendrello 8ddb290852
Merge pull request #1049 from containernetworking/dependabot/go_modules/plugins/debug/github.com/containernetworking/plugins-1.4.0
build(deps): bump github.com/containernetworking/plugins from 1.2.0 to 1.4.0 in /plugins/debug
2024-01-08 16:58:55 +01:00
Tomofumi Hayashi d1276f1ed4 Add Marshal function in Result/NetConf to omit empty value
This change fix to avoid empty DNS field in NetConf/Result
if DNS is empty.

Signed-off-by: Tomofumi Hayashi <tohayash@redhat.com>
2023-12-19 01:02:21 +09:00
Casey Callendrello 66c292a7c5
Merge pull request #1041 from alopintsev/add-route-attributes
Add route attributes - MTU, AdvMSS, Priority
2023-12-18 16:58:10 +01:00
Casey Callendrello 2317778d4d
Merge pull request #1010 from squeed/cni-versions
spec, libcni: add cniVersions field in CNI configurations
2023-12-11 18:00:26 +01:00
Casey Callendrello a6a989119a libcni: implement version negotiation
This implements the `cniVersions` field in the network configuration
list. It allows for CNI plugins to specify that they support multiple
versions, and the runtime may select the highest version it supports.

Signed-off-by: Casey Callendrello <c1@caseyc.net>
2023-12-11 17:48:56 +01:00
Casey Callendrello 1362169a1f go get github.com/Masterminds/semver/v3
Signed-off-by: Casey Callendrello <c1@caseyc.net>
2023-12-11 17:39:41 +01:00
Casey Callendrello f51f8eafe2 SPEC: add version negotiation
This adds a new top-level configuration field, `cniVersions`, where
network configurations can indicate support for more than one version.

This is to enable easy upgrading of CNI versions in plugins without
lock-step updates to the runtime.

Signed-off-by: Casey Callendrello <c1@caseyc.net>
2023-12-11 17:39:41 +01:00
dependabot[bot] f8d7b81f43
build(deps): bump github.com/containernetworking/plugins
Bumps [github.com/containernetworking/plugins](https://github.com/containernetworking/plugins) from 1.2.0 to 1.4.0.
- [Release notes](https://github.com/containernetworking/plugins/releases)
- [Commits](https://github.com/containernetworking/plugins/compare/v1.2.0...v1.4.0)

---
updated-dependencies:
- dependency-name: github.com/containernetworking/plugins
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-11 01:10:55 +00:00
dependabot[bot] 6bac4ee028
build(deps): bump alpine in /.github/actions/retest-action
Bumps alpine from 3.18 to 3.19.

---
updated-dependencies:
- dependency-name: alpine
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-11 00:47:34 +00:00
dependabot[bot] 91db252b9d
build(deps): bump actions/setup-go from 4 to 5
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 4 to 5.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](https://github.com/actions/setup-go/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-11 00:45:33 +00:00
Casey Callendrello d2bbac8e63
Merge pull request #1045 from squeed/ref-cleanup
cleanup: update references, make links relative
2023-12-05 17:34:52 +01:00
Casey Callendrello 0e4c0e225b cleanup: update references, make links relative
Go through and update some incorrect / obsolete links and references.

No code changes.

Signed-off-by: Casey Callendrello <c1@caseyc.net>
2023-12-05 12:50:31 +01:00
Casey Callendrello 2cec989c82
Merge pull request #1030 from squeed/status-impl
libcni, skel: implement STATUS
2023-12-04 16:58:37 +01:00
Casey Callendrello 4caff13753 libcni, skel: implement STATUS
This adds an implementation of STATUS to libcni and skel, along with
tests.

Signed-off-by: Casey Callendrello <c1@caseyc.net>
2023-12-04 16:49:48 +01:00
Casey Callendrello 9c19f13738
Merge pull request #1003 from squeed/spec-status
spec: STATUS wording
2023-12-04 16:48:40 +01:00
Casey Callendrello d654dde532
Merge pull request #1044 from containernetworking/dependabot/go_modules/golang-15a4b9dd4e
build(deps): bump the golang group with 1 update
2023-12-04 12:02:07 +01:00
dependabot[bot] b9a9324609
build(deps): bump the golang group with 1 update
Bumps the golang group with 1 update: [github.com/onsi/ginkgo/v2](https://github.com/onsi/ginkgo).

- [Release notes](https://github.com/onsi/ginkgo/releases)
- [Changelog](https://github.com/onsi/ginkgo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/ginkgo/compare/v2.13.1...v2.13.2)

---
updated-dependencies:
- dependency-name: github.com/onsi/ginkgo/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: golang
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-04 00:04:37 +00:00
Alexander Lopintsev 4a0bccb249 describe mtu, advmss, priority attributes of route object in specification
Signed-off-by: Alexander Lopintsev <alex@fort.plus>
2023-11-28 18:25:07 +03:00
Alexander Lopintsev efffbeeef4 Add route attributes - MTU, AdvMSS, Priority
Signed-off-by: Alexander Lopintsev <alex@fort.plus>
2023-11-21 23:45:49 +03:00
Dan Williams 93878e6530
Merge pull request #1038 from s1061123/fix-netnsoverride
Fix if condition for netns override
2023-11-20 10:08:23 -06:00
Tomofumi Hayashi 26cfdbe95b
Merge branch 'main' into fix-netnsoverride 2023-11-20 22:58:42 +09:00
Casey Callendrello cf9ca9e003
Merge pull request #1040 from containernetworking/dependabot/go_modules/golang-027c04c118
build(deps): bump the golang group with 2 updates
2023-11-15 10:08:39 +01:00
Tomofumi Hayashi baaee10d93
Merge branch 'main' into fix-netnsoverride 2023-11-14 01:04:26 +09:00
dependabot[bot] cd1b855eae
build(deps): bump the golang group with 2 updates
Bumps the golang group with 2 updates: [github.com/onsi/ginkgo/v2](https://github.com/onsi/ginkgo) and [github.com/onsi/gomega](https://github.com/onsi/gomega).


Updates `github.com/onsi/ginkgo/v2` from 2.13.0 to 2.13.1
- [Release notes](https://github.com/onsi/ginkgo/releases)
- [Changelog](https://github.com/onsi/ginkgo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/ginkgo/compare/v2.13.0...v2.13.1)

Updates `github.com/onsi/gomega` from 1.29.0 to 1.30.0
- [Release notes](https://github.com/onsi/gomega/releases)
- [Changelog](https://github.com/onsi/gomega/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/gomega/compare/v1.29.0...v1.30.0)

---
updated-dependencies:
- dependency-name: github.com/onsi/ginkgo/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: golang
- dependency-name: github.com/onsi/gomega
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: golang
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-13 00:29:26 +00:00
Casey Callendrello c18965d032
Merge pull request #1019 from Yejining/fix/spec-typo
SPEC: fix misspelled word
2023-11-06 17:34:19 +01:00
Casey Callendrello 911855c357
Merge pull request #1031 from mmorel-35/replace
use replace directive for github.com/containernetworking/cni
2023-11-06 17:33:20 +01:00
Casey Callendrello 8640c3ff16
Merge pull request #990 from containernetworking/dependabot/docker/dot-github/actions/retest-action/alpine-3.18
build(deps): bump alpine from 3.17 to 3.18 in /.github/actions/retest-action
2023-11-06 17:33:10 +01:00
Casey Callendrello 58eb836866
Merge pull request #1015 from jayanthvn/main
Add AWS VPC CNI for 3P list
2023-11-06 17:32:52 +01:00
Tomofumi Hayashi 6ed65ac99d Fix if condition for netns override
Signed-off-by: Tomofumi Hayashi <tohayash@redhat.com>
2023-11-06 17:32:01 +01:00
Matthieu MOREL f67e207467 use replace directive for github.com/containernetworking/cni
Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>
2023-11-06 17:27:47 +01:00
Casey Callendrello 17331add54
Merge pull request #1036 from containernetworking/dependabot/go_modules/golang-a9a3abe015
build(deps): bump the golang group with 1 update
2023-11-06 17:09:18 +01:00
dependabot[bot] cfdc39b16b
build(deps): bump the golang group with 1 update
Bumps the golang group with 1 update: [github.com/onsi/gomega](https://github.com/onsi/gomega).

- [Release notes](https://github.com/onsi/gomega/releases)
- [Changelog](https://github.com/onsi/gomega/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/gomega/compare/v1.28.0...v1.29.0)

---
updated-dependencies:
- dependency-name: github.com/onsi/gomega
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: golang
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-30 00:48:29 +00:00
Casey Callendrello 509c4908cf spec: STATUS wording
This adds a new verb, STATUS, which indicates whether or not a plugin is
ready to accept ADD requests. Runtimes may choose to use the STATUS
mechanism to determine if a network is ready.

Signed-off-by: Casey Callendrello <c1@caseyc.net>
2023-10-23 11:40:38 +02:00
Casey Callendrello 5968d61a1e
Merge pull request #1020 from containernetworking/dependabot/github_actions/actions/checkout-4
build(deps): bump actions/checkout from 3 to 4
2023-10-23 10:56:01 +02:00
Casey Callendrello 94fd332ecd
Merge pull request #1029 from containernetworking/dependabot/go_modules/golang-91599c5cf7
build(deps): bump the golang group with 2 updates
2023-10-23 10:55:03 +02:00
dependabot[bot] 0c68fe12dc
build(deps): bump the golang group with 2 updates
Bumps the golang group with 2 updates: [github.com/onsi/ginkgo/v2](https://github.com/onsi/ginkgo) and [github.com/onsi/gomega](https://github.com/onsi/gomega).


Updates `github.com/onsi/ginkgo/v2` from 2.11.0 to 2.13.0
- [Release notes](https://github.com/onsi/ginkgo/releases)
- [Changelog](https://github.com/onsi/ginkgo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/ginkgo/compare/v2.11.0...v2.13.0)

Updates `github.com/onsi/gomega` from 1.27.10 to 1.28.0
- [Release notes](https://github.com/onsi/gomega/releases)
- [Changelog](https://github.com/onsi/gomega/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/gomega/compare/v1.27.10...v1.28.0)

---
updated-dependencies:
- dependency-name: github.com/onsi/ginkgo/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: golang
- dependency-name: github.com/onsi/gomega
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: golang
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-23 00:58:18 +00:00
Casey Callendrello a6eaa01146
Merge pull request #987 from mmorel-35/patch-1
Define Makefile with golangci-lint
2023-10-18 16:45:32 +02:00
Casey Callendrello 58553d6cdb
Merge pull request #994 from mmorel-35/debug
Upgrade debug module go version to 1.18
2023-10-18 16:37:57 +02:00
Casey Callendrello c9fdf812d2
Merge branch 'main' into patch-1 2023-10-18 16:37:14 +02:00
Casey Callendrello 82b44c5282
Merge pull request #988 from mmorel-35/linter-gocritic
enable gocritic linter
2023-10-18 16:35:34 +02:00
Matthieu MOREL 29ba516d90 Upgrade debug module go version to 1.18
Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>
2023-10-18 07:44:33 +02:00
Matthieu MOREL b7d75ff4da Define Makefile with golangci-lint
As govet and gofmt are now handled by golangci-lint, there is no need to apply them here.

Install golangci-lint

Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>
2023-10-18 07:44:03 +02:00
Matthieu MOREL 4683716a59 enable gocritic linter
Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>
2023-10-18 07:43:38 +02:00
Casey Callendrello 2cfe79728d
Merge pull request #1028 from squeed/dependabot-batch
dependabot: batch updates
2023-10-17 13:02:29 +02:00
Casey Callendrello 91629d5751 dependabot: batch updates
This combines go pkg updates in to a weekly branch, saving bother and
rebase time.

Signed-off-by: Casey Callendrello <c1@caseyc.net>
2023-10-17 12:38:53 +02:00
Casey Callendrello b15c73737e
Merge pull request #1022 from henry118/gc
implement GC keyword for CNI spec 1.1
2023-10-16 17:27:07 +02:00
Henry Wang e86c9ca58f resume and complete gc impl
Signed-off-by: Henry Wang <henwang@amazon.com>
2023-10-13 20:46:19 +00:00
Casey Callendrello 02155e0c8c skel: add gc support
Signed-off-by: Casey Callendrello <c1@caseyc.net>
2023-10-13 20:46:19 +00:00
Casey Callendrello d9f1e675b7 wip GC implementation -- spec
Signed-off-by: Casey Callendrello <c1@caseyc.net>
2023-10-13 20:46:19 +00:00
Casey Callendrello 8fdefe9412 noop: support command log file
Signed-off-by: Casey Callendrello <c1@caseyc.net>
2023-10-13 20:46:19 +00:00
Casey Callendrello 743a44d404
Merge pull request #1024 from s1061123/add-maintainer
Add Tomofumi Hayashi as a CNI maintainer
2023-10-02 17:28:04 +02:00
Tomofumi Hayashi 004b09b6a6 Add Tomofumi Hayashi as a CNI maintainer
Signed-off-by: Tomofumi Hayashi <tohayash@redhat.com>
2023-09-26 00:14:20 +09:00
Jayanth Varavani 902534de61
Merge branch 'main' into main 2023-09-14 22:28:17 -07:00
dependabot[bot] 11077bcc03
build(deps): bump actions/checkout from 3 to 4
Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-11 00:42:05 +00:00
Yejin Kim 0021b93858 SPEC: fix misspelled word
Signed-off-by: Yejin Kim <kimyejin.kr@gmail.com>
2023-09-08 16:22:10 +00:00
Dan Williams f6506e215f
Merge pull request #981 from squeed/spec-gc
SPEC: add wording for GC
2023-08-07 10:56:19 -05:00
Casey Callendrello 3072cfe946 SPEC: add wording for GC verb
GC, or garbage collection, is intended as a way for runtimes to tell
plugins about valid attachments, enabling them to delete any leaked or
stale resources.

Signed-off-by: Casey Callendrello <c1@caseyc.net>
2023-08-07 17:49:53 +02:00
Dan Williams 3bbe370e68
Merge pull request #1013 from haiyux/fix/k8sUrl
fix kubernetes document url in readme
2023-08-07 10:37:19 -05:00
Dan Williams 44a6eef2b2
Merge pull request #1011 from squeed/fix-version-registration
pkg/types: explicitly import all versions
2023-08-07 10:35:28 -05:00
Jayanth Varavani 837171b012 Add AWS VPC CNI for 3P list
Signed-off-by: Jayanth Varavani <1111446+jayanthvn@users.noreply.github.com>
2023-08-03 17:35:00 +00:00
haiyux 215259b17d fix kubernetes document url in readme
Signed-off-by: haiyux <haiyux@foxmail.com>
2023-07-28 15:55:12 +08:00
Casey Callendrello 4a096f6be2 pkg/types: explicitly import all versions
There was a bug in which some versions didn't register their creation
function, thus leaving some types un-decodeable. Fix that by explicitly
importing all known versions.

Signed-off-by: Casey Callendrello <c1@caseyc.net>
2023-07-20 10:50:22 +02:00
Casey Callendrello ca96f4ca96
Merge pull request #967 from squeed/setup-1.1
Prepare for spec version v1.1
2023-07-14 00:28:22 +02:00
Casey Callendrello a899051e44 libcni: add version v1.1.0
This adds support for CNI spec v1.1.0. Since there are no result type
changes expected, this means that we can use the existing v1.0.0 types.
That takes a bit of plumbing to decouple the type from the version.

Signed-off-by: Casey Callendrello <c1@caseyc.net>
2023-07-13 22:57:00 +02:00
Casey Callendrello c768dcb129 SPEC: bump version to v1.1.0-dev
Signed-off-by: Casey Callendrello <c1@caseyc.net>
2023-07-13 22:57:00 +02:00
Dan Williams 622537633d
Merge pull request #1001 from containernetworking/dependabot/go_modules/github.com/onsi/ginkgo/v2-2.11.0
build(deps): bump github.com/onsi/ginkgo/v2 from 2.9.7 to 2.11.0
2023-07-10 10:23:54 -05:00
dependabot[bot] e34f9d2bff
build(deps): bump github.com/onsi/ginkgo/v2 from 2.9.7 to 2.11.0
Bumps [github.com/onsi/ginkgo/v2](https://github.com/onsi/ginkgo) from 2.9.7 to 2.11.0.
- [Release notes](https://github.com/onsi/ginkgo/releases)
- [Changelog](https://github.com/onsi/ginkgo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/ginkgo/compare/v2.9.7...v2.11.0)

---
updated-dependencies:
- dependency-name: github.com/onsi/ginkgo/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-19 01:26:35 +00:00
Dan Williams e255525e4d
Merge pull request #995 from containernetworking/dependabot/go_modules/github.com/onsi/ginkgo/v2-2.9.7
build(deps): bump github.com/onsi/ginkgo/v2 from 2.9.4 to 2.9.7
2023-06-05 10:30:24 -05:00
dependabot[bot] 2161bf8c24
build(deps): bump github.com/onsi/ginkgo/v2 from 2.9.4 to 2.9.7
Bumps [github.com/onsi/ginkgo/v2](https://github.com/onsi/ginkgo) from 2.9.4 to 2.9.7.
- [Release notes](https://github.com/onsi/ginkgo/releases)
- [Changelog](https://github.com/onsi/ginkgo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/ginkgo/compare/v2.9.4...v2.9.7)

---
updated-dependencies:
- dependency-name: github.com/onsi/ginkgo/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-05 01:23:59 +00:00
dependabot[bot] a8fdbf9c40
build(deps): bump alpine in /.github/actions/retest-action
Bumps alpine from 3.17 to 3.18.

---
updated-dependencies:
- dependency-name: alpine
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-15 01:22:35 +00:00
Dan Williams dc0779e8ce
Merge pull request #989 from containernetworking/dependabot/go_modules/github.com/onsi/ginkgo/v2-2.9.4
build(deps): bump github.com/onsi/ginkgo/v2 from 2.9.2 to 2.9.4
2023-05-08 10:55:03 -05:00
dependabot[bot] b6608f8c87
build(deps): bump github.com/onsi/ginkgo/v2 from 2.9.2 to 2.9.4
Bumps [github.com/onsi/ginkgo/v2](https://github.com/onsi/ginkgo) from 2.9.2 to 2.9.4.
- [Release notes](https://github.com/onsi/ginkgo/releases)
- [Changelog](https://github.com/onsi/ginkgo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/ginkgo/compare/v2.9.2...v2.9.4)

---
updated-dependencies:
- dependency-name: github.com/onsi/ginkgo/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-08 01:08:09 +00:00
dependabot[bot] 15612fccaa build(deps): bump github.com/onsi/ginkgo/v2 from 2.8.0 to 2.9.2
Bumps [github.com/onsi/ginkgo/v2](https://github.com/onsi/ginkgo) from 2.8.0 to 2.9.2.
- [Release notes](https://github.com/onsi/ginkgo/releases)
- [Changelog](https://github.com/onsi/ginkgo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/ginkgo/compare/v2.8.0...v2.9.2)

---
updated-dependencies:
- dependency-name: github.com/onsi/ginkgo/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-21 13:22:18 +02:00
dependabot[bot] 10ec024835 build(deps): bump actions/setup-go from 3 to 4
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 3 to 4.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](https://github.com/actions/setup-go/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-20 14:22:01 +02:00
Casey Callendrello cfb0b54407
Merge pull request #954 from containernetworking/dependabot/go_modules/github.com/vishvananda/netns-0.0.4
build(deps): bump github.com/vishvananda/netns from 0.0.2 to 0.0.4
2023-04-20 14:21:23 +02:00
Casey Callendrello c9cfcaa2c7
Merge pull request #950 from containernetworking/dependabot/go_modules/plugins/debug/github.com/containernetworking/plugins-1.2.0
build(deps): bump github.com/containernetworking/plugins from 1.1.1 to 1.2.0 in /plugins/debug
2023-04-20 14:20:51 +02:00
dependabot[bot] db718fc6d5
build(deps): bump github.com/containernetworking/plugins
Bumps [github.com/containernetworking/plugins](https://github.com/containernetworking/plugins) from 1.1.1 to 1.2.0.
- [Release notes](https://github.com/containernetworking/plugins/releases)
- [Commits](https://github.com/containernetworking/plugins/compare/v1.1.1...v1.2.0)

---
updated-dependencies:
- dependency-name: github.com/containernetworking/plugins
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-20 11:30:55 +00:00
Casey Callendrello 4093160909
Merge pull request #976 from mmorel-35/golangci-lint
enable more linters
2023-04-20 13:25:32 +02:00
Matthieu MOREL 9302e5ff67 enable more linters
Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>
2023-04-20 13:08:34 +02:00
Casey Callendrello 9a71f523e2
Merge pull request #984 from weizhoublue/pr/welan/spiderpool
3rd party plugins - spiderpool
2023-04-20 13:07:29 +02:00
weizhou.lan@daocloud.io 05f80e4fd9 3rd party plugins - spiderpool
Signed-off-by: weizhou.lan@daocloud.io <weizhou.lan@daocloud.io>
2023-03-29 21:35:21 +08:00
Mike Zappa f8aa587bba
Merge pull request #963 from henry118/cachelist
Porting over getCachedNetworkInfo routine from cri-o
2023-03-14 13:23:13 -06:00
Henry Wang aef15f60b3 Porting over getCachedNetworkInfo routine from cri-o
Signed-off-by: Henry Wang <henwang@amazon.com>
2023-03-13 16:05:01 +00:00
Casey Callendrello 6626820a52
Merge pull request #973 from squeed/add-meeting
add meeting link to README
2023-03-06 16:33:41 +01:00
Casey Callendrello 435fcb1414 add meeting link to README
Signed-off-by: Casey Callendrello <c1@caseyc.net>
2023-03-06 16:32:57 +01:00
Casey Callendrello 52b1cb11a5
Merge pull request #958 from mmorel-35/errorlint
ci(lint): enable errorlint linter
2023-02-20 18:05:01 +01:00
Matthieu MOREL d4c7848c2a ci(lint): enable errorlint linter
Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>
2023-02-14 20:22:23 +01:00
Dan Williams d42556829e
Merge pull request #962 from yanggangtony/clean-notes
fix wrong notes for ValidateInterfaceName()
2023-02-13 10:14:48 -06:00
yanggang 1ffb655895
fix wrong notes for ValidateInterfaceName()
Signed-off-by: yanggang <gang.yang@daocloud.io>
2023-02-10 09:30:07 +08:00
Casey Callendrello cb0d26535b
Merge pull request #936 from aditighag/pr/aditighag/add-cgroup-path-to-cni-args
Extend capabilities with cgroup path
2023-02-09 11:44:20 +01:00
Aditi Ghag 420e5949b0 Extend capabilities with cgroup path
Container runtimes can pass pod cgroup path
when requested by CNI plugins.

Details around the use cases for this field are
documented - https://github.com/kubernetes/kubernetes/issues/113342.

Signed-off-by: Aditi Ghag <aditi@cilium.io>
2023-02-08 17:54:04 +01:00
dependabot[bot] 024c57f43f
build(deps): bump github.com/vishvananda/netns from 0.0.2 to 0.0.4
Bumps [github.com/vishvananda/netns](https://github.com/vishvananda/netns) from 0.0.2 to 0.0.4.
- [Release notes](https://github.com/vishvananda/netns/releases)
- [Commits](https://github.com/vishvananda/netns/compare/v0.0.2...v0.0.4)

---
updated-dependencies:
- dependency-name: github.com/vishvananda/netns
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-06 16:11:09 +00:00
Dan Williams 0821ff55fc
Merge pull request #959 from containernetworking/dependabot/go_modules/github.com/onsi/ginkgo/v2-2.8.0
build(deps): bump github.com/onsi/ginkgo/v2 from 2.7.0 to 2.8.0
2023-02-06 10:08:01 -06:00
dependabot[bot] 121798a08d
build(deps): bump github.com/onsi/ginkgo/v2 from 2.7.0 to 2.8.0
Bumps [github.com/onsi/ginkgo/v2](https://github.com/onsi/ginkgo) from 2.7.0 to 2.8.0.
- [Release notes](https://github.com/onsi/ginkgo/releases)
- [Changelog](https://github.com/onsi/ginkgo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/ginkgo/compare/v2.7.0...v2.8.0)

---
updated-dependencies:
- dependency-name: github.com/onsi/ginkgo/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-06 01:14:50 +00:00
Casey Callendrello f89a8c6a3e
Merge pull request #953 from mmorel-35/lint
ci(lint): setup lint job
2023-01-30 17:41:22 +01:00
Matthieu MOREL da8672c859 ci(lint): setup lint job
Creates a lint job with a yamllint and a golangci-lint
Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>
2023-01-27 11:03:37 +01:00
Casey Callendrello c5603949c1
Merge pull request #944 from containernetworking/dependabot/go_modules/github.com/onsi/ginkgo/v2-2.7.0
build(deps): bump github.com/onsi/ginkgo/v2 from 2.6.1 to 2.7.0
2023-01-16 10:31:55 +01:00
dependabot[bot] 8ac5c8a7f1
build(deps): bump github.com/onsi/ginkgo/v2 from 2.6.1 to 2.7.0
Bumps [github.com/onsi/ginkgo/v2](https://github.com/onsi/ginkgo) from 2.6.1 to 2.7.0.
- [Release notes](https://github.com/onsi/ginkgo/releases)
- [Changelog](https://github.com/onsi/ginkgo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/ginkgo/compare/v2.6.1...v2.7.0)

---
updated-dependencies:
- dependency-name: github.com/onsi/ginkgo/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-16 09:21:27 +00:00
Casey Callendrello ed461a9b0e
Merge pull request #945 from containernetworking/dependabot/go_modules/github.com/vishvananda/netns-0.0.2
build(deps): bump github.com/vishvananda/netns from 0.0.1 to 0.0.2
2023-01-16 10:13:26 +01:00
dependabot[bot] 5fa3464d59
build(deps): bump github.com/vishvananda/netns from 0.0.1 to 0.0.2
Bumps [github.com/vishvananda/netns](https://github.com/vishvananda/netns) from 0.0.1 to 0.0.2.
- [Release notes](https://github.com/vishvananda/netns/releases)
- [Commits](https://github.com/vishvananda/netns/compare/v0.0.1...v0.0.2)

---
updated-dependencies:
- dependency-name: github.com/vishvananda/netns
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-16 01:27:27 +00:00
Casey Callendrello 6a43fb5511
Merge pull request #938 from mmorel-35/main
ci(deps): setup dependabot
2023-01-09 18:04:09 +01:00
Matthieu MOREL 45761d9522 update github.com/vishvananda/netns to v0.0.1
Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>
2022-12-26 21:32:38 +00:00
dependabot[bot] aeb1d8ebf8 build(deps): bump github.com/containernetworking/plugins
Bumps [github.com/containernetworking/plugins](https://github.com/containernetworking/plugins) from 0.9.1 to 1.1.1.
- [Release notes](https://github.com/containernetworking/plugins/releases)
- [Commits](https://github.com/containernetworking/plugins/compare/v0.9.1...v1.1.1)

---
updated-dependencies:
- dependency-name: github.com/containernetworking/plugins
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>
2022-12-26 21:32:38 +00:00
dependabot[bot] dd2d40c4f6 build(deps): bump github.com/containernetworking/cni in /plugins/debug
Bumps [github.com/containernetworking/cni](https://github.com/containernetworking/cni) from 1.0.1 to 1.1.2.
- [Release notes](https://github.com/containernetworking/cni/releases)
- [Commits](https://github.com/containernetworking/cni/compare/v1.0.1...v1.1.2)

---
updated-dependencies:
- dependency-name: github.com/containernetworking/cni
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>
2022-12-26 21:32:38 +00:00
Matthieu MOREL 97954935c5 Update dependabot.yml
Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>
2022-12-26 21:32:38 +00:00
dependabot[bot] 0a26996875 build(deps): bump alpine in /.github/actions/retest-action
Bumps alpine from 3.10 to 3.17.

---
updated-dependencies:
- dependency-name: alpine
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>
2022-12-26 21:32:38 +00:00
dependabot[bot] cc036171cc build(deps): bump github.com/onsi/gomega from 1.17.0 to 1.24.2
Bumps [github.com/onsi/gomega](https://github.com/onsi/gomega) from 1.17.0 to 1.24.2.
- [Release notes](https://github.com/onsi/gomega/releases)
- [Changelog](https://github.com/onsi/gomega/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/gomega/compare/v1.17.0...v1.24.2)

---
updated-dependencies:
- dependency-name: github.com/onsi/gomega
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>
2022-12-26 21:32:38 +00:00
Matthieu MOREL 55be4ccd0d Create dependabot.yml
Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>
2022-12-26 21:32:38 +00:00
Eng Zer Jun f024754da8 refactor: move from io/ioutil to io and os packages
The io/ioutil package has been deprecated as of Go 1.16 [1]. This commit
replaces the existing io/ioutil functions with their new definitions in
io and os packages.

[1]: https://golang.org/doc/go1.16#ioutil
Signed-off-by: Eng Zer Jun <engzerjun@gmail.com>
2022-12-12 17:44:39 +01:00
Michael Zappa e18f6322ff Update email to gmail
Signed-off-by: Michael Zappa <michael.zappa@gmail.com>
2022-12-12 17:44:01 +01:00
Dan Williams 6e5ac36b42
Merge pull request #935 from austinvazquez/update-github-actions-packages
Upgrade GitHub actions packages to resolve NodeJS 12 warnings
2022-12-05 10:12:12 -06:00
Austin Vazquez 58488a6214 Upgrade GitHub actions packages to resolve NodeJS 12 warnings
Upgrades actions/checkout and actions/setup-go packages to v3 to resolve
CI workflow NodeJS 12 warnings.

Signed-off-by: Austin Vazquez <macedonv@amazon.com>
2022-12-01 21:43:45 +00:00
Dan Williams e629d106b4
Merge pull request #920 from loxi-admin/main
Removed loxilight as it is not supported anymore
2022-11-14 10:06:37 -06:00
loxi-admin 62709e0a59 Removed loxilight as it is not supported anymore
Signed-off-by: loxi-admin <admin@netlox.io>
2022-10-31 16:33:09 +01:00
Dan Williams 76aaefb2cb libcni: handle string-type disableCheck values
CNI spec 0.4x (and possibly earlier) mistakenly specified disableCheck
as a string type, while 1.0 cleaned that up to be a real boolean.

We should be nice and interpret the string value.

Fixes: https://github.com/containernetworking/cni/issues/877

Signed-off-by: Dan Williams <dcbw@redhat.com>
2022-10-31 16:32:33 +01:00
Bruce Ma 04dce8c7d3 testhelpers: use `go mod tidy` to ensure all necessary dependencies before building
Signed-off-by: Bruce Ma <brucema19901024@gmail.com>
2022-10-31 16:28:20 +01:00
Dan Williams 307ed01652
Merge pull request #914 from squeed/iso
cdc: update email
2022-10-10 10:52:34 -05:00
Casey Callendrello be9139d588 cdc: update email
Signed-off-by: Casey Callendrello <cdc@redhat.com>
2022-10-10 17:51:55 +02:00
Dan Williams 30fd41546b
Merge pull request #913 from squeed/bump-go
Bump go to v1.19
2022-10-10 10:41:14 -05:00
Casey Callendrello dc22d0462d go.mod: bump to go1.18
Signed-off-by: Casey Callendrello <cdc@redhat.com>
2022-10-10 17:27:50 +02:00
Casey Callendrello 69967699d7 github: bump go version to v1.19
Signed-off-by: Casey Callendrello <cdc@redhat.com>
2022-10-10 17:26:56 +02:00
Casey Callendrello 08fb4605fc go fmt
Signed-off-by: Casey Callendrello <cdc@redhat.com>
2022-10-10 17:26:29 +02:00
s00613818 dbf33e2c59 fix 714-plugin add netns validation reinforcement
Signed-off-by: Poor12 <shentiecheng@huawei.com>
2022-10-10 17:19:31 +02:00
Casey Callendrello 8dba3824fb libcni: add specific type for CHECK not supported
Previously, we returned an arbitrary string error if the given CNI
version didn't support CHECK. This is not useful for downstream
consumption and matching.

So, declare an error variable so that users can use `errors.Is()`
instead of matching on a string.

Signed-off-by: Casey Callendrello <c1@caseyc.net>
2022-09-19 17:13:58 +02:00
Dan Williams 40fa4795fb
Merge pull request #908 from ShubhamTatvamasi/patch-1
Updated Calico project link
2022-08-08 10:06:39 -05:00
Shubham Tatvamasi a710a7b4c1 Updated Calico project link
Signed-off-by: Shubham Tatvamasi <shubhamtatvamasi@gmail.com>
2022-08-03 11:14:34 +05:30
Dan Williams 3363d14368
Merge pull request #904 from brandond/fix_null_output
Fix successfully unmarshalled nil raw result
2022-07-27 10:46:15 -05:00
Brad Davidson 1c7c696779 Fix successfully unmarshalled nil raw result
Properly handle "null" as plugin output. This successfully unmarshals to a nil map, which crashes later when assigning confVersion to the map.

Signed-off-by: Brad Davidson <brad.davidson@rancher.com>
2022-07-15 15:24:07 -07:00
Dan Williams 812a8cfa8a
Merge pull request #902 from drivebyer/drivebyer-patch-1
spec: fix format
2022-07-13 10:41:02 -05:00
Yang Wu 58b77bdf71 spec: fix format
Signed-off-by: drivebyer <wuyangmuc@gmail.com>
2022-07-01 21:45:33 +08:00
Michael C. Cambria 3ec19197ef
Merge pull request #896 from dcbw/empty-result-version-regression
invoke: if Result CNIVersion is empty use netconf CNIVersion
2022-05-25 13:12:46 -04:00
Dan Williams 55fe94e885 invoke: if Result CNIVersion is empty use netconf CNIVersion
The CNI Spec requires plugins to return a CNIVersion in the Result
that is the same as the CNIVersion of the incoming CNI config.

Empty CNIVersions are specified to map to 0.1.0 (since the first
CNI spec didn't have versioning) and libcni handles that automatically.

Unfortunately some versions of Weave don't do that and depend on
a libcni <= 0.8 quirk that used the netconf version if the result
version was empty. This is technically a libcni regression, though
the plugin itself is violating the specification.

Thus for backwards compatibility assume that if the Result
CNIVersion is empty, the netconf CNIVersion should be used as
the Result version.

Fixes: https://github.com/containernetworking/cni/issues/895

Signed-off-by: Dan Williams <dcbw@redhat.com>
2022-05-25 10:57:53 -05:00
Dan Williams 940e662699
Merge pull request #894 from fujitatomoya/bugfix-20220503-golint-error-cnitool
cnitool: address golint error
2022-05-11 10:43:46 -05:00
Tomoya Fujita 99eac24911 cnitool: address golint error
exported const EnvCNIPath should have comment (or a comment on this block) or be unexported

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2022-05-03 15:23:55 -07:00
Dan Williams 08f8596ff3
Merge pull request #893 from squeed/handle-empty-version
libcni: handle empty version when parsing version
2022-04-27 14:52:20 -05:00
Casey Callendrello 1054f8ead7 libcni: handle empty version when parsing version
Without this Delete failed for empty-version configs.

Update the tests to catch this case.

Signed-off-by: Casey Callendrello <cdc@redhat.com>
2022-04-27 21:47:32 +02:00
Dan Williams f32e3df6fb
Merge pull request #884 from jeefy/add-security-header
Add security heading to README
2022-04-13 10:43:50 -05:00
Casey Callendrello c9114c284a
Merge branch 'main' into add-security-header 2022-03-30 18:09:04 +02:00
Sascha Grunert 54f1587d67 Switch to ginkgo/v2
Update ginkgo to the next major version which has been released a while
ago.

Signed-off-by: Sascha Grunert <sgrunert@redhat.com>
2022-03-30 18:05:12 +02:00
Casey Callendrello bb46e27465
Merge branch 'main' into add-security-header 2022-03-23 16:52:18 +01:00
Dan Williams e6dea17746
Merge pull request #885 from squeed/mz2maint
Maintainers: add Mike Zappa
2022-03-23 10:51:13 -05:00
Jeffrey Sica aba8f8b1fb add security heading to README
Signed-off-by: Jeffrey Sica <me@jeefy.dev>
2022-03-16 22:44:09 -05:00
Casey Callendrello 4b46fe6d73 Maintainers: add Mike Zappa
Signed-off-by: Casey Callendrello <cdc@redhat.com>
2022-03-16 16:38:32 +01:00
Bruce Ma 2f6d8b1930 introduce hybridnet to thrid-party plugins
Signed-off-by: Bruce Ma <brucema19901024@gmail.com>
2022-03-09 17:56:18 +01:00
Dan Williams 35efaabf93
Merge pull request #875 from netlox-dev/master
Updated README to reflect loxilight CNI plugin provided by netlox.io
2022-03-02 10:39:29 -06:00
NetLOX e4a9f965d0
Merge branch 'containernetworking:master' into master 2022-03-01 20:28:28 +09:00
Dan Williams 96a1883818
Merge pull request #880 from howardjohn/json/unmarshal-pointer
Fix incorrect pointer inputs to `json.Unmarshal`
2022-02-23 10:42:06 -06:00
John Howard 3e49ce16b5 Fix incorrect pointer inputs to `json.Unmarshal`
See https://github.com/howardjohn/go-unmarshal-double-pointer for more
info on why this is not safe and how this is detected.

Signed-off-by: John Howard <howardjohn@google.com>
2022-02-22 12:59:59 -08:00
NetLOX 96425dafeb
Merge branch 'containernetworking:master' into master 2022-02-10 09:57:13 +09:00
yanghesong b92c836f3a fix version of cni
v0.8.1 does not have a directory of github.com/containernetworking/cni/pkg/types/100
refer to https://github.com/containernetworking/cni/tree/v0.8.1/pkg/types

Signed-off-by: yanghesong <hesong.yang@foxmail.com>
2022-01-19 10:46:50 -06:00
Andreas Karis 269bf6103d Spec: Container runtime shall tear down namespaces
Explicitly state that the container runtime, not the plugin, shall tear
down network namespaces.

Signed-off-by: Andreas Karis <ak.karis@gmail.com>
2022-01-12 18:23:42 +01:00
NetLOX 48fac6a491 Update README.md
Signed-off-by: netlox-io <admin@netlox.io>
2022-01-05 12:19:52 +09:00
NetLOX 798e63d83f Updated README.md to include Netlox loxilight CNI
Signed-off-by: netlox-io <admin@netlox.io>
2022-01-05 12:19:52 +09:00
Dan Williams 4756a3616c
Merge pull request #865 from pkit/pkit/exec_plugins
[exec-plugins]: support plugin lists
2021-11-17 10:38:18 -06:00
Dan Williams cff680034b
Merge branch 'master' into pkit/exec_plugins 2021-11-17 10:34:15 -06:00
Dan Williams 10f777cb7d
Merge pull request #868 from kpaschen/multus-readme
documentation: update Multus link in README.md
2021-11-17 10:33:48 -06:00
Developer 9070cb3eec documentation: update Multus link in README.md to point to the k8snetworkplumbingwg repository
Signed-off-by: kpaschen <kathrin.paschen@gmail.com>
2021-11-16 14:07:48 +01:00
Bruce Ma 1d9f32dc0e
Merge pull request #864 from tklauser/skel-superfluous-err-nil-check
skel: remove superfluous err nil check in (*dispatcher).pluginMain
2021-11-08 15:37:44 +08:00
Constantine Peresypkin 21cd5f0c1f [exec-plugins]: support plugin lists
add ability to run configurations with plugin lists/chaning
see SPEC >= 0.3.1

Signed-off-by: Constantine Peresypkin <constantine@caspiandb.com>
2021-10-31 17:51:23 +02:00
Tobias Klauser c3625975b3 skel: remove superfluous err nil check in (*dispatcher).pluginMain
There is no need to check err against nil, just return it.

Signed-off-by: Tobias Klauser <tklauser@distanz.ch>
2021-10-25 15:23:12 +02:00
Dan Williams 6a92df875a
Merge pull request #863 from rosenhouse/remove-gabe
Remove Gabe Rosenhouse as maintainer
2021-10-06 10:31:34 -05:00
Gabriel Rosenhouse 42f24747b7 Remove Gabe Rosenhouse as maintainer
Signed-off-by: Gabriel Rosenhouse <grosenhouse@vmware.com>
2021-09-28 16:37:03 -07:00
Bruce Ma 5725f27869
Merge pull request #860 from squeed/help-print-version
skel: print out CNI versions supported in help text.
2021-09-09 09:39:28 +08:00
Casey Callendrello 2e4887bf8a skel: print out CNI versions supported in help text.
Signed-off-by: Casey Callendrello <cdc@redhat.com>
2021-09-08 15:31:29 +02:00
Dan Williams 1694fd7b57
Merge pull request #858 from squeed/version-from
pkg/version: add VersionsFrom function
2021-09-07 08:04:59 -05:00
Casey Callendrello c7f5f70554 pkg/version: add VersionsFrom function
So plugins can easily declare support for "from version 0.3 and
beyond".

Signed-off-by: Casey Callendrello <cdc@redhat.com>
2021-09-07 11:44:54 +02:00
Dan Williams 5608690f77 spec: bump to 1.0.0
Signed-off-by: Dan Williams <dcbw@redhat.com>
2021-08-04 18:01:03 +02:00
Dan Williams 699ad28878
Merge pull request #853 from squeed/bump-spec-upgrades
Docs: bump spec version information
2021-08-04 10:59:48 -05:00
Casey Callendrello 30e06a8b76 Docs: bump spec version information
Signed-off-by: Casey Callendrello <cdc@redhat.com>
2021-08-04 17:58:20 +02:00
Dan Williams 0dfe02421e
Merge pull request #851 from DrmagicE/master
docs: correct the extension name of the configuration file
2021-08-04 10:22:43 -05:00
DrmagicE a9562466f1 docs: revise cnitool docs
Signed-off-by: DrmagicE <379342542@qq.com>
2021-07-24 16:33:41 +08:00
Paul Holzinger 63a3bca188 wrap returned errors
fmt.Errorf errors should wrap the original error with %w.
This allows the caller to check the errors with `errors.Is()`.

see: https://blog.golang.org/go1.13-errors

Signed-off-by: Paul Holzinger <pholzing@redhat.com>
2021-07-21 17:56:49 +02:00
DrmagicE b277ec1bc2 docs: correct the extension name of the configuration file
Signed-off-by: DrmagicE <379342542@qq.com>
2021-07-18 20:51:40 +08:00
Bruce Ma 34a8a464e1 chore: standardize documentation on IP assignment
Signed-off-by: Bruce Ma <brucema19901024@gmail.com>
2021-07-07 17:12:04 +02:00
Dan Williams 76bf3de7f8 types: ensure empty CNIVersion always creates/converts to 0.1.0
Signed-off-by: Dan Williams <dcbw@redhat.com>
2021-06-30 18:01:14 +02:00
Dan Williams b4e078af7a
Merge pull request #845 from jayunit100/patch-1
Add breadcrumbs to the api.go
2021-06-23 10:04:41 -05:00
jay vyas 4feedb9dde Add breadcrumbs for CNI.go
Signed-off-by: jay vyas <jvyas@vmware.com>
2021-06-16 11:36:18 -04:00
Dan Williams 57cf1cee7d types/create: add CreateFromBytes()
Turns out a number of users want to read in JSON and
create a Result object from it, and CNI didn't have
a function to do that in one call. Instead you had to
create a ConfigDecoder first, then create a Result
from the given version.

That's silly, let's just make a function to do that
and let everyone do less work.

Signed-off-by: Dan Williams <dcbw@redhat.com>
2021-06-16 17:28:23 +02:00
Bruce Ma 4fdc5f62c9 chore: support both value type and pointer type in LoadArgs
Signed-off-by: Bruce Ma <brucema19901024@gmail.com>
2021-06-02 17:45:22 +02:00
Dan Williams 3a8fc073b5
Merge pull request #842 from containernetworking/remove-bryan
Remove Bryan Boreham as maintainer
2021-05-26 10:25:32 -05:00
Bryan Boreham f30a8249fd Remove Bryan Boreham as maintainer
Signed-off-by: Bryan Boreham <bjboreham@gmail.com>
2021-05-21 14:46:40 +00:00
Tomofumi Hayashi c63d850d60
Add debug plugin to help debugging/troubleshooting (#818)
This change introduces debug plugin to help to develop CNI plugins.

Signed-off-by: Tomofumi Hayashi <tohayash@redhat.com>
2021-05-19 11:22:44 -04:00
Dan Williams 37ce8d6fbe
Merge pull request #839 from Nordix/spec-updates
spec: Some minor corrections
2021-05-05 10:34:04 -05:00
Björn Svensson 9546b704c8 spec: Some minor corrections
Corrected typos, links, formatting and added some details.

Signed-off-by: Björn Svensson <bjorn.a.svensson@est.tech>
2021-04-30 23:50:24 +02:00
Michael C. Cambria 7ba00c594c
Merge pull request #838 from squeed/no-args-env
Spec: Bring 1.0's treatment of "args" in line with 0.4.0
2021-04-28 11:26:23 -04:00
Casey Callendrello 3a13f6854d Spec: Bring 1.0's treatment of "args" in line with 0.4.0
The spec deviated from the existing behavior, and it was never actually
implemented in libcni. So this is a no-op.

Signed-off-by: Casey Callendrello <cdc@redhat.com>
2021-04-28 11:23:56 -04:00
Dan Williams 05ea7269e1
Merge pull request #835 from hs0210/work
Fix typo in SPEC.md
2021-04-28 10:20:51 -05:00
Bruce Ma 16f664ce57
Merge pull request #836 from cuirunxing-hub/master
upgrade kube-ovn new github site
2021-04-26 10:25:43 +08:00
cuirunxing-hub c92642bda0 upgrade kube-ovn new github site
Signed-off-by: cuirunxing-hub <cuirunxing@inspur.com>
2021-04-25 19:07:50 +08:00
Hu Shuai 67ec90448e Fix typo in SPEC.md
Signed-off-by: Hu Shuai <hus.fnst@cn.fujitsu.com>
2021-04-25 13:24:29 +08:00
Bryan Boreham e33f449ec9
Merge pull request #833 from cuirunxing-hub/master
typos correct
2021-04-19 13:59:50 +01:00
cuirunxing-hub 0555966685 typos correct
Signed-off-by: cuirunxing-hub <cuirunxing@inspur.com>
2021-04-15 17:55:08 +08:00
Casey Callendrello 8b25a7bf9e
Merge pull request #805 from squeed/spec-rewrite
Rewrite spec.md for 1.0.0
2021-03-31 17:15:48 +02:00
Casey Callendrello 8ad568b543 Rewrite spec.md for 1.0.0
The protocol is unchanged, but the specification was badly in need of a
rewrite.

Signed-off-by: Casey Callendrello <cdc@redhat.com>
2021-03-31 17:14:35 +02:00
Bruce Ma ea59a90103
Merge pull request #819 from chandanAggarwal/update_azure_cni_reference
Adding reference to Azure CNI as 3rd party plugin in README
2021-03-26 14:51:12 +08:00
Chandan Aggarwal 00169178ed Adding reference to Azure CNI as 3rd party plugin in readme
Signed-off-by: Chandan Aggarwal <caggar@microsoft.com>
2021-03-22 12:59:28 -07:00
Dan Williams c2f6afa41f
Merge pull request #802 from fasaxc/log-plugin-name
Log out the plugin name on ADD/DEL failure.
2021-03-03 10:16:29 -06:00
Bryan Boreham 9c075a3e40
Merge branch 'master' into log-plugin-name 2021-03-03 16:10:03 +00:00
Dan Williams 23b841a269
Merge pull request #813 from dcbw/020-no-ip-testcase
types/040: add testcase for <= 0.2.0 Result requirement of one or more IPs
2021-02-17 11:33:32 -06:00
Dan Williams e00ed00197
Merge branch 'master' into 020-no-ip-testcase 2021-02-17 10:21:57 -06:00
Dan Williams ad59be0959 types/040: add testcase for <= 0.2.0 Result requirement of one or more IPs
The 0.2.0 spec strongly suggests via lack of the "(optional)" tag that
a Result must contain one or two IP addresses. We already throw an
error for that case when down-converting a 0.3.0+ Result to <= 0.2.0,
but we didn't have a testcase for it.

Signed-off-by: Dan Williams <dcbw@redhat.com>
2021-02-10 10:49:48 -06:00
Dan Williams b5ab16f010
Merge pull request #810 from squeed/github-action
Switch from Travis to GH Actions
2021-02-03 10:52:38 -06:00
Casey Callendrello e32b586636 remove build badges from homepage
Signed-off-by: Casey Callendrello <cdc@redhat.com>
2021-02-03 17:48:45 +01:00
Casey Callendrello 296290a913 Switch from Travis to GH Actions
Signed-off-by: Casey Callendrello <cdc@redhat.com>
2021-02-03 17:48:44 +01:00
Casey Callendrello a199e6aa75 go fmt
Signed-off-by: Casey Callendrello <cdc@redhat.com>
2021-02-01 13:26:11 +01:00
Dan Williams a5c35b1d17
Merge pull request #808 from squeed/plugin-name
tighten up plugin-finding logic
2021-01-20 11:08:45 -06:00
Casey Callendrello ada67263b1 tighten up plugin-finding logic
Signed-off-by: Casey Callendrello <cdc@redhat.com>
2021-01-20 18:04:44 +01:00
Dan Williams 15391a8c4a
Merge pull request #801 from mccv1r0/alias
Add "alias" to conventions
2021-01-20 11:01:10 -06:00
Michael Cambria eec3755640 Add "alias" to conventions
Signed-off-by: Michael Cambria <mcambria@redhat.com>
2021-01-20 11:59:17 -05:00
Dan Williams 216a7099cb
Merge pull request #807 from hs0210/work
Fix typo in pkg/types/internal/convert.go
2021-01-19 11:06:47 -06:00
Hu Shuai 867451c7a0 Fix typo in pkg/types/internal/convert.go
Signed-off-by: Hu Shuai <hus.fnst@cn.fujitsu.com>
2021-01-15 09:47:40 +08:00
Dan Williams 77cd8fe3d6
Merge pull request #803 from champtar/maintainers-typo
maintainers: fix typo
2021-01-06 10:27:10 -06:00
Dan Williams 75e7788841
Merge pull request #804 from Ashon/issues/fix-typo-in-convention
Fix typo in CONVENTIONS.md
2021-01-06 10:10:32 -06:00
ashon 679ed9d310 Fix typo in CONVENTIONS.md
Signed-off-by: ashon <ashon8813@gmail.com>
2020-12-30 23:17:53 +09:00
Etienne Champetier 6d8228e1d3 maintainers: fix typo
Signed-off-by: Etienne Champetier <champetier.etienne@gmail.com>
2020-12-29 17:25:04 -05:00
Shaun Crampton c7357803b1 Markups.
Signed-off-by: Shaun Crampton <shaun@tigera.io>
2020-12-23 15:15:17 +00:00
Shaun Crampton b678c260c4 Log out the plugin name on ADD/DEL failure.
Makes it easier to diagnose conf lists when one plugin fails.

Signed-off-by: Shaun Crampton <shaun@tigera.io>
2020-12-21 11:01:14 +00:00
Casey Callendrello 62e54113f4
Merge pull request #800 from dcbw/100-remove-SupportedVersions
types: unexport SupportedVersions
2020-12-16 17:46:44 +01:00
Dan Williams e781c94eea types: remove SupportedVersions
SupportedVersions isn't really needed outside the types themselves.

Signed-off-by: Dan Williams <dcbw@redhat.com>
2020-12-16 08:52:57 -06:00
Dan Williams d1e1ae3c34
Merge pull request #783 from dcbw/100
types changes for 1.0.0
2020-11-19 07:59:36 -06:00
Dan Williams 7555ca3250 spec: bump to 1.0.0-pre-release and remove 'version' from Result addresses
Redundant and easily determined from the address itself.

Signed-off-by: Dan Williams <dcbw@redhat.com>
2020-11-18 11:10:51 -06:00
Dan Williams 6823eba0ec tests: small cleanup and removal of one useless testcase
The testcase checking if a prevResult was invalid is not actually
useful, because it was testing that an empty prevResult could be
unmarshalled. But due to an oversight, prevResults may not have
a CNIVersion key, which is all we can check for validity.

Signed-off-by: Dan Williams <dcbw@redhat.com>
2020-11-11 11:03:15 -06:00
Dan Williams 3805b13a3b types: add 1.0.0
Only change this time is removal of the IPConfig "Version" field
which was deemed redundant.

Signed-off-by: Dan Williams <dcbw@redhat.com>
2020-11-11 11:03:15 -06:00
Dan Williams 0050bfa528 types: implement convert module and make types use it
Moving to spec 1.0.0 requires more complicated conversions, so
put the converter determination logic in a separate module that
any of the types can call to convert between arbitrary versions.
This is necessary to ensure the types don't have import cycles.

This also implements downconversion, which we never claimed
*not* to support, but didn't implement.

It also verifies that the Result object unmarshalled by
each result type's NewResult() function is actually supported
by the result type. This is a potentially breaking change as
this was not previously done, and for example may now fail
attempts to read a cached PrevResult written by a previous
version.

Signed-off-by: Dan Williams <dcbw@redhat.com>
2020-10-28 12:16:07 -05:00
Dan Williams 6fc72a49e9
Merge pull request #781 from dcbw/go-113
Bump release build Go version to 1.14
2020-08-05 14:43:21 -05:00
Dan Williams 90311ea2b0 Bump release build Go version to 1.14
Signed-off-by: Dan Williams <dcbw@redhat.com>
2020-08-05 10:58:15 -05:00
Dan Williams e91547363e
Merge pull request #780 from containernetworking/add-security-info
Add security reporting info
2020-08-05 10:48:31 -05:00
Bryan Boreham f9b5c9b162 Add security reporting info
Signed-off-by: Bryan Boreham <bjboreham@gmail.com>
2020-08-05 15:39:57 +00:00
Bryan Boreham 17b2a04fa4
Merge pull request #776 from clebio/revert_error_codes
Revert formatting of error codes
2020-07-08 16:08:16 +01:00
Caleb Hyde e5c65a5bf3 Revert formatting of error codes
In a prior change, reformatting of the errors codes within the
specification changed their values from positive to negative integers.

Signed-off-by: Caleb Hyde <caleb.hyde@gmail.com>
2020-07-03 14:49:25 -07:00
Dan Williams 6b482fdc76
Merge pull request #774 from squeed/go-mod
Switch to go modules
2020-07-01 10:13:30 -05:00
Casey Callendrello 65bf6884d1 Travis: bump go versions
Drop 1.11, 1.12, add 1.14.

Signed-off-by: Casey Callendrello <cdc@redhat.com>
2020-06-30 10:37:04 +02:00
Casey Callendrello a7cceb916d add go.mod
Signed-off-by: Casey Callendrello <cdc@redhat.com>
2020-06-30 10:37:04 +02:00
Casey Callendrello 93a7425495 testhelpers: clean up how we build against old libcni targets
Now that go has modules, we don't need to do anything particularly
special to build against old cni revisions. Just let `go get` sort it
out for us.

Signed-off-by: Casey Callendrello <cdc@redhat.com>
2020-06-30 10:37:01 +02:00
Casey Callendrello 11db36c11b
Merge pull request #768 from asears/md-doc-lint-updates-3
Fix linting issues in docs, add headers to json example, update errors into table
2020-06-10 17:43:06 +02:00
Casey Callendrello 9ec274acd6
Merge branch 'master' into md-doc-lint-updates-3 2020-06-10 17:38:01 +02:00
Bryan Boreham 0016c95d2e
Merge pull request #769 from caboteria/readme-tweaks
Readme tweaks
2020-06-10 16:36:33 +01:00
toby cabot c78d465e9f Replace 2019 conference announcement with links to the recordings
While KubeCon/CloudNativeCon 2019 is in the past the recordings of the
sessions are helpful.

Signed-off-by: toby cabot <toby@caboteria.org>
2020-06-07 18:12:05 -04:00
toby cabot fc1de42c01 Sync contact methods in README and CONTRIBUTING
Now that the CNI slack has been in use for ~7 months it's no longer
necessary to have it as a top-level banner headline.  It should be in
CONTRIBUTING.md, though, with the other project contact channels.

Signed-off-by: toby cabot <toby@caboteria.org>
2020-06-07 18:12:05 -04:00
asears c815aca145
Update the SPEC, CODE-OF-CONDUCT, CONTRIBUTING, RELEASING with minor formatting changes and linting updates.
Signed-off-by: asears <asears@users.noreply.github.com>
2020-06-03 21:41:13 -04:00
Casey Callendrello f92762a8bf
Merge pull request #765 from rkamudhan/patch-4
adding OVN4NFV-K8s-Plugin as 3rd party plugin
2020-05-13 17:13:39 +02:00
Kuralamudhan Ramakrishnan 17a6379deb Update README.md
Signed-off-by: r.kuralamudhan <kuralamudhan.ramakrishnan@intel.com>
2020-05-06 21:12:32 -07:00
Kuralamudhan Ramakrishnan 956c943a7e adding OVN4NFV-K8s-Plugin as 3rd party plugin
Adding [OVN4NFV-K8S-Plugin](https://github.com/opnfv/ovn4nfv-k8s-plugin) is
OVN based CNI controller plugin to provide cloud-native based Service function
chaining(SFC), Multiple OVN overlay networking, dynamic subnet creation,
dynamic creation of virtual networks, VLAN Provider network,
Direct provider network and pluggable with other Multi-network plugins,
ideal for edge-based cloud-native workloads in Multi-cluster networking

Signed-off-by: r.kuralamudhan <kuralamudhan.ramakrishnan@intel.com>
2020-05-06 21:11:32 -07:00
Bryan Boreham 77436456f2
Merge pull request #763 from saschagrunert/version-retry
Retry exec commands on text file busy
2020-04-22 16:55:15 +01:00
Dan Williams acc3dadce9
Merge pull request #764 from adrianchiris/typo-infiniband-guid
Typo fixes for infiniband GUID
2020-04-22 10:53:43 -05:00
Sascha Grunert e2a736653a
Retry exec commands on text file busy
This makes the plugin validation a bit more robust by retrying the
execution if the plugin is in text file busy state. This can happen if
we write the config and the plugin at once. To avoid such races we now
try up to 5 seconds for the plugin validation.

Signed-off-by: Sascha Grunert <sgrunert@suse.com>
2020-04-16 09:11:52 +02:00
Adrian Chiris 76b18ea294 Typo fixes for infiniband GUID
- Remove underscore in the Area description of the entry
- Modify the capability name to be camelCase instead of underscores
  to be consistent with other capabilities (e.g `portMappings`, `ipRanges`)

As there was no CNI released and no implementations of this capability
(That i know of) i reckon we can do the attribute name adjustment.

Signed-off-by: Adrian Chiris <adrianc@mellanox.com>
2020-04-16 10:08:07 +03:00
Casey Callendrello bf84331c94
Merge pull request #761 from adrianchiris/device-id_runtime_conf
Add DeviceID attribute to RuntimeConfig
2020-04-08 17:33:34 +02:00
Michael C. Cambria 7d9ec90bff
Merge pull request #760 from dcbw/really-return-stderr
invoke: capture and return stderr if plugin exits unexpectedly
2020-04-08 11:32:44 -04:00
Dan Williams 44dabed6ce invoke: capture and return stderr if plugin exits unexpectedly
The way raw_exec invokes the command doesn't actually pass back
stderr, despite ExitError having that capability.  c.Run()
internally calls c.Wait() but that doesn't capture stderr and
insert it into the returned ExitError. So we have to do that
ourselves.

Fixes: https://github.com/containernetworking/cni/issues/732
Fixes: https://github.com/containernetworking/cni/pull/759

Signed-off-by: Dan Williams <dcbw@redhat.com>
2020-04-08 09:29:41 -05:00
Adrian Chiris 279bc6c21b Add DeviceID attribute to RuntimeConfig
This commit attempts to standardize the consumption
of a device identifier by CNI's for networks that
is associated with a device resource and require its
identifier to perform configurations.

Signed-off-by: Adrian Chiris <adrianc@mellanox.com>
2020-04-08 12:42:13 +03:00
Dan Williams fdcc7b15d7 test: allow specific package testing with PKGS=<x> ./test.sh
Signed-off-by: Dan Williams <dcbw@redhat.com>
2020-04-01 11:52:44 -05:00
Dan Williams cf734d46be
Merge pull request #742 from adrianchiris/master
Add GUID to well known Capabilities
2020-04-01 09:22:49 -05:00
Adrian Chiris ca0082f790 Add Infiniband GUID to well known Capabilities
This commit extends CONVENTION.md Well-known Capabilities
to include `infiniband_guid`. this shall be used by CNI plugins
who wish to set Infiniband GUID address for a network interface.

Change-Id: I3716e0a9ea660937a73feee00ea9c5b1a92ab478
Signed-off-by: Adrian Chiris <adrianc@mellanox.com>
2020-03-12 17:41:08 +02:00
Dan Williams 4fae32b849
Merge pull request #755 from libesz/fix-conflist-samples
Remove extra ',' chars which makes conflist examples invalid.
2020-03-04 10:16:08 -06:00
Gergo Huszty 38353fabbb Remove extra ',' chars which makes conflist examples invalid.
Signed-off-by: Gergo Huszty <gergo.huszty@ibm.com>
2020-02-28 15:18:06 +01:00
Dan Williams 56ace59a9e
Merge pull request #752 from dcbw/maintainers-remove-stefan
maintainers: remove Stefan per personal request
2020-02-19 10:46:25 -06:00
Dan Williams 134f603c67 maintainers: remove Stefan per personal request
We contacted Stefan and he said he could be dropped as a CNI maintainer.

Signed-off-by: Dan Williams <dcbw@redhat.com>
2020-02-19 10:28:21 -06:00
Piotr Skamruk c252795066
Merge pull request #751 from dcbw/mcambria-maintainer
Add Michael Cambria as a CNI maintainer
2020-02-13 11:05:30 +01:00
Dan Williams 1435c6bddb Add Michael Cambria as a CNI maintainer
Signed-off-by: Dan Williams <dcbw@redhat.com>
2020-02-12 10:11:19 -06:00
Piotr Skamruk f69d1f2cab
Merge pull request #745 from truongnh1992/insert_link
Update link freenode.org to freenode.net
2020-01-30 11:15:46 +01:00
Nguyen Hai Truong 6b46a03d2b Update link freenode.org to freenode.net
The domain freenode.org is no longer up-to-date,
use freenode.net instead

Signed-off-by: Nguyen Hai Truong <truongnh@vn.fujitsu.com>
2020-01-30 16:30:46 +07:00
Bryan Boreham 0adeb0ed54
Merge pull request #744 from containernetworking/update-1-0-roadmap
Update roadmap for 1.0 plans
2020-01-29 16:54:22 +00:00
Bryan Boreham 075e303ba8 Update roadmap for 1.0 plans
Signed-off-by: Bryan Boreham <bryan@weave.works>
2020-01-29 16:20:26 +00:00
Bryan Boreham d0fd3ff4e9
Merge pull request #583 from jellonek/resultasstring
Update Result.String implementation to work as declared in interface
2020-01-22 16:31:49 +00:00
Bryan Boreham c2b68cd595
Merge pull request #738 from mars1024/black-box-testing
pkg/utils: utility package should use black-box testing
2020-01-22 16:31:07 +00:00
Bryan Boreham 5c2bade00b
Merge pull request #740 from gavinfish/json_comments
Change language identifier to jsonc for json with comments
2020-01-19 19:01:04 +00:00
gavinfish 0b1c6497ed Change language identifier to jsonc for json with comments
Signed-off-by: gavinfish <drfish.me@gmail.com>
2020-01-17 13:59:17 +08:00
Piotr Skamruk 704c56d291 Update tests
Signed-off-by: Piotr Skamruk <piotr.skamruk@gmail.com>
2020-01-15 12:26:12 +01:00
Piotr Skamruk f208f19fd3 Remove Result.String
Closes #581

Signed-off-by: Piotr Skamruk <piotr.skamruk@gmail.com>
2020-01-15 12:26:12 +01:00
Bruce Ma cc6e8afa5a pkg/utils: utility package should use black-box testing
Signed-off-by: Bruce Ma <brucema19901024@gmail.com>
2020-01-10 20:36:12 +08:00
Dan Williams a09ec7a992
Merge pull request #736 from q384566678/add-utils-test
pkg/utils: add utils_test
2020-01-08 10:22:03 -06:00
Piotr Skamruk 6680f870ab
Merge pull request #735 from zjj2wry/intercepte-stderr
intercept netplugin std error
2020-01-08 17:12:57 +01:00
Zhou Hao 025e32f89e pkg/utils: add utils_test
Signed-off-by: Zhou Hao <zhouhao@cn.fujitsu.com>
2019-12-27 11:32:54 +08:00
郑佳金 6f29b01658 intercept netplugin std error
Signed-off-by: 郑佳金 <zhengjiajin2016@gmail.com>
Signed-off-by: 郑佳金 <zhengjiajin2016@gmail.com>
2019-12-19 14:08:13 +08:00
Bryan Boreham 4c077d2fec
Merge pull request #730 from oilbeater/add-kube-ovn
Add Kube-OVN to CNI list
2019-12-11 16:08:43 +00:00
Bryan Boreham d65360849f
Merge branch 'master' into add-kube-ovn 2019-12-11 16:08:22 +00:00
Casey Callendrello 340972aad4
Merge pull request #731 from yasensim/master
Add Project Antrea in CNI list
2019-12-11 17:07:30 +01:00
oilbeater 777584fb17 Add Kube-OVN to CNI list
Signed-off-by: oilbeater <liumengxinfly@gmail.com>
2019-12-05 14:24:05 +08:00
Bryan Boreham 7aed59f421
Merge pull request #727 from rktidwell/master
DOCUMENTATION: Address incomplete instructions in CONTRIBUTING.md
2019-12-04 16:34:45 +00:00
Dan Williams dce771e2a0
Merge pull request #725 from containernetworking/new-maintainers
Add Bruce Ma and Piotr Skarmuk as maintainers
2019-12-04 10:09:07 -06:00
yasensim e00fa533de appending antrea to the cni list
Signed-off-by: yasensim <yasensim@gmail.com>
2019-12-03 20:45:50 -08:00
yasensim 7815be7f0a Add Project Antrea in CNI list
Signed-off-by: yasensim <yasensim@gmail.com>
2019-12-02 17:27:29 -08:00
Matt Dupre dcb71ae8c9
Merge pull request #729 from weibeld/master
Add table of contents to SPEC.md
2019-11-27 11:39:56 -05:00
Daniel Weibel f2fa4a3fa0 Add table of contents
Generated wit markdown-toc:

markdown-toc --no-firsth1 --maxdepth=2 SPEC.md

Signed-off-by: Daniel Weibel <danielmweibel@gmail.com>
2019-11-26 19:37:48 +08:00
Ryan Tidwell b36de6e00b
DOCUMENTATION: Address incomplete instructions in CONTRIBUTING.md
This change addresses a few missing steps and out-of-date items in the
contributor docs such as fetching proper dependencies and removing
references to paths that no longer exist.

Fixes #726

Signed-off-by: Ryan Tidwell <rtidwell@suse.com>
2019-11-25 09:34:48 -06:00
Bryan Boreham ade6ad342d
Merge pull request #728 from noironetworks/cisco-cni
Adding Cisco ACI to CNI plugin list
2019-11-25 12:26:09 +00:00
Sumit Naiksatam c35ac21f14 Adding Cisco ACI to CNI plugin list
Signed-off-by: Sumit Naiksatam <sumitnaiksatam@gmail.com>
2019-11-22 10:36:38 -08:00
Bryan Boreham 3eb88acd2a Add Bruce Ma and Piotr Skarmuk as maintainers
Signed-off-by: Bryan Boreham <bryan@weave.works>
2019-11-16 11:45:09 +00:00
Bryan Boreham 46b9d6fa20
Merge pull request #724 from squeed/readme-update
Update README to sunset slack and call out kubecon presentations
2019-11-13 19:28:37 +00:00
Casey Callendrello 6c6a3156c3 Update README to sunset slack and call out kubecon presentations
Signed-off-by: Casey Callendrello <cdc@redhat.com>
2019-11-13 17:36:00 +01:00
Piotr Skamruk c2508f1804
Merge pull request #722 from containernetworking/note-binaries
Add a note to README about where to find the binaries
2019-11-07 12:41:35 +01:00
Bryan Boreham b89eff5321 Add a note to README about where to find the binaries
Signed-off-by: Bryan Boreham <bjboreham@gmail.com>
2019-11-06 16:56:52 +00:00
Dan Williams 2d4bc3d57a
Merge pull request #721 from mccv1r0/issue720
When CNI version isn't supplied in config, use default.
2019-10-16 11:11:06 -05:00
Michael Cambria 4eec64802d When the CNI version isn't supplied in config, use default.
Signed-off-by: Michael Cambria <mccv1r0@gmail.com>
2019-10-16 11:20:00 -04:00
Bryan Boreham 63202159f1
Merge pull request #712 from mars1024/modify/ifname_validation
add interface name validation
2019-10-09 16:15:59 +01:00
Bruce Ma eefc06974c SPEC: update validation rules for interface name in docs and address some comments
Signed-off-by: Bruce Ma <brucema19901024@gmail.com>
2019-10-08 12:01:17 +08:00
Piotr Skamruk 904ce461b0
Merge pull request #715 from mars1024/typo/spec
spec: fix some typo in docs
2019-10-03 22:07:54 +02:00
Dan Williams 8f2d48e6e1
Merge pull request #716 from yuxiaobo96/cni-update3
Correct word spelling mistakes
2019-10-02 10:17:22 -05:00
Bruce Ma b5188cf64f spec: fix some typo in docs
Signed-off-by: Bruce Ma <brucema19901024@gmail.com>
2019-09-29 14:11:31 +08:00
yuxiaobo c94fcd742c Perfect annotation: Correct word spelling mistakes
Signed-off-by: yuxiaobo <yuxiaobogo@163.com>
2019-09-26 15:34:44 +08:00
Bryan Boreham 02a25a7635
Merge pull request #713 from janisz/patch-1
Bump golang
2019-09-25 16:08:28 +01:00
Piotr Skamruk 15d37883e6
Merge pull request #710 from hwdef/add-err-handle-p/t/noop
add err handling in plugins/test/noop/
2019-09-24 18:46:20 +02:00
Bruce Ma 7be1ac932d add interface name validation to libcni and skel
Signed-off-by: Bruce Ma <brucema19901024@gmail.com>
2019-09-20 11:30:22 +08:00
Bruce Ma 9f4a623222 utils: add validation function for interface name
Signed-off-by: Bruce Ma <brucema19901024@gmail.com>
2019-09-20 11:29:05 +08:00
janisz 2a82881b00
Bump golang
Refs:
* https://github.com/containernetworking/plugins/pull/386
* https://golang.org/doc/go1.13

Signed-off-by: Tomasz Janiszewski <janiszt@gmail.com>
2019-09-19 10:15:35 +02:00
Casey Callendrello bd4e822851
Merge pull request #711 from johscheuer/update-cnitool-docs
Update cnitool docs
2019-09-18 14:21:55 +02:00
Johannes M. Scheuermann c0f784d16d Update cnitool docs
Signed-off-by: Johannes M. Scheuermann <joh.scheuer@gmail.com>
2019-09-17 21:45:26 +02:00
hwdef 94399d569c add err handling in plugins/test/noop/
Signed-off-by: hwdef <hwdef97@gmail.com>
2019-09-13 01:30:09 +08:00
Piotr Skamruk 83439463f7
Merge pull request #704 from dcbw/cache-ifname
libcni: also cache IfName
2019-09-04 17:32:31 +02:00
Bryan Boreham bd64c46461
Merge pull request #698 from mccv1r0/issue679
validate containerID and networkName
2019-09-04 16:29:24 +01:00
Dan Williams 9d4429ded4
Merge pull request #705 from dcbw/fix-cache-result-key
libcni: fix cache file 'result' key name
2019-09-04 10:18:19 -05:00
Michael Cambria d8dfb56fd8 validate containerID and networkName
ensure they contain only letters and numbers
2019-09-03 17:54:22 -04:00
Dan Williams e4a11bae33 libcni: cache file operations require full uniqueness tuple in RuntimeConf
We can't generate a cache file path without all of network name,
container ID, and interface name in the RuntimeConf. They should
be there already anyway (otherwise errors will occur) but we should
return those errors earlier.
2019-09-03 11:19:18 -05:00
Dan Williams a83f3cb0f4 libcni: also cache IfName, network name, and container ID
We should have all components of the uniqueness tuple in the cache file
so that they can be read out of the file without trying to parse the
cache file name.
2019-09-03 10:04:18 -05:00
Dan Williams 894863c200 libcni: fix cache file 'result' key name 2019-08-29 20:39:33 -05:00
Dan Williams 13b6ad6447
Merge pull request #699 from mars1024/cleanup/skel
skel: remove needless functions and types
2019-08-21 10:26:33 -05:00
Bruce Ma f3654f3d2d skel: remove needless functions and types
Signed-off-by: Bruce Ma <brucema19901024@gmail.com>
2019-08-16 18:18:23 +08:00
Dan Williams 8c6c47d1c7
Merge pull request #686 from mars1024/cleanup/skel_error
skel: clean up errors in skel and add some well-known error codes
2019-08-07 10:13:50 -05:00
Bruce Ma 3e79703c66 modify some well-known errors
Signed-off-by: Bruce Ma <brucema19901024@gmail.com>
2019-08-01 11:21:26 +08:00
Dan Williams d19c2358a5
Merge pull request #691 from mars1024/docs/add_some_capabilities
docs: add ips and mac to well-known capabilities and fix some typo
2019-07-25 11:11:49 -05:00
Dan Williams d34f7f4ead
Merge pull request #692 from mars1024/bugfix/validatePlugin
libcni: find plugin in exec
2019-07-25 11:11:35 -05:00
Bruce Ma 1318d7c94c libcni: find plugin in exec
Signed-off-by: Bruce Ma <brucema19901024@gmail.com>
2019-07-25 10:16:23 +08:00
Piotr Skamruk df124b22e4
Merge pull request #678 from mccv1r0/pr661
libcni: add config caching [v2]
2019-07-20 11:32:07 +02:00
Michael Cambria 9af40ed73a libcni: add config caching
Cache the config JSON, CNI_ARGs, and CapabilityArgs on ADD operations,
    and remove them on DEL. This allows runtimes to retrieve the cached
    information to ensure that the pod is given the same config and options
    on DEL as it was given on ADD.

    Add versioning to beginning of cache file
    Combine cached configuration and results into one file

Signed-off-by: Michael Cambria <mcambria@redhat.com>
2019-07-19 17:49:42 -04:00
Casey Callendrello 461cf2b48f
Merge pull request #690 from BSWANG/master
readme: add Alibaba Cloud CNI plugin 'Terway' to the list
2019-07-17 11:20:17 -04:00
Casey Callendrello b2c510b758
Merge pull request #689 from rosenhouse/bump-linux-version
bump linux to Bionic 18.04 in Travis and Vagrant
2019-07-17 11:12:12 -04:00
Bruce Ma 722a488070 docs: add ips and mac to well-known capabilities and fix some typo
Signed-off-by: Bruce Ma <brucema19901024@gmail.com>
2019-07-16 22:50:35 +08:00
Bruce Ma 227c438835 SPEC: add some well-known error codes
Signed-off-by: Bruce Ma <brucema19901024@gmail.com>
2019-07-15 22:50:17 +08:00
Bruce Ma ba034ef5da testcases: make testcase use suitable error code
Signed-off-by: Bruce Ma <brucema19901024@gmail.com>
2019-07-15 22:33:23 +08:00
Bruce Ma 4b29940c46 skel: clean up typed Errors in skel
Signed-off-by: Bruce Ma <brucema19901024@gmail.com>
2019-07-15 21:36:18 +08:00
Bruce Ma 50192c0c4c types : add NewError method
Signed-off-by: Bruce Ma <brucema19901024@gmail.com>
2019-07-15 20:04:01 +08:00
bingshen.wbs 0af0477c17 readme: add Alibaba Cloud CNI plugin 'Terway' to the list
Signed-off-by: bingshen.wbs <bingshen.wbs@alibaba-inc.com>
2019-07-11 13:55:48 +08:00
Gabe Rosenhouse b92d83cb59 bump linux to Bionic 18.04 in Travis and Vagrant 2019-07-10 08:32:50 -07:00
Dan Williams 220a58f522
Merge pull request #688 from dholbach/patch-1
add missing comma, fixes syntax error
2019-07-10 10:03:39 -05:00
Daniel Holbach a48337ac26 add missing commas, fix syntax error
Bryan spotted the second one - well spotted
2019-07-05 11:39:39 +02:00
Matt Dupre dc71cd2ba6
Merge pull request #681 from mars1024/clean_up
clean up : fix staticcheck warnings
2019-07-03 16:03:56 +01:00
Michael Cambria 5077b14734 Add stringifyArgs and parseArgs functions to utils.go
Signed-off-by: Michael Cambria <mcambria@redhat.com>
2019-06-28 11:56:55 -04:00
Dan Williams ef03d0458c
Merge pull request #682 from dcbw/cachedir
libcni: add InitCNIConfigWithCacheDir() and deprecate RuntimeConfig.CacheDir
2019-06-26 08:03:07 -05:00
Dan Williams 80ad241eb6 libcni: add InitCNIConfigWithCacheDir() and deprecate RuntimeConfig.CacheDir
CacheDir is a property of the entire CNIConfig object and there is no
point to having it be different between container invocations. It was
originally put in RuntimeConfig to make re-vendoring easier so that
users wouldn't have to update calls to InitCNIConfig.
2019-06-20 09:39:25 -05:00
Bruce Ma 5dbeae87ff clean up : remove useless variable
Signed-off-by: Bruce Ma <brucema19901024@gmail.com>
2019-06-19 23:53:31 +08:00
Bruce Ma a03dc28d25 clean up : fix staticcheck warnings
Signed-off-by: Bruce Ma <brucema19901024@gmail.com>
2019-06-19 19:45:29 +08:00
Casey Callendrello dc953e2fd9
Merge pull request #668 from adelina-t/patch-1
Docs: Small typo fixes in main README.md
2019-06-12 17:24:20 +02:00
Casey Callendrello 4cfb7b5689
Merge pull request #663 from mars1024/modify/cniargs_for_delegate
delegate : allow delegation funcs override CNI_COMMAND env automatically in heritance
2019-06-05 17:31:12 +02:00
Dan Williams 33bb6067c0
Merge pull request #672 from dcbw/go-v1.12
Release: bump go to v1.12
2019-06-03 08:52:59 -05:00
Dan Williams e3a51e54fd Release: bump go to v1.12 2019-05-29 11:32:55 -05:00
Dan Williams d11ef1fb4e
Merge pull request #669 from mccv1r0/gnlcached
Add GetNetworkListCachedResult method to CNI interface
2019-05-29 10:30:36 -05:00
Michael Cambria 53e9245e7b add GetNetworkListCachedResult to CNI interface 2019-05-17 11:59:05 -04:00
Bruce Ma 1475a6e3c0 add some testcases for invoke/args
Signed-off-by: Bruce Ma <brucema19901024@gmail.com>
2019-05-16 17:14:00 +08:00
Bruce Ma e67d6cba0f delegate : allow delegation funcs override CNI_COMMAND env automatically in heritance
Signed-off-by: Bruce Ma <brucema19901024@gmail.com>
2019-05-16 16:49:18 +08:00
Bryan Boreham 5bab2c0253
Merge pull request #662 from mars1024/bugfix/cniargs_override
invoke : ensure custom envs of CNIArgs are prepended to process envs
2019-05-15 16:51:49 +01:00
Adelina Tuvenie cbca75246a Docs: Small typo fixes in main README.md 2019-05-15 18:29:54 +03:00
Dan Williams a94ff7bdaf
Merge pull request #666 from s1061123/add_route_override
Add cni-route-override to CNI plugin list
2019-05-15 10:27:46 -05:00
Tomofumi Hayashi 0469a05ab5 Add cni-route-override to CNI plugin list 2019-05-09 15:36:31 +09:00
Bruce Ma e40cce2373 add deduplication in return of CNIArgs
Signed-off-by: Bruce Ma <brucema19901024@gmail.com>
2019-05-09 13:57:16 +08:00
Bruce Ma ea274f7629 ensure custom envs of CNIArgs are prepended to process envs
Signed-off-by: Bruce Ma <brucema19901024@gmail.com>
2019-05-05 12:34:14 +08:00
Dan Williams f726df9103
Merge pull request #659 from Colstuwjx/cnitool-check-example
Upgrade to cni spec v0.4.0.
2019-05-01 10:05:57 -05:00
Colstuwjx ffc61ed2d3 Upgrade to cni spec v0.4.0. 2019-04-29 18:20:30 +08:00
Casey Callendrello 7d76556571
Merge pull request #658 from squeed/bump-spec-040
SPEC: add references to spec version 0.4.0
2019-04-26 18:00:55 +02:00
Casey Callendrello c47f15614b SPEC: add references to spec version 0.4.0 2019-04-26 14:04:09 +02:00
Dan Williams ec5ec68fa4
Merge pull request #655 from danwinship/error-no-error
Return a better error when the plugin returns none
2019-04-17 10:22:34 -05:00
Dan Williams 530eed4d27
Merge pull request #651 from SataQiu/fix-spell-20190404
Fix some spell errors
2019-04-15 16:45:14 -05:00
Dan Williams 121ab0058f
Merge pull request #652 from SataQiu/fix-link-20190409
Make link point to the correct version
2019-04-15 16:44:47 -05:00
Dan Williams de8cf72507
Merge pull request #653 from SataQiu/fix-spell-error
Fix spell error for configuration json
2019-04-15 16:44:03 -05:00
Dan Winship 59d8d02f67 Return a better error when the plugin returns none 2019-04-12 14:19:01 -04:00
SataQiu 8361cffee2 make link point to the correct version 2019-04-09 17:46:58 +08:00
SataQiu 4461d54929 fix spell error for configuration json 2019-04-09 17:09:24 +08:00
SataQiu 207be7e31e fix some spell errors 2019-04-04 23:36:59 +08:00
Bryan Boreham 0471e018e5
Merge pull request #647 from xichengliudui/fix404notfound
update spec-upgrades.md make it point to a specific web page
2019-04-03 17:02:41 +01:00
Dan Williams c9c5031a1b
Merge pull request #650 from xichengliudui/patch-2
Fix 404 not found
2019-04-03 10:05:58 -05:00
lIuDuI 6bddfcd3bb
Fix 404 not found 2019-03-30 19:09:43 +08:00
Dan Williams fbb95fff8a
Merge pull request #597 from dcbw/conventions-ip-prefix
CONVENTIONS: add support for IP prefixes
2019-03-20 10:58:02 -05:00
Matt Dupre f7e07eec38
Merge pull request #622 from trungnvfet/fix_http_https
Change http to https for security links
2019-03-20 15:52:47 +00:00
Bryan Boreham c976b421df
Merge pull request #627 from longkb/update_deprecated_link
Update deprecated link in doc
2019-03-20 15:51:05 +00:00
Dan Williams 139f460f90
Merge pull request #632 from hnwolf/fix_removed_document_link
Remove incorrect document link
2019-03-20 10:49:31 -05:00
Dan Williams fe0a28d82a
Merge pull request #639 from nagiesek/dnsCapabilityUpdate
Update conventions.md to include dns capability
2019-03-20 10:48:37 -05:00
Dan Williams 2c4013efcd
Merge pull request #621 from truongnh1992/fix-typo
doc: fix typo
2019-03-20 10:43:25 -05:00
Dan Williams 0dba47b1f9
Merge pull request #641 from dcbw/spec-clarify-routes
SPEC.md: clarify meaning of 'routes'
2019-03-20 10:41:23 -05:00
Dan Williams 4554856998 SPEC.md: clarify meaning of 'routes'
Routes are expected to be relevant to the sandbox interface.
2019-03-20 10:38:40 -05:00
Dan Williams f4fdaa2304
Merge pull request #617 from luksa/convert_from_020_gw
Don't copy gw from IP4.Gateway to Route.GW When converting from 0.2.0
2019-03-20 10:35:18 -05:00
1693291525@qq.com f55f75bf0e update spec-upgrades.md 2019-03-20 02:24:27 -04:00
Marko Lukša 83ed2459f7 Add tests provided by Dan Williams (@dbcw) 2019-03-15 10:04:20 +01:00
Marko Lukša 16d174d296 Don't copy gw from IP4.Gateway to Route.GW When converting from 0.2.0
When converting from 0.2.0 to 0.3.x, we used to copy the Gateway defined
under IP4.Gateway to each route that had GW set to nil. This is wrong
because the result of converting from 0.2.0 to 0.3.x and back to 0.2.0
doesn't match the original config.

Also, the spec says the following about routes.gw: "IP of the gateway.
If omitted, a default gateway is assumed (as determined by the CNI
plugin)."

So, it should be up to the CNI plugin to determine what to do when the
gateway in a route isn't specified. Therefore, when converting from
0.2.0 to 0.3.x, we should NOT populate the gateway field.
2019-03-15 10:01:46 +01:00
Kim Bao Long f00ca5f42b Update deprecated link in doc
Currently, the modified **plugins/main** was removed from master branch.
It's only available in **v0.3.0** tag. So this commit aims to update the
deprecated link to the working one.

Signed-off-by: Kim Bao Long <longkb@vn.fujitsu.com>
2019-03-14 08:13:14 +07:00
Casey Callendrello 6896907ab4
Merge pull request #626 from opensvc/arnaudveron-opensvc-addon
Add OpenSVC to the list of container runtimes
2019-03-13 16:42:41 +01:00
Casey Callendrello 3d19d747b6
Merge pull request #635 from longkb/remove_the_deprecated_links
Remove deprecated link in SPEC.md
2019-03-13 16:17:05 +01:00
Dan Williams 203f70b6bb
Merge pull request #636 from andrey-ko/result-print-to
add PrintTo method to Result interface
2019-03-13 10:14:36 -05:00
Dan Williams 2e00c58a3b
Merge pull request #637 from ajacoutot/typo
invoke: fix typo (opensbd -> openbsd)
2019-03-13 10:11:52 -05:00
Nathan Gieseker 5761cff2ef Update conventions.md to include dns capability 2019-03-12 21:44:55 -07:00
Antoine Jacoutot b3342a6e36 invoke: fix typo (opensbd -> openbsd) 2019-02-28 10:06:17 +01:00
akolomentsev 667a69ea9d add PrintTo method to Result interface, in order to allow to marshal result to any writer, not only stdout
Signed-off-by: Andrey Kolomentsev <andrey.kolomentsev@gmail.com>
2019-02-27 20:13:32 -08:00
Nguyen Hung Phuong b69a8d52b6 Remove incorrect document link 2019-02-26 08:39:29 +07:00
Kim Bao Long 8574263c4e Remove deprecated link in SPEC.md
Current, the URLs that link docs of **rkt_networking_proposal** and
**rkt_networking_design** are already dead. So this commit aims to
clean up them from **SPEC.md**.

Co-Authored-By: Nguyen Phuong An <AnNP@vn.fujitsu.com>
Signed-off-by: Kim Bao Long <longkb@vn.fujitsu.com>
2019-02-25 11:22:43 +07:00
Arnaud Veron b1838c7f90
Add OpenSVC to the list of container runtimes 2019-02-21 15:49:41 +01:00
Nguyen Hai Truong f36276e1bc doc: fix typo
Although it is spelling mistakes, it might make
affects while reading.

Signed-off-by: Nguyen Hai Truong <truongnh@vn.fujitsu.com>
2019-02-20 20:11:01 -08:00
Matt Dupre 8fa373b4fd
Merge pull request #624 from annp1987/remove_duplicate_worlds
Remove duplicate word 'the the'.
2019-02-20 16:30:19 +00:00
Matt Dupre 405805692c
Merge pull request #625 from huynq0911/fix_http_https
Change http to https for security links
2019-02-20 16:29:46 +00:00
Nguyen Quang Huy c96f9d1d95 Change http to https for security links
For security, we should change http into https links.
2019-02-20 15:07:48 +07:00
Nguyen Phuong An 5ba9bb8fa0 Remove duplicate word 'the the'.
Signed-off-by: Nguyen Phuong An <AnNP@vn.fujitsu.com>
2019-02-20 13:56:29 +07:00
Nguyen Van Trung ab0ee6fa98 Change http to https for security links
Signed-off-by: Nguyen Van Trung <trungnv@vn.fujitsu.com>
2019-02-15 10:18:04 +07:00
Bryan Boreham 7aacc67f18
Merge pull request #619 from annp1987/master
Use /usr/bin/env bash in shebang for docker-run.sh
2019-02-13 16:57:51 +00:00
Nguyen Phuong An bfcd424c0e Use /usr/bin/env bash in shebang for docker-run.sh 2019-02-13 15:26:08 +07:00
Bryan Boreham 51885a9b69
Merge pull request #618 from SchSeba/small_fix
Fix cnitool readme
2019-02-07 17:16:36 +00:00
Sebastian Sch c9bc5b79ec Fix cnitool readme.
build.sh script doesn't exist anymore change to build_linux.sh or
build_windows.sh
2019-02-07 12:18:41 +02:00
Dan Williams 8e8abe455b
Merge pull request #616 from JoeWrightss/patch-4
Fix some typos in SPEC.md
2019-02-06 10:15:30 -06:00
Bryan Boreham 4425e7e337
Merge pull request #615 from squeed/update-maint
Update Casey's email address.
2019-01-31 14:47:32 +00:00
Casey Callendrello 93bfd62fb1 Update Casey's email address. 2019-01-31 15:26:25 +01:00
zhoulin xie 06b37e1ff5 Fix some typos in SPEC.md
Signed-off-by: zhoulin xie <zhoulin.xie@daocloud.io>
2019-01-31 22:25:12 +08:00
Dan Williams 0cf415d434
Merge pull request #613 from bradmbock/add-vmwnsx-cni
Added VMware NSX CNI plugin to CNI plugin list.
2019-01-30 10:33:20 -06:00
Dan Williams 0126b26304
Merge pull request #614 from yeya24/patch/fixtypo
fix some typos
2019-01-30 10:05:05 -06:00
yeya24 afdc49d0a6 fix some typos 2019-01-29 22:55:01 +08:00
Bradley Bock d6ffc8d74f Added VMware NSX CNI plugin to CNI plugin list. 2019-01-28 14:17:37 -08:00
Bryan Boreham 762bb8ae5d
Merge pull request #602 from liucimin/fix_version_ParseVersion_function
fix the ParseVersion when the version is  will get fnAtoi
2019-01-23 16:29:37 +00:00
Matt Dupre 1c35013cf4
Merge pull request #612 from JoeWrightss/patch-3
Fix some typos in comment
2019-01-23 16:07:50 +00:00
zhoulin xie ce8938617e Fix some typos in comment
Signed-off-by: zhoulin xie <zhoulin.xie@daocloud.io>
2019-01-17 19:38:40 +08:00
Casey Callendrello ce46ad0130
Merge pull request #609 from containernetworking/community-20190130
Announce next community meeting in Jan 2019
2019-01-09 21:41:49 -08:00
Bryan Boreham b74b969c00 Announce next community meeting in Jan 2019
Signed-off-by: Bryan Boreham <bjboreham@gmail.com>
2019-01-09 16:34:24 +00:00
Dan Williams e2bdb13345
Merge pull request #607 from rst0git/master
readme: Update CNI plugins install example
2019-01-09 09:52:05 -06:00
liucimin 94bf13891a fix the ParseVersion when the version is will get fnAtoi 2018-12-25 14:31:39 +08:00
Radostin Stoyanov d6ed2fd5a3 readme: Update CNI plugins install example
Commit containernetworking/plugins@4e1f780 (Split build.sh into two
OS-specific scripts) removes the build.sh file. Now we have
build_linux.sh and build_windows.sh

Signed-off-by: Radostin Stoyanov <rstoyanov1@gmail.com>
2018-12-20 21:53:17 +00:00
Casey Callendrello 213b3fdea2
Merge pull request #600 from nak3/valid-args-num
Validate correct number of args in cnitool
2018-12-19 11:51:28 -05:00
Dan Williams b7ae50cbb4
Merge pull request #604 from JoeWrightss/patch-1
Fix some typos
2018-12-19 10:40:27 -06:00
Dan Williams 3de1875e27
Merge pull request #603 from containernetworking/bboreham-patch-1
Update roadmap with change from GET to CHECK
2018-12-19 10:40:04 -06:00
JoeWrightss 3a3a701be0 Fix some typos
Signed-off-by: JoeWrightss <zhoulin.xie@daocloud.io>
2018-12-07 23:19:51 +08:00
Bryan Boreham 34a6112b3b
Update roadmap with change from GET to CHECK 2018-12-06 12:42:47 +00:00
Kenjiro Nakayama 3d6117d63e Validate correct number of args in cnitool
Currently, cnitool command needs 4 args - e.g cnitool add <net>
<netns>. This patch changes to the number of valide args to 4 from 3.
2018-11-29 17:20:24 +09:00
Dan Williams 66dafdf3e9 CONVENTIONS: add support for IP prefixes
While many plugins already have IPAM range info not all do. Without
a range/subnet in configuration, passing a plain IP address in
CNI_ARGS or via 'args' does not provide enough information.
2018-11-28 16:04:33 -06:00
Dan Williams fdcc5db51b
Merge pull request #596 from nak3/update-go-version
Update supported Go version statement
2018-11-28 10:08:47 -06:00
Kenjiro Nakayama 297457de48 Update supported Go version statement 2018-11-28 16:13:03 +09:00
Dan Williams dba13c4822
Merge pull request #593 from Levovar/add_danm
README: expands list of 3rd party plugins with DANM
2018-11-14 10:12:16 -06:00
Levente Kale c744c26d2e README: expands list of 3rd party plugins with a new option
DANM -a K8s networking solution using CNI- was recently made opensource.
PR expands the list of 3rd party plugins with the reference to the project.
2018-11-10 13:19:32 +01:00
Dan Williams 951c0fb806
Merge pull request #592 from rosenhouse/bump-to-go-1.11
bump travis and vagrantfile to go 1.11
2018-11-07 09:28:20 -06:00
Gabe Rosenhouse b271ce351d bump travis and vagrantfile to go 1.11 2018-10-31 08:39:54 -07:00
Dan Williams 47f042a96e
Merge pull request #590 from containernetworking/move-del-text
Move spec text about DEL and errors
2018-10-31 10:33:24 -05:00
Bryan Boreham a8a44a721a
Merge pull request #591 from containernetworking/eagain
Add a well-known error for "try again"
2018-10-28 18:18:44 +02:00
Bryan Boreham cc562d1b44 Add a well-known error for "try again" 2018-10-17 15:37:38 +00:00
Bryan Boreham 24aa1f4030 Move spec text about DEL and errors
This paragraph was under a heading specifically about configuration
lists, but it applies to all DEL operations whether in a list or not.

Signed-off-by: Bryan Boreham <bjboreham@gmail.com>
2018-10-17 15:31:04 +00:00
Dan Williams 1dba71945f
Merge pull request #588 from GodloveD/add-singularity-to-readme
Added Singularity to the README.md.
2018-10-17 10:25:04 -05:00
GodloveD f301e8fe68 Added Singularity to the README.md. It began supporting cni in 3.0. 2018-10-09 17:08:42 -04:00
Dan Williams 777324ca6b
Merge pull request #584 from dcbw/get-to-check
skel,invoke,libcni: GET -> CHECK
2018-09-26 11:43:02 -05:00
Dan Williams df75bcfdbe libcni: caching should use new uniqueness tuple (netname, containerid, ifname) 2018-09-26 11:41:14 -05:00
Dan Williams 89d8d97746 types,libcni: implement DisableCheck 2018-09-26 11:20:47 -05:00
Dan Williams 68c963649f libcni: add GetNetwork[List]CachedResult
Now that GET no longer exists and no longer returns the
cached result, provide a public method to return the cached
result that runtimes can use.
2018-09-26 11:03:05 -05:00
Dan Williams 42e857f0a2 skel,invoke,libcni: GET -> CHECK
Major change is that CHECK no longer returns a Result, per the spec.
2018-09-26 10:51:49 -05:00
Dan Williams f145359def
Merge pull request #568 from liucimin/feature_exec_timeout
libcni/invoke: add context argument to enable cancelation and timeouts
2018-09-26 10:19:17 -05:00
liucimin c5ab1c3fc6 add the timeout context 2018-09-25 09:52:59 +08:00
Dan Williams 7418eded11
Merge pull request #587 from mccv1r0/cnitool
cnitool: Honor interface name supplied via CNI_IFNAME environment variable.
2018-09-24 16:13:51 -05:00
Michael Cambria 626458fb67 gofmt needed 2018-09-24 14:51:38 -04:00
Michael Cambria 01eda4e0cf Use interface name supplied via CNI_IFNAME environment variable, when set. Default to eth0 otherwise 2018-09-24 13:29:28 -04:00
Dan Williams 93b8b11111
Merge pull request #579 from matthewdupre/check-spec
Replace GET action in spec with proposed CHECK action
2018-09-12 10:55:48 -05:00
Casey Callendrello d59bfbf33e SPEC: CHECK: clarify difference between "tracked" and "non-tracked" resources 2018-09-12 17:13:07 +02:00
Gabe Rosenhouse 47cf2da00b
Merge pull request #578 from lifubang/loopback020
when type is loopback, IP4 or IP6 is always nil
2018-08-22 08:22:31 -07:00
Matt Dupre 57da9a82b0 Have GET action not return anything on success 2018-08-22 15:37:56 +01:00
Matt Dupre 97b1f58534 Replace GET action in spec with proposed CHECK action 2018-08-15 15:46:46 +01:00
Lifubang 5ef0e9aa4c when loopback, IP4 or IP6 is always nil
Signed-off-by: Lifubang <lifubang@aliyun.com>
2018-08-13 18:50:23 +08:00
Dan Williams 779dc12496
Merge pull request #570 from dcbw/prev-result-helpers
types/version: add helper to parse PrevResult
2018-08-09 13:33:51 -05:00
Dan Williams d2836a7b59 types/version: add helper to parse PrevResult
Plugins can use the new helpers to parse a prevResult.  When parsing
their NetConf structure, assuming it has a types.NetConf embedded:

  type MyConf struct {
    types.NetConf
    ...
  }

  conf := MyConf{}
  if err := json.Unmarshal(stdin, &conf); err != nil {
    return nil, fmt.Errorf("failed to parse network configuration: %v", err)
  }

  if err := version.ParsePrevResult(&conf.NetConf); err != nil {
    return nil, err
  }

and then when the plugin wishes to access the PrevResult it can transform
conf.PrevResult into any Result version it wants:

  if conf.PrevResult != nil {
    res, err := current.NewResultFromResult(conf.PrevResult)
    if err != nil {
      return nil, fmt.Errorf("could not convert result to current version: %v", err)
    }
    for _, ip := range res.IPs {
      ...
    }
  }
2018-08-09 13:27:57 -05:00
Dan Williams cbd2718984
Merge pull request #572 from dcbw/skel-about
skel: only print about message, not errors
2018-08-08 10:46:45 -05:00
Dan Williams 9c9cc5365d
Merge pull request #574 from xiangpengzhao/add-knitter
List Knitter as 3rd party plugin
2018-08-08 10:32:51 -05:00
Dan Williams 0750650021
Merge pull request #576 from acmcodercom/master
Add name field in 99-loopback.conf because network name is required.
2018-08-08 10:32:19 -05:00
Lifubang 9fc6eb434c Add name field in 99-loopback.conf because network name is required. 2018-08-08 10:30:45 +08:00
xiangpengzhao 478f7b417e List Knitter as 3rd party plugin 2018-08-03 14:16:29 +08:00
Dan Williams 0f6a509e4b
Merge pull request #565 from dcbw/spec-members
spec: be explicit about optional and required structure members
2018-08-02 12:44:13 -05:00
Dan Williams 2ce2c24cc2 skel: only print about message, not errors
Instead of:

CNI_COMMAND env variable missing
<about message here>
{
    "code": 100,
    "msg": "required env variables missing"
}

print:
<about message here>
2018-07-26 10:51:40 -05:00
Dan Williams e67bb289cc
Merge pull request #566 from dineshb-jnpr/master
Add Juniper Contrail to 3rd party plugin list
2018-07-05 16:07:35 -05:00
dineshb-jnpr 9bd516a786 Add Juniper Contrail to 3rd party plugin list
This change is to update the 3rd Party plugin section of README
to include and list the K8s CNI solution from Juniper Networks, INC.
2018-07-02 17:23:29 -07:00
Dan Williams 9a34081fef spec: be explicit about optional and require structure members
Fixes: #564
2018-06-28 13:12:35 -05:00
Dan Williams d974c1dd54
Merge pull request #562 from jingax10/master
Fix broken build for cni introduced by https://github.com/containernetworking/cni/pull/524
2018-06-28 12:02:30 -05:00
Jing Ai 96343561e0 Fix broken build for cni introduced by https://github.com/containernetworking/cni/pull/524. 2018-06-20 22:42:04 -07:00
Dan Williams aed7a2f2a1
Merge pull request #524 from squeed/validate
libcni: add ValidateNetwork and ValidateNetworkList functions
2018-06-20 10:39:28 -05:00
Casey Callendrello 07c1a6da47
Merge pull request #558 from dcbw/libcni-exec-interface
libcni: make exec handling an interface for better downstream testing
2018-06-13 17:08:47 +02:00
Casey Callendrello b34795b3ce libcni: add ValidateNetwork and ValidateNetworkList functions
These perform some static validation on a network configuration,
ensuring it is generally executable.
2018-06-13 16:35:17 +02:00
Dan Williams ffc1e7b446 invoke: fix an existing gofmt complaint 2018-06-11 15:33:53 -05:00
Dan Williams 09dac4ecdc libcni: make exec handling an interface for better downstream testing
When a downstream project just uses libcni, it takes a lot of harness
code to set up binaries on-disk that get called.  Instead we can handle
that by passing an execer interface to libcni APIs that gets called
instead, which the downstreams can use to avoid execing thing entirely
and instead check arguments/environment in code.

Downstream users that currently create a CNIConfig object like this:

    cniconf := libcni.CNIConfig{Path: []string{"foobar"}}

Can now fake out all on-disk binary exec/find operations with:

    cniconf := libcni.NewCNIConfig([]string{"foobar"}, <exec interface>)
2018-06-11 15:33:53 -05:00
Dan Williams 86431d5c16
Merge pull request #557 from containernetworking/if-name-matters
Network interface name matters, updated
2018-05-31 13:41:59 -05:00
Bryan Boreham 6106bbf116 Use explicit identifiers in key tuple 2018-05-30 15:49:04 +00:00
Bryan Boreham df12d88b58 A couple of tests should be testing 0.3.1 explicitly 2018-05-30 15:40:47 +00:00
Casey Callendrello 3ffb039b29
Merge pull request #554 from squeed/skel-plugin-version
skel: add support for plugin version string
2018-05-30 17:34:47 +02:00
Mike Spreitzer 7f05cbb33b First draft that passes tests 2018-05-30 15:16:38 +00:00
Mike Spreitzer b43173e5d2 start drafting change to make ifname matter 2018-05-30 15:11:19 +00:00
Casey Callendrello 35d510c0ee
Merge pull request #556 from squeed/bw-convention
Conventions: add bandwidth limits
2018-05-30 17:07:14 +02:00
Casey Callendrello d2be2fa823 Conventions: add bandwidth limits 2018-05-25 19:09:56 +02:00
Casey Callendrello 197eadbe52 skel: add support for plugin version string 2018-05-24 14:26:42 +02:00
Casey Callendrello 85488fbd8d
Merge pull request #490 from dcbw/get-action
Add a GET action to the CNI spec, packages, and sample plugin
2018-05-23 17:22:32 +02:00
Dan Williams ef2ae62d8f
Merge pull request #553 from dcbw/ovn-kube
README: add ovn-kubernetes to the plugin list
2018-05-22 11:32:32 -05:00
Dan Williams fed8bad0cb libcni: add GET support and result caching
Per the spec, a Result from ADD is cached and provided to GET and DEL
commands.
2018-05-21 16:05:31 -05:00
Dan Williams 49ebeedfbb libcni: use Add/DelNetworkList to implement Add/DelNetwork 2018-05-21 16:05:31 -05:00
Dan Williams 23a5d8e617 spec/invoke/skel: add GET command 2018-05-21 16:05:31 -05:00
Dan Williams 7b8b5da47b spec/pkg: bump version to 0.4.0 2018-05-16 10:21:17 -05:00
Dan Williams eea7a08b3c pkg/version: add some version-handling utility functions 2018-05-16 10:21:15 -05:00
Dan Williams 4372fe2566 README: add ovn-kubernetes to plugin list 2018-05-15 20:34:17 -05:00
Matt Dupre a700ea864b
Merge pull request #543 from qianzhangxa/remove-version-param
spec: remove the version parameter from ADD and DEL commands.
2018-03-07 16:29:29 +00:00
Bryan Boreham 974e2cf668
Merge pull request #546 from mhausenblas/patch-1
fixes the title in specification
2018-03-07 16:20:52 +00:00
Michael Hausenblas cd2d779931
fixes the title in specification
Might be an historical artefact but I believe the correct full name for CNI should be Container Network Interface 

CC: @bboreham
2018-03-06 09:54:24 +00:00
Dan Williams 9aab8b3e9f
Merge pull request #538 from rosenhouse/bump-golang-1.10
travis: bump golang versions
2018-02-28 10:02:33 -06:00
Gabe Rosenhouse 05a3c9c213 travis: bump golang versions
- test against Go 1.10
- stop testing against Go 1.8

since Go language maintainers no longer support 1.8
see: https://golang.org/doc/devel/release.html#policy
2018-02-28 07:00:16 -08:00
Qian Zhang e3b496a3b6 spec: remove the version parameter from ADD and DEL commands.
The CNI spec version passed from runtime to plugin is the `cniVersion`
field in the network configuration rather than a parameter to the ADD
and DEL command. So, remove the version parameter from the spec, and
also describe how the `cniVersion` field in the network configuration
should be used by the plugin.

Fixes #542
2018-02-27 09:59:57 +08:00
Gabe Rosenhouse 142cde0c76
Merge pull request #536 from dcbw/require-network-name
pkg/skel: return error if JSON config has no network name
2018-02-17 19:21:24 -08:00
Gabe Rosenhouse 826f754e82
Merge pull request #535 from dcbw/no-sudo-required
tests: sudo not actually required
2018-02-17 18:55:25 -08:00
Dan Williams d4bae8512a pkg/skel: return error if JSON config has no network name
SPEC.md has indicated that 'name' is required since at least mid-2015,
but that was not enforced in pkg/skel or our unit tests.  That could
lead to configs passing no network name and plugins like host-local
that require the name failing or causing odd behavior.

Since the name is required by the spec, require it in the code too.
2018-02-16 16:14:30 -06:00
Dan Williams c3fc2ae6e4 tests: sudo not actually required
The legacy example plugin and its libcni backwards_compatibility_test.go
just use the noop plugin, which doesn't do anything with the netns path
or make any interfaces inside the netns.  So we can remove anything
netns related from the test, which means we no longer need sudo.
2018-02-16 11:46:03 -06:00
Dan Williams 6cddc9a378
Merge pull request #529 from joerg84/patch-1
Described Mesos as Apache project.
2018-02-07 09:47:58 -06:00
Joerg Schad 55d1c96643
Described Mesos as Apache project. 2018-01-24 21:34:26 +01:00
Casey Callendrello 693cc869fd
Merge pull request #525 from squeed/range-arg
CONVENTIONS: add new `ipRanges` capability.
2018-01-24 18:39:33 +01:00
Gabe Rosenhouse 1773a1f24c
Merge pull request #528 from huangjiuyuan/fix-typo
Fix typo in CONVENTIONS.md
2018-01-19 14:30:55 -08:00
Gabe Rosenhouse d59d5c6cda
Merge pull request #96 from zachgersh/make-ipam-type
pkg/types: Makes IPAM concrete type
2018-01-17 10:42:08 -08:00
huangjiuyuan bb3445a503 Fix typo in CONVENTIONS.md 2018-01-11 10:24:03 +08:00
Bryan Boreham 9ded5d186f
Merge pull request #527 from Intel-Corp/dev/bonding-cni-3rd-party-readme-pr
Adding the Bonding CNI plugin as 3rd party plugin
2018-01-10 16:53:04 +00:00
Kuralamudhan Ramakrishnan 6787bb6591
Update README.md 2017-12-15 18:20:33 +00:00
Kuralamudhan Ramakrishnan 7099c732cb
Adding the Bonding CNI plugin as 3rd party plugin
- Bonding CNI provides a method for aggregating multiple network interfaces into a single logical "bonded" interface.
- Linx Bonding drivers provides various flavour of bonded interface depending on the mode (bonding policies), such as round robin, active aggregation according to the 802.3 ad specification
For more information on the bonding driver. Please refer to [kernel doc(https://www.kernel.org/doc/Documentation/networking/bonding.txt)
2017-12-15 16:41:43 +00:00
Gabe Rosenhouse 97ce633ca1
Merge pull request #526 from davidk01/patch-1
kurma is no longer maintained
2017-12-13 08:24:23 -08:00
david karapetyan 74b3024fa8
kurma is no longer maintained
Kurma is a derelict project so should be removed
2017-12-12 14:09:12 -08:00
Casey Callendrello 68ae5290ea CONVENTIONS: add new `ipRanges` capability.
Also, restructure the document slightly.
2017-12-08 16:28:59 +01:00
Casey Callendrello 9b747fabbd
Merge pull request #516 from rosenhouse/add-appveyor-badge
README: add badge for appveyor (Windows CI) status
2017-12-07 11:51:46 +01:00
Gabriel Rosenhouse f8fb397969 README: add badge for appveyor (Windows CI) 2017-12-03 14:13:21 -08:00
Matt Dupre 869505bebd
Merge pull request #517 from matthewdupre/maintainers-update
Maintainers: replace Tom Denham with Matt Dupre
2017-11-21 17:21:36 +00:00
Gabe Rosenhouse 1f824fbf2c
Merge pull request #521 from rosenhouse/types-tests
backfill some unit tests
2017-11-15 08:08:07 -08:00
Gabriel Rosenhouse cf260a13d7 pks/types/current: backfill a couple more unit tests 2017-11-15 00:33:43 -08:00
Gabriel Rosenhouse 61a8bf2ee1 pkg/types: non-pointer Route marshals as JSON
also: backfill unit tests for JSON and string formatting of custom types
2017-11-15 00:33:43 -08:00
Gabe Rosenhouse a88bf49d86
Merge pull request #520 from aaithal/addECSToReadme
readme: add Amazon ECS to runtimes and plugins list
2017-11-15 00:31:47 -08:00
Aithal 0844a98e03 readme: add Amazon ECS to the list 2017-11-14 15:09:32 -08:00
Gabe Rosenhouse eeded9f55e
Merge pull request #518 from porridge/patch-1
Fix indentation
2017-11-07 20:02:14 -08:00
Marcin Owsiany 525880d8bc
Fix indentation 2017-11-07 20:30:02 +01:00
Matt Dupre 464d07ffdb Maintainers: replace Tom Denham with Matt Dupre 2017-11-01 15:34:48 +00:00
Gabe Rosenhouse 384d8c0b52
Merge pull request #514 from rosenhouse/no-vendor
Remove vendor directory
2017-11-01 08:11:26 -07:00
Gabriel Rosenhouse fa8f141f29 Remove vendor dir, simplify build & test tooling
"Libraries should never vendor their dependencies"
  https://peter.bourgon.org/go-best-practices-2016/#dependency-management
The only thing in there were test-support packages anyhow

Remove Godeps folder, nothing to manage!

Remove build.sh
- not much here to build now that we've split out plugins
   - for non-AMD64 platforms, travis just runs go build directly

Clean up test.sh
- no vendor --> simpler logic for enumerating packages
- use "bash strict mode"
   http://redsymbol.net/articles/unofficial-bash-strict-mode
- coveralls token not required for public repos

Simplify .travis.yml, update to work without vendor
- fewer env vars
- go get -t ./...

Clean up Vagrantfile
- no godep required anymore
- bump Golang version 1.9.2
2017-10-31 22:32:18 -07:00
Gabriel Rosenhouse 7f03ee4537 libcni: fix go vet error 2017-10-31 22:24:53 -07:00
Casey Callendrello 4228ca8928 Merge pull request #508 from squeed/cnitool-cid
cnitool: generate container id from the netns path
2017-10-25 14:58:57 +02:00
Gabe Rosenhouse 97314481b4 Merge pull request #515 from rosenhouse/appveyor
Add CI for Windows
2017-10-23 08:19:28 -07:00
Gabriel Rosenhouse 449ac26e38 ci: add appveyor file for Windows CI coverage 2017-10-22 23:21:14 -07:00
Gabriel Rosenhouse 36a22467d9 Windows compatibility: all tests pass! 2017-10-22 23:21:08 -07:00
Gabe Rosenhouse 40837d1ff0 Merge pull request #510 from squeed/containerid
spec, skel: Make the container ID required and unique.
2017-10-21 12:29:23 -07:00
Casey Callendrello b9995395c0 cnitool: generate container id from the netns path, add docs
We shouldn't be creating networks with a blank containerid. Let's
synthesize one from the netns path.

Also, add a basic README.
2017-10-20 14:28:11 +02:00
Casey Callendrello a0a74bbcd3 spec, skel: Make the container ID required and unique.
Previously, the spec did not require the container id to be set.
However, almost every plugin relies on it being unique, including the
CNI-maintained plugins. So, change the spec to require the use of
containerid and that plugins should treat it as a primary key.
2017-10-20 14:26:18 +02:00
Casey Callendrello 03e5b5ff9e Add "spec containes unreleased changes" warning. 2017-10-20 14:26:18 +02:00
Gabe Rosenhouse 30c6934e5c Merge pull request #499 from rosenhouse/golang-to-1.9
Golang versions: add 1.9, drop 1.7
2017-10-18 15:34:51 -07:00
Gabe Rosenhouse 43744a3fce pkg/invoke: fix test pollution 2017-10-18 14:51:35 -07:00
Gabe Rosenhouse baecfa6fda Golang versions: add 1.9.1, drop 1.7 2017-10-18 14:51:33 -07:00
Casey Callendrello ff7c3e02e3 Merge pull request #509 from containernetworking/update-community-sync
Add 2017-10-04 community sync
2017-09-22 11:39:36 +02:00
Casey Callendrello 4b9e11a526 Add 2017-10-04 community sync 2017-09-22 11:39:09 +02:00
Dan Williams b30c419f96 Merge pull request #497 from squeed/args-add-ip
Conventions: add the "ips" arg
2017-09-20 09:50:34 -05:00
Gabe Rosenhouse 6614adf4a4 Merge pull request #505 from dcbw/op-ordering
spec: add notes about ADD/DEL ordering
2017-09-18 20:20:24 -07:00
Dan Williams c0ca8f4db9 spec: add notes about ADD/DEL ordering 2017-09-18 12:25:30 -05:00
Dan Williams b7c7ec76ed Merge pull request #504 from dcbw/gov-voting
governance: add notes about public voting
2017-09-18 12:24:09 -05:00
Gabe Rosenhouse 5da3960e2b Merge pull request #506 from Intel-Corp/dev/vhostuser3rdparty-pr
README: List vhostuser CNI as a 3rd party plugin
2017-09-18 09:34:26 -07:00
kuralamudhan ramakrishnan a35068121c README: List vhostuser as a 3rd party plugin 2017-09-16 00:47:37 +01:00
Dan Williams a2d35cb6a1 Merge pull request #500 from matthewdupre/spec-language
Use more RFC2119 style language in specification (must, should...)
2017-09-14 14:46:52 -05:00
Dan Williams 27a848cc18 Merge pull request #502 from anfernee/version
skel: VERSION shouldn't block on stdin
2017-09-14 13:25:24 -05:00
Dan Williams 5c552f3ffc governance: add notes about public voting 2017-09-13 17:24:40 -05:00
Yongkun Anfernee Gui f5302a2aa9 VERSION shouldn't block on stdin
Right now, I have to do the following
  $ echo | CNI_COMMAND=VERSION bridge
or
  CNI_COMMAND=VERSION bridge
  ^D
to be able to get the result back.

But VERSION doesn't need to read from stdin.
2017-09-07 22:48:10 -07:00
Gabe Rosenhouse 909fe7d10d Merge pull request #501 from rosenhouse/test-all-packages
Test all non-vendored packages
2017-08-30 19:32:53 -07:00
Gabe Rosenhouse a2b0a28ce5 Test all non-vendored packages 2017-08-30 14:59:16 -07:00
Casey Callendrello 98ace24557 Merge pull request #493 from dcbw/governance
docs: add Governance procedure documentation
2017-08-30 17:14:54 +02:00
Gabe Rosenhouse a677030029 Merge pull request #492 from dcbw/roadmap
roadmap: add 1.0 items and clean things up
2017-08-30 08:12:30 -07:00
Dan Williams 3f72c535ea docs: add Governance procedure documentation 2017-08-30 10:10:11 -05:00
Matt Dupre fa69c6eb6a Use more RFC2119 style language in specification (must, should...) 2017-08-30 15:35:18 +01:00
Gabe Rosenhouse 3477001a1b Merge pull request #489 from squeed/clean-build
Release: clean the builddir when building.
2017-08-23 09:25:12 -07:00
Casey Callendrello 0a0d0da638 Conventions: add the "ips" arg 2017-08-22 18:10:07 +02:00
Dan Williams 80c688516d Merge pull request #494 from dcbw/validate-type
libcni: return error if Type is empty
2017-08-18 12:50:31 -05:00
Dan Williams 9c87b066ce Merge pull request #495 from rosenhouse/spec-list-older-versions
Spec lists git tags pointing to old spec versions
2017-08-18 12:49:39 -05:00
Gabriel Rosenhouse 31e696618c spec: table of git tags to older versions 2017-08-17 20:43:48 -07:00
Dan Williams 95599124da libcni: return error if Type is empty
A network config missing Type is pretty useless since it means we can't
find the plugin to execute.  So return an error if Type isn't given.
2017-08-17 22:34:33 -05:00
Dan Williams 5adb89b6d2 roadmap: add 1.0 items and clean things up 2017-08-16 13:14:10 -05:00
Casey Callendrello 6c680c05dd Release: clean the builddir when building. 2017-08-14 14:27:44 +02:00
Bryan Boreham a7885cb6f8 Merge pull request #487 from John-Lin/readme-add-plugin-linen
README plugins list: add Linen CNI plugin
2017-07-28 14:30:50 +01:00
John-Lin adfecd3434 README plugins list: add Linen CNI plugin 2017-07-27 18:35:42 +08:00
Dan Williams b69e640cc0 Merge pull request #482 from feiskyer/cnitool
cnitool: add support for CNI_ARGS
2017-07-12 10:03:31 -05:00
Pengfei Ni feeb3d4e4e cnitool: add support for CNI_ARGS
Signed-off-by: Pengfei Ni <feiskyer@gmail.com>
2017-07-12 07:06:23 +08:00
Casey Callendrello 2370e65262 Merge pull request #481 from tomdee/s390x
scripts/release.sh: Add in s390x architecture
2017-07-06 19:46:10 +02:00
Tom Denham 269d35531d
scripts/release.sh: Add in s390x architecture 2017-07-06 10:39:44 -07:00
Gabe Rosenhouse a2da8f8d7f Merge pull request #427 from squeed/convention-chaining
Conventions: add convention around chaining interfaces
2017-07-05 08:32:19 -07:00
Casey Callendrello 777d535934 Merge pull request #460 from aaithal/loadArgsPanic
pkg/types: safer typecasting for TextUnmarshaler when loading args
2017-07-05 17:15:23 +02:00
Casey Callendrello 6a6f9d7f5c Merge pull request #473 from tomdee/improve-and-document-release
Update and document release process
2017-06-29 11:06:28 +02:00
Tom Denham 34dfbc5d3c
Update and document release process
Replace the rkt release process with a docker one.
Document the process that we follow for each release.
2017-06-28 08:39:26 -07:00
Dan Williams 49d814cf37 Merge pull request #477 from dcbw/fix-interface-index-marshaling
types: fix marshalling of omitted "interfaces" key in IPConfig JSON
2017-06-22 09:39:14 -05:00
Dan Williams 30ab80517f types: fix marshalling of omitted "interfaces" key in IPConfig JSON
Plugins that don't have knowledge of interfaces, like host-local or
other IPAM plugins, should not set the 'interfaces' key of their
returned "Result" JSON.  This should then not be translated into
an interface index of 0, which it was due to the int marshaling and
omitempty.

Instead, ensure that an omitted 'interface' in JSON ends up being
nil in the IPConfig structure, and that a nil ensures that no 'interfaces'
key is present in the JSON.

Yes, this means that plugins must call the 'current.Int(x)' method
when setting the Interfaces member.  Oh well.

Fixes: https://github.com/containernetworking/cni/issues/404
2017-06-21 09:56:12 -05:00
Casey Callendrello 8a045b00e4 Merge pull request #478 from squeed/community-sync
Update note about next Community Sync, 2017-06-21
2017-06-16 13:43:57 +02:00
Casey Callendrello 1af3421e7f Update note about next Community Sync, 2017-06-21 2017-06-16 13:43:05 +02:00
Gabe Rosenhouse be0ea12b47 Merge pull request #474 from rosenhouse/readme-updates
readme updates
2017-06-12 10:06:42 -07:00
Gabe Rosenhouse 95bdbc9368 Merge pull request #475 from rosenhouse/backfill-test-delegate
pkg/invoke: backfill tests of delegate add & del
2017-06-11 22:04:12 -07:00
Gabe Rosenhouse 396da95a80 pkg/invoke: backfill tests of delegate add & del 2017-06-08 23:09:58 -07:00
Gabe Rosenhouse 831120ae89 readme: update link for Cloud Foundry CNI integration 2017-06-08 21:27:52 -07:00
Gabe Rosenhouse fa94835b04 readme updates
- update some links to the new plugins repo.
- update guidance on golang versions
2017-06-08 21:23:10 -07:00
zachgersh 832a52582b pkg/types: split out NetConf.IPAM into own type 2017-06-08 20:42:22 -07:00
Casey Callendrello 2d3fa8467d Merge pull request #406 from GheRivero/cni_error
Return cni.type.Error instead of plain error
2017-06-08 20:02:11 +02:00
Gabe Rosenhouse 6828d6c444 Merge pull request #472 from caniszczyk/add-cncf-ref
Add CNCF reference in README
2017-06-07 18:05:18 -07:00
Gabe Rosenhouse 4b5fac1edd Merge pull request #471 from rosenhouse/clean-up-vendor
Clean up vendor
2017-06-07 12:19:40 -07:00
Chris Aniszczyk 32c26940ca Add CNCF reference in README
Signed-off-by: Chris Aniszczyk <caniszczyk@gmail.com>
2017-06-07 11:23:59 -07:00
Gabe Rosenhouse 8b39063724 Merge pull request #470 from rosenhouse/fixup-vagrantfile
Vagrantfile updates
2017-06-07 07:41:19 -07:00
Casey Callendrello 10ba4c5165 Merge pull request #467 from containernetworking/clarify-args-release
Clarify first released spec with 'args' was 0.2.0
2017-06-07 09:57:31 +02:00
Gabe Rosenhouse d63e1e546a vendor: cleanup unused dependencies after plugin split 2017-06-06 21:08:57 -07:00
Gabe Rosenhouse c2034d812f godeps: update Go version to 1.8 2017-06-06 20:58:48 -07:00
Gabe Rosenhouse e5a22d1aaf Vagrantfile updates
- installs latest Golang
2017-06-06 20:49:44 -07:00
Gabe Rosenhouse ce265ef329 Merge pull request #464 from squeed/remove-pkg
pkg: move packages from cni to plugins
2017-06-06 18:58:28 -07:00
Gabe Rosenhouse 438b74fa33 Merge pull request #469 from mcastelino/document_fix
Documentation: Update README.md to reflect plugin split
2017-06-06 18:52:09 -07:00
Gabe Rosenhouse 449bd85e11 Merge pull request #461 from ericchiang/fix-env-order
pkg/invoke: ensure that custom env vars are prepended to the env
2017-06-06 18:48:43 -07:00
Manohar Castelino d44eee2ee7 Documentation: Update README.md to reflect plugin split
Update README.md to call out how to use priv-net-run.sh and
docker-run.sh scripts post plugin split.

Signed-off-by: Manohar Castelino <manohar.r.castelino@intel.com>
2017-06-06 18:37:23 -07:00
Gabe Rosenhouse 5e25038244 pkg/invoke: add unit test for env var ordering in Args.AsEnv 2017-06-06 18:37:04 -07:00
Bryan Boreham f41070deb0 Clarify first released spec with 'args' was 0.2.0 2017-06-03 14:21:43 +01:00
Bryan Boreham 98826b72cc Merge pull request #465 from caniszczyk/add-coc
Add Code of Conduct
2017-06-03 13:47:28 +01:00
Bryan Boreham 86d081b730 Merge pull request #463 from rosenhouse/readme-add-plugin-silk
readme plugins list: add Silk CNI plugin
2017-06-03 13:45:58 +01:00
Chris Aniszczyk 17d4eea2b6 Add Code of Conduct
CNCF projects are expected to follow a CoC.

Signed-off-by: Chris Aniszczyk <caniszczyk@gmail.com>
2017-06-02 09:10:25 -07:00
Casey Callendrello fe14708aee pkg: move packages from cni to plugins 2017-06-02 16:25:42 +02:00
Gabe Rosenhouse dd2db4544e readme: add Silk CNI plugin 2017-05-31 11:23:45 -07:00
Aithal c8658027aa pkg/types: modify LoadArgs to return a named error when an unmarshalable condition is detected 2017-05-31 09:11:48 -07:00
Dan Williams 73c65fbe83 Merge pull request #462 from christx2/patch-1
Update Readme.md
2017-05-31 10:14:48 -05:00
ChristophLondon 5ccd280188 Update Readme.md
adding nuage networks cni plugin and repo location
2017-05-31 12:04:23 +01:00
Eric Chiang 49fb84b284 pkg/invoke: ensure that custom env vars are prepended to the env
Ensure that custom values take priority over values defined in the
environment by prepending them to the env, not appending.
2017-05-29 17:32:49 -07:00
Casey Callendrello c71a7c068f Merge pull request #457 from dcbw/move-plugins
plugins: moved to containernetworking/plugins
2017-05-24 18:46:33 +02:00
Dan Williams bc0d09e572 plugins: moved to containernetworking/plugins 2017-05-23 13:37:15 -05:00
Aithal 5b4c559d4d pkg/types: safer typecasting for TextUnmarshaler when loading args
types.LoadArgs does an unsafe typecast for env args key value pairs.
This can cause a panic if the config type fields do not implement
the encoding.TextUnmarshaler interface. This commit modifies it to
do a safe type cast and return an error in such scenarios.
2017-05-22 13:13:57 -07:00
Casey Callendrello 8ea1605c26 Merge pull request #455 from knobunc/docs/add-openshift-to-readme
Add OpenShift to the list of container runtimes that support CNI
2017-05-17 10:52:33 +02:00
Dan Williams 2e9bb5c646 Merge pull request #452 from caniszczyk/add-cni-logo
Add CNI Logo to README
2017-05-16 14:32:03 -05:00
Benjamin Bennett 099bde1f26 Add OpenShift to the list of container runtimes that support CNI
OpenShift uses CNI, so I thought a link to the appropriate docs made
sense in the top-level readme where it lists the other supporting
runtimes.
2017-05-16 08:33:35 -04:00
Chris Aniszczyk 41cff3fdfb Add CNI Logo to README
Signed-off-by: Chris Aniszczyk <caniszczyk@gmail.com>
2017-05-15 08:18:23 -05:00
Tom Denham 0ebcfe7ea3 Merge pull request #425 from tomdee/small-build-improvements
scripts/release-with-rkt.sh Minor improvements
2017-05-10 14:34:47 -05:00
Casey Callendrello 1a9288c3c0 Merge pull request #450 from gunjan5/spec-version-mismatch
cniVersion and config structure mismatch
2017-05-08 11:12:25 +02:00
Dan Williams e03c76b852 Merge pull request #445 from feiskyer/cniversion
Add cniVersion to Result
2017-05-05 17:47:26 -05:00
gunjan5 efe4f37afa cniVersion and config structure mismatch 2017-05-05 14:50:58 -07:00
Tom Denham b686cad062 Merge pull request #447 from gunjan5/invalid-spec-json
Fix invalid json
2017-05-04 16:06:04 -07:00
gunjan5 5e3be5a44f fix invalid json 2017-05-04 15:59:11 -07:00
Pengfei Ni f197c01b62 Fix testings accross the project
Signed-off-by: Pengfei Ni <feiskyer@gmail.com>
2017-05-03 23:21:44 +08:00
Pengfei Ni 27a5b994c2 Add cniVersion to Result
Signed-off-by: Pengfei Ni <feiskyer@gmail.com>
2017-05-03 21:38:42 +08:00
Dan Williams 231f6c4c53 Merge pull request #433 from dunjut/master
scripts: cleanup() should rm net container in docker-run.sh
2017-04-28 20:58:17 -05:00
Dan Williams a6c96acda4 Merge pull request #440 from feiskyer/cap
cnitool: support capablity args
2017-04-28 20:55:31 -05:00
Dan Williams 010c8a9503 Merge pull request #439 from dnardo/netlink-update
vendor: Update vishvanana/netlink dependency.
2017-04-25 10:24:05 -05:00
Pengfei Ni 73a2d131ee cnitool: support capablity args
Signed-off-by: Pengfei Ni <feiskyer@gmail.com>
2017-04-25 17:35:03 +08:00
Dan Williams 21d96393ea Merge pull request #391 from tangle329/master
Validate rangeStart and rangeEnd specified in conf
2017-04-24 22:00:07 -05:00
Tom Denham 8eddd61fed Merge pull request #435 from micahhausler/patch-1
Fixed table formatting
2017-04-24 11:21:33 -07:00
Daniel Nardo 0de29de33e Update netlink dependency. 2017-04-24 10:31:24 -07:00
Micah Hausler 87fbabeeef Fixed table formatting 2017-04-21 15:22:56 -04:00
du 6ee2048ec2 scripts: cleanup() should rm net container in docker-run.sh 2017-04-21 18:27:46 +08:00
Bryan Boreham 279cae5ec5 Merge pull request #432 from dunjut/master
README: build the plugins using ./build.sh, not ./build
2017-04-21 11:12:49 +01:00
du 43c11dda15 README: build the plugins using ./build.sh, not ./build 2017-04-21 15:29:57 +08:00
Gabe Rosenhouse 4a2f3ddd5d Merge pull request #430 from kris-nova/remove-proposal
Wondering if this is still a proposal?
2017-04-16 11:13:24 -07:00
Kris Nova 392e1bbd10 Changing Proposal->Specification in Spec 2017-04-16 11:01:23 -06:00
Casey Callendrello ddebb44ad8 Conventions: add convention around chaining interfaces 2017-04-11 19:10:56 +02:00
Tom Denham 72dbcda3d6
scripts/release-with-rkt.sh Minor improvements
- Use a new Fedora to get a more up to date Go version
- Don't build so many archives, they are not used.
- Handle the new build.sh script
- Just use host networking with rkt
2017-04-07 13:54:39 -07:00
Gabe Rosenhouse 137b4975ec Merge pull request #419 from asridharan/cnitool
Added documentation for `cnitool`
2017-04-07 10:20:07 -07:00
Avinash Sridharan 382686ed63 Documentation: Added documentation for `cnitool`.
Added documentation to simplify the usage of `cnitool` and the
settings expected to be used with `cnitool`.
2017-04-05 21:40:24 +00:00
Avinash Sridharan d427963784 libcni: Fixed tests that were checking error strings.
Checking error strings makes these tests flaky, especially if the
error string is changed in libcni. Have gone ahead an introduced a new
error type `NoConfigsFoundError` and the Match is against the error
type making it more deterministic.
2017-04-05 21:40:24 +00:00
Avinash Sridharan 5b0d6a0c8f libcni: Improved error messages.
Improved the error reporting to be more descriptive when configuration
files are not filed while using the cni-tool.
2017-04-05 21:40:16 +00:00
Tom Denham 361ec957fb Merge pull request #413 from dcbw/spec-fix-ips
spec/plugins: fix 'ip'->'ips' in the spec, bump to 0.3.1
2017-04-05 14:39:44 -07:00
Dan Williams 9445711fa5 spec/plugins: fix 'ip'->'ips' in the spec, bump to 0.3.1 2017-04-05 12:46:24 -05:00
Gabe Rosenhouse d4bbce1865 Merge pull request #423 from rosenhouse/update-travis
travis: shift forward to Go 1.8 and 1.7
2017-04-05 09:26:49 -07:00
Gabe Rosenhouse ff92701693 Merge pull request #421 from kshafiee/patch-1
Added entry for CNI-Genie
2017-04-05 09:24:21 -07:00
Gabe Rosenhouse a39d6845fa travis: shift forward to Go 1.8 and 1.7 2017-04-05 09:11:35 -07:00
Dan Williams d66edc3180 Merge pull request #400 from lstoll/lstoll-rename-build-script
Rename build script to avoid conflict with bazel
2017-04-05 11:03:23 -05:00
kshafiee aaba1b0224 Added entry for CNI-Genie
CNI-Genie enables orchestrators (kubernetes, mesos) for seamless connectivity to choice of CNI plugins (calico, canal, romana, weave) configured on a Node
2017-04-03 12:00:02 -07:00
Tom Denham 470e86eb8b Update community sync detail 2017-03-31 14:38:48 -07:00
Tom Denham 73c6616946 Merge pull request #414 from kad/s390x
Enable s390x build
2017-03-27 10:10:06 -07:00
Alexander Kanevskiy 61f3b4b315 Enable s390x build 2017-03-23 00:51:29 +02:00
Tom Denham 0799f5732f Merge pull request #412 from tomdee/safe-del
plugins/*: Don't error if the device doesn't exist
2017-03-22 09:47:29 -07:00
Tom Denham 13824487c6
plugins/*: Don't error if the device doesn't exist
I wasn't able to test or update the dhcp plugin but from a code read it
should be fine. All the other plugins are tested and fixed
2017-03-22 08:52:29 -07:00
Tom Denham 699380d687 Merge pull request #407 from aaronlevy/del-err
plugins/meta/flannel: If net config is missing do not return err on DEL
2017-03-21 10:43:40 -07:00
Ghe Rivero e00a647504 Return type.Error instead of plain error
This way, we can use Error.Code attribute to check error type without
needing to regexp the message string
2017-03-18 10:43:02 +01:00
Aaron Levy 74d4cbed76 plugins/meta/flannel: If net config is missing do not return err on DEL 2017-03-17 13:37:33 -07:00
Gabe Rosenhouse b87126377a Merge pull request #396 from cf-container-networking/vendored-types-leakage
pkg/ip: do not leak types from vendored netlink package
2017-03-16 19:20:40 -07:00
Brandon Philips c235b448d4 Merge pull request #401 from chrismarino/patch-1
Added Romana to list of CNI providers...
2017-03-15 12:38:04 -07:00
chrismarino c1c7826922 Added Romana to list of CNI providers...
Added Romana to list of CNI providers.
2017-03-15 12:10:21 -07:00
Lincoln Stoll 1b9caefba5 Rename build script to avoid conflict with bazel
It's difficult to include this repository using bazel, because
the file named "build" conflicts with new_go_repository generation
on case-insensitive filesystems (ref
https://github.com/bazelbuild/rules_go/issues/234). This change
renames the file to something that doesn't conflict, and also
renames the test script for consistency.
2017-03-15 15:59:44 +11:00
Gabe Rosenhouse f51dd618c5 pkg/ip: improve docstring for SetupVeth 2017-03-13 11:27:12 -07:00
Gabe Rosenhouse e4a0583d7a pkg/ip: SetupVeth returns net.Interface 2017-03-10 09:49:46 -08:00
Tang Le 1b65890795 Validate rangeStart and rangeEnd specified in conf
Signed-off-by: Tang Le <tangle3@wanda.cn>
2017-03-10 12:51:05 +08:00
Jay Dunkelberger 58c834c4f3 pkg/ip: do not leak types from vendored netlink package
The exported function SetupVeth now returns a package-defined type.

Signed-off-by: Gabe Rosenhouse <grosenhouse@pivotal.io>
2017-03-09 13:55:15 -08:00
Gabe Rosenhouse 4f36e5994e Merge pull request #393 from containernetworking/community-sync-readme
readme.md: Add link to community sync
2017-03-08 16:46:44 -08:00
Tom Denham 5d5b16f0b3 readme.md: Add link to community sync 2017-03-08 15:54:15 -08:00
Gabe Rosenhouse 4ce9b019aa Merge pull request #366 from ehazlett/ipam-host-local-initial-reserved-ip
Do not error if last reserved not found after initial creation
2017-03-06 07:13:06 -08:00
Evan Hazlett ac50624393
fix unrelated failing tests
Signed-off-by: Evan Hazlett <ejhazlett@gmail.com>
2017-03-02 15:37:43 -05:00
Evan Hazlett 2678be24d0
add test for ensuring initial subnet creation does not contain an error
Signed-off-by: Evan Hazlett <ejhazlett@gmail.com>
2017-03-02 15:16:09 -05:00
Evan Hazlett 474ba16901 do not error if last_reserved_ip is missing for host local ipam
Signed-off-by: Evan Hazlett <ejhazlett@gmail.com>
2017-03-02 13:59:21 -05:00
Tom Denham 1f6efc28b5 Merge pull request #387 from tangle329/master
Check n.IPAM before use it in LoadIPAMConfig function
2017-03-02 09:23:02 -08:00
Dan Williams 75a5cf1bf0 Merge pull request #373 from dcbw/conflist-runtime-config
spec,libcni: add support for injecting runtimeConfig into plugin stdin data
2017-03-02 11:17:00 -06:00
Tang Le 1003822e53 Check n.IPAM before use it in LoadIPAMConfig function
Signed-off-by: Tang Le <tangle3@wanda.cn>
2017-03-02 10:25:19 +08:00
Dan Williams 71c4a60741 spec,libcni: add support for injecting runtimeConfig into plugin stdin data
Add a new CapabilityArgs member to the RuntimeConf struct which runtimes can
use to pass arbitrary capability-based keys to the plugin.  Elements of this
member will be filtered against the plugin's advertised capabilities (from
its config JSON) and then added to a new "runtimeConfig" top-level map added
to the config JSON sent to the plugin on stdin.

Also "runtime_config"->"runtimeConfig" in CONVENTIONS.md to make
capitalization consistent with other CNI config keys like "cniVersion".
2017-03-01 10:49:40 -06:00
Tom Denham 6008de1ffd Merge pull request #382 from rosenhouse/doc-spec-upgrade
Documentation: guidance for upgrading to CNI Spec v0.3.0
2017-03-01 08:45:56 -08:00
Casey Callendrello 09e5feae1f Minor rewording about default config version 2017-02-28 19:44:25 +01:00
Dan Williams 7d8c23dd59 Merge pull request #383 from dcbw/test-fixes
tests: misc test fixes
2017-02-27 16:13:45 -06:00
Dan Williams d6d012b88f Merge pull request #374 from squeed/config-list-upconvert
libcni: up-convert a Config to a ConfigList when no other configs are found.
2017-02-27 15:10:37 -06:00
Dan Williams 1f4a8c2802 Merge pull request #377 from squeed/ptp-sandbox
plugins/main/ptp: set the Sandbox property on the response
2017-02-27 15:06:57 -06:00
Dan Williams fc174a6635 Merge pull request #380 from bboreham/patch-1
Add Bryan Boreham as maintainer
2017-02-27 15:06:26 -06:00
Dan Williams 35153e7de8 pkg/types: misc current types testcase cleanups 2017-02-27 13:28:07 -06:00
Dan Williams 5a984f0dee pkg/testutils: return errors after restoring stdout
Ensures Ginkgo is able to print detailed failure messages instead
of them being captured by the pipe.
2017-02-27 13:27:19 -06:00
Dan Williams 94e02e0768 Merge pull request #375 from aaithal/complieOnNonLinuxPlatforms
pkg/ns: refactored so that builds succeed on non-linux platforms
2017-02-27 11:46:22 -06:00
Gabe Rosenhouse bbc23ea761 docs: table formatting is hard 2017-02-27 07:57:00 -08:00
Gabe Rosenhouse b6e9bf8372 docs: fill-out and correct version conversion table 2017-02-27 07:53:09 -08:00
Gabe Rosenhouse 379d47760c docs: minor improvements to spec-upgrades 2017-02-27 07:17:37 -08:00
Casey Callendrello 119d6f20f5 docs: add small upgrade instructions 2017-02-27 14:57:31 +01:00
Casey Callendrello 70c153eb41 docs: minor improvements to 0.3.0 upgrade guidance 2017-02-27 14:27:56 +01:00
Gabe Rosenhouse fa53446854 docs: Edits to v0.3.0 upgrade guidance 2017-02-26 16:53:52 -08:00
Dan Williams 328ea56e75 docs: CNI versioning for 0.3.0 upgrade 2017-02-26 13:26:14 -08:00
Casey Callendrello 6e6ad53ea8 libcni: up-convert a Config to a ConfigList when no other configs are found. 2017-02-24 19:44:06 +01:00
Tom Denham 1bc8141105 Merge pull request #379 from Intel-Corp/master
README: List multus as 3rd party plugin
2017-02-23 10:40:59 -08:00
Aithal 460e1c5be3 vendor: Update vishvanana/netlink dependency 2017-02-23 09:53:11 -08:00
Aithal 7eeb21a999 pkg/ip: refactored so that builds succeed on non-linux platforms
moved functions that depend on linux packages into a separate file and added
nop methods with a build tag for non-linux platforms in a new file.
2017-02-23 09:52:13 -08:00
Aithal 0dc0845c51 pkg/ns, pkg/types: refactored non linux build fix code to
Make GetCurrentNS platform specific instead of getCurrentThreadNetNSPath
2017-02-23 09:50:48 -08:00
Dan Williams aafc728ec0 Merge pull request #371 from rosenhouse/spec-netconf-routes
spec: Remove `routes` from Network Configuration
2017-02-22 20:32:41 -06:00
Bryan Boreham 32246b315c Replace Michael Bridgen with Bryan Boreham 2017-02-22 17:06:04 +00:00
Kuralamudhan Ramakrishnan 2c33eb4f22 README: List multus as 3rd party plugin
refer #343 issue
2017-02-22 16:13:12 +00:00
Gabe Rosenhouse 9f03c4bc2e Merge pull request #376 from zackhsi/fix-grammar
Fix grammar
2017-02-20 17:51:38 -08:00
Casey Callendrello 7fa16f18d9 plugins/main/ptp: set the Sandbox property on the response 2017-02-21 00:28:19 +01:00
Zack Hsi c783679114 Fix grammar 2017-02-20 11:21:49 -08:00
Aithal c3cde44480 pkg/ns: refactored so that builds succeed on non-linux platforms
moved functions that depend on linux packages (sys/unix) into a separate file
and added nop methods with a build tag for non-linux platforms in a new file.
2017-02-20 10:00:55 -08:00
Gabe Rosenhouse 3ba89873ac Merge pull request #372 from rosenhouse/consolidate-host-local-docs
docs: consolidate host-local documentation
2017-02-20 06:33:32 -08:00
Gabe Rosenhouse eb2ff7fe57 docs: consolidate host-local documentation
- remove the README.md in the plugins/ipam/host-local (no other plugin
  has this)
- merge that info into the main Documentation/host-local.md
2017-02-15 15:20:13 -08:00
Gabe Rosenhouse 31dafa2f7a spec: Remove `routes` from Network Configuration
- this is supported by some IPAM plugins but is not required of all of
  them
2017-02-15 15:02:23 -08:00
Tom Denham 6f3b0abcc7 Merge pull request #369 from containernetworking/port-mapping
CONVENTIONS.md: Update details on port-mappings
2017-02-14 11:47:59 -08:00
Tom Denham e3da6e1cfb
More markups 2017-02-13 10:14:45 -08:00
Tom Denham 95ca9d393a
Update with feedback 2017-02-09 14:12:33 -08:00
Tom Denham 3fbf94a90a CONVENTIONS.md: Update details on port-mappings
After discussion with the CNI maintainers and representatives from Mesos and Kubernetes we've settled on this approach for passing the dynamic port mapping information from runtimes to plugins.

Including it in the plugin config allows "operators" to signal that they expect port mapping information to be passed to a specific plugin (or plugins). This follows the model that config passed in the plugin config should be acted upon by a plugin.

Runtimes can optionally return an error to users if they find no plugin in the chain that supports port mappings.

I'm sure we'd all like to see this merged soon so I'll try to turn any feedback quickly (and I'm happy to get any and all feedback - spellings, unclear working, wrong JSON terminology etc..)
2017-02-08 12:43:04 -08:00
Gabe Rosenhouse f85ac247e6 Merge pull request #361 from ofiliz/issue-360
invoke: Enable plugin file names with extensions
2017-02-08 10:15:49 -08:00
Onur Filiz 271be6c8df invoke: Enable plugin file names with extensions
A CNI network configuration file contains the plugin's executable file name.
Some platforms like Windows require a file name extension for executables.
This causes unnecessary burden on admins as they now have to maintain two
versions of each type of netconfig file, which differ only by the ".exe"
extension. A much simpler design is for invoke package to also look for
well-known extensions on platforms that require it. Existing tests are
improved and new tests are added to cover the new behavior.

Fixes #360
2017-02-04 12:01:47 -08:00
Gabe Rosenhouse fc7de21fe2 Merge pull request #364 from pires/build_tag
pkg/utils/sysctl/sysctl_linux.go: couple fixes
2017-02-03 14:18:39 -05:00
Paulo Pires 4c0b8f7054 pkg/utils/sysctl/sysctl_linux.go: fix typo. 2017-02-03 05:39:04 +00:00
Paulo Pires 246300ccfe
pkg/utils/sysctl/sysctl_linux.go: fix build tag. 2017-02-03 05:38:53 +00:00
Tom Denham e663e98444 Merge pull request #359 from ofiliz/issue-355
libcni, pkg\invoke: Use OS-specific list separator when parsing CNI_PATH
2017-02-02 12:35:07 -08:00
Onur 0d9a1d70f8 spec, libcni, pkg/invoke: Use OS-agnostic separator when parsing CNI_PATH
Hardcoding the list separator character as ":" causes CNI to fail when parsing
CNI_PATH on other operating systems. For example, Windows uses ";" as list
separator because ":" can legally appear in paths such as "C:\path\to\file".
This change replaces use of ":" with OS-agnostic APIs or os.PathListSeparator.

Fixes #358
2017-01-28 16:47:58 -08:00
Gabe Rosenhouse c4271dba67 Merge pull request #145 from dcbw/pass-ifname-back
Pass interface name and MAC back to runtime
2017-01-25 11:20:57 -08:00
Dan Williams d5acb127b8 spec/plugins: return interface details and multiple IP addresses to runtime
Updates the spec and plugins to return an array of interfaces and IP details
to the runtime including:

- interface names and MAC addresses configured by the plugin
- whether the interfaces are sandboxed (container/VM) or host (bridge, veth, etc)
- multiple IP addresses configured by IPAM and which interface they
have been assigned to

Returning interface details is useful for runtimes, as well as allowing
more flexible chaining of CNI plugins themselves.  For example, some
meta plugins may need to know the host-side interface to be able to
apply firewall or traffic shaping rules to the container.
2017-01-25 11:31:18 -06:00
Dan Williams befad17174 pkg/ipam: add testcases 2017-01-25 11:31:18 -06:00
Dan Williams b0b896f79a plugins/flannel: organize test JSON alphabetically
Otherwise the test fails, since Go's JSON marshaller prints
dict items alphabetically in its String() call.
2017-01-25 11:31:18 -06:00
Dan Williams ad2a5ccb61 macvlan/ipvlan: use common RenameLink method 2017-01-25 11:31:18 -06:00
Dan Williams befb95977c types: make Result an interface and move existing Result to separate package 2017-01-25 11:31:18 -06:00
Dan Williams cb4cd0e12c testutils: pass netConf in for version operations; pass raw result out for tests 2017-01-25 11:31:18 -06:00
Gabe Rosenhouse dcadf4f9b5 Merge pull request #355 from rosenhouse/skel-main-errors
skel: adds PluginMainWithError which returns a *types.Error
2017-01-23 07:42:57 -08:00
Gabe Rosenhouse af5c2f303a skel: adds PluginMainWithError which returns a *types.Error
Enables plugins to do their own error-handling and cleanup logic
2017-01-22 16:57:42 -08:00
Gabe Rosenhouse 1acc927ab0 Merge pull request #352 from containernetworking/conventions
Documentation: Add conventions doc
2017-01-20 13:50:24 -08:00
Tom Denham 04f357344d Update CONVENTIONS.md 2017-01-20 12:49:00 -08:00
Gabe Rosenhouse e476ab7189 Merge pull request #346 from dcbw/ordered-chaining
api/plugins: implement ordered plugin chaining
2017-01-20 08:35:37 -08:00
Dan Williams 0c2a034f01 api,libcni: add network config list-based plugin chaining
Using a new ".configlist" file format that allows specifying
a list of CNI network configurations to run, add new libcni
helper functions to call each plugin in the list, injecting
the overall name, CNI version, and previous plugin's Result
structure into the configuration of the next plugin.
2017-01-19 22:28:21 -06:00
Dan Williams 7e16c86a52 spec: add network configuration list specification 2017-01-19 22:28:20 -06:00
Tom Denham 947b79e334
Spec/Conventions: Update to include plugin config 2017-01-19 18:05:28 -08:00
Dan Williams 0a1b48f520 noop: allow specifying debug file in config JSON
Chaining sends different config JSON to each plugin, but the same
environment, and if we want to test multiple noop plugin runs in
the same chain we need a way of telling each run to use a different
debug file.
2017-01-19 09:56:42 -06:00
Tom Denham d716d73444 Documentation: Add conventions doc 2017-01-17 15:53:24 -08:00
Tom Denham 530dd71e19 Merge pull request #334 from squeed/host-local-dns
ipam/host-local: add ResolvConf argument for DNS configuration
2017-01-12 17:06:04 -08:00
Tom Denham fa58dd8889 Merge pull request #350 from rosenhouse/spec-version
spec: notice of version
2017-01-12 13:42:25 -08:00
Gabe Rosenhouse 20fde00ea6 spec: notice of version 2017-01-11 18:49:10 -08:00
Casey Callendrello 5cde14cd7b ipam/host-local: add ResolvConf argument for DNS configuration
This adds the option `resolvConf` to the host-local IPAM configuration.
If specified, the plugin will try to parse the file as a resolv.conf(5)
type file and return it in the DNS response.
2017-01-11 18:48:35 +01:00
Tom Denham 4406607649 Merge pull request #115 from aanm/implementing-ipam-as-lib
ipam/host-local: Move allocator and config to backend
2017-01-10 13:06:46 -08:00
André Martins f60111b093 ipam/host-local: Move allocator and config to backend
Signed-off-by: André Martins <aanm90@gmail.com>
2016-12-21 04:43:21 +00:00
Gabe Rosenhouse 98ff61aac3 Merge pull request #325 from squeed/update-maintainer
MAINTAINERS: hi CaseyC!
2016-12-19 11:02:20 -08:00
Casey Callendrello 6dc01a772b MAINTAINERS: hi CaseyC! 2016-12-19 12:38:29 -05:00
Gabe Rosenhouse e3bcc3e41b Merge pull request #345 from rosenhouse/travis-go-versions
travis: roll forward the versions of Go that we test
2016-12-16 08:06:17 -08:00
Gabe Rosenhouse bc1d61e3db travis: roll forward the versions of Go that we test 2016-12-15 18:41:07 -08:00
Gabe Rosenhouse 652bae1deb Merge pull request #314 from rosenhouse/noop-helpful-message
plugins/noop: return a helpful message for test authors
2016-12-15 18:35:43 -08:00
Gabe Rosenhouse 47f6604b5a Merge pull request #344 from dcbw/interface-checks
all: assert internal objects implement interfaces
2016-12-15 18:34:57 -08:00
Gabe Rosenhouse 1c1cf2faf7 Merge branch 'master' into interface-checks 2016-12-15 18:20:34 -08:00
Gabe Rosenhouse ef00f717a6 Merge pull request #341 from dcbw/host-local-trim
host-local: trim whitespace from container IDs and disk file contents
2016-12-15 18:18:25 -08:00
Dan Williams 5852c60bc4 all: assert internal objects implement interfaces 2016-12-14 17:09:01 -06:00
Dan Williams 20ae9e156d Merge pull request #340 from vzcambria/master
Fix README.md host-local IPAM range start/end typo
2016-12-09 18:22:08 -06:00
Dan Williams 76028d7f8c host-local: trim whitespace from container IDs and disk file contents
It doesn't seem like container IDs should really have whitespace or
newlines in them.  As a complete edge-case, manipulating the host-local
store's IP reservations with 'echo' puts a newline at the end, which
caused matching to fail in ReleaseByID().  Don't ask...
2016-12-09 18:16:38 -06:00
VZ Cambria 2ba6d20007 Fix README.md host-local IPAM range start/end typo 2016-12-06 10:40:55 -05:00
Konstantinos Karampogias accac60053 plugins/noop: return a helpful message for test authors
Signed-off-by: Gabe Rosenhouse <grosenhouse@pivotal.io>
2016-12-02 15:04:01 -08:00
Dan Williams b0808a006a Merge pull request #335 from JulienBalestra/json_ext
libcni conf: support .json file extension
2016-12-02 13:33:36 -06:00
Julien Balestra a85593ce0d libcni conf: support .json file extension
Content of the files are json, the configuration load should support .json
2016-11-23 10:48:32 +01:00
Dan Williams de002ff25a Merge pull request #322 from cf-container-networking/flannel-cni-stateDir
Allow flannel CNI plugin stateDir to be configurable
2016-11-18 15:27:35 -06:00
Mark St.Godard 6eac0ee904 flannel: rename stateDir to dataDir
Rename StateDir to DataDir for flannel CNI plugin
2016-11-17 15:54:29 -06:00
Gabe Rosenhouse 46e3a6b7be Merge pull request #330 from bboreham/ns-warning
Expand description of goroutine+netns problems
2016-11-17 08:00:31 -08:00
Bryan Boreham c20f895fa8 Expand description of goroutine+netns problems 2016-11-17 15:45:38 +00:00
Gabe Rosenhouse ba6c5af5dc Merge pull request #327 from dcbw/host-local-tests
host-local: add DataDir IPAM conf option and use it for testcases
2016-11-14 08:17:40 -08:00
Mark St.Godard 15de81eac6 flannel: updated flannel test to use pkg/testutils
reworked the flannel test to use testutils CmdAddWithResult and
CmdDelWithResult
2016-11-13 12:16:32 -06:00
Gabe Rosenhouse 8b827d74b3 Merge pull request #328 from markstgodard/noop-cni-args
noop plugin: support multiple CNI_ARGS
2016-11-10 22:24:58 -08:00
Mark St.Godard 48d3b46eb2 noop plugin: support multiple CNI_ARGS
Updated noop plugin to parse the CNI_ARGS pairs, to allow more than just
the DEBUG arg.
2016-11-10 16:04:42 -06:00
Dan Williams 7b2006eed8 Merge pull request #324 from lstoll/lstoll-ipvlan-l3s
ipvlan: Support L3-Symmetric mode
2016-11-10 12:53:59 -06:00
Dan Williams e085226f82 host-local: add DataDir IPAM conf option and use it for testcases
Add an e2e host-local plugin testcase, which requires being able
to pass the datadir into the plugin so we can erase it later.
We're not always guaranteed to have access to the default data
dir location, plus it should probably be configurable anyway.
2016-11-10 12:12:16 -06:00
Mark St.Godard e6113c6517 flannel: update flannel documentation
add optional 'stateDir' to flannel docs
2016-11-09 09:34:35 -06:00
Gabe Rosenhouse 79643855f7 Merge pull request #307 from squeed/backwards-compatability-tests
testing: test invocation of newer plugins with an older libcni
2016-11-07 14:25:09 -08:00
Mark St.Godard f0daefa63d flannel: add unit tests for config loading
backfill unit tests to add coverage for loadFlannelNetConf and
loadFlannelSubnetEnv
2016-11-05 23:27:14 -05:00
Lincoln Stoll 9201f3f1d9
ipvlan: Support ipvlan l3s mode.
This landed in kernel in 4fbae7d
2016-11-05 13:41:07 -07:00
Lincoln Stoll 2ad1d04561
vendor: github.com/vishvananda/netlink@a1f8555
Update to current master, to get IPVLAN L3s def.
2016-11-05 13:41:01 -07:00
Mark St.Godard fa264e6e36 flannel: add integration test suite
add new test for flannel plugin that delegates to the noop plugin and
validates that 'storeDir' can be configurable
2016-11-05 10:47:07 -05:00
Mark St.Godard b463642ac0 Allow flannel CNI plugin stateDir to be configurable
- Add optional 'stateDir' to flannel NetConf, if not present default to
/var/lib/cni/flannel

Signed-off-by: Jay Dunkelberger <ldunkelberger@pivotal.io>
2016-11-03 17:10:13 -05:00
Casey Callendrello 8c6f6e0a60 testing: test invocation of newer plugins with an older libcni 2016-10-25 16:33:35 +02:00
Gabe Rosenhouse 0e09ad29df Merge pull request #312 from tomdee/slack
Add slack badge and link
2016-10-24 16:25:48 -07:00
Tom Denham bb8bcf08a8 Add slack badge and link 2016-10-24 16:23:04 -07:00
Gabe Rosenhouse 194f8ce6ea Merge pull request #311 from rkamudhan/patch-2
Update ipvlan.md
2016-10-23 16:23:55 -07:00
Kuralamudhan Ramakrishnan 6e77c7f4f7 Update ipvlan.md
invalid json format
2016-10-23 23:18:44 +01:00
Gabe Rosenhouse bff05e7572 Merge pull request #308 from rosenhouse/fix-skel-tests-containerid
skel tests: correct name of CNI_CONTAINERID in tests of env vars
2016-10-17 15:02:27 -07:00
Gabe Rosenhouse 8be31e1c6f skel tests: correct name of CNI_CONTAINERID in tests of env vars 2016-10-16 17:18:57 -07:00
Gabe Rosenhouse 3450f08611 Merge pull request #305 from yuewko/add_infoblox_link
Add link to Infoblox IPAM plugin
2016-10-16 17:08:51 -07:00
Yue Ko c24fce2ae8 Add link to Infoblox IPAM plugin
Added link to Infoblox IPAM plugin in README.
2016-10-11 11:07:30 -07:00
Gabe Rosenhouse d872391998 Merge pull request #304 from danp/patch-1
docs/ptp: fix markdown formatting
2016-10-09 22:39:31 -07:00
Dan Peterson 85ab5c0f21 docs/ptp: fix markdown formatting 2016-10-09 21:22:27 -03:00
Dan Williams af88b7c6cf Merge pull request #301 from rosenhouse/backwards-compatibility-tests
A test verifies that libcni is backwards compatible with legacy plugins
2016-10-06 10:10:02 -05:00
Gabe Rosenhouse 165ca6f719 travis: git clone at great depth
ensures that git commits referenced by backwards compatibility tests are
always available in CI.
2016-10-05 20:48:53 -07:00
Gabe Rosenhouse 0087f302ea Merge pull request #302 from rosenhouse/readme-3rd-party-plugins
3rd party plugins belong in their own repos
2016-10-03 08:39:23 -07:00
Gabe Rosenhouse b2ef4aa1a7 docs: on contributing plugins to this repo 2016-10-02 23:44:59 -07:00
Gabe Rosenhouse 51b51a0182 testing: adds basic test of backwards compatibility 2016-10-02 22:18:53 -07:00
Dan Williams a29fc24f11 Merge pull request #296 from rosenhouse/plugins-require-versioned-config
Plugins validate cniVersion of NetConf
2016-09-22 17:11:34 -05:00
Dan Williams b5e0a4ef2e Merge pull request #298 from freehan/fix-host-local-30
host-local: fix allocation of last address in a /30
2016-09-22 09:13:32 -05:00
Minhan Xia 52e4358cbd host-local: fix allocation of last address in a /30 2016-09-21 13:10:05 -07:00
Brandon Philips e70e6034d2 Merge pull request #299 from tgraf/cilium-link
List cilium as 3rd party plugin
2016-09-19 14:31:54 -07:00
Gabe Rosenhouse 5b696f3307 skel: use named constant for Incompatible CNI Version error code 2016-09-19 13:25:33 -07:00
Gabe Rosenhouse 0135e2751e versioning: ipam config is not versioned 2016-09-19 13:25:28 -07:00
Gabe Rosenhouse fba37620e0 versioning: plugins require version match with config
infer version 0.1.0 when config is missing an explicit "cniVersion" field
2016-09-19 13:00:49 -07:00
Gabe Rosenhouse 64bbcd8cf7 versioning: add basic version decode for network config 2016-09-19 12:57:08 -07:00
Thomas Graf 75d983ec25 README: List cilium as 3rd party plugin
Signed-off-by: Thomas Graf <tgraf@suug.ch>
2016-09-19 13:45:39 +02:00
Gabe Rosenhouse fd150a4c97 skel: Plugins require a cniVersion in the NetConf 2016-09-18 21:30:57 -07:00
Gabe Rosenhouse 56032390fe Merge pull request #287 from rosenhouse/multi-version
Plugins report a list of supported versions
2016-09-12 13:44:14 -07:00
Gabe Rosenhouse 3f4011b3ec Merge pull request #295 from rosenhouse/update-readme-cloudfoundry-link
readme: update link to CloudFoundry usage of CNI
2016-09-08 00:47:04 -04:00
Gabe Rosenhouse d32ccb1899 readme: update link to CloudFoundry usage of CNI 2016-09-06 23:48:48 -04:00
Gabe Rosenhouse 7958b9f0cc versioning: revert spec version to 0.2.0 2016-09-06 15:37:42 -04:00
Gabe Rosenhouse d5e2e375d4 versioning: misc cleanups
highlights:
 - NetConf struct finally includes cniVersion field
 - improve test coverage of current version report behavior
 - godoc a few key functions
 - allow tests to control version list reported by no-op plugin
2016-09-06 15:35:58 -04:00
Gabe Rosenhouse 07a8a28637 Merge pull request #294 from dcbw/host-local-ranges
Fix handling of host-local IP address ranges
2016-09-05 23:24:42 -04:00
Dan Williams 95a9ea0bd2 host-local: don't allocate the broadcast address or allow invalid networks
There aren't any IPs to allocate in /32 or /31 networks, so don't allow them.
2016-09-02 17:20:09 -05:00
Dan Williams 959af1e6ab host-local: ensure requested IPs are within the given range
And also make sure that RangeStart and RangeEnd are sane.
2016-09-02 16:52:29 -05:00
Dan Williams dfc4f7cd2a host-local: don't allocate past RangeEnd
When RangeEnd is given, a.end = RangeEnd+1.

If when getSearchRange() is called and lastReservedIP equals
RangeEnd, a.nextIP() only compares lastReservedIP (which in this
example is RangeEnd) against a.end (which in this example is
RangeEnd+1) and they clearly don't match, so a.nextIP() returns
start=RangeEnd+1 and end=RangeEnd.

Get() happily allocates RangeEnd+1 because it only compares 'cur'
to the end returned by getSearchRange(), not to a.end, and thus
allocates past RangeEnd.

Since a.end is inclusive (eg, host-local will allocate a.end) the
fix is to simply set a.end equal to RangeEnd.
2016-09-02 16:37:34 -05:00
Gabe Rosenhouse bf31ed1591 invoke: better name and unit test coverage for GetVersionInfo 2016-09-02 16:39:01 -04:00
Gabe Rosenhouse 97192fc979 invoke: correctly infer version for 0.1.0-vintage plugins
Older plugins return a known error when issued the VERSION command.
Capture this error and report it as a 0.1.0 version plugin.
2016-09-02 16:02:07 -04:00
Gabe Rosenhouse deb4466041 versioning: adds tooling to compile a program against a given old CNI version
Allows us to write tests that cover interactions between components of
differing versions
2016-09-02 16:02:02 -04:00
Gabe Rosenhouse dea1c6e44d pkg/invoke: refactor plugin exec and backfill unit tests 2016-09-02 15:59:25 -04:00
Gabe Rosenhouse adf28a84c6 versioning: document meaning of 'Legacy' version support 2016-09-02 11:26:17 -04:00
Gabe Rosenhouse 536cb5b99b versioning: plugins report a list of supported versions
Further progress on versioning support (Issue #266).
Bump CNI spec version to 0.3.0
2016-09-02 11:26:17 -04:00
Gabe Rosenhouse c5e39a87f7 Merge pull request #293 from rosenhouse/backfill-invoke-exec-tests
Backfill tests for plugin execution
2016-08-31 20:49:08 -04:00
Dan Williams e0ea82b229 Merge pull request #290 from asridharan/dcos
Fixed the check for network namespace path.
2016-08-31 18:19:02 -05:00
Avinash Sridharan 7281d5792a pkg/ns: fixed the check for network namespace path.
The expectation on older kernels (< 3.19) was to have the network
namespace always be a directory. This is not true if the network
namespace is bind mounted to a file, and will make the plugin fail
erroneously in such cases. The fix is to remove this assumption
completely and just do a basic check on the file system types being
returned.

Fixes #288
2016-08-31 21:41:00 +00:00
Dan Williams 4d77bc177c Merge pull request #291 from tomdee/SetupVeth
pkg/ip: Ensure that SetupVeth returns correct hostVeth
2016-08-31 16:32:09 -05:00
Tom Denham 726c6b4578 pkg/ip: Ensure that SetupVeth returns correct hostVeth
The veth is moved from the container NS to the host NS.
This is handled by the code that sets the link to UP but the wrong
hostVeth is returned to the calling code.
2016-08-31 14:17:25 -07:00
Gabe Rosenhouse d3ecadb860 invoke: backfill tests for plugin execution 2016-08-30 23:52:47 -04:00
Gabe Rosenhouse 9d5e6e60e7 Merge pull request #241 from luxas/add_platforms
Add arm64 and ppc64le as new platforms
2016-08-13 15:11:28 -07:00
Lucas Käldström 39f8436a8b release: Release binaries for arm, arm64 and ppc64le too
Modify the releasing script to cross-compile for the new architectures, but also keep backwards-compability

ref #209
2016-08-13 12:45:04 +03:00
Lucas Käldström 21d25959ee travis: Cross-compile for arm, arm64 and ppc64le on every build
Cross-compile cni for arm, arm64 and ppc64le with go1.6 only
Allow go tip to fail
Set fast_finish to true, which means travis will instantly return build failure when any of the required builds fail

ref #209
2016-08-13 12:44:45 +03:00
Lucas Käldström fc58b79b45 build: Update the build script to make it possible to build for other architectures
This makes it possible to cross-compile cni like so:
$ GOARCH=arm ./build
$ GOARCH=arm64 ./build
$ GOARCH=ppc64le ./build

ref #209
2016-08-13 12:44:28 +03:00
Lucas Käldström 488db88fd1 vendor: Bump golang.org/x/sys/unix to a newer version
The current vendor of sys/unix is really old, and doesn't work on arm64 and ppc64le
Updating to the latest version might also fix other issues

ref #209
2016-08-13 12:44:04 +03:00
Tom Denham 349d66d51c Merge pull request #267 from rosenhouse/version-cmd
Adds VERSION command
2016-08-12 15:06:22 -07:00
Tom Denham 5a67f5d6fb Merge pull request #273 from zreigz/reconfigure-bridge-ip
Reconfigure bridge IP address
2016-08-12 15:04:22 -07:00
Tom Denham 60bb795c2e Merge pull request #274 from lukasredynk/update_netlink
Update of netlink version
2016-08-12 13:07:26 -07:00
Tom Denham 113dfd61bd Merge pull request #280 from prateekgogia/bug-fix-155
pkg/ip: Return correct error if container name provided exists, and test cases
2016-08-12 13:04:49 -07:00
Prateek Gogia ad8a052dd3 pkg/ip: Return correct error if container name provided exists, and test cases
If interface name for a container provided by a user is already present,
Veth creation fails with incorrect error.
If os.IsExist error is returned by makeVethPair:
* Check for peer name, if exists generate another random peer name,
* else, IsExist error is due to container interface present, return error.

Fixes #155
2016-08-11 18:51:03 +00:00
Lukasz Zajaczkowski 8cb3a9323a libcni: add util function InjectConf 2016-08-08 13:21:25 +02:00
Stefan Junker 7c579af789 Merge pull request #277 from steveeJ/test-allocator-reserveip
ipam/host-local/allocator tests: cover requested IP
2016-08-02 14:43:43 -07:00
Lukasz Zajaczkowski ee64ac74bc documentation: add description for forceAddress parameter 2016-08-02 07:33:44 +02:00
Lukasz Zajaczkowski d09b18dac4 plugins: reconfigure bridge IP address
Add possibility to reconfigure bridge IP address when there is a new value.
New boolean flag added to net configuration to force IP change if it is need.
Otherwise code behaves as previously and throws error
2016-08-02 07:33:17 +02:00
Stefan Junker 054fa9e42d ipam/host-local/allocator tests: cover requested IP
Further:
* improve error handling
2016-08-01 18:13:15 -07:00
Stefan Junker f5ead7969a Merge pull request #254 from steveeJ/ip-based-macaddr
pkg/ipam: IP based macaddr
2016-08-01 17:43:49 -07:00
Stefan Junker c0ac3913a1 pkg/utils/hwaddr tests: cover v4 in v6 addr 2016-08-01 16:50:40 -07:00
Lukas Redynk 35ce5d68e0 Update of netlink version
This is required for
https://github.com/containernetworking/cni/issues/251 because version of
netlink used doesn't support creating tap devices.
2016-08-01 14:04:03 +02:00
Tom Denham c1ff202179 Merge pull request #272 from rosenhouse/types-of-users
README: distinguish between runtimes and plugins
2016-07-25 16:35:58 -07:00
Stefan Junker 5a52316ab5 plugins/{bridge,macvlan} test: ensure hardware addr
* bridge: Test the following interface's hardware address for the CNI specific
prefix:
  - bridge with IP address
  - container veth
* plugins/macvlan test: ensure hardware addr
2016-07-22 15:40:00 -07:00
Stefan Junker f68cea27bc pkg/ip link_test: ensure SetHWAddrByIP has an effect 2016-07-22 15:34:54 -07:00
Stefan Junker 55fd81f775 plugins/ptp: set the host veth hwaddr correctly 2016-07-22 15:34:54 -07:00
Stefan Junker 7d19c01818 pkg/ip: use iface name in SetHWAddrByIP 2016-07-22 15:34:53 -07:00
Stefan Junker 8e1c215116 pkg/ip tests: cover SetupVeth and DelLinkByName* 2016-07-22 15:34:53 -07:00
Stefan Junker 924b30b57d plugins: set MAC addresses based on IP
This will give deterministic MAC addresses for all interfaces CNI
creates and manages the IP for:
* bridge: container veth and host bridge
* macvlan: container veth
* ptp: container veth and host veth
2016-07-22 15:34:53 -07:00
Stefan Junker 3a1354cff6 pkg/utils/hwaddr: migrate code from IPAM pkg 2016-07-22 15:18:38 -07:00
Stefan Junker 96867eae05 pkg/ipam{,test}: improve error handling and tests
* _suite.go and _test.go file should be in the same package, using the
  _test package for that, which requires some fields and methods to be
  exported
* Introduce error type for cleaner error handling
* test adaptions for error type checking
2016-07-22 15:17:22 -07:00
Minhan Xia 1c1424c472 add tests for generateHardwardAddr 2016-07-22 15:01:57 -07:00
Minhan Xia b2e53181d1 configure mac address based on assigned ip 2016-07-22 14:57:52 -07:00
Gabe Rosenhouse 39a8fe4766 Merge pull request #270 from rosenhouse/integration-tests
Add integration tests of libcni
2016-07-22 13:36:20 -07:00
Gabe Rosenhouse c657c61767 plugins: cleanup tests of no-op plugin 2016-07-22 13:30:55 -07:00
Gabe Rosenhouse 424a8b38cf README: distinguish between runtimes and plugins
Adds a link to the 3rd party SR-IOV plugin.
2016-07-22 13:22:35 -07:00
Gabe Rosenhouse dfd2c142ae docs: fix tuning doc, correct the result json
it does not report the version, it never did
2016-07-21 13:06:05 -07:00
Gabe Rosenhouse 7bc96f05bb spec: add version command and increment version to 0.2.0 2016-07-21 12:59:08 -07:00
Gabe Rosenhouse 0506bae8fc spec: remove CNI_VERSION env var, was never been implemented
No one uses this, and it is causing confusion (see #150)
2016-07-20 13:39:51 -07:00
Gabe Rosenhouse bacdc3668d libcni: add integration test coverage 2016-07-15 13:01:08 -07:00
Gabe Rosenhouse 5835c2bbb1 plugins: adds new no-op plugin that may be used as a test-double
Plugin can be configured to record all inputs and to respond with
arbitrary stdout or error message.  Will support upcoming integration
testing.
2016-07-15 12:48:02 -07:00
Gabe Rosenhouse 7f098f7c4a Merge pull request #269 from rosenhouse/skel-dependency-injection
Refactor skel with dependency injection
2016-07-15 12:45:18 -07:00
Gabe Rosenhouse a2aff8c6a8 misc: fix up copyright dates 2016-07-14 16:09:27 -07:00
Tom Denham f484990a00 Merge pull request #263 from feiskyer/ptp-test
plugins/main/ptp: add functional tests
2016-07-14 14:32:05 -06:00
Gabe Rosenhouse 63755d7c57 pkg/skel: plugins now respond to VERSION command
To support CNI spec versioning, plugins must be able to report version
information to container runtimes.
2016-07-14 00:24:32 -04:00
Gabe Rosenhouse f436418525 pkg/skel: improve error message for missing CNI_COMMAND env var
This makes the error message for missing CNI_COMMAND consistent with
that of other required environment variables.
2016-07-14 00:24:32 -04:00
Gabe Rosenhouse c17e700759 pkg/skel: missing env var log lines appear in stderr
Previously, the log lines appeared in stdout before the JSON encoding of
the error message.  That would break JSON parsing of stdout.  Instead, we use
stderr for these unstructured logs, consistent with the CNI spec.
2016-07-14 00:24:27 -04:00
Gabe Rosenhouse 791d259e55 pkg/skel: refactor to use dependency injection
Extract dependencies on os to enable more complete unit test coverage
2016-07-14 00:06:58 -04:00
Pengfei Ni cd14fc0e06 plugins/main/ptp: add functional tests 2016-07-11 09:25:36 +08:00
Gabe Rosenhouse 8a93673209 Merge pull request #264 from rosenhouse/test-with-vagrant
Add Vagrantfile, document how to run test suite in a vagrant VM
2016-07-06 11:33:32 -07:00
Gabe Rosenhouse 0c2b5f5139 Add Vagrantfile, document how to run test suite in a vagrant VM 2016-07-05 23:12:02 -07:00
Tom Denham b8e92ed030 Merge pull request #247 from tomdee/spec-args-field
SPEC: introduce "args" field and new error code
2016-06-14 15:31:23 -07:00
Tom Denham 7b381a1af2 SPEC: introduce "args" field and new error code
Based on previous discussions on the CNI maintainers calls, the spec is
unclear on 1) when CNI_ARGS should be used and 2) the fact the dynamic
config can be passed in through the network JSON.

This PR makes it clear that per-container config can be passed in
through the network JSON, adding a top level `args` field into
which orchestrators can add additional metadata without worrying that
plugins might reject the additional data. It also allows for plugins to
reject unknown fields passed in at the top level.

Using JSON is preferable to CNI_ARGS since it allows namespaced and
structured data. CNI_ARGS is a flat list of KV pairs which has reserved
characters with no escaping rules defined.
CNI_ARGS may still be used by orchestrators that want the simplicity of
passing the network config JSON as specified by the user, unchanged
through to the CNI plugin. But for any kind of structured data, it's
recommended that the `args` field in the JSON is used instead.
2016-06-14 15:02:51 -07:00
Lee Calcote 4666a24b6d README: grammatical corrections 2016-06-14 09:54:15 -07:00
Pengfei Ni 0136a73037 pkg/ns: fix misspelling in comment 2016-06-14 09:52:58 -07:00
Stefan Junker 3bee295ba5 Merge pull request #240 from steveeJ/unmarshall-string
pkg/types: unmarshal string tests
2016-06-13 18:30:24 -07:00
Stefan Junker 3476000725 pkg/types: cover string for unmarshal tests 2016-06-13 18:18:36 -07:00
Stefan Junker 17dcf87589 Merge pull request #238 from tomdee/string-unmarshaller
pkg/types: Add UnmarshallableString type
2016-06-13 18:17:48 -07:00
Stefan Junker c3c0803817 Merge pull request #243 from containernetworking/maintainers-update-email
MAINTAINERS: Update @tomdee email address
2016-06-10 01:41:42 +02:00
Tom Denham 0ffad38994 MAINTAINERS: Update @tomdee email address 2016-06-09 16:32:26 -07:00
Tom Denham 031567c216 pkg/types: Add UnmarshallableString type
Allow strings to be unmarshalled for CNI_ARGS

CNI_ARGS uses types.LoadArgs to populate a struct.
The fields in the struct must meet the TextUnmarshaler interface.

This code adds a UnmarshallableString type to assist with this.
2016-06-09 14:45:58 -07:00
Stefan Junker c6864e1060 Merge pull request #229 from steveeJ/cleanup-travis
travis: cleanup and bump go versions
2016-06-02 20:43:11 +02:00
Minhan Xia 2445a960a9 plugins/ipam: round robin ip allocation for host-local ipam
This changes the ip allocation logic to round robin. Before this, host-local IPAM searched for available IPs from start of subnet. Hence it tends to allocate IPs that had been used recently. This is not ideal since it may cause collisions.
2016-06-02 20:37:05 +02:00
Tom Denham 5c3c171642 Merge pull request #239 from steveeJ/master
MAINTAINERS: remove @zachgersh, add @dcbw
2016-06-02 09:00:07 -07:00
Michael Bridgen 4a292cd451 Merge pull request #218 from steveeJ/roadmap
Add ROADMAP document
2016-06-02 16:51:57 +01:00
Stefan Junker f82af22b10 ROADMAP: initial version 2016-06-02 17:50:14 +02:00
Stefan Junker 4f9faf60bb MAINTAINERS: remove @zachgersh, add @dcbw
Thank you Zach for all the great work done on CNI, farewell!
At the same time we are happy to welcome Dan amongst us who has already
contributed lots of valuable work!
2016-06-02 01:15:24 +02:00
Stefan Junker f44f4cf953 Merge pull request #219 from squaremo/more-contrib
docs: details on PR acceptance policy
2016-06-01 16:03:17 +02:00
Michael Bridgen ddc0ca4791 docs: details on PR acceptance policy 2016-06-01 12:23:21 +01:00
Michael Bridgen e90d8c12b1 Merge pull request #228 from jieyu/add_mesos
Added Mesos to CNI users.
2016-05-30 11:13:34 +01:00
Stefan Junker ffff8ac2fd Merge pull request #231 from steveeJ/ns-verifiy-errors
pkg/ns: introduce error types to indicate NS verification
2016-05-27 15:57:40 +02:00
Stefan Junker 35f3a090b2 pkg/ns: introduce error types indicate NS verification 2016-05-27 13:50:16 +02:00
Stefan Junker 131ecc4055 Merge pull request #230 from steveeJ/netns-optional-on-del
plugins: don't require CNI_NETNS for DEL command
2016-05-27 13:49:05 +02:00
Stefan Junker d582c9ce8f skel/test: add case for empty NETNS 2016-05-27 12:26:42 +02:00
Stefan Junker 72337159c1 plugins: don't require CNI_NETNS for DEL command
This will allow to free up the IPAM allocations when the caller doesn't
have access to the network namespace anymore, e.g. due to a reboot.
2016-05-27 10:57:39 +02:00
Stefan Junker 7f90f9d559 pkg/skel: allow arg requriements specified by CMD 2016-05-27 10:56:24 +02:00
Jie Yu d6674e0f49 README: add Mesos as CNI users 2016-05-26 09:42:10 -07:00
Stefan Junker 3b5d7a3bb0 travis: use go1.5.4 2016-05-26 15:33:58 +02:00
Stefan Junker 572fcc7076 travis: cleanup and bump go versions
* cleanup structure
* explicitly use go-{1.5.3,1.6.2}
2016-05-26 14:43:18 +02:00
Stefan Junker 6f63d9d707 Merge pull request #227 from steveeJ/ns-verify
pkg/ns: consider PROCFS during NS verification
2016-05-26 13:22:29 +02:00
Stefan Junker 3bab8a2805 pkg/ns: consider PROCFS during NS verification
This is an attempt to bring compatibility with Kernel <3.19, where NSFS
where PROCFS was used for network namespaces.
2016-05-26 12:42:50 +02:00
Stefan Junker 6fb30a6700 Merge pull request #222 from steveeJ/ns-check-path
pkg/ns: verify netns when initialized with GetNS
2016-05-25 08:54:10 +02:00
Stefan Junker d6751cea24 pkg/ns: test IsNSFS() 2016-05-24 22:30:49 +02:00
Stefan Junker c43ccc703a pkg/ns: test case for rejecting a non-ns nspath 2016-05-24 22:30:49 +02:00
Stefan Junker 76ea259ff9 pkg/ns: verify netns when initialized with GetNS 2016-05-24 22:30:49 +02:00
Stefan Junker c29cd52628 Merge pull request #223 from steveeJ/ns-respect-close
pkg/ns: don't allow operations after Close()
2016-05-24 22:16:09 +02:00
Stefan Junker 2de97b7e98 pkg/ns: add tests cases for Close()'d NS 2016-05-24 21:15:51 +02:00
Stefan Junker b23895a7c7 pkg/ns: don't allow operations after Close() 2016-05-24 20:52:00 +02:00
493 changed files with 14377 additions and 100404 deletions

2
.gitattributes vendored Normal file
View File

@ -0,0 +1,2 @@
# Don't rewrite line endings
*.go -text

View File

@ -0,0 +1,7 @@
FROM alpine:3.20
RUN apk add --no-cache curl jq
COPY entrypoint.sh /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]

View File

@ -0,0 +1,11 @@
name: 'Re-Test'
description: 'Re-Runs the last workflow for a PR'
inputs:
token:
description: 'GitHub API Token'
required: true
runs:
using: 'docker'
image: 'Dockerfile'
env:
GITHUB_TOKEN: ${{ inputs.token }}

45
.github/actions/retest-action/entrypoint.sh vendored Executable file
View File

@ -0,0 +1,45 @@
#!/bin/sh
set -ex
if ! jq -e '.issue.pull_request' ${GITHUB_EVENT_PATH}; then
echo "Not a PR... Exiting."
exit 0
fi
if [ "$(jq -r '.comment.body' ${GITHUB_EVENT_PATH})" != "/retest" ]; then
echo "Nothing to do... Exiting."
exit 0
fi
PR_URL=$(jq -r '.issue.pull_request.url' ${GITHUB_EVENT_PATH})
curl --request GET \
--url "${PR_URL}" \
--header "authorization: Bearer ${GITHUB_TOKEN}" \
--header "content-type: application/json" > pr.json
ACTOR=$(jq -r '.user.login' pr.json)
BRANCH=$(jq -r '.head.ref' pr.json)
curl --request GET \
--url "https://api.github.com/repos/${GITHUB_REPOSITORY}/actions/runs?event=pull_request&actor=${ACTOR}&branch=${BRANCH}" \
--header "authorization: Bearer ${GITHUB_TOKEN}" \
--header "content-type: application/json" | jq '.workflow_runs | max_by(.run_number)' > run.json
RERUN_URL=$(jq -r '.rerun_url' run.json)
curl --request POST \
--url "${RERUN_URL}" \
--header "authorization: Bearer ${GITHUB_TOKEN}" \
--header "content-type: application/json"
REACTION_URL="$(jq -r '.comment.url' ${GITHUB_EVENT_PATH})/reactions"
curl --request POST \
--url "${REACTION_URL}" \
--header "authorization: Bearer ${GITHUB_TOKEN}" \
--header "accept: application/vnd.github.squirrel-girl-preview+json" \
--header "content-type: application/json" \
--data '{ "content" : "rocket" }'

27
.github/dependabot.yml vendored Normal file
View File

@ -0,0 +1,27 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
- package-ecosystem: "docker"
directory: "/.github/actions/retest-action"
schedule:
interval: "weekly"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
- package-ecosystem: "gomod"
directory: "/"
schedule:
interval: "weekly"
groups:
golang:
patterns:
- "*"
- package-ecosystem: "gomod"
directory: "/plugins/debug"
schedule:
interval: "weekly"

17
.github/workflows/commands.yml vendored Normal file
View File

@ -0,0 +1,17 @@
name: commands
on:
issue_comment:
types: [created]
jobs:
retest:
if: github.repository == 'containernetworking/cni'
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: Re-Test Action
uses: ./.github/actions/retest-action
with:
token: ${{ secrets.REPO_ACCESS_TOKEN }}

40
.github/workflows/scorecard.yml vendored Normal file
View File

@ -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

96
.github/workflows/test.yaml vendored Normal file
View File

@ -0,0 +1,96 @@
---
name: test
on: ["push", "pull_request"]
env:
GO_VERSION: "1.22"
LINUX_ARCHES: "amd64 386 arm arm64 s390x mips64le ppc64le"
jobs:
lint:
name: Lint
permissions:
contents: read
pull-requests: read
runs-on: ubuntu-latest
steps:
- name: setup go
uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2
with:
go-version: ${{ env.GO_VERSION }}
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- uses: ibiqlik/action-yamllint@2576378a8e339169678f9939646ee3ee325e845c # v3.1.1
with:
format: auto
config_file: .yamllint.yaml
- uses: golangci/golangci-lint-action@aaa42aa0628b4ae2578232a66b541047968fac86 # v6.1.0
with:
args: --verbose
version: v1.57.1
build:
name: Build all linux architectures
needs: lint
runs-on: ubuntu-latest
steps:
- name: setup go
uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2
with:
go-version: ${{ env.GO_VERSION }}
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: Build on all supported architectures
run: |
set -e
for arch in ${LINUX_ARCHES}; do
echo "Building for arch $arch"
GOARCH=$arch go build ./...
done
test-linux:
name: Run tests on Linux amd64
needs: build
runs-on: ubuntu-latest
steps:
- name: setup go
uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2
with:
go-version: ${{ env.GO_VERSION }}
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: Install test binaries
run: |
go install github.com/mattn/goveralls@v0.0.12
go install github.com/modocache/gover@latest
- name: test
run: COVERALLS=1 ./test.sh
- env:
COVERALLS_TOKEN: ${{ secrets.GITHUB_TOKEN }}
name: Send coverage to coveralls
run: |
PATH=$PATH:$(go env GOPATH)/bin
gover
goveralls -coverprofile=gover.coverprofile -service=github
test-win:
name: Build and run tests on Windows
needs: build
runs-on: windows-latest
steps:
- name: setup go
uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2
with:
go-version: ${{ env.GO_VERSION }}
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: test
run: bash ./test.sh

6
.gitignore vendored
View File

@ -1,3 +1,5 @@
bin/
gopath/
.idea/
*.sw[ponm]
.vagrant
release-*
cnitool/cnitool

30
.golangci.yml Normal file
View File

@ -0,0 +1,30 @@
linters:
enable:
- contextcheck
- errcheck
- errorlint
- gci
- ginkgolinter
- gocritic
- gofumpt
- govet
- ineffassign
- misspell
- nolintlint
- nonamedreturns
- predeclared
- staticcheck
- typecheck
- unconvert
- unused
- whitespace
linters-settings:
gci:
sections:
- standard
- default
- prefix(github.com/containernetworking)
run:
timeout: 5m

View File

@ -1,29 +0,0 @@
language: go
sudo: required
dist: trusty
go:
- 1.5.3
- 1.6
- tip
matrix:
allow_failures:
- go: tip
env:
global:
- TOOLS_CMD=golang.org/x/tools/cmd
- PATH=$GOROOT/bin:$PATH
- GO15VENDOREXPERIMENT=1
install:
- go get ${TOOLS_CMD}/cover
- go get github.com/modocache/gover
- go get github.com/mattn/goveralls
script:
- ./test
notifications:
email: false

10
.yamllint.yaml Normal file
View File

@ -0,0 +1,10 @@
---
extends: default
rules:
document-start: disable
line-length: disable
truthy:
ignore: |
.github/workflows/*.yml
.github/workflows/*.yaml

3
CODE-OF-CONDUCT.md Normal file
View File

@ -0,0 +1,3 @@
# Community Code of Conduct
CNI follows the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/main/code-of-conduct.md).

View File

@ -1,24 +1,25 @@
# How to Contribute
cni is [Apache 2.0 licensed](LICENSE) and accepts contributions via GitHub
CNI is [Apache 2.0 licensed](LICENSE) and accepts contributions via GitHub
pull requests. This document outlines some of the conventions on development
workflow, commit message formatting, contact points and other resources to make
it easier to get your contribution accepted.
We welcome improvements to documentation as well as to code.
We gratefully welcome improvements to documentation as well as to code.
# Certificate of Origin
## Certificate of Origin
By contributing to this project you agree to the Developer Certificate of
Origin (DCO). This document was created by the Linux Kernel community and is a
simple statement that you, as a contributor, have the legal right to make the
contribution. See the [DCO](DCO) file for details.
# Email and Chat
## Email and Chat
The project uses the the cni-dev email list and IRC chat:
The project uses the cni-dev email list, IRC chat, and Slack:
- Email: [cni-dev](https://groups.google.com/forum/#!forum/cni-dev)
- IRC: #[containernetworking](irc://irc.freenode.org:6667/#containernetworking) channel on freenode.org
- IRC: #[containernetworking](irc://irc.freenode.net:6667/#containernetworking) channel on [freenode.net](https://freenode.net/)
- Slack: #cni on the [CNCF slack](https://slack.cncf.io/). NOTE: the previous CNI Slack (containernetworking.slack.com) has been sunsetted.
Please avoid emailing maintainers found in the MAINTAINERS file directly. They
are very busy and read the mailing lists.
@ -33,15 +34,53 @@ are very busy and read the mailing lists.
This is a rough outline of how to prepare a contribution:
- Create a topic branch from where you want to base your work (usually branched from master).
- Create a topic branch from where you want to base your work (usually branched from main).
- Make commits of logical units.
- Make sure your commit messages are in the proper format (see below).
- Push your changes to a topic branch in your fork of the repository.
- If you changed code, make sure the tests pass, and add any new tests as appropriate.
- Make sure any new code files have a license header.
- If you changed code:
- add automated tests to cover your changes, using the [Ginkgo](https://onsi.github.io/ginkgo/) & [Gomega](https://onsi.github.io/gomega/) style
- if the package did not previously have any test coverage, add it to the list
of `TESTABLE` packages in the `test.sh` script.
- run the full test script and ensure it passes
- Make sure any new code files have a license header (this is now enforced by automated tests)
- Submit a pull request to the original repository.
Thanks for your contributions!
## How to run the test suite
We generally require test coverage of any new features or bug fixes.
Here's how you can run the test suite on any system (even Mac or Windows) using
[Vagrant](https://www.vagrantup.com/) and a hypervisor of your choice:
```bash
vagrant up
vagrant ssh
# you're now in a shell in a virtual machine
sudo su
cd /go/src/github.com/containernetworking/cni
# to run the full test suite
./test.sh
# to focus on a particular test suite
cd libcni
go test
```
## Acceptance policy
These things will make a PR more likely to be accepted:
- a well-described requirement
- tests for new code
- tests for old code!
- new code and tests follow the conventions in old code and tests
- a good commit message (see below)
In general, we will merge a PR once two maintainers have endorsed it.
Trivial changes (e.g., corrections to spelling) may get waved through.
For substantial changes, more people may become involved, and you might get asked to resubmit the PR or divide the changes into more than one PR.
### Format of the Commit Message
@ -49,7 +88,7 @@ We follow a rough convention for commit messages that is designed to answer two
questions: what changed and why. The subject line should feature the what and
the body of the commit should describe the why.
```
```md
scripts: add the test-cluster command
this uses tmux to setup a test cluster that you can easily kill and
@ -60,7 +99,7 @@ Fixes #38
The format can be described more formally as follows:
```
```md
<subsystem>: <what changed>
<BLANK LINE>
<why this change was made>
@ -72,3 +111,18 @@ The first line is the subject and should be no longer than 70 characters, the
second line is always blank, and other lines should be wrapped at 80 characters.
This allows the message to be easier to read on GitHub as well as in various
git tools.
## 3rd party plugins
So you've built a CNI plugin. Where should it live?
Short answer: We'd be happy to link to it from our [list of 3rd party plugins](README.md#3rd-party-plugins).
But we'd rather you kept the code in your own repo.
Long answer: An advantage of the CNI model is that independent plugins can be
built, distributed and used without any code changes to this repository. While
some widely used plugins (and a few less-popular legacy ones) live in this repo,
we're reluctant to add more.
If you have a good reason why the CNI maintainers should take custody of your
plugin, please open an issue or PR.

120
CONVENTIONS.md Normal file
View File

@ -0,0 +1,120 @@
# Extension conventions
There are three ways of passing information to plugins using the Container Network Interface (CNI), none of which require the [spec](SPEC.md) to be updated. These are
- plugin specific fields in the JSON config
- `args` field in the JSON config
- `CNI_ARGS` environment variable
This document aims to provide guidance on which method should be used and to provide a convention for how common information should be passed.
Establishing these conventions allows plugins to work across multiple runtimes. This helps both plugins and the runtimes.
## Plugins
* Plugin authors should aim to support these conventions where it makes sense for their plugin. This means they are more likely to "just work" with a wider range of runtimes.
* Plugins should accept arguments according to these conventions if they implement the same basic functionality as other plugins. If plugins have shared functionality that isn't covered by these conventions then a PR should be opened against this document.
## Runtimes
* Runtime authors should follow these conventions if they want to pass additional information to plugins. This will allow the extra information to be consumed by the widest range of plugins.
* These conventions serve as an abstraction for the runtime. For example, port forwarding is highly implementation specific, but users should be able to select the plugin of their choice without changing the runtime.
# Current conventions
Additional conventions can be created by creating PRs which modify this document.
## Dynamic Plugin specific fields (Capabilities / Runtime Configuration)
[Plugin specific fields](SPEC.md#network-configuration) formed part of the original CNI spec and have been present since the initial release.
> Plugins may define additional fields that they accept and may generate an error if called with unknown fields. The exception to this is the args field may be used to pass arbitrary data which may be ignored by plugins.
A plugin can define any additional fields it needs to work properly. It should return an error if it can't act on fields that were expected or where the field values were malformed.
This method of passing information to a plugin is recommended when the following conditions hold:
* The configuration has specific meaning to the plugin (i.e. it's not just general meta data)
* the plugin is expected to act on the configuration or return an error if it can't
Dynamic information (i.e. data that a runtime fills out) should be placed in a `runtimeConfig` section. Plugins can request
that the runtime insert this dynamic configuration by explicitly listing their `capabilities` in the network configuration.
For example, the configuration for a port mapping plugin might look like this to an operator (it should be included as part of a [network configuration list](SPEC.md#network-configuration-lists).
```json
{
"name" : "ExamplePlugin",
"type" : "port-mapper",
"capabilities": {"portMappings": true}
}
```
But the runtime would fill in the mappings so the plugin itself would receive something like this.
```json
{
"name" : "ExamplePlugin",
"type" : "port-mapper",
"runtimeConfig": {
"portMappings": [
{"hostPort": 8080, "containerPort": 80, "protocol": "tcp"}
]
}
}
```
### Well-known Capabilities
| Area | Purpose | Capability | Spec and Example | Runtime implementations | Plugin Implementations |
| ----- | ------- | -----------| ---------------- | ----------------------- | --------------------- |
| port mappings | Pass mapping from ports on the host to ports in the container network namespace. | `portMappings` | A list of portmapping entries.<br/> <pre>[<br/> { "hostPort": 8080, "containerPort": 80, "protocol": "tcp" },<br /> { "hostPort": 8000, "containerPort": 8001, "protocol": "udp" }<br /> ]<br /></pre> | kubernetes | CNI `portmap` plugin |
| ip ranges | Dynamically configure the IP range(s) for address allocation. Runtimes that manage IP pools, but not individual IP addresses, can pass these to plugins. | `ipRanges` | The same as the `ranges` key for `host-local` - a list of lists of subnets. The outer list is the number of IPs to allocate, and the inner list is a pool of subnets for each allocation. <br/><pre>[<br/> [<br/> { "subnet": "10.1.2.0/24", "rangeStart": "10.1.2.3", "rangeEnd": 10.1.2.99", "gateway": "10.1.2.254" } <br/> ]<br/>]</pre> | none | CNI `host-local` plugin |
| bandwidth limits | Dynamically configure interface bandwidth limits | `bandwidth` | Desired bandwidth limits. Rates are in bits per second, burst values are in bits. <pre> { "ingressRate": 2048, "ingressBurst": 1600, "egressRate": 4096, "egressBurst": 1600 } </pre> | none | CNI `bandwidth` plugin |
| dns | Dynamically configure dns according to runtime | `dns` | Dictionary containing a list of `servers` (string entries), a list of `searches` (string entries), a list of `options` (string entries). <pre>{ <br> "searches" : [ "internal.yoyodyne.net", "corp.tyrell.net" ] <br> "servers": [ "8.8.8.8", "10.0.0.10" ] <br />} </pre> | kubernetes | CNI `win-bridge` plugin, CNI `win-overlay` plugin |
| ips | Dynamically allocate IPs for container interface. Runtime which has the ability of address allocation can pass these to plugins. | `ips` | A list of `IP` (\<ip\>\[/\<prefix\>\]). <pre> [ "192.168.0.1", 10.10.0.1/24", "3ffe:ffff:0:01ff::2", "3ffe:ffff:0:01ff::1/64" ] </pre> The plugin may require the IP address to include a prefix length. | none | CNI `static` plugin, CNI `host-local` plugin |
| mac | Dynamically assign MAC. Runtime can pass this to plugins which need MAC as input. | `mac` | `MAC` (string entry). <pre> "c2:11:22:33:44:55" </pre> | none | CNI `tuning` plugin |
| infiniband guid | Dynamically assign Infiniband GUID to network interface. Runtime can pass this to plugins which need Infiniband GUID as input. | `infinibandGUID` | `GUID` (string entry). <pre> "c2:11:22:33:44:55:66:77" </pre> | none | CNI [`ib-sriov-cni`](https://github.com/Mellanox/ib-sriov-cni) plugin |
| device id | Provide device identifier which is associated with the network to allow the CNI plugin to perform device dependent network configurations. | `deviceID` | `deviceID` (string entry). <pre> "0000:04:00.5" </pre> | none | CNI `host-device` plugin |
| aliases | Provide a list of names that will be mapped to the IP addresses assigned to this interface. Other containers on the same network may use one of these names to access the container.| `aliases` | List of `alias` (string entry). <pre> ["my-container", "primary-db"] </pre> | none | CNI `alias` plugin |
| cgroup path | Provide the cgroup path for pod as requested by CNI plugins. | `cgroupPath` | `cgroupPath` (string entry). <pre>"/kubelet.slice/kubelet-kubepods.slice/kubelet-kubepods-burstable.slice/kubelet-kubepods-burstable-pod28ce45bc_63f8_48a3_a99b_cfb9e63c856c.slice" </pre> | none | CNI `host-local` plugin |
## "args" in network config
`args` in [network config](SPEC.md#network-configuration) were reserved as a field in the `0.2.0` release of the CNI spec.
> args (dictionary): Optional additional arguments provided by the container runtime. For example a dictionary of labels could be passed to CNI plugins by adding them to a labels field under args.
`args` provide a way of providing more structured data than the flat strings that CNI_ARGS can support.
`args` should be used for _optional_ meta-data. Runtimes can place additional data in `args` and plugins that don't understand that data should just ignore it. Runtimes should not require that a plugin understands or consumes that data provided, and so a runtime should not expect to receive an error if the data could not be acted on.
This method of passing information to a plugin is recommended when the information is optional and the plugin can choose to ignore it. It's often that case that such information is passed to all plugins by the runtime without regard for whether the plugin can understand it.
The conventions documented here are all namespaced under `cni` so they don't conflict with any existing `args`.
For example:
```jsonc
{
"cniVersion":"0.2.0",
"name":"net",
"args":{
"cni":{
"labels": [{"key": "app", "value": "myapp"}]
}
},
// <REST OF CNI CONFIG HERE>
"ipam":{
// <IPAM CONFIG HERE>
}
}
```
| Area | Purpose| Spec and Example | Runtime implementations | Plugin Implementations |
| ----- | ------ | ------------ | ----------------------- | ---------------------- |
| labels | Pass`key=value` labels to plugins | <pre>"labels" : [<br /> { "key" : "app", "value" : "myapp" },<br /> { "key" : "env", "value" : "prod" }<br />] </pre> | none | none |
| ips | Request specific IPs | Spec:<pre>"ips": ["\<ip\>[/\<prefix\>]", ...]</pre>Examples:<pre>"ips": ["10.2.2.42/24", "2001:db8::5"]</pre> The plugin may require the IP address to include a prefix length. | none | host-local, static |
## CNI_ARGS
CNI_ARGS formed part of the original CNI spec and have been present since the initial release.
> `CNI_ARGS`: Extra arguments passed in by the user at invocation time. Alphanumeric key-value pairs separated by semicolons; for example, "FOO=BAR;ABC=123"
The use of `CNI_ARGS` is deprecated and "args" should be used instead. If a runtime passes an equivalent key via `args` (eg the `ips` `args` Area and the `CNI_ARGS` `IP` Field) and the plugin understands `args`, the plugin must ignore the CNI_ARGS Field.
| Field | Purpose| Spec and Example | Runtime implementations | Plugin Implementations |
| ------ | ------ | ---------------- | ----------------------- | ---------------------- |
| IP | Request a specific IP from IPAM plugins | Spec:<pre>IP=\<ip\>[/\<prefix\>]</pre>Example: <pre>IP=192.168.10.4/24</pre> The plugin may require the IP addresses to include a prefix length. | *rkt* supports passing additional arguments to plugins and the [documentation](https://coreos.com/rkt/docs/latest/networking/overriding-defaults.html) suggests IP can be used. | host-local, static |
## Chained Plugins
If plugins are agnostic about the type of interface created, they SHOULD work in a chained mode and configure existing interfaces. Plugins MAY also create the desired interface when not run in a chain.
For example, the `bridge` plugin adds the host-side interface to a bridge. So, it should accept any previous result that includes a host-side interface, including `tap` devices. If not called as a chained plugin, it creates a `veth` pair first.
Plugins that meet this convention are usable by a larger set of runtimes and interfaces, including hypervisors and DPDK providers.

View File

@ -1,40 +0,0 @@
# bridge plugin
## Overview
With bridge plugin, all containers (on the same host) are plugged into a bridge (virtual switch) that resides in the host network namespace.
The containers receive one end of the veth pair with the other end connected to the bridge.
An IP address is only assigned to one end of the veth pair -- one residing in the container.
The bridge itself can also be assigned an IP address, turning it into a gateway for the containers.
Alternatively, the bridge can function purely in L2 mode and would need to be bridged to the host network interface (if other than container-to-container communication on the same host is desired).
The network configuration specifies the name of the bridge to be used.
If the bridge is missing, the plugin will create one on first use and, if gateway mode is used, assign it an IP that was returned by IPAM plugin via the gateway field.
## Example configuration
```
{
"name": "mynet",
"type": "bridge",
"bridge": "mynet0",
"isDefaultGateway": true,
"ipMasq": true,
"hairpinMode": true,
"ipam": {
"type": "host-local",
"subnet": "10.10.0.0/16"
}
}
```
## Network configuration reference
* `name` (string, required): the name of the network.
* `type` (string, required): "bridge".
* `bridge` (string, optional): name of the bridge to use/create. Defaults to "cni0".
* `isGateway` (boolean, optional): assign an IP address to the bridge. Defaults to false.
* `isDefaultGateway` (boolean, optional): Sets isGateway to true and makes the assigned IP the default route. Defaults to false.
* `ipMasq` (boolean, optional): set up IP Masquerade on the host for traffic originating from this network and destined outside of it. Defaults to false.
* `mtu` (integer, optional): explicitly set MTU to the specified value. Defaults to the value chosen by the kernel.
* `hairpinMode` (boolean, optional): set hairpin mode for interfaces on the bridge. Defaults to false.
* `ipam` (dictionary, required): IPAM configuration to be used for this network.

19
Documentation/cnitool.md Normal file
View File

@ -0,0 +1,19 @@
# Overview
The `cnitool` is a utility that can be used to test a CNI plugin
without the need for a container runtime. The `cnitool` takes a
`network name` and a `network namespace` and a command to `ADD` or
`DEL`,.i.e, attach or detach containers from a network. The `cnitool`
relies on the following environment variables to operate properly:
* `NETCONFPATH`: This environment variable needs to be set to a
directory. It defaults to `/etc/cni/net.d`. The `cnitool` searches
for CNI configuration files in this directory with the extension
`*.conf` or `*.json`. It loads all the CNI configuration files in
this directory and if it finds a CNI configuration with the `network
name` given to the cnitool it returns the corresponding CNI
configuration, else it returns `nil`.
* `CNI_PATH`: For a given CNI configuration `cnitool` will search for
the corresponding CNI plugin in this path.
For the full documentation of `cnitool` see the [cnitool docs](../cnitool/README.md)

View File

@ -1,35 +0,0 @@
# dhcp plugin
## Overview
With dhcp plugin the containers can get an IP allocated by a DHCP server already running on your network.
This can be especially useful with plugin types such as [macvlan](https://github.com/containernetworking/cni/blob/master/Documentation/macvlan.md).
Because a DHCP lease must be periodically renewed for the duration of container lifetime, a separate daemon is required to be running.
The same plugin binary can also be run in the daemon mode.
## Operation
To use the dhcp IPAM plugin, first launch the dhcp daemon:
```
# Make sure the unix socket has been removed
$ rm -f /run/cni/dhcp.sock
$ ./dhcp daemon
```
Alternatively, you can use systemd socket activation protocol.
Be sure that the .socket file uses /run/cni/dhcp.sock as the socket path.
With the daemon running, containers using the dhcp plugin can be launched.
## Example configuration
```
{
"ipam": {
"type": "dhcp",
}
}
## Network configuration reference
* `type` (string, required): "dhcp"

View File

@ -1,87 +0,0 @@
# flannel plugin
## Overview
This plugin is designed to work in conjunction with [flannel](https://github.com/coreos/flannel), a network fabric for containers.
When flannel daemon is started, it outputs a `/run/flannel/subnet.env` file that looks like this:
```
FLANNEL_NETWORK=10.1.0.0/16
FLANNEL_SUBNET=10.1.17.1/24
FLANNEL_MTU=1472
FLANNEL_IPMASQ=true
```
This information reflects the attributes of flannel network on the host.
The flannel CNI plugin uses this information to configure another CNI plugin, such as bridge plugin.
## Operation
Given the following network configuration file and the contents of `/run/flannel/subnet.env` above,
```
{
"name": "mynet",
"type": "flannel"
}
```
the flannel plugin will generate another network configuration file:
```
{
"name": "mynet",
"type": "bridge",
"mtu": 1472,
"ipMasq": false,
"isGateway": true,
"ipam": {
"type": "host-local",
"subnet": "10.1.17.0/24"
}
}
```
It will then invoke the bridge plugin, passing it the generated configuration.
As can be seen from above, the flannel plugin, by default, will delegate to the bridge plugin.
If additional configuration values need to be passed to the bridge plugin, it can be done so via the `delegate` field:
```
{
"name": "mynet",
"type": "flannel",
"delegate": {
"bridge": "mynet0",
"mtu": 1400
}
}
```
This supplies a configuration parameter to the bridge plugin -- the created bridge will now be named `mynet0`.
Notice that `mtu` has also been specified and this value will not be overwritten by flannel plugin.
Additionally, the `delegate` field can be used to select a different kind of plugin altogether.
To use `ipvlan` instead of `bridge`, the following configuration can be specified:
```
{
"name": "mynet",
"type": "flannel",
"delegate": {
"type": "ipvlan",
"master": "eth0"
}
}
```
## Network configuration reference
* `name` (string, required): the name of the network
* `type` (string, required): "flannel"
* `subnetFile` (string, optional): full path to the subnet file written out by flanneld. Defaults to /run/flannel/subnet.env
* `delegate` (dictionary, optional): specifies configuration options for the delegated plugin.
flannel plugin will always set the following fields in the delegated plugin configuration:
* `name`: value of its "name" field.
* `ipam`: "host-local" type will be used with "subnet" set to `$FLANNEL_SUBNET`.
flannel plugin will set the following fields in the delegated plugin configuration if they are not present:
* `ipMasq`: the inverse of `$FLANNEL_IPMASQ`
* `mtu`: `$FLANNEL_MTU`
Additionally, for the bridge plugin, `isGateway` will be set to `true`, if not present.

View File

@ -1,41 +0,0 @@
# host-local plugin
## Overview
host-local IPAM plugin allocates IPv4 addresses out of a specified address range.
It stores the state locally on the host filesystem, therefore ensuring uniqueness of IP addresses on a single host.
## Example configuration
```
{
"ipam": {
"type": "host-local",
"subnet": "10.10.0.0/16",
"rangeStart": "10.10.1.20",
"rangeEnd": "10.10.3.50",
"gateway": "10.10.0.254",
"routes": [
{ "dst": "0.0.0.0/0" },
{ "dst": "192.168.0.0/16", "gw": "10.10.5.1" }
]
}
}
```
## Network configuration reference
* `type` (string, required): "host-local".
* `subnet` (string, required): CIDR block to allocate out of.
* `rangeStart` (string, optional): IP inside of "subnet" from which to start allocating addresses. Defaults to ".2" IP inside of the "subnet" block.
* `rangeEnd` (string, optional): IP inside of "subnet" with which to end allocating addresses. Defaults to ".254" IP inside of the "subnet" block.
* `gateway` (string, optional): IP inside of "subnet" to designate as the gateway. Defaults to ".1" IP inside of the "subnet" block.
* `routes` (string, optional): list of routes to add to the container namespace. Each route is a dictionary with "dst" and optional "gw" fields. If "gw" is omitted, value of "gateway" will be used.
## Supported arguments
The following [CNI_ARGS](https://github.com/containernetworking/cni/blob/master/SPEC.md#parameters) are supported:
* `ip`: request a specific IP address from the subnet. If it's not available, the plugin will exit with an error
## Files
Allocated IP addresses are stored as files in /var/lib/cni/networks/$NETWORK_NAME.

View File

@ -1,40 +0,0 @@
# ipvlan plugin
## Overview
ipvlan is a new [addition](https://lwn.net/Articles/620087/) to the Linux kernel.
Like its cousin macvlan, it virtualizes the host interface.
However unlike macvlan which generates a new MAC address for each interface, ipvlan devices all share the same MAC.
The kernel driver inspects the IP address of each packet when making a decision about which virtual interface should process the packet.
Because all ipvlan interfaces share the MAC address with the host interface, DHCP can only be used in conjunction with ClientID (currently not supported by DHCP plugin).
## Example configuration
```
{
"name": "mynet",
"type": "ipvlan",
"master": "eth0",
"ipam": {
"type": "host-local",
"subnet": "10.1.2.0/24",
}
}
```
## Network configuration reference
* `name` (string, required): the name of the network.
* `type` (string, required): "ipvlan".
* `master` (string, required): name of the host interface to enslave.
* `mode` (string, optional): one of "l2", "l3". Defaults to "l2".
* `mtu` (integer, optional): explicitly set MTU to the specified value. Defaults to the value chosen by the kernel.
* `ipam` (dictionary, required): IPAM configuration to be used for this network.
## Notes
* `ipvlan` does not allow virtual interfaces to communicate with the master interface.
Therefore the container will not be able to reach the host via `ipvlan` interface.
Be sure to also have container join a network that provides connectivity to the host (e.g. `ptp`).
* A single master interface can not be enslaved by both `macvlan` and `ipvlan`.

View File

@ -1,34 +0,0 @@
# macvlan plugin
## Overview
[macvlan](http://backreference.org/2014/03/20/some-notes-on-macvlanmacvtap/) functions like a switch that is already connected to the host interface.
A host interface gets "enslaved" with the virtual interfaces sharing the physical device but having distinct MAC addresses.
Since each macvlan interface has its own MAC address, it makes it easy to use with existing DHCP servers already present on the network.
## Example configuration
```
{
"name": "mynet",
"type": "macvlan",
"master": "eth0",
"ipam": {
"type": "dhcp"
}
}
```
## Network configuration reference
* `name` (string, required): the name of the network
* `type` (string, required): "macvlan"
* `master` (string, required): name of the host interface to enslave
* `mode` (string, optional): one of "bridge", "private", "vepa", "passthrough". Defaults to "bridge".
* `mtu` (integer, optional): explicitly set MTU to the specified value. Defaults to the value chosen by the kernel.
* `ipam` (dictionary, required): IPAM configuration to be used for this network.
## Notes
* If are testing on a laptop, please remember that most wireless cards do not support being enslaved by macvlan.
* A single master interface can not be enslaved by both `macvlan` and `ipvlan`.

View File

@ -1,30 +0,0 @@
# ptp plugin
## Overview
The ptp plugin creates a point-to-point link between a container and the host by using a veth device.
One end of the veth pair is placed inside a container and the other end resides on the host.
The host-local IPAM plugin can be used to allocate an IP address to the container.
The traffic of the container interface will be routed through the interface of the host.
## Example network configuration
```
{
"name": "mynet",
"type": "ptp",
"ipam": {
"type": "host-local",
"subnet": "10.1.1.0/24"
},
"dns": {
"nameservers": [ "10.1.1.1", "8.8.8.8" ]
}
}
## Network configuration reference
* `name` (string, required): the name of the network
* `type` (string, required): "ptp"
* `ipMasq` (boolean, optional): set up IP Masquerade on the host for traffic originating from this network and destined outside of it. Defaults to false.
* `mtu` (integer, optional): explicitly set MTU to the specified value. Defaults to value chosen by the kernel.
* `ipam` (dictionary, required): IPAM configuration to be used for this network.
* `dns` (dictionary, optional): DNS information to return as described in the [Result](/SPEC.md#result).

View File

@ -0,0 +1,294 @@
# How to Upgrade to CNI Specification v1.0
CNI v1.0 has the following changes:
- non-List configurations are removed
- the `version` field in the `interfaces` array was redundant and is removed
## libcni Changes in CNI v1.0
**`/pkg/types/current` no longer exists**
This means that runtimes need to explicitly select a version they support.
This reduces code breakage when revendoring cni into other projects and
returns the decision on which CNI Spec versions a plugin supports to the
plugin's authors.
For example, your Go imports might look like
```go
import (
cniv1 "github.com/containernetworking/cni/pkg/types/100"
)
```
# Changes in CNI v0.4
CNI v0.4 has the following important changes:
- A new verb, "CHECK", was added. Runtimes can now ask plugins to verify the status of a container's attachment
- A new configuration flag, `disableCheck`, which indicates to the runtime that configuration should not be CHECK'ed
No changes were made to the result type.
# How to upgrade to CNI Specification v0.3.0 and later
The 0.3.0 specification contained a small error. The Result structure's `ip` field should have been renamed to `ips` to be consistent with the IPAM result structure definition; this rename was missed when updating the Result to accommodate multiple IP addresses and interfaces. All first-party CNI plugins (bridge, host-local, etc) were updated to use `ips` (and thus be inconsistent with the 0.3.0 specification) and most other plugins have not been updated to the 0.3.0 specification yet, so few (if any) users should be impacted by this change.
The 0.3.1 specification corrects the `Result` structure to use the `ips` field name as originally intended. This is the only change between 0.3.0 and 0.3.1.
Version 0.3.0 of the [CNI Specification](https://github.com/containernetworking/cni/blob/spec-v0.3.0/SPEC.md) provides rich information
about container network configuration, including details of network interfaces
and support for multiple IP addresses.
To support this new data, the specification changed in a couple significant
ways that will impact CNI users, plugin authors, and runtime authors.
This document provides guidance for how to upgrade:
- [For CNI Users](#for-cni-users)
- [For Plugin Authors](#for-plugin-authors)
- [For Runtime Authors](#for-runtime-authors)
**Note**: the CNI Spec is versioned independently from the GitHub releases
for this repo. For example, Release v0.4.0 supports Spec version v0.2.0,
and Release v0.5.0 supports Spec v0.3.0.
----
## For CNI Users
If you maintain CNI configuration files for a container runtime that uses CNI,
ensure that the configuration files specify a `cniVersion` field and that the
version there is supported by your container runtime and CNI plugins.
Configuration files without a version field should be given version 0.2.0.
The CNI spec includes example configuration files for
[single plugins](SPEC.md#example-configurations)
and for [lists of chained plugins](SPEC.md#example-configurations).
Consult the documentation for your runtime and plugins to determine what
CNI spec versions they support. Test any plugin upgrades before deploying to
production. You may find [cnitool](https://github.com/containernetworking/cni/tree/main/cnitool)
useful. Specifically, your configuration version should be the lowest common
version supported by your plugins.
## For Plugin Authors
This section provides guidance for upgrading plugins to CNI Spec Version 0.3.0.
### General guidance for all plugins (language agnostic)
To provide the smoothest upgrade path, **existing plugins should support
multiple versions of the CNI spec**. In particular, plugins with existing
installed bases should add support for CNI spec version 1.0.0 while maintaining
compatibility with older versions.
To do this, two changes are required. First, a plugin should advertise which
CNI spec versions it supports. It does this by responding to the `VERSION`
command with the following JSON data:
```json
{
"cniVersion": "1.0.0",
"supportedVersions": [ "0.1.0", "0.2.0", "0.3.0", "0.3.1", "0.4.0", "1.0.0" ]
}
```
Second, for the `ADD` command, a plugin must respect the `cniVersion` field
provided in the [network configuration JSON](SPEC.md#network-configuration).
That field is a request for the plugin to return results of a particular format:
- If the `cniVersion` field is not present, then spec v0.2.0 should be assumed
and v0.2.0 format result JSON returned.
- If the plugin doesn't support the version, the plugin must error.
- Otherwise, the plugin must return a [CNI Result](SPEC.md#result)
in the format requested.
Result formats for older CNI spec versions are available in the
[git history for SPEC.md](https://github.com/containernetworking/cni/commits/main/SPEC.md).
For example, suppose a plugin, via its `VERSION` response, advertises CNI specification
support for v0.2.0 and v0.3.0. When it receives `cniVersion` key of `0.2.0`,
the plugin must return result JSON conforming to CNI spec version 0.2.0.
### Specific guidance for plugins written in Go
Plugins written in Go may leverage the Go language packages in this repository
to ease the process of upgrading and supporting multiple versions. CNI
[Library and Plugins Release v0.5.0](https://github.com/containernetworking/cni/releases/tag/v0.5.0)
includes important changes to the Golang APIs. Plugins using these APIs will
require some changes now, but should more-easily handle spec changes and
new features going forward.
For plugin authors, the biggest change is that `types.Result` is now an
interface implemented by concrete struct types in the `types/100`,
`types/040`, and `types/020` subpackages.
Internally, plugins should use the latest spec version (eg `types/100`) structs,
and convert to or from specific versions when required. A typical plugin will
only need to do a single conversion when it is about to complete and
needs to print the result JSON in the requested `cniVersion` format to stdout.
The library function `types.PrintResult()` simplifies this by converting and
printing in a single call.
Additionally, the plugin should advertise which CNI Spec versions it supports
via the 3rd argument to `skel.PluginMain()`.
Here is some example code
```go
import (
"github.com/containernetworking/cni/pkg/skel"
"github.com/containernetworking/cni/pkg/types"
current "github.com/containernetworking/cni/pkg/types/100"
"github.com/containernetworking/cni/pkg/version"
)
func cmdAdd(args *skel.CmdArgs) error {
// determine spec version to use
var netConf struct {
types.NetConf
// other plugin-specific configuration goes here
}
err := json.Unmarshal(args.StdinData, &netConf)
cniVersion := netConf.CNIVersion
// plugin does its work...
// set up interfaces
// assign addresses, etc
// construct the result
result := &current.Result{
Interfaces: []*current.Interface{ ... },
IPs: []*current.IPs{ ... },
...
}
// print result to stdout, in the format defined by the requested cniVersion
return types.PrintResult(result, cniVersion)
}
func main() {
skel.PluginMain(cmdAdd, cmdDel, version.All)
}
```
Alternately, to use the result from a delegated IPAM plugin, the `result`
value might be formed like this:
```go
ipamResult, err := ipam.ExecAdd(netConf.IPAM.Type, args.StdinData)
result, err := current.NewResultFromResult(ipamResult)
```
Other examples of spec v0.3.0-compatible plugins are the
[main plugins in this repo](https://github.com/containernetworking/plugins/)
## For Runtime Authors
This section provides guidance for upgrading container runtimes to support
CNI Spec Version 0.3.0 and later.
### General guidance for all runtimes (language agnostic)
#### Support multiple CNI spec versions
To provide the smoothest upgrade path and support the broadest range of CNI
plugins, **container runtimes should support multiple versions of the CNI spec**.
In particular, runtimes with existing installed bases should add support for CNI
spec version 0.3.0 and later while maintaining compatibility with older versions.
To support multiple versions of the CNI spec, runtimes should be able to
call both new and legacy plugins, and handle the results from either.
When calling a plugin, the runtime must request that the plugin respond in a
particular format by specifying the `cniVersion` field in the
[Network Configuration](SPEC.md#network-configuration)
JSON block. The plugin will then respond with
a [Result](SPEC.md#result)
in the format defined by that CNI spec version, and the runtime must parse
and handle this result.
#### Handle errors due to version incompatibility
Plugins may respond with error indicating that they don't support the requested
CNI version (see [Well-known Error Codes](SPEC.md#well-known-error-codes)),
e.g.
```json
{
"cniVersion": "0.2.0",
"code": 1,
"msg": "CNI version not supported"
}
```
In that case, the runtime may retry with a lower CNI spec version, or take
some other action.
#### (optional) Discover plugin version support
Runtimes may discover which CNI spec versions are supported by a plugin, by
calling the plugin with the `VERSION` command. The `VERSION` command was
added in CNI spec v0.2.0, so older plugins may not respect it. In the absence
of a successful response to `VERSION`, assume that the plugin only supports
CNI spec v0.1.0.
#### Handle missing data in v0.3.0 and later results
The Result for the `ADD` command in CNI spec version 0.3.0 and later includes
a new field `interfaces`. An IP address in the `ip` field may describe which
interface it is assigned to, by placing a numeric index in the `interface`
subfield.
However, some plugins which are v0.3.0 and later compatible may nonetheless
omit the `interfaces` field and/or set the `interface` index value to `-1`.
Runtimes should gracefully handle this situation, unless they have good reason
to rely on the existence of the interface data. In that case, provide the user
an error message that helps diagnose the issue.
### Specific guidance for container runtimes written in Go
Container runtimes written in Go may leverage the Go language packages in this
repository to ease the process of upgrading and supporting multiple versions.
CNI [Library and Plugins Release v0.5.0](https://github.com/containernetworking/cni/releases)
includes important changes to the Golang APIs. Runtimes using these APIs will
require some changes now, but should more-easily handle spec changes and
new features going forward.
For runtimes, the biggest changes to the Go libraries are in the `types` package.
It has been refactored to make working with versioned results simpler. The top-level
`types.Result` is now an opaque interface instead of a struct, and APIs exposed by
other packages, such as the high-level `libcni` package, have been updated to use
this interface. Concrete types are now per-version subpackages. The `types/current`
subpackage contains the latest (spec v0.3.0) types.
When up-converting older result types to spec v0.3.0 and later, fields new in
spec v0.3.0 and later (like `interfaces`) may be empty. Conversely, when
down-converting v0.3.0 and later results to an older version, any data in
those fields will be lost.
| From | 0.1 | 0.2 | 0.3 | 0.4 | 1.0 |
|--------|-----|-----|-----|-----|-----|
| To 0.1 | ✔ | ✔ | x | x | x |
| To 0.2 | ✔ | ✔ | x | x | x |
| To 0.3 | ✴ | ✴ | ✔ | ✔ | ✔ |
| To 0.4 | ✴ | ✴ | ✔ | ✔ | ✔ |
| To 1.0 | ✴ | ✴ | ✔ | ✔ | ✔ |
Key:
> ✔ : lossless conversion <br>
> ✴ : higher-version output may have empty fields <br>
> x : lower-version output is missing some data <br>
A container runtime should use `current.NewResultFromResult()` to convert the
opaque `types.Result` to a concrete `current.Result` struct. It may then
work with the fields exposed by that struct:
```go
// runtime invokes the plugin to get the opaque types.Result
// this may conform to any CNI spec version
resultInterface, err := libcni.AddNetwork(ctx, netConf, runtimeConf)
// upconvert result to the current 0.3.0 spec
result, err := current.NewResultFromResult(resultInterface)
// use the result fields ....
for _, ip := range result.IPs { ... }
```

View File

@ -1,36 +0,0 @@
# tuning plugin
## Overview
This plugin can change some system controls (sysctls) in the network namespace.
It does not create any network interfaces and therefore does not bring connectivity by itself.
It is only useful when used in addition to other plugins.
## Operation
The following network configuration file
```
{
"name": "mytuning",
"type": "tuning",
"sysctl": {
"net.core.somaxconn": "500"
}
}
```
will set /proc/sys/net/core/somaxconn to 500.
Other sysctls can be modified as long as they belong to the network namespace (`/proc/sys/net/*`).
A successful result would simply be:
```
{
"cniVersion": "0.1.0"
}
```
## Network sysctls documentation
Some network sysctls are documented in the Linux sources:
- [Documentation/sysctl/net.txt](https://www.kernel.org/doc/Documentation/sysctl/net.txt)
- [Documentation/networking/ip-sysctl.txt](https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt)
- [Documentation/networking/](https://www.kernel.org/doc/Documentation/networking/)

44
GOVERNANCE.md Normal file
View File

@ -0,0 +1,44 @@
# CNI Governance
This document defines project governance for the project.
## Voting
The CNI project employs "organization voting" to ensure no single organization can dominate the project.
Individuals not associated with or employed by a company or organization are allowed one organization vote.
Each company or organization (regardless of the number of maintainers associated with or employed by that company/organization) receives one organization vote.
In other words, if two maintainers are employed by Company X, two by Company Y, two by Company Z, and one maintainer is an un-affiliated individual, a total of four "organization votes" are possible; one for X, one for Y, one for Z, and one for the un-affiliated individual.
Any maintainer from an organization may cast the vote for that organization.
For formal votes, a specific statement of what is being voted on should be added to the relevant github issue or PR, and a link to that issue or PR added to the maintainers meeting agenda document.
Maintainers should indicate their yes/no vote on that issue or PR, and after a suitable period of time, the votes will be tallied and the outcome noted.
## Changes in Maintainership
New maintainers are proposed by an existing maintainer and are elected by a 2/3 majority organization vote.
Maintainers can be removed by a 2/3 majority organization vote.
## Approving PRs
Non-specification-related PRs may be merged after receiving at least two organization votes.
Changes to the CNI Specification also follow the normal PR approval process (eg, 2 organization votes), but any maintainer can request that the approval require a 2/3 majority organization vote.
## Github Project Administration
Maintainers will be added to the containernetworking GitHub organization and added to the GitHub cni-maintainers team, and made a GitHub maintainer of that team.
After 6 months a maintainer will be made an "owner" of the GitHub organization.
## Changes in Governance
All changes in Governance require a 2/3 majority organization vote.
## Other Changes
Unless specified above, all other changes to the project require a 2/3 majority organization vote.
Additionally, any maintainer may request that any change require a 2/3 majority organization vote.

184
Godeps/Godeps.json generated
View File

@ -1,184 +0,0 @@
{
"ImportPath": "github.com/containernetworking/cni",
"GoVersion": "go1.6",
"Packages": [
"./..."
],
"Deps": [
{
"ImportPath": "github.com/coreos/go-iptables/iptables",
"Comment": "v0.1.0",
"Rev": "fbb73372b87f6e89951c2b6b31470c2c9d5cfae3"
},
{
"ImportPath": "github.com/coreos/go-systemd/activation",
"Comment": "v2-53-g2688e91",
"Rev": "2688e91251d9d8e404e86dd8f096e23b2f086958"
},
{
"ImportPath": "github.com/d2g/dhcp4",
"Rev": "f0e4d29ff0231dce36e250b2ed9ff08412584bca"
},
{
"ImportPath": "github.com/d2g/dhcp4client",
"Rev": "bed07e1bc5b85f69c6f0fd73393aa35ec68ed892"
},
{
"ImportPath": "github.com/onsi/ginkgo",
"Comment": "v1.2.0-29-g7f8ab55",
"Rev": "7f8ab55aaf3b86885aa55b762e803744d1674700"
},
{
"ImportPath": "github.com/onsi/ginkgo/config",
"Comment": "v1.2.0-29-g7f8ab55",
"Rev": "7f8ab55aaf3b86885aa55b762e803744d1674700"
},
{
"ImportPath": "github.com/onsi/ginkgo/internal/codelocation",
"Comment": "v1.2.0-29-g7f8ab55",
"Rev": "7f8ab55aaf3b86885aa55b762e803744d1674700"
},
{
"ImportPath": "github.com/onsi/ginkgo/internal/containernode",
"Comment": "v1.2.0-29-g7f8ab55",
"Rev": "7f8ab55aaf3b86885aa55b762e803744d1674700"
},
{
"ImportPath": "github.com/onsi/ginkgo/internal/failer",
"Comment": "v1.2.0-29-g7f8ab55",
"Rev": "7f8ab55aaf3b86885aa55b762e803744d1674700"
},
{
"ImportPath": "github.com/onsi/ginkgo/internal/leafnodes",
"Comment": "v1.2.0-29-g7f8ab55",
"Rev": "7f8ab55aaf3b86885aa55b762e803744d1674700"
},
{
"ImportPath": "github.com/onsi/ginkgo/internal/remote",
"Comment": "v1.2.0-29-g7f8ab55",
"Rev": "7f8ab55aaf3b86885aa55b762e803744d1674700"
},
{
"ImportPath": "github.com/onsi/ginkgo/internal/spec",
"Comment": "v1.2.0-29-g7f8ab55",
"Rev": "7f8ab55aaf3b86885aa55b762e803744d1674700"
},
{
"ImportPath": "github.com/onsi/ginkgo/internal/specrunner",
"Comment": "v1.2.0-29-g7f8ab55",
"Rev": "7f8ab55aaf3b86885aa55b762e803744d1674700"
},
{
"ImportPath": "github.com/onsi/ginkgo/internal/suite",
"Comment": "v1.2.0-29-g7f8ab55",
"Rev": "7f8ab55aaf3b86885aa55b762e803744d1674700"
},
{
"ImportPath": "github.com/onsi/ginkgo/internal/testingtproxy",
"Comment": "v1.2.0-29-g7f8ab55",
"Rev": "7f8ab55aaf3b86885aa55b762e803744d1674700"
},
{
"ImportPath": "github.com/onsi/ginkgo/internal/writer",
"Comment": "v1.2.0-29-g7f8ab55",
"Rev": "7f8ab55aaf3b86885aa55b762e803744d1674700"
},
{
"ImportPath": "github.com/onsi/ginkgo/reporters",
"Comment": "v1.2.0-29-g7f8ab55",
"Rev": "7f8ab55aaf3b86885aa55b762e803744d1674700"
},
{
"ImportPath": "github.com/onsi/ginkgo/reporters/stenographer",
"Comment": "v1.2.0-29-g7f8ab55",
"Rev": "7f8ab55aaf3b86885aa55b762e803744d1674700"
},
{
"ImportPath": "github.com/onsi/ginkgo/types",
"Comment": "v1.2.0-29-g7f8ab55",
"Rev": "7f8ab55aaf3b86885aa55b762e803744d1674700"
},
{
"ImportPath": "github.com/onsi/gomega",
"Comment": "v1.0-71-g2152b45",
"Rev": "2152b45fa28a361beba9aab0885972323a444e28"
},
{
"ImportPath": "github.com/onsi/gomega/format",
"Comment": "v1.0-71-g2152b45",
"Rev": "2152b45fa28a361beba9aab0885972323a444e28"
},
{
"ImportPath": "github.com/onsi/gomega/gbytes",
"Comment": "v1.0-71-g2152b45",
"Rev": "2152b45fa28a361beba9aab0885972323a444e28"
},
{
"ImportPath": "github.com/onsi/gomega/gexec",
"Comment": "v1.0-71-g2152b45",
"Rev": "2152b45fa28a361beba9aab0885972323a444e28"
},
{
"ImportPath": "github.com/onsi/gomega/internal/assertion",
"Comment": "v1.0-71-g2152b45",
"Rev": "2152b45fa28a361beba9aab0885972323a444e28"
},
{
"ImportPath": "github.com/onsi/gomega/internal/asyncassertion",
"Comment": "v1.0-71-g2152b45",
"Rev": "2152b45fa28a361beba9aab0885972323a444e28"
},
{
"ImportPath": "github.com/onsi/gomega/internal/oraclematcher",
"Comment": "v1.0-71-g2152b45",
"Rev": "2152b45fa28a361beba9aab0885972323a444e28"
},
{
"ImportPath": "github.com/onsi/gomega/internal/testingtsupport",
"Comment": "v1.0-71-g2152b45",
"Rev": "2152b45fa28a361beba9aab0885972323a444e28"
},
{
"ImportPath": "github.com/onsi/gomega/matchers",
"Comment": "v1.0-71-g2152b45",
"Rev": "2152b45fa28a361beba9aab0885972323a444e28"
},
{
"ImportPath": "github.com/onsi/gomega/matchers/support/goraph/bipartitegraph",
"Comment": "v1.0-71-g2152b45",
"Rev": "2152b45fa28a361beba9aab0885972323a444e28"
},
{
"ImportPath": "github.com/onsi/gomega/matchers/support/goraph/edge",
"Comment": "v1.0-71-g2152b45",
"Rev": "2152b45fa28a361beba9aab0885972323a444e28"
},
{
"ImportPath": "github.com/onsi/gomega/matchers/support/goraph/node",
"Comment": "v1.0-71-g2152b45",
"Rev": "2152b45fa28a361beba9aab0885972323a444e28"
},
{
"ImportPath": "github.com/onsi/gomega/matchers/support/goraph/util",
"Comment": "v1.0-71-g2152b45",
"Rev": "2152b45fa28a361beba9aab0885972323a444e28"
},
{
"ImportPath": "github.com/onsi/gomega/types",
"Comment": "v1.0-71-g2152b45",
"Rev": "2152b45fa28a361beba9aab0885972323a444e28"
},
{
"ImportPath": "github.com/vishvananda/netlink",
"Rev": "ecf47fd5739b3d2c3daf7c89c4b9715a2605c21b"
},
{
"ImportPath": "github.com/vishvananda/netlink/nl",
"Rev": "ecf47fd5739b3d2c3daf7c89c4b9715a2605c21b"
},
{
"ImportPath": "golang.org/x/sys/unix",
"Rev": "e11762ca30adc5b39fdbfd8c4250dabeb8e456d3"
}
]
}

5
Godeps/Readme generated
View File

@ -1,5 +0,0 @@
This directory tree is generated automatically by godep.
Please do not edit.
See https://github.com/tools/godep for more information.

View File

@ -1,5 +1,14 @@
Gabe Rosenhouse <grosenhouse@pivotal.io> (@rosenhouse)
Michael Bridgen <michael@weave.works> (@squaremo)
Stefan Junker <stefan.junker@coreos.com> (@steveeJ)
Tom Denham <tom.denham@metaswitch.com> (@tomdee)
Zach Gershman <zachgersh@gmail.com> (@zachgersh)
Bruce Ma <brucema19901024@gmail.com> (@mars1024)
Casey Callendrello <cdc@isovalent.com> (@squeed)
Michael Cambria <mcambria@redhat.com> (@mccv1r0)
Michael Zappa <Michael.Zappa@gmail.com> (@MikeZappa87)
Tomofumi Hayashi <s1061123@gmail.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)

1
Makefile Normal file
View File

@ -0,0 +1 @@
include mk/lint.mk

127
README.md
View File

@ -1,19 +1,36 @@
[![Build Status](https://travis-ci.org/containernetworking/cni.svg?branch=master)](https://travis-ci.org/containernetworking/cni)
[![Coverage Status](https://coveralls.io/repos/github/containernetworking/cni/badge.svg?branch=master)](https://coveralls.io/github/containernetworking/cni?branch=master)
![CNI Logo](logo.png)
---
# CNI - the Container Network Interface
[![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/2446/badge)](https://bestpractices.coreinfrastructure.org/projects/2446)
[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/containernetworking/cni/badge)](https://securityscorecards.dev/viewer/?uri=github.com/containernetworking/cni)
## What is CNI?
The CNI (_Container Network Interface_) 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.
CNI concerns itself only with network connectivity of containers and removing allocated resources when the container is deleted.
Because of this focus CNI has a wide range of support and the specification is simple to implement.
Because of this focus, CNI has a wide range of support and the specification is simple to implement.
As well as the [specification](SPEC.md), this repository contains the Go source code of a library for integrating CNI into applications, an example command-line tool, a template for making new plugins, and the supported plugins.
As well as the [specification](SPEC.md), this repository contains the Go source code of a [library for integrating CNI into applications](libcni) and an [example command-line tool](cnitool) for executing CNI plugins. A [separate repository contains reference plugins](https://github.com/containernetworking/plugins) and a template for making new plugins.
The template code makes it straight-forward to create a CNI plugin for an existing container networking project.
CNI also makes a good framework for creating a new container networking project from scratch.
Here are the recordings of two sessions that the CNI maintainers hosted at KubeCon/CloudNativeCon 2019:
- [Introduction to CNI](https://youtu.be/YjjrQiJOyME)
- [CNI deep dive](https://youtu.be/zChkx-AB5Xc)
## Contributing to CNI
We welcome contributions, including [bug reports](https://github.com/containernetworking/cni/issues), and code and documentation improvements.
If you intend to contribute to code or documentation, please read [CONTRIBUTING.md](CONTRIBUTING.md). Also see the [contact section](#contact) in this README.
The CNI project has a [weekly meeting](https://meet.jit.si/CNIMaintainersMeeting). It takes place Mondays at 11:00 US/Eastern. All are welcome to join.
## Why develop CNI?
Application containers on Linux are a rapidly evolving area, and within this area networking is not well addressed as it is highly environment-specific.
@ -22,37 +39,59 @@ We believe that many container runtimes and orchestrators will seek to solve the
To avoid duplication, we think it is prudent to define a common interface between the network plugins and container execution: hence we put forward this specification, along with libraries for Go and a set of plugins.
## Who is using CNI?
### Container runtimes
- [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)
- [Cloud Foundry - a platform for cloud applications](https://github.com/cloudfoundry-incubator/cf-networking-release)
- [Apache Mesos - a distributed systems kernel](https://github.com/apache/mesos/blob/master/docs/cni.md)
- [Amazon ECS - a highly scalable, high performance container management service](https://aws.amazon.com/ecs/)
- [Singularity - container platform optimized for HPC, EPC, and AI](https://github.com/sylabs/singularity)
- [OpenSVC - orchestrator for legacy and containerized application stacks](https://docs.opensvc.com/latest/fr/agent.configure.cni.html)
- [rkt - container engine](https://coreos.com/blog/rkt-cni-networking.html)
- [Kurma - container runtime](http://kurma.io/)
- [Kubernetes - a system to simplify container operations](http://kubernetes.io/docs/admin/network-plugins/)
- [Cloud Foundry - a platform for cloud applications](https://github.com/cloudfoundry-incubator/guardian-cni-adapter)
- [Weave - a multi-host Docker network](https://github.com/weaveworks/weave)
- [Project Calico - a layer 3 virtual network](https://github.com/projectcalico/calico-cni)
### 3rd party plugins
- [Project Calico - a layer 3 virtual network](https://github.com/projectcalico/calico)
- [Contiv Networking - policy networking for various use cases](https://github.com/contiv/netplugin)
- [SR-IOV](https://github.com/hustcat/sriov-cni)
- [Cilium - eBPF & XDP for containers](https://github.com/cilium/cilium)
- [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)
- [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)
- [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)
- [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)
- [Bonding CNI - a Link aggregating plugin to address failover and high availability network](https://github.com/Intel-Corp/bond-cni)
- [ovn-kubernetes - an container network plugin built on Open vSwitch (OVS) and Open Virtual Networking (OVN) with support for both Linux and Windows](https://github.com/openvswitch/ovn-kubernetes)
- [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)
- [DANM - a CNI-compliant networking solution for TelCo workloads running on Kubernetes](https://github.com/nokia/danm)
- [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)
- [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)
- [Project Antrea - an Open vSwitch k8s CNI](https://github.com/vmware-tanzu/antrea)
- [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)
- [Spiderpool - An IP Address Management (IPAM) CNI plugin of Kubernetes for managing static ip for underlay network](https://github.com/spidernet-io/spiderpool)
- [AWS VPC CNI - Networking plugin for pod networking in Kubernetes using Elastic Network Interfaces on AWS](https://github.com/aws/amazon-vpc-cni-k8s)
## Contributing to CNI
The CNI team also maintains some [core plugins in a separate repository](https://github.com/containernetworking/plugins).
We welcome contributions, including [bug reports](https://github.com/containernetworking/cni/issues), and code and documentation improvements.
If you intend to contribute to code or documentation, please read [CONTRIBUTING.md](CONTRIBUTING.md). Also see the [contact section](#contact) in this README.
## How do I use CNI?
### Requirements
CNI requires Go 1.5+ to build.
The CNI spec is language agnostic. To use the Go language libraries in this repository, you'll need a recent version of Go. You can find the Go versions covered by our [automated tests](https://travis-ci.org/containernetworking/cni/builds) in [.travis.yaml](.travis.yml).
Go 1.5 users will need to set GO15VENDOREXPERIMENT=1 to get vendored
dependencies. This flag is set by default in 1.6.
### Reference Plugins
### Included Plugins
This repository includes a number of common plugins in the `plugins/` directory.
Please see the [Documentation/](Documentation/) directory for documentation about particular plugins.
The CNI project maintains a set of [reference plugins](https://github.com/containernetworking/plugins) that implement the CNI specification.
NOTE: the reference plugins used to live in this repository but have been split out into a [separate repository](https://github.com/containernetworking/plugins) as of May 2017.
### Running the plugins
The scripts/ directory contains two scripts, `priv-net-run.sh` and `docker-run.sh`, that can be used to exercise the plugins.
After building and installing the [reference plugins](https://github.com/containernetworking/plugins), you can use the `priv-net-run.sh` and `docker-run.sh` scripts in the `scripts/` directory to exercise the plugins.
**note - priv-net-run.sh depends on `jq`**
@ -62,6 +101,7 @@ Start out by creating a netconf file to describe a network:
$ mkdir -p /etc/cni/net.d
$ cat >/etc/cni/net.d/10-mynet.conf <<EOF
{
"cniVersion": "0.2.0",
"name": "mynet",
"type": "bridge",
"bridge": "cni0",
@ -78,6 +118,8 @@ $ cat >/etc/cni/net.d/10-mynet.conf <<EOF
EOF
$ cat >/etc/cni/net.d/99-loopback.conf <<EOF
{
"cniVersion": "0.2.0",
"name": "lo",
"type": "loopback"
}
EOF
@ -88,14 +130,15 @@ The directory `/etc/cni/net.d` is the default location in which the scripts will
Next, build the plugins:
```bash
$ ./build
$ cd $GOPATH/src/github.com/containernetworking/plugins
$ ./build_linux.sh # or build_windows.sh
```
Finally, execute a command (`ifconfig` in this example) in a private network namespace that has joined the `mynet` network:
```bash
$ CNI_PATH=`pwd`/bin
$ cd scripts
$ CNI_PATH=$GOPATH/src/github.com/containernetworking/plugins/bin
$ cd $GOPATH/src/github.com/containernetworking/cni/scripts
$ sudo CNI_PATH=$CNI_PATH ./priv-net-run.sh ifconfig
eth0 Link encap:Ethernet HWaddr f2:c2:6f:54:b8:2b
inet addr:10.22.0.2 Bcast:0.0.0.0 Mask:255.255.0.0
@ -103,7 +146,7 @@ eth0 Link encap:Ethernet HWaddr f2:c2:6f:54:b8:2b
UP BROADCAST MULTICAST MTU:1500 Metric:1
RX packets:1 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:1 overruns:0 carrier:0
collisions:0 txqueuelen:0
collisions:0 txqueuelen:0
RX bytes:90 (90.0 B) TX bytes:0 (0.0 B)
lo Link encap:Local Loopback
@ -112,7 +155,7 @@ lo Link encap:Local Loopback
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
collisions:0 txqueuelen:0
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
```
@ -124,8 +167,8 @@ Use the instructions in the previous section to define a netconf and build the p
Next, docker-run.sh script wraps `docker run`, to execute the plugins prior to entering the container:
```bash
$ CNI_PATH=`pwd`/bin
$ cd scripts
$ CNI_PATH=$GOPATH/src/github.com/containernetworking/plugins/bin
$ cd $GOPATH/src/github.com/containernetworking/cni/scripts
$ sudo CNI_PATH=$CNI_PATH ./docker-run.sh --rm busybox:latest ifconfig
eth0 Link encap:Ethernet HWaddr fa:60:70:aa:07:d1
inet addr:10.22.0.2 Bcast:0.0.0.0 Mask:255.255.0.0
@ -133,7 +176,7 @@ eth0 Link encap:Ethernet HWaddr fa:60:70:aa:07:d1
UP BROADCAST MULTICAST MTU:1500 Metric:1
RX packets:1 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:1 overruns:0 carrier:0
collisions:0 txqueuelen:0
collisions:0 txqueuelen:0
RX bytes:90 (90.0 B) TX bytes:0 (0.0 B)
lo Link encap:Local Loopback
@ -142,22 +185,36 @@ lo Link encap:Local Loopback
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
collisions:0 txqueuelen:0
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
```
## What might CNI do in the future?
CNI currently covers a wide range of needs for network configuration due to it simple model and API.
CNI currently covers a wide range of needs for network configuration due to its simple model and API.
However, in the future CNI might want to branch out into other directions:
- Dynamic updates to existing network configuration
- Dynamic policies for network bandwidth and firewall rules
If these topics of are interest please contact the team via the mailing list or IRC and find some like minded people in the community to put a proposal together.
If these topics are of interest, please contact the team via the mailing list or IRC and find some like-minded people in the community to put a proposal together.
## Where are the binaries?
The plugins moved to a separate repo:
https://github.com/containernetworking/plugins, and the releases there
include binaries and checksums.
Prior to release 0.7.0 the `cni` release also included a `cnitool`
binary; as this is a developer tool we suggest you build it yourself.
## Contact
For any questions about CNI, please reach out on the mailing list:
For any questions about CNI, please reach out via:
- Email: [cni-dev](https://groups.google.com/forum/#!forum/cni-dev)
- IRC: #[containernetworking](irc://irc.freenode.org:6667/#containernetworking) channel on freenode.org
- IRC: #[containernetworking](irc://irc.freenode.net:6667/#containernetworking) channel on [freenode.net](https://freenode.net/)
- Slack: #cni on the [CNCF slack](https://slack.cncf.io/). NOTE: the previous CNI Slack (containernetworking.slack.com) has been sunsetted.
## Security
If you have a _security_ issue to report, please do so privately to the email addresses listed in the [MAINTAINERS](MAINTAINERS) file.

19
RELEASING.md Normal file
View File

@ -0,0 +1,19 @@
# Release process
## Preparing for a release
Releases are performed by maintainers and should usually be discussed and planned at a maintainer meeting.
- Choose the version number. It should be prefixed with `v`, e.g. `v1.2.3`
- Take a quick scan through the PRs and issues to make sure there isn't anything crucial that _must_ be in the next release.
- Create a draft of the release note
- Discuss the level of testing that's needed and create a test plan if sensible
- Check what version of `go` is used in the build container, updating it if there's a new stable release.
## Publishing the release
1. Make sure you are on the master branch and don't have any local uncommitted changes.
1. Create a signed tag for the release `git tag -s $VERSION` (Ensure that GPG keys are created and added to GitHub)
1. Push the tag to git `git push origin <TAG>`
1. Create a release on Github, using the tag which was just pushed.
1. Add the release note to the release.
1. Announce the release on at least the CNI mailing, IRC and Slack.

18
ROADMAP.md Normal file
View File

@ -0,0 +1,18 @@
# CNI Roadmap
This document defines a high level roadmap for CNI development.
The list below is not complete, and we advise to get the current project state from the [milestones defined in GitHub](https://github.com/containernetworking/cni/milestones).
## CNI Milestones
### [v1.0.0](https://github.com/containernetworking/cni/milestones/v1.0.0)
- Targeted for April 2020
- More precise specification language
- Stable SPEC
- Complete test coverage
### Beyond v1.0.0
- Conformance test suite for CNI plugins (both reference and 3rd party)
- Signed release binaries

1303
SPEC.md

File diff suppressed because it is too large Load Diff

30
build
View File

@ -1,30 +0,0 @@
#!/usr/bin/env bash
set -e
ORG_PATH="github.com/containernetworking"
REPO_PATH="${ORG_PATH}/cni"
if [ ! -h gopath/src/${REPO_PATH} ]; then
mkdir -p gopath/src/${ORG_PATH}
ln -s ../../../.. gopath/src/${REPO_PATH} || exit 255
fi
export GO15VENDOREXPERIMENT=1
export GOBIN=${PWD}/bin
export GOPATH=${PWD}/gopath
echo "Building API"
go build "$@" ${REPO_PATH}/libcni
echo "Building reference CLI"
go install "$@" ${REPO_PATH}/cnitool
echo "Building plugins"
PLUGINS="plugins/meta/* plugins/main/* plugins/ipam/*"
for d in $PLUGINS; do
if [ -d $d ]; then
plugin=$(basename $d)
echo " " $plugin
go install "$@" ${REPO_PATH}/$d
fi
done

77
cnitool/README.md Normal file
View File

@ -0,0 +1,77 @@
# cnitool
`cnitool` is a simple program that executes a CNI configuration. It will
add or remove an interface in an already-created network namespace.
## Environment Variables
* `NETCONFPATH`: This environment variable needs to be set to a
directory. It defaults to `/etc/cni/net.d`. The `cnitool` searches
for CNI configuration files in this directory according to the following priorities:
1. Search files with the extension `*.conflist`, representing a list of plugin configurations.
2. If there are no `*.conflist` files in the directory, search files with the extension `*.conf` or `*.json`,
representing a single plugin configuration.
It loads all the CNI configuration files in
this directory and if it finds a CNI configuration with the `network
name` given to the cnitool it returns the corresponding CNI
configuration, else it returns `nil`.
* `CNI_PATH`: For a given CNI configuration `cnitool` will search for
the corresponding CNI plugin in this path.
## Example invocation
First, install cnitool:
```bash
go get github.com/containernetworking/cni
go install github.com/containernetworking/cni/cnitool
```
Then, check out and build the plugins. All commands should be run from this directory.
```bash
git clone https://github.com/containernetworking/plugins.git
cd plugins
./build_linux.sh
# or
./build_windows.sh
```
Create a network configuration
```bash
echo '{"cniVersion":"0.4.0","name":"myptp","type":"ptp","ipMasq":true,"ipam":{"type":"host-local","subnet":"172.16.29.0/24","routes":[{"dst":"0.0.0.0/0"}]}}' | sudo tee /etc/cni/net.d/10-myptp.conf
```
Create a network namespace. This will be called `testing`:
```bash
sudo ip netns add testing
```
Add the container to the network:
```bash
sudo CNI_PATH=./bin cnitool add myptp /var/run/netns/testing
```
Check whether the container's networking is as expected (ONLY for spec v0.4.0+):
```bash
sudo CNI_PATH=./bin cnitool check myptp /var/run/netns/testing
```
Test that it works:
```bash
sudo ip -n testing addr
sudo ip netns exec testing ping -c 1 4.2.2.2
```
And clean up:
```bash
sudo CNI_PATH=./bin cnitool del myptp /var/run/netns/testing
sudo ip netns del testing
```

View File

@ -1,87 +0,0 @@
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import (
"fmt"
"os"
"path/filepath"
"strings"
"github.com/containernetworking/cni/libcni"
)
const (
EnvCNIPath = "CNI_PATH"
EnvNetDir = "NETCONFPATH"
DefaultNetDir = "/etc/cni/net.d"
CmdAdd = "add"
CmdDel = "del"
)
func main() {
if len(os.Args) < 3 {
usage()
return
}
netdir := os.Getenv(EnvNetDir)
if netdir == "" {
netdir = DefaultNetDir
}
netconf, err := libcni.LoadConf(netdir, os.Args[2])
if err != nil {
exit(err)
}
netns := os.Args[3]
cninet := &libcni.CNIConfig{
Path: strings.Split(os.Getenv(EnvCNIPath), ":"),
}
rt := &libcni.RuntimeConf{
ContainerID: "cni",
NetNS: netns,
IfName: "eth0",
}
switch os.Args[1] {
case CmdAdd:
_, err := cninet.AddNetwork(netconf, rt)
exit(err)
case CmdDel:
exit(cninet.DelNetwork(netconf, rt))
}
}
func usage() {
exe := filepath.Base(os.Args[0])
fmt.Fprintf(os.Stderr, "%s: Add or remove network interfaces from a network namespace\n", exe)
fmt.Fprintf(os.Stderr, " %s %s <net> <netns>\n", exe, CmdAdd)
fmt.Fprintf(os.Stderr, " %s %s <net> <netns>\n", exe, CmdDel)
os.Exit(1)
}
func exit(err error) {
if err != nil {
fmt.Fprintf(os.Stderr, "%s\n", err)
os.Exit(1)
}
os.Exit(0)
}

156
cnitool/cnitool.go Normal file
View File

@ -0,0 +1,156 @@
// Copyright 2015 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import (
"context"
"crypto/sha512"
"encoding/json"
"fmt"
"os"
"path/filepath"
"strings"
"github.com/containernetworking/cni/libcni"
)
// Protocol parameters are passed to the plugins via OS environment variables.
const (
EnvCNIPath = "CNI_PATH"
EnvNetDir = "NETCONFPATH"
EnvCapabilityArgs = "CAP_ARGS"
EnvCNIArgs = "CNI_ARGS"
EnvCNIIfname = "CNI_IFNAME"
DefaultNetDir = "/etc/cni/net.d"
CmdAdd = "add"
CmdCheck = "check"
CmdDel = "del"
CmdGC = "gc"
CmdStatus = "status"
)
func parseArgs(args string) ([][2]string, error) {
var result [][2]string
pairs := strings.Split(args, ";")
for _, pair := range pairs {
kv := strings.Split(pair, "=")
if len(kv) != 2 || kv[0] == "" || kv[1] == "" {
return nil, fmt.Errorf("invalid CNI_ARGS pair %q", pair)
}
result = append(result, [2]string{kv[0], kv[1]})
}
return result, nil
}
func main() {
if len(os.Args) < 4 {
usage()
}
netdir := os.Getenv(EnvNetDir)
if netdir == "" {
netdir = DefaultNetDir
}
netconf, err := libcni.LoadNetworkConf(netdir, os.Args[2])
if err != nil {
exit(err)
}
var capabilityArgs map[string]interface{}
capabilityArgsValue := os.Getenv(EnvCapabilityArgs)
if len(capabilityArgsValue) > 0 {
if err = json.Unmarshal([]byte(capabilityArgsValue), &capabilityArgs); err != nil {
exit(err)
}
}
var cniArgs [][2]string
args := os.Getenv(EnvCNIArgs)
if len(args) > 0 {
cniArgs, err = parseArgs(args)
if err != nil {
exit(err)
}
}
ifName, ok := os.LookupEnv(EnvCNIIfname)
if !ok {
ifName = "eth0"
}
netns := os.Args[3]
netns, err = filepath.Abs(netns)
if err != nil {
exit(err)
}
// Generate the containerid by hashing the netns path
s := sha512.Sum512([]byte(netns))
containerID := fmt.Sprintf("cnitool-%x", s[:10])
cninet := libcni.NewCNIConfig(filepath.SplitList(os.Getenv(EnvCNIPath)), nil)
rt := &libcni.RuntimeConf{
ContainerID: containerID,
NetNS: netns,
IfName: ifName,
Args: cniArgs,
CapabilityArgs: capabilityArgs,
}
switch os.Args[1] {
case CmdAdd:
result, err := cninet.AddNetworkList(context.TODO(), netconf, rt)
if result != nil {
_ = result.Print()
}
exit(err)
case CmdCheck:
err := cninet.CheckNetworkList(context.TODO(), netconf, rt)
exit(err)
case CmdDel:
exit(cninet.DelNetworkList(context.TODO(), netconf, rt))
case CmdGC:
// Currently just invoke GC without args, hence all network interface should be GC'ed!
exit(cninet.GCNetworkList(context.TODO(), netconf, nil))
case CmdStatus:
exit(cninet.GetStatusNetworkList(context.TODO(), netconf))
}
}
func usage() {
exe := filepath.Base(os.Args[0])
fmt.Fprintf(os.Stderr, "%s: Add, check, remove, gc or status network interfaces from a network namespace\n", exe)
fmt.Fprintf(os.Stderr, " %s add <net> <netns>\n", exe)
fmt.Fprintf(os.Stderr, " %s check <net> <netns>\n", exe)
fmt.Fprintf(os.Stderr, " %s del <net> <netns>\n", exe)
fmt.Fprintf(os.Stderr, " %s gc <net> <netns>\n", exe)
fmt.Fprintf(os.Stderr, " %s status <net> <netns>\n", exe)
os.Exit(1)
}
func exit(err error) {
if err != nil {
fmt.Fprintf(os.Stderr, "%s\n", err)
os.Exit(1)
}
os.Exit(0)
}

22
go.mod Normal file
View File

@ -0,0 +1,22 @@
module github.com/containernetworking/cni
go 1.21
require (
github.com/onsi/ginkgo/v2 v2.20.1
github.com/onsi/gomega v1.34.1
github.com/vishvananda/netns v0.0.4
)
require (
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 // indirect
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
golang.org/x/net v0.28.0 // indirect
golang.org/x/sys v0.23.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
)

36
go.sum Normal file
View File

@ -0,0 +1,36 @@
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/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
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/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
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/pprof v0.0.0-20240727154555-813a5fbdbec8 h1:FKHo8hFI3A+7w0aUQuYXQ+6EN5stWmeY/AZqtM8xk9k=
github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo=
github.com/onsi/ginkgo/v2 v2.20.1 h1:YlVIbqct+ZmnEph770q9Q7NVAz4wwIiVNahee6JyUzo=
github.com/onsi/ginkgo/v2 v2.20.1/go.mod h1:lG9ey2Z29hR41WMVthyJBGUBcBhGOtoPF2VFMvBXFCI=
github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
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/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
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/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8=
github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM=
golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
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/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -14,50 +14,877 @@
package libcni
// Note this is the actual implementation of the CNI specification, which
// is reflected in the SPEC.md file.
// it is typically bundled into runtime providers (i.e. containerd or cri-o would use this
// before calling runc or hcsshim). It is also bundled into CNI providers as well, for example,
// to add an IP to a container, to parse the configuration of the CNI and so on.
import (
"context"
"encoding/json"
"errors"
"fmt"
"os"
"path/filepath"
"sort"
"strings"
"github.com/containernetworking/cni/pkg/invoke"
"github.com/containernetworking/cni/pkg/types"
"github.com/containernetworking/cni/pkg/types/create"
"github.com/containernetworking/cni/pkg/utils"
"github.com/containernetworking/cni/pkg/version"
)
var (
CacheDir = "/var/lib/cni"
// slightly awkward wording to preserve anyone matching on error strings
ErrorCheckNotSupp = fmt.Errorf("does not support the CHECK command")
)
const (
CNICacheV1 = "cniCacheV1"
)
// A RuntimeConf holds the arguments to one invocation of a CNI plugin
// excepting the network configuration, with the nested exception that
// the `runtimeConfig` from the network configuration is included
// here.
type RuntimeConf struct {
ContainerID string
NetNS string
IfName string
Args [][2]string
// A dictionary of capability-specific data passed by the runtime
// to plugins as top-level keys in the 'runtimeConfig' dictionary
// of the plugin's stdin data. libcni will ensure that only keys
// in this map which match the capabilities of the plugin are passed
// to the plugin
CapabilityArgs map[string]interface{}
// DEPRECATED. Will be removed in a future release.
CacheDir string
}
type NetworkConfig struct {
Network *types.NetConf
// Use PluginConfig instead of NetworkConfig, the NetworkConfig
// backwards-compat alias will be removed in a future release.
type NetworkConfig = PluginConfig
type PluginConfig struct {
Network *types.PluginConf
Bytes []byte
}
type NetworkConfigList struct {
Name string
CNIVersion string
DisableCheck bool
DisableGC bool
LoadOnlyInlinedPlugins bool
Plugins []*PluginConfig
Bytes []byte
}
type NetworkAttachment struct {
ContainerID string
Network string
IfName string
Config []byte
NetNS string
CniArgs [][2]string
CapabilityArgs map[string]interface{}
}
type GCArgs struct {
ValidAttachments []types.GCAttachment
}
type CNI interface {
AddNetwork(net *NetworkConfig, rt *RuntimeConf) (*types.Result, error)
DelNetwork(net *NetworkConfig, rt *RuntimeConf) error
AddNetworkList(ctx context.Context, net *NetworkConfigList, rt *RuntimeConf) (types.Result, error)
CheckNetworkList(ctx context.Context, net *NetworkConfigList, rt *RuntimeConf) error
DelNetworkList(ctx context.Context, net *NetworkConfigList, rt *RuntimeConf) error
GetNetworkListCachedResult(net *NetworkConfigList, rt *RuntimeConf) (types.Result, error)
GetNetworkListCachedConfig(net *NetworkConfigList, rt *RuntimeConf) ([]byte, *RuntimeConf, error)
AddNetwork(ctx context.Context, net *PluginConfig, rt *RuntimeConf) (types.Result, error)
CheckNetwork(ctx context.Context, net *PluginConfig, rt *RuntimeConf) error
DelNetwork(ctx context.Context, net *PluginConfig, rt *RuntimeConf) error
GetNetworkCachedResult(net *PluginConfig, rt *RuntimeConf) (types.Result, error)
GetNetworkCachedConfig(net *PluginConfig, rt *RuntimeConf) ([]byte, *RuntimeConf, error)
ValidateNetworkList(ctx context.Context, net *NetworkConfigList) ([]string, error)
ValidateNetwork(ctx context.Context, net *PluginConfig) ([]string, error)
GCNetworkList(ctx context.Context, net *NetworkConfigList, args *GCArgs) error
GetStatusNetworkList(ctx context.Context, net *NetworkConfigList) error
GetCachedAttachments(containerID string) ([]*NetworkAttachment, error)
GetVersionInfo(ctx context.Context, pluginType string) (version.PluginInfo, error)
}
type CNIConfig struct {
Path []string
Path []string
exec invoke.Exec
cacheDir string
}
func (c *CNIConfig) AddNetwork(net *NetworkConfig, rt *RuntimeConf) (*types.Result, error) {
pluginPath, err := invoke.FindInPath(net.Network.Type, c.Path)
// CNIConfig implements the CNI interface
var _ CNI = &CNIConfig{}
// NewCNIConfig returns a new CNIConfig object that will search for plugins
// in the given paths and use the given exec interface to run those plugins,
// or if the exec interface is not given, will use a default exec handler.
func NewCNIConfig(path []string, exec invoke.Exec) *CNIConfig {
return NewCNIConfigWithCacheDir(path, "", exec)
}
// NewCNIConfigWithCacheDir returns a new CNIConfig object that will search for plugins
// in the given paths use the given exec interface to run those plugins,
// or if the exec interface is not given, will use a default exec handler.
// The given cache directory will be used for temporary data storage when needed.
func NewCNIConfigWithCacheDir(path []string, cacheDir string, exec invoke.Exec) *CNIConfig {
return &CNIConfig{
Path: path,
cacheDir: cacheDir,
exec: exec,
}
}
func buildOneConfig(name, cniVersion string, orig *PluginConfig, prevResult types.Result, rt *RuntimeConf) (*PluginConfig, error) {
var err error
inject := map[string]interface{}{
"name": name,
"cniVersion": cniVersion,
}
// Add previous plugin result
if prevResult != nil {
inject["prevResult"] = prevResult
}
// Ensure every config uses the same name and version
orig, err = InjectConf(orig, inject)
if err != nil {
return nil, err
}
if rt != nil {
return injectRuntimeConfig(orig, rt)
}
return invoke.ExecPluginWithResult(pluginPath, net.Bytes, c.args("ADD", rt))
return orig, nil
}
func (c *CNIConfig) DelNetwork(net *NetworkConfig, rt *RuntimeConf) error {
pluginPath, err := invoke.FindInPath(net.Network.Type, c.Path)
// This function takes a libcni RuntimeConf structure and injects values into
// a "runtimeConfig" dictionary in the CNI network configuration JSON that
// will be passed to the plugin on stdin.
//
// Only "capabilities arguments" passed by the runtime are currently injected.
// These capabilities arguments are filtered through the plugin's advertised
// capabilities from its config JSON, and any keys in the CapabilityArgs
// matching plugin capabilities are added to the "runtimeConfig" dictionary
// sent to the plugin via JSON on stdin. For example, if the plugin's
// capabilities include "portMappings", and the CapabilityArgs map includes a
// "portMappings" key, that key and its value are added to the "runtimeConfig"
// dictionary to be passed to the plugin's stdin.
func injectRuntimeConfig(orig *PluginConfig, rt *RuntimeConf) (*PluginConfig, error) {
var err error
rc := make(map[string]interface{})
for capability, supported := range orig.Network.Capabilities {
if !supported {
continue
}
if data, ok := rt.CapabilityArgs[capability]; ok {
rc[capability] = data
}
}
if len(rc) > 0 {
orig, err = InjectConf(orig, map[string]interface{}{"runtimeConfig": rc})
if err != nil {
return nil, err
}
}
return orig, nil
}
// ensure we have a usable exec if the CNIConfig was not given one
func (c *CNIConfig) ensureExec() invoke.Exec {
if c.exec == nil {
c.exec = &invoke.DefaultExec{
RawExec: &invoke.RawExec{Stderr: os.Stderr},
PluginDecoder: version.PluginDecoder{},
}
}
return c.exec
}
type cachedInfo struct {
Kind string `json:"kind"`
ContainerID string `json:"containerId"`
Config []byte `json:"config"`
IfName string `json:"ifName"`
NetworkName string `json:"networkName"`
NetNS string `json:"netns,omitempty"`
CniArgs [][2]string `json:"cniArgs,omitempty"`
CapabilityArgs map[string]interface{} `json:"capabilityArgs,omitempty"`
RawResult map[string]interface{} `json:"result,omitempty"`
Result types.Result `json:"-"`
}
// getCacheDir returns the cache directory in this order:
// 1) global cacheDir from CNIConfig object
// 2) deprecated cacheDir from RuntimeConf object
// 3) fall back to default cache directory
func (c *CNIConfig) getCacheDir(rt *RuntimeConf) string {
if c.cacheDir != "" {
return c.cacheDir
}
if rt.CacheDir != "" {
return rt.CacheDir
}
return CacheDir
}
func (c *CNIConfig) getCacheFilePath(netName string, rt *RuntimeConf) (string, error) {
if netName == "" || rt.ContainerID == "" || rt.IfName == "" {
return "", fmt.Errorf("cache file path requires network name (%q), container ID (%q), and interface name (%q)", netName, rt.ContainerID, rt.IfName)
}
return filepath.Join(c.getCacheDir(rt), "results", fmt.Sprintf("%s-%s-%s", netName, rt.ContainerID, rt.IfName)), nil
}
func (c *CNIConfig) cacheAdd(result types.Result, config []byte, netName string, rt *RuntimeConf) error {
cached := cachedInfo{
Kind: CNICacheV1,
ContainerID: rt.ContainerID,
Config: config,
IfName: rt.IfName,
NetworkName: netName,
NetNS: rt.NetNS,
CniArgs: rt.Args,
CapabilityArgs: rt.CapabilityArgs,
}
// We need to get type.Result into cachedInfo as JSON map
// Marshal to []byte, then Unmarshal into cached.RawResult
data, err := json.Marshal(result)
if err != nil {
return err
}
return invoke.ExecPluginWithoutResult(pluginPath, net.Bytes, c.args("DEL", rt))
err = json.Unmarshal(data, &cached.RawResult)
if err != nil {
return err
}
newBytes, err := json.Marshal(&cached)
if err != nil {
return err
}
fname, err := c.getCacheFilePath(netName, rt)
if err != nil {
return err
}
if err := os.MkdirAll(filepath.Dir(fname), 0o700); err != nil {
return err
}
return os.WriteFile(fname, newBytes, 0o600)
}
func (c *CNIConfig) cacheDel(netName string, rt *RuntimeConf) error {
fname, err := c.getCacheFilePath(netName, rt)
if err != nil {
// Ignore error
return nil
}
return os.Remove(fname)
}
func (c *CNIConfig) getCachedConfig(netName string, rt *RuntimeConf) ([]byte, *RuntimeConf, error) {
var bytes []byte
fname, err := c.getCacheFilePath(netName, rt)
if err != nil {
return nil, nil, err
}
bytes, err = os.ReadFile(fname)
if err != nil {
// Ignore read errors; the cached result may not exist on-disk
return nil, nil, nil
}
unmarshaled := cachedInfo{}
if err := json.Unmarshal(bytes, &unmarshaled); err != nil {
return nil, nil, fmt.Errorf("failed to unmarshal cached network %q config: %w", netName, err)
}
if unmarshaled.Kind != CNICacheV1 {
return nil, nil, fmt.Errorf("read cached network %q config has wrong kind: %v", netName, unmarshaled.Kind)
}
newRt := *rt
if unmarshaled.CniArgs != nil {
newRt.Args = unmarshaled.CniArgs
}
newRt.CapabilityArgs = unmarshaled.CapabilityArgs
return unmarshaled.Config, &newRt, nil
}
func (c *CNIConfig) getLegacyCachedResult(netName, cniVersion string, rt *RuntimeConf) (types.Result, error) {
fname, err := c.getCacheFilePath(netName, rt)
if err != nil {
return nil, err
}
data, err := os.ReadFile(fname)
if err != nil {
// Ignore read errors; the cached result may not exist on-disk
return nil, nil
}
// Load the cached result
result, err := create.CreateFromBytes(data)
if err != nil {
return nil, err
}
// Convert to the config version to ensure plugins get prevResult
// in the same version as the config. The cached result version
// should match the config version unless the config was changed
// while the container was running.
result, err = result.GetAsVersion(cniVersion)
if err != nil {
return nil, fmt.Errorf("failed to convert cached result to config version %q: %w", cniVersion, err)
}
return result, nil
}
func (c *CNIConfig) getCachedResult(netName, cniVersion string, rt *RuntimeConf) (types.Result, error) {
fname, err := c.getCacheFilePath(netName, rt)
if err != nil {
return nil, err
}
fdata, err := os.ReadFile(fname)
if err != nil {
// Ignore read errors; the cached result may not exist on-disk
return nil, nil
}
cachedInfo := cachedInfo{}
if err := json.Unmarshal(fdata, &cachedInfo); err != nil || cachedInfo.Kind != CNICacheV1 {
return c.getLegacyCachedResult(netName, cniVersion, rt)
}
newBytes, err := json.Marshal(&cachedInfo.RawResult)
if err != nil {
return nil, fmt.Errorf("failed to marshal cached network %q config: %w", netName, err)
}
// Load the cached result
result, err := create.CreateFromBytes(newBytes)
if err != nil {
return nil, err
}
// Convert to the config version to ensure plugins get prevResult
// in the same version as the config. The cached result version
// should match the config version unless the config was changed
// while the container was running.
result, err = result.GetAsVersion(cniVersion)
if err != nil {
return nil, fmt.Errorf("failed to convert cached result to config version %q: %w", cniVersion, err)
}
return result, nil
}
// GetNetworkListCachedResult returns the cached Result of the previous
// AddNetworkList() operation for a network list, or an error.
func (c *CNIConfig) GetNetworkListCachedResult(list *NetworkConfigList, rt *RuntimeConf) (types.Result, error) {
return c.getCachedResult(list.Name, list.CNIVersion, rt)
}
// GetNetworkCachedResult returns the cached Result of the previous
// AddNetwork() operation for a network, or an error.
func (c *CNIConfig) GetNetworkCachedResult(net *PluginConfig, rt *RuntimeConf) (types.Result, error) {
return c.getCachedResult(net.Network.Name, net.Network.CNIVersion, rt)
}
// GetNetworkListCachedConfig copies the input RuntimeConf to output
// RuntimeConf with fields updated with info from the cached Config.
func (c *CNIConfig) GetNetworkListCachedConfig(list *NetworkConfigList, rt *RuntimeConf) ([]byte, *RuntimeConf, error) {
return c.getCachedConfig(list.Name, rt)
}
// GetNetworkCachedConfig copies the input RuntimeConf to output
// RuntimeConf with fields updated with info from the cached Config.
func (c *CNIConfig) GetNetworkCachedConfig(net *PluginConfig, rt *RuntimeConf) ([]byte, *RuntimeConf, error) {
return c.getCachedConfig(net.Network.Name, rt)
}
// GetCachedAttachments returns a list of network attachments from the cache.
// The returned list will be filtered by the containerID if the value is not empty.
func (c *CNIConfig) GetCachedAttachments(containerID string) ([]*NetworkAttachment, error) {
dirPath := filepath.Join(c.getCacheDir(&RuntimeConf{}), "results")
entries, err := os.ReadDir(dirPath)
if err != nil {
if os.IsNotExist(err) {
return nil, nil
}
return nil, err
}
fileNames := make([]string, 0, len(entries))
for _, e := range entries {
fileNames = append(fileNames, e.Name())
}
sort.Strings(fileNames)
attachments := []*NetworkAttachment{}
for _, fname := range fileNames {
if len(containerID) > 0 {
part := fmt.Sprintf("-%s-", containerID)
pos := strings.Index(fname, part)
if pos <= 0 || pos+len(part) >= len(fname) {
continue
}
}
cacheFile := filepath.Join(dirPath, fname)
bytes, err := os.ReadFile(cacheFile)
if err != nil {
continue
}
cachedInfo := cachedInfo{}
if err := json.Unmarshal(bytes, &cachedInfo); err != nil {
continue
}
if cachedInfo.Kind != CNICacheV1 {
continue
}
if len(containerID) > 0 && cachedInfo.ContainerID != containerID {
continue
}
if cachedInfo.IfName == "" || cachedInfo.NetworkName == "" {
continue
}
attachments = append(attachments, &NetworkAttachment{
ContainerID: cachedInfo.ContainerID,
Network: cachedInfo.NetworkName,
IfName: cachedInfo.IfName,
Config: cachedInfo.Config,
NetNS: cachedInfo.NetNS,
CniArgs: cachedInfo.CniArgs,
CapabilityArgs: cachedInfo.CapabilityArgs,
})
}
return attachments, nil
}
func (c *CNIConfig) addNetwork(ctx context.Context, name, cniVersion string, net *PluginConfig, prevResult types.Result, rt *RuntimeConf) (types.Result, error) {
c.ensureExec()
pluginPath, err := c.exec.FindInPath(net.Network.Type, c.Path)
if err != nil {
return nil, err
}
if err := utils.ValidateContainerID(rt.ContainerID); err != nil {
return nil, err
}
if err := utils.ValidateNetworkName(name); err != nil {
return nil, err
}
if err := utils.ValidateInterfaceName(rt.IfName); err != nil {
return nil, err
}
newConf, err := buildOneConfig(name, cniVersion, net, prevResult, rt)
if err != nil {
return nil, err
}
return invoke.ExecPluginWithResult(ctx, pluginPath, newConf.Bytes, c.args("ADD", rt), c.exec)
}
// AddNetworkList executes a sequence of plugins with the ADD command
func (c *CNIConfig) AddNetworkList(ctx context.Context, list *NetworkConfigList, rt *RuntimeConf) (types.Result, error) {
var err error
var result types.Result
for _, net := range list.Plugins {
result, err = c.addNetwork(ctx, list.Name, list.CNIVersion, net, result, rt)
if err != nil {
return nil, fmt.Errorf("plugin %s failed (add): %w", pluginDescription(net.Network), err)
}
}
if err = c.cacheAdd(result, list.Bytes, list.Name, rt); err != nil {
return nil, fmt.Errorf("failed to set network %q cached result: %w", list.Name, err)
}
return result, nil
}
func (c *CNIConfig) checkNetwork(ctx context.Context, name, cniVersion string, net *PluginConfig, prevResult types.Result, rt *RuntimeConf) error {
c.ensureExec()
pluginPath, err := c.exec.FindInPath(net.Network.Type, c.Path)
if err != nil {
return err
}
newConf, err := buildOneConfig(name, cniVersion, net, prevResult, rt)
if err != nil {
return err
}
return invoke.ExecPluginWithoutResult(ctx, pluginPath, newConf.Bytes, c.args("CHECK", rt), c.exec)
}
// CheckNetworkList executes a sequence of plugins with the CHECK command
func (c *CNIConfig) CheckNetworkList(ctx context.Context, list *NetworkConfigList, rt *RuntimeConf) error {
// CHECK was added in CNI spec version 0.4.0 and higher
if gtet, err := version.GreaterThanOrEqualTo(list.CNIVersion, "0.4.0"); err != nil {
return err
} else if !gtet {
return fmt.Errorf("configuration version %q %w", list.CNIVersion, ErrorCheckNotSupp)
}
if list.DisableCheck {
return nil
}
cachedResult, err := c.getCachedResult(list.Name, list.CNIVersion, rt)
if err != nil {
return fmt.Errorf("failed to get network %q cached result: %w", list.Name, err)
}
for _, net := range list.Plugins {
if err := c.checkNetwork(ctx, list.Name, list.CNIVersion, net, cachedResult, rt); err != nil {
return err
}
}
return nil
}
func (c *CNIConfig) delNetwork(ctx context.Context, name, cniVersion string, net *PluginConfig, prevResult types.Result, rt *RuntimeConf) error {
c.ensureExec()
pluginPath, err := c.exec.FindInPath(net.Network.Type, c.Path)
if err != nil {
return err
}
newConf, err := buildOneConfig(name, cniVersion, net, prevResult, rt)
if err != nil {
return err
}
return invoke.ExecPluginWithoutResult(ctx, pluginPath, newConf.Bytes, c.args("DEL", rt), c.exec)
}
// DelNetworkList executes a sequence of plugins with the DEL command
func (c *CNIConfig) DelNetworkList(ctx context.Context, list *NetworkConfigList, rt *RuntimeConf) error {
var cachedResult types.Result
// Cached result on DEL was added in CNI spec version 0.4.0 and higher
if gtet, err := version.GreaterThanOrEqualTo(list.CNIVersion, "0.4.0"); err != nil {
return err
} else if gtet {
if cachedResult, err = c.getCachedResult(list.Name, list.CNIVersion, rt); err != nil {
_ = c.cacheDel(list.Name, rt)
cachedResult = nil
}
}
for i := len(list.Plugins) - 1; i >= 0; i-- {
net := list.Plugins[i]
if err := c.delNetwork(ctx, list.Name, list.CNIVersion, net, cachedResult, rt); err != nil {
return fmt.Errorf("plugin %s failed (delete): %w", pluginDescription(net.Network), err)
}
}
_ = c.cacheDel(list.Name, rt)
return nil
}
func pluginDescription(net *types.PluginConf) string {
if net == nil {
return "<missing>"
}
pluginType := net.Type
out := fmt.Sprintf("type=%q", pluginType)
name := net.Name
if name != "" {
out += fmt.Sprintf(" name=%q", name)
}
return out
}
// AddNetwork executes the plugin with the ADD command
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)
if err != nil {
return nil, err
}
if err = c.cacheAdd(result, net.Bytes, net.Network.Name, rt); err != nil {
return nil, fmt.Errorf("failed to set network %q cached result: %w", net.Network.Name, err)
}
return result, nil
}
// CheckNetwork executes the plugin with the CHECK command
func (c *CNIConfig) CheckNetwork(ctx context.Context, net *PluginConfig, rt *RuntimeConf) error {
// 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 {
return err
} else if !gtet {
return fmt.Errorf("configuration version %q %w", net.Network.CNIVersion, ErrorCheckNotSupp)
}
cachedResult, err := c.getCachedResult(net.Network.Name, net.Network.CNIVersion, rt)
if err != nil {
return fmt.Errorf("failed to get network %q cached result: %w", net.Network.Name, err)
}
return c.checkNetwork(ctx, net.Network.Name, net.Network.CNIVersion, net, cachedResult, rt)
}
// DelNetwork executes the plugin with the DEL command
func (c *CNIConfig) DelNetwork(ctx context.Context, net *PluginConfig, rt *RuntimeConf) error {
var cachedResult types.Result
// Cached result on DEL was added in CNI spec version 0.4.0 and higher
if gtet, err := version.GreaterThanOrEqualTo(net.Network.CNIVersion, "0.4.0"); err != nil {
return err
} else if gtet {
cachedResult, err = c.getCachedResult(net.Network.Name, net.Network.CNIVersion, rt)
if err != nil {
return fmt.Errorf("failed to get network %q cached result: %w", net.Network.Name, err)
}
}
if err := c.delNetwork(ctx, net.Network.Name, net.Network.CNIVersion, net, cachedResult, rt); err != nil {
return err
}
_ = c.cacheDel(net.Network.Name, rt)
return nil
}
// ValidateNetworkList checks that a configuration is reasonably valid.
// - all the specified plugins exist on disk
// - every plugin supports the desired version.
//
// Returns a list of all capabilities supported by the configuration, or error
func (c *CNIConfig) ValidateNetworkList(ctx context.Context, list *NetworkConfigList) ([]string, error) {
version := list.CNIVersion
// holding map for seen caps (in case of duplicates)
caps := map[string]interface{}{}
errs := []error{}
for _, net := range list.Plugins {
if err := c.validatePlugin(ctx, net.Network.Type, version); err != nil {
errs = append(errs, err)
}
for c, enabled := range net.Network.Capabilities {
if !enabled {
continue
}
caps[c] = struct{}{}
}
}
if len(errs) > 0 {
return nil, fmt.Errorf("%v", errs)
}
// make caps list
cc := make([]string, 0, len(caps))
for c := range caps {
cc = append(cc, c)
}
return cc, nil
}
// ValidateNetwork checks that a configuration is reasonably valid.
// It uses the same logic as ValidateNetworkList)
// Returns a list of capabilities
func (c *CNIConfig) ValidateNetwork(ctx context.Context, net *PluginConfig) ([]string, error) {
caps := []string{}
for c, ok := range net.Network.Capabilities {
if ok {
caps = append(caps, c)
}
}
if err := c.validatePlugin(ctx, net.Network.Type, net.Network.CNIVersion); err != nil {
return nil, err
}
return caps, nil
}
// validatePlugin checks that an individual plugin's configuration is sane
func (c *CNIConfig) validatePlugin(ctx context.Context, pluginName, expectedVersion string) error {
c.ensureExec()
pluginPath, err := c.exec.FindInPath(pluginName, c.Path)
if err != nil {
return err
}
if expectedVersion == "" {
expectedVersion = "0.1.0"
}
vi, err := invoke.GetVersionInfo(ctx, pluginPath, c.exec)
if err != nil {
return err
}
for _, vers := range vi.SupportedVersions() {
if vers == expectedVersion {
return nil
}
}
return fmt.Errorf("plugin %s does not support config version %q", pluginName, expectedVersion)
}
// GetVersionInfo reports which versions of the CNI spec are supported by
// the given plugin.
func (c *CNIConfig) GetVersionInfo(ctx context.Context, pluginType string) (version.PluginInfo, error) {
c.ensureExec()
pluginPath, err := c.exec.FindInPath(pluginType, c.Path)
if err != nil {
return nil, err
}
return invoke.GetVersionInfo(ctx, pluginPath, c.exec)
}
// GCNetworkList will do two things
// - dump the list of cached attachments, and issue deletes as necessary
// - issue a GC to the underlying plugins (if the version is high enough)
func (c *CNIConfig) GCNetworkList(ctx context.Context, list *NetworkConfigList, args *GCArgs) error {
// If DisableGC is set, then don't bother GCing at all.
if list.DisableGC {
return nil
}
// First, get the list of cached attachments
cachedAttachments, err := c.GetCachedAttachments("")
if err != nil {
return nil
}
var validAttachments map[types.GCAttachment]interface{}
if args != nil {
validAttachments = make(map[types.GCAttachment]interface{}, len(args.ValidAttachments))
for _, a := range args.ValidAttachments {
validAttachments[a] = nil
}
}
var errs []error
for _, cachedAttachment := range cachedAttachments {
if cachedAttachment.Network != list.Name {
continue
}
// we found this attachment
gca := types.GCAttachment{
ContainerID: cachedAttachment.ContainerID,
IfName: cachedAttachment.IfName,
}
if _, ok := validAttachments[gca]; ok {
continue
}
// otherwise, this attachment wasn't valid and we should issue a CNI DEL
rt := RuntimeConf{
ContainerID: cachedAttachment.ContainerID,
NetNS: cachedAttachment.NetNS,
IfName: cachedAttachment.IfName,
Args: cachedAttachment.CniArgs,
CapabilityArgs: cachedAttachment.CapabilityArgs,
}
if err := c.DelNetworkList(ctx, list, &rt); err != nil {
errs = append(errs, fmt.Errorf("failed to delete stale attachment %s %s: %w", rt.ContainerID, rt.IfName, err))
}
}
// now, if the version supports it, issue a GC
if gt, _ := version.GreaterThanOrEqualTo(list.CNIVersion, "1.1.0"); gt {
inject := map[string]interface{}{
"name": list.Name,
"cniVersion": list.CNIVersion,
}
if args != nil {
inject["cni.dev/valid-attachments"] = args.ValidAttachments
// #1101: spec used incorrect variable name
inject["cni.dev/attachments"] = args.ValidAttachments
}
for _, plugin := range list.Plugins {
// build config here
pluginConfig, err := InjectConf(plugin, inject)
if err != nil {
errs = append(errs, fmt.Errorf("failed to generate configuration to GC plugin %s: %w", plugin.Network.Type, err))
}
if err := c.gcNetwork(ctx, pluginConfig); err != nil {
errs = append(errs, fmt.Errorf("failed to GC plugin %s: %w", plugin.Network.Type, err))
}
}
}
return errors.Join(errs...)
}
func (c *CNIConfig) gcNetwork(ctx context.Context, net *PluginConfig) error {
c.ensureExec()
pluginPath, err := c.exec.FindInPath(net.Network.Type, c.Path)
if err != nil {
return err
}
args := c.args("GC", &RuntimeConf{})
return invoke.ExecPluginWithoutResult(ctx, pluginPath, net.Bytes, args, c.exec)
}
func (c *CNIConfig) GetStatusNetworkList(ctx context.Context, list *NetworkConfigList) error {
// If the version doesn't support status, abort.
if gt, _ := version.GreaterThanOrEqualTo(list.CNIVersion, "1.1.0"); !gt {
return nil
}
inject := map[string]interface{}{
"name": list.Name,
"cniVersion": list.CNIVersion,
}
for _, plugin := range list.Plugins {
// build config here
pluginConfig, err := InjectConf(plugin, inject)
if err != nil {
return fmt.Errorf("failed to generate configuration to get plugin STATUS %s: %w", plugin.Network.Type, err)
}
if err := c.getStatusNetwork(ctx, pluginConfig); err != nil {
return err // Don't collect errors here, so we return a clean error code.
}
}
return nil
}
func (c *CNIConfig) getStatusNetwork(ctx context.Context, net *PluginConfig) error {
c.ensureExec()
pluginPath, err := c.exec.FindInPath(net.Network.Type, c.Path)
if err != nil {
return err
}
args := c.args("STATUS", &RuntimeConf{})
return invoke.ExecPluginWithoutResult(ctx, pluginPath, net.Bytes, args, c.exec)
}
// =====
@ -68,6 +895,6 @@ func (c *CNIConfig) args(action string, rt *RuntimeConf) *invoke.Args {
NetNS: rt.NetNS,
PluginArgs: rt.Args,
IfName: rt.IfName,
Path: strings.Join(c.Path, ":"),
Path: strings.Join(c.Path, string(os.PathListSeparator)),
}
}

2023
libcni/api_test.go Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,100 @@
// Copyright 2016 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package libcni_test
import (
"context"
"fmt"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/onsi/gomega/gexec"
"github.com/containernetworking/cni/libcni"
"github.com/containernetworking/cni/pkg/version/legacy_examples"
)
var _ = Describe("Backwards compatibility", func() {
var cacheDirPath string
BeforeEach(func() {
var err error
cacheDirPath, err = os.MkdirTemp("", "cni_cachedir")
Expect(err).NotTo(HaveOccurred())
})
AfterEach(func() {
Expect(os.RemoveAll(cacheDirPath)).To(Succeed())
})
It("correctly handles the response from a legacy plugin", func() {
example := legacy_examples.V010
pluginPath, err := example.Build()
Expect(err).NotTo(HaveOccurred())
netConf, err := libcni.ConfFromBytes([]byte(fmt.Sprintf(
`{ "name": "old-thing", "type": "%s" }`, example.Name)))
Expect(err).NotTo(HaveOccurred())
runtimeConf := &libcni.RuntimeConf{
ContainerID: "some-container-id",
NetNS: "/some/netns/path",
IfName: "eth0",
}
cniConfig := libcni.NewCNIConfigWithCacheDir([]string{filepath.Dir(pluginPath)}, cacheDirPath, nil)
result, err := cniConfig.AddNetwork(context.TODO(), netConf, runtimeConf)
Expect(err).NotTo(HaveOccurred())
Expect(result).To(Equal(legacy_examples.ExpectedResult))
err = cniConfig.DelNetwork(context.TODO(), netConf, runtimeConf)
Expect(err).NotTo(HaveOccurred())
Expect(os.RemoveAll(pluginPath)).To(Succeed())
})
It("correctly handles the request from a runtime with an older libcni", func() {
if runtime.GOOS == "windows" {
Skip("cannot build old runtime on windows")
}
example := legacy_examples.V010_Runtime
binPath, err := example.Build()
Expect(err).NotTo(HaveOccurred())
for _, configName := range example.NetConfs {
conf, err := example.GenerateNetConf(configName)
if err != nil {
Fail("Failed to generate config name " + configName + ": " + err.Error())
}
defer conf.Cleanup()
cmd := exec.Command(binPath, pluginDirs...)
cmd.Stdin = strings.NewReader(conf.Config)
session, err := gexec.Start(cmd, GinkgoWriter, GinkgoWriter)
Expect(err).NotTo(HaveOccurred())
Eventually(session).Should(gexec.Exit(0))
}
})
})

View File

@ -16,35 +16,293 @@ package libcni
import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"slices"
"sort"
"strings"
"github.com/containernetworking/cni/pkg/types"
"github.com/containernetworking/cni/pkg/version"
)
func ConfFromBytes(bytes []byte) (*NetworkConfig, error) {
conf := &NetworkConfig{Bytes: bytes}
if err := json.Unmarshal(bytes, &conf.Network); err != nil {
return nil, fmt.Errorf("error parsing configuration: %s", err)
type NotFoundError struct {
Dir string
Name string
}
func (e NotFoundError) Error() string {
return fmt.Sprintf(`no net configuration with name "%s" in %s`, e.Name, e.Dir)
}
type NoConfigsFoundError struct {
Dir string
}
func (e NoConfigsFoundError) Error() string {
return fmt.Sprintf(`no net configurations found in %s`, e.Dir)
}
// This will not validate that the plugins actually belong to the netconfig by ensuring
// that they are loaded from a directory named after the networkName, relative to the network config.
//
// 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)
}
if conf.Network.Type == "" {
return nil, fmt.Errorf("error parsing configuration: missing 'type'")
}
return conf, nil
}
func ConfFromFile(filename string) (*NetworkConfig, error) {
bytes, err := ioutil.ReadFile(filename)
// Given a path to a directory containing a network configuration, and the name of a network,
// 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 {
return nil, fmt.Errorf("error reading %s: %s", filename, err)
return nil, fmt.Errorf("failed to read plugin config files in %s: %w", pluginConfPath, err)
}
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 NetworkConfFromBytes(confBytes []byte) (*NetworkConfigList, error) {
rawList := make(map[string]interface{})
if err := json.Unmarshal(confBytes, &rawList); err != nil {
return nil, fmt.Errorf("error parsing configuration list: %w", err)
}
rawName, ok := rawList["name"]
if !ok {
return nil, fmt.Errorf("error parsing configuration list: no name")
}
name, ok := rawName.(string)
if !ok {
return nil, fmt.Errorf("error parsing configuration list: invalid name type %T", rawName)
}
var cniVersion string
rawVersion, ok := rawList["cniVersion"]
if ok {
cniVersion, ok = rawVersion.(string)
if !ok {
return nil, fmt.Errorf("error parsing configuration list: invalid cniVersion type %T", rawVersion)
}
}
rawVersions, ok := rawList["cniVersions"]
if ok {
// Parse the current package CNI version
rvs, ok := rawVersions.([]interface{})
if !ok {
return nil, fmt.Errorf("error parsing configuration list: invalid type for cniVersions: %T", rvs)
}
vs := make([]string, 0, len(rvs))
for i, rv := range rvs {
v, ok := rv.(string)
if !ok {
return nil, fmt.Errorf("error parsing configuration list: invalid type for cniVersions index %d: %T", i, rv)
}
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)
} else if !gt {
// Skip versions "greater" than this implementation of the spec
vs = append(vs, v)
}
}
// if cniVersion was already set, append it to the list for sorting.
if cniVersion != "" {
gt, err := version.GreaterThan(cniVersion, version.Current())
if err != nil {
return nil, fmt.Errorf("error parsing configuration list: invalid cniVersion %s: %w", cniVersion, err)
} else if !gt {
// ignore any versions higher than the current implemented spec version
vs = append(vs, cniVersion)
}
}
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 {
cniVersion = vs[len(vs)-1]
}
}
readBool := func(key string) (bool, error) {
rawVal, ok := rawList[key]
if !ok {
return false, nil
}
if b, ok := rawVal.(bool); ok {
return b, nil
}
s, ok := rawVal.(string)
if !ok {
return false, fmt.Errorf("error parsing configuration list: invalid type %T for %s", rawVal, key)
}
s = strings.ToLower(s)
switch s {
case "false":
return false, nil
case "true":
return true, nil
}
return false, fmt.Errorf("error parsing configuration list: invalid value %q for %s", s, key)
}
disableCheck, err := readBool("disableCheck")
if err != nil {
return nil, err
}
disableGC, err := readBool("disableGC")
if err != nil {
return nil, err
}
loadOnlyInlinedPlugins, err := readBool("loadOnlyInlinedPlugins")
if err != nil {
return nil, err
}
list := &NetworkConfigList{
Name: name,
DisableCheck: disableCheck,
DisableGC: disableGC,
LoadOnlyInlinedPlugins: loadOnlyInlinedPlugins,
CNIVersion: cniVersion,
Bytes: confBytes,
}
var plugins []interface{}
plug, ok := rawList["plugins"]
// We can have a `plugins` list key in the main conf,
// 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{})
if !ok {
return nil, fmt.Errorf("error parsing configuration list: invalid 'plugins' type %T", plug)
}
if len(plugins) == 0 {
return nil, fmt.Errorf("error parsing configuration list: no plugins in list")
}
for i, conf := range plugins {
newBytes, err := json.Marshal(conf)
if err != nil {
return nil, fmt.Errorf("failed to marshal plugin config %d: %w", i, err)
}
netConf, err := ConfFromBytes(newBytes)
if err != nil {
return nil, fmt.Errorf("failed to parse plugin config %d: %w", i, err)
}
list.Plugins = append(list.Plugins, netConf)
}
return list, nil
}
func NetworkConfFromFile(filename string) (*NetworkConfigList, error) {
bytes, err := os.ReadFile(filename)
if err != nil {
return nil, fmt.Errorf("error reading %s: %w", filename, err)
}
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 ConfFiles(dir string) ([]string, error) {
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) {
// In part, adapted from rkt/networking/podenv.go#listFiles
files, err := ioutil.ReadDir(dir)
files, err := os.ReadDir(dir)
switch {
case err == nil: // break
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
default:
return nil, err
@ -55,20 +313,24 @@ func ConfFiles(dir string) ([]string, error) {
if f.IsDir() {
continue
}
if filepath.Ext(f.Name()) == ".conf" {
confFiles = append(confFiles, filepath.Join(dir, f.Name()))
fileExt := filepath.Ext(f.Name())
for _, ext := range extensions {
if fileExt == ext {
confFiles = append(confFiles, filepath.Join(dir, f.Name()))
}
}
}
return confFiles, nil
}
// Deprecated: This file format is no longer supported, use NetworkConfXXX and NetworkPluginXXX functions
func LoadConf(dir, name string) (*NetworkConfig, error) {
files, err := ConfFiles(dir)
files, err := ConfFiles(dir, []string{".conf", ".json"})
switch {
case err != nil:
return nil, err
case len(files) == 0:
return nil, fmt.Errorf("no net configurations found")
return nil, NoConfigsFoundError{Dir: dir}
}
sort.Strings(files)
@ -81,5 +343,103 @@ func LoadConf(dir, name string) (*NetworkConfig, error) {
return conf, nil
}
}
return nil, fmt.Errorf(`no net configuration with name "%s" in %s`, name, dir)
return nil, NotFoundError{dir, name}
}
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"})
if err != nil {
return nil, err
}
sort.Strings(files)
for _, confFile := range files {
conf, err := NetworkConfFromFile(confFile)
if err != nil {
return nil, err
}
if conf.Name == name {
return conf, nil
}
}
// Deprecated: Try and load a network configuration file (instead of list)
// from the same name, then upconvert.
singleConf, err := LoadConf(dir, name)
if err != nil {
// A little extra logic so the error makes sense
var ncfErr NoConfigsFoundError
if len(files) != 0 && errors.As(err, &ncfErr) {
// Config lists found but no config files found
return nil, NotFoundError{dir, name}
}
return nil, err
}
return ConfListFromConf(singleConf)
}
// 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{})
err := json.Unmarshal(original.Bytes, &config)
if err != nil {
return nil, fmt.Errorf("unmarshal existing network bytes: %w", err)
}
for key, value := range newValues {
if key == "" {
return nil, fmt.Errorf("keys cannot be empty")
}
if value == nil {
return nil, fmt.Errorf("key '%s' value must not be nil", key)
}
config[key] = value
}
newBytes, err := json.Marshal(config)
if err != nil {
return nil, err
}
return NetworkPluginConfFromBytes(newBytes)
}
// ConfListFromConf "upconverts" a network config in to a NetworkConfigList,
// with the single network as the only entry in the list.
//
// 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.
// This may seem a bit strange, but it's to make the Bytes fields
// actually make sense. Otherwise, the generated json is littered with
// golang default values.
rawConfig := make(map[string]interface{})
if err := json.Unmarshal(original.Bytes, &rawConfig); err != nil {
return nil, err
}
rawConfigList := map[string]interface{}{
"name": original.Network.Name,
"cniVersion": original.Network.CNIVersion,
"plugins": []interface{}{rawConfig},
}
b, err := json.Marshal(rawConfigList)
if err != nil {
return nil, err
}
return ConfListFromBytes(b)
}

798
libcni/conf_test.go Normal file
View File

@ -0,0 +1,798 @@
// Copyright 2016 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package libcni_test
import (
"fmt"
"os"
"path/filepath"
"strings"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/containernetworking/cni/libcni"
"github.com/containernetworking/cni/pkg/types"
)
var _ = Describe("Loading configuration from disk", func() {
Describe("LoadConf", func() {
var (
configDir string
pluginConfig []byte
)
BeforeEach(func() {
var err error
configDir, err = os.MkdirTemp("", "plugin-conf")
Expect(err).NotTo(HaveOccurred())
pluginConfig = []byte(`{ "name": "some-plugin", "type": "foobar", "some-key": "some-value" }`)
Expect(os.WriteFile(filepath.Join(configDir, "50-whatever.conf"), pluginConfig, 0o600)).To(Succeed())
})
AfterEach(func() {
Expect(os.RemoveAll(configDir)).To(Succeed())
})
It("finds the network config file for the plugin of the given type", func() {
netConfig, err := libcni.LoadConf(configDir, "some-plugin")
Expect(err).NotTo(HaveOccurred())
Expect(netConfig).To(Equal(&libcni.PluginConfig{
Network: &types.PluginConf{
Name: "some-plugin",
Type: "foobar",
},
Bytes: pluginConfig,
}))
})
Context("when the config directory does not exist", func() {
BeforeEach(func() {
Expect(os.RemoveAll(configDir)).To(Succeed())
})
It("returns a useful error", func() {
_, err := libcni.LoadConf(configDir, "some-plugin")
Expect(err).To(MatchError(libcni.NoConfigsFoundError{Dir: configDir}))
})
})
Context("when the config file is .json extension instead of .conf", func() {
BeforeEach(func() {
Expect(os.Remove(configDir + "/50-whatever.conf")).To(Succeed())
pluginConfig = []byte(`{ "name": "some-plugin", "some-key": "some-value", "type": "foobar" }`)
Expect(os.WriteFile(filepath.Join(configDir, "50-whatever.json"), pluginConfig, 0o600)).To(Succeed())
})
It("finds the network config file for the plugin of the given type", func() {
netConfig, err := libcni.LoadConf(configDir, "some-plugin")
Expect(err).NotTo(HaveOccurred())
Expect(netConfig).To(Equal(&libcni.PluginConfig{
Network: &types.PluginConf{
Name: "some-plugin",
Type: "foobar",
},
Bytes: pluginConfig,
}))
})
})
Context("when there is no config for the desired plugin", func() {
It("returns a useful error", func() {
_, err := libcni.LoadConf(configDir, "some-other-plugin")
Expect(err).To(MatchError(ContainSubstring(`no net configuration with name "some-other-plugin" in`)))
})
})
Context("when a config file is malformed", func() {
BeforeEach(func() {
Expect(os.WriteFile(filepath.Join(configDir, "00-bad.conf"), []byte(`{`), 0o600)).To(Succeed())
})
It("returns a useful error", func() {
_, err := libcni.LoadConf(configDir, "some-plugin")
Expect(err).To(MatchError(`error parsing configuration: unexpected end of JSON input`))
})
})
Context("when the config is in a nested subdir", func() {
BeforeEach(func() {
subdir := filepath.Join(configDir, "subdir1", "subdir2")
Expect(os.MkdirAll(subdir, 0o700)).To(Succeed())
pluginConfig = []byte(`{ "name": "deep", "some-key": "some-value" }`)
Expect(os.WriteFile(filepath.Join(subdir, "90-deep.conf"), pluginConfig, 0o600)).To(Succeed())
})
It("will not find the config", func() {
_, err := libcni.LoadConf(configDir, "deep")
Expect(err).To(MatchError(HavePrefix("no net configuration with name")))
})
})
})
Describe("Capabilities", func() {
var configDir string
BeforeEach(func() {
var err error
configDir, err = os.MkdirTemp("", "plugin-conf")
Expect(err).NotTo(HaveOccurred())
pluginConfig := []byte(`{ "name": "some-plugin", "type": "noop", "cniVersion": "0.3.1", "capabilities": { "portMappings": true, "somethingElse": true, "noCapability": false } }`)
Expect(os.WriteFile(filepath.Join(configDir, "50-whatever.conf"), pluginConfig, 0o600)).To(Succeed())
})
AfterEach(func() {
Expect(os.RemoveAll(configDir)).To(Succeed())
})
It("reads plugin capabilities from network config", func() {
netConfig, err := libcni.LoadConf(configDir, "some-plugin")
Expect(err).NotTo(HaveOccurred())
Expect(netConfig.Network.Capabilities).To(Equal(map[string]bool{
"portMappings": true,
"somethingElse": true,
"noCapability": false,
}))
})
})
Describe("ConfFromFile", func() {
Context("when the file cannot be opened", func() {
It("returns a useful error", func() {
_, err := libcni.ConfFromFile("/tmp/nope/not-here")
Expect(err).To(MatchError(HavePrefix(`error reading /tmp/nope/not-here: open /tmp/nope/not-here`)))
})
})
Context("when the file is missing 'type'", func() {
var fileName, configDir string
BeforeEach(func() {
var err error
configDir, err = os.MkdirTemp("", "plugin-conf")
Expect(err).NotTo(HaveOccurred())
fileName = filepath.Join(configDir, "50-whatever.conf")
pluginConfig := []byte(`{ "name": "some-plugin", "some-key": "some-value" }`)
Expect(os.WriteFile(fileName, pluginConfig, 0o600)).To(Succeed())
})
AfterEach(func() {
Expect(os.RemoveAll(configDir)).To(Succeed())
})
It("returns a useful error", func() {
_, err := libcni.ConfFromFile(fileName)
Expect(err).To(MatchError(`error parsing configuration: missing 'type'`))
})
})
})
Describe("NetworkPluginConfFromBytes", func() {
Context("when the config is missing 'type'", func() {
It("returns a useful error", func() {
_, err := libcni.ConfFromBytes([]byte(`{ "name": "some-plugin", "some-key": "some-value" }`))
Expect(err).To(MatchError(`error parsing configuration: missing 'type'`))
})
})
})
Describe("LoadNetworkConf", func() {
var (
configDir string
configList []byte
)
BeforeEach(func() {
var err error
configDir, err = os.MkdirTemp("", "plugin-conf")
Expect(err).NotTo(HaveOccurred())
configList = []byte(`{
"name": "some-network",
"cniVersion": "0.2.0",
"disableCheck": true,
"plugins": [
{
"type": "host-local",
"subnet": "10.0.0.1/24"
},
{
"type": "bridge",
"mtu": 1400
},
{
"type": "port-forwarding",
"ports": {"20.0.0.1:8080": "80"}
}
]
}`)
Expect(os.WriteFile(filepath.Join(configDir, "50-whatever.conflist"), configList, 0o600)).To(Succeed())
})
AfterEach(func() {
Expect(os.RemoveAll(configDir)).To(Succeed())
})
It("finds the network config file for the plugin of the given type", func() {
netConfigList, err := libcni.LoadNetworkConf(configDir, "some-network")
Expect(err).NotTo(HaveOccurred())
Expect(netConfigList).To(Equal(&libcni.NetworkConfigList{
Name: "some-network",
CNIVersion: "0.2.0",
DisableCheck: true,
Plugins: []*libcni.PluginConfig{
{
Network: &types.PluginConf{Type: "host-local"},
Bytes: []byte(`{"subnet":"10.0.0.1/24","type":"host-local"}`),
},
{
Network: &types.PluginConf{Type: "bridge"},
Bytes: []byte(`{"mtu":1400,"type":"bridge"}`),
},
{
Network: &types.PluginConf{Type: "port-forwarding"},
Bytes: []byte(`{"ports":{"20.0.0.1:8080":"80"},"type":"port-forwarding"}`),
},
},
Bytes: configList,
}))
})
Context("when there is a config file with the same name as the list", func() {
BeforeEach(func() {
configFile := []byte(`{
"name": "some-network",
"cniVersion": "0.2.0",
"type": "bridge"
}`)
Expect(os.WriteFile(filepath.Join(configDir, "49-whatever.conf"), configFile, 0o600)).To(Succeed())
})
It("Loads the config list first", func() {
netConfigList, err := libcni.LoadNetworkConf(configDir, "some-network")
Expect(err).NotTo(HaveOccurred())
Expect(netConfigList.Plugins).To(HaveLen(3))
})
It("falls back to the config file", func() {
Expect(os.Remove(filepath.Join(configDir, "50-whatever.conflist"))).To(Succeed())
netConfigList, err := libcni.LoadNetworkConf(configDir, "some-network")
Expect(err).NotTo(HaveOccurred())
Expect(netConfigList.Plugins).To(HaveLen(1))
Expect(netConfigList.Plugins[0].Network.Type).To(Equal("bridge"))
})
})
Context("when the config directory does not exist", func() {
BeforeEach(func() {
Expect(os.RemoveAll(configDir)).To(Succeed())
})
It("returns a useful error", func() {
_, err := libcni.LoadNetworkConf(configDir, "some-network")
Expect(err).To(MatchError(libcni.NoConfigsFoundError{Dir: configDir}))
})
})
Context("when there is no config for the desired network name", func() {
It("returns a useful error", func() {
_, err := libcni.LoadNetworkConf(configDir, "some-other-network")
Expect(err).To(MatchError(libcni.NotFoundError{Dir: configDir, Name: "some-other-network"}))
})
})
Context("when a config file is malformed", func() {
BeforeEach(func() {
Expect(os.WriteFile(filepath.Join(configDir, "00-bad.conflist"), []byte(`{`), 0o600)).To(Succeed())
})
It("returns a useful error", func() {
_, err := libcni.LoadNetworkConf(configDir, "some-plugin")
Expect(err).To(MatchError(`error parsing configuration list: unexpected end of JSON input`))
})
})
Context("when the config is in a nested subdir", func() {
BeforeEach(func() {
subdir := filepath.Join(configDir, "subdir1", "subdir2")
Expect(os.MkdirAll(subdir, 0o700)).To(Succeed())
configList = []byte(`{
"name": "deep",
"cniVersion": "0.2.0",
"plugins": [
{
"type": "host-local",
"subnet": "10.0.0.1/24"
},
]
}`)
Expect(os.WriteFile(filepath.Join(subdir, "90-deep.conflist"), configList, 0o600)).To(Succeed())
})
It("will not find the config", func() {
_, err := libcni.LoadNetworkConf(configDir, "deep")
Expect(err).To(MatchError(HavePrefix("no net configuration with name")))
})
})
Context("when disableCheck is a string not a boolean", func() {
It("will read a 'true' value and convert to boolean", func() {
configList = []byte(`{
"name": "some-network",
"cniVersion": "0.4.0",
"disableCheck": "true",
"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.DisableCheck).To(BeTrue())
})
It("will read a 'false' value and convert to boolean", func() {
configList = []byte(`{
"name": "some-network",
"cniVersion": "0.4.0",
"disableCheck": "false",
"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.DisableCheck).To(BeFalse())
})
It("will return an error on an unrecognized value", func() {
const badValue string = "adsfasdfasf"
configList = []byte(fmt.Sprintf(`{
"name": "some-network",
"cniVersion": "0.4.0",
"disableCheck": "%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 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("NetworkConfFromFile", func() {
Context("when the file cannot be opened", func() {
It("returns a useful error", func() {
_, err := libcni.NetworkConfFromFile("/tmp/nope/not-here")
Expect(err).To(MatchError(HavePrefix(`error reading /tmp/nope/not-here: open /tmp/nope/not-here`)))
})
})
})
Describe("InjectConf", func() {
var testNetConfig *libcni.PluginConfig
BeforeEach(func() {
testNetConfig = &libcni.PluginConfig{
Network: &types.PluginConf{Name: "some-plugin", Type: "foobar"},
Bytes: []byte(`{ "name": "some-plugin", "type": "foobar" }`),
}
})
Context("when function parameters are incorrect", func() {
It("returns unmarshal error", func() {
conf := &libcni.PluginConfig{
Network: &types.PluginConf{Name: "some-plugin"},
Bytes: []byte(`{ cc cc cc}`),
}
_, err := libcni.InjectConf(conf, map[string]interface{}{"": nil})
Expect(err).To(MatchError(HavePrefix(`unmarshal existing network bytes`)))
})
It("returns key error", func() {
_, err := libcni.InjectConf(testNetConfig, map[string]interface{}{"": nil})
Expect(err).To(MatchError(HavePrefix(`keys cannot be empty`)))
})
It("returns newValue error", func() {
_, err := libcni.InjectConf(testNetConfig, map[string]interface{}{"test": nil})
Expect(err).To(MatchError(HavePrefix(`key 'test' value must not be nil`)))
})
})
Context("when new string value added", func() {
It("adds the new key & value to the config", func() {
newPluginConfig := []byte(`{"name":"some-plugin","test":"test","type":"foobar"}`)
resultConfig, err := libcni.InjectConf(testNetConfig, map[string]interface{}{"test": "test"})
Expect(err).NotTo(HaveOccurred())
Expect(resultConfig).To(Equal(&libcni.PluginConfig{
Network: &types.PluginConf{
Name: "some-plugin",
Type: "foobar",
},
Bytes: newPluginConfig,
}))
})
It("adds the new value for exiting key", func() {
newPluginConfig := []byte(`{"name":"some-plugin","test":"changedValue","type":"foobar"}`)
resultConfig, err := libcni.InjectConf(testNetConfig, map[string]interface{}{"test": "test"})
Expect(err).NotTo(HaveOccurred())
resultConfig, err = libcni.InjectConf(resultConfig, map[string]interface{}{"test": "changedValue"})
Expect(err).NotTo(HaveOccurred())
Expect(resultConfig).To(Equal(&libcni.PluginConfig{
Network: &types.PluginConf{
Name: "some-plugin",
Type: "foobar",
},
Bytes: newPluginConfig,
}))
})
It("adds existing key & value", func() {
newPluginConfig := []byte(`{"name":"some-plugin","test":"test","type":"foobar"}`)
resultConfig, err := libcni.InjectConf(testNetConfig, map[string]interface{}{"test": "test"})
Expect(err).NotTo(HaveOccurred())
resultConfig, err = libcni.InjectConf(resultConfig, map[string]interface{}{"test": "test"})
Expect(err).NotTo(HaveOccurred())
Expect(resultConfig).To(Equal(&libcni.PluginConfig{
Network: &types.PluginConf{
Name: "some-plugin",
Type: "foobar",
},
Bytes: newPluginConfig,
}))
})
It("adds sub-fields of NetworkConfig.Network to the config", func() {
expectedPluginConfig := []byte(`{"dns":{"domain":"local","nameservers":["server1","server2"]},"name":"some-plugin","type":"bridge"}`)
servers := []string{"server1", "server2"}
newDNS := &types.DNS{Nameservers: servers, Domain: "local"}
// inject DNS
resultConfig, err := libcni.InjectConf(testNetConfig, map[string]interface{}{"dns": newDNS})
Expect(err).NotTo(HaveOccurred())
// inject type
resultConfig, err = libcni.InjectConf(resultConfig, map[string]interface{}{"type": "bridge"})
Expect(err).NotTo(HaveOccurred())
Expect(resultConfig).To(Equal(&libcni.PluginConfig{
Network: &types.PluginConf{Name: "some-plugin", Type: "bridge", DNS: types.DNS{Nameservers: servers, Domain: "local"}},
Bytes: expectedPluginConfig,
}))
})
})
})
})
var _ = Describe("NetworkConfFromBytes", func() {
Describe("Version selection", func() {
makeConfig := func(versions ...string) []byte {
// ugly fake json encoding, but whatever
vs := []string{}
for _, v := range versions {
vs = append(vs, fmt.Sprintf(`"%s"`, v))
}
return []byte(fmt.Sprintf(`{"name": "test", "cniVersions": [%s], "plugins": [{"type": "foo"}]}`, strings.Join(vs, ",")))
}
It("correctly selects the maximum version", func() {
conf, err := libcni.NetworkConfFromBytes(makeConfig("1.1.0", "0.4.0", "1.0.0"))
Expect(err).NotTo(HaveOccurred())
Expect(conf.CNIVersion).To(Equal("1.1.0"))
})
It("selects the highest version supported by libcni", func() {
conf, err := libcni.NetworkConfFromBytes(makeConfig("99.0.0", "1.1.0", "0.4.0", "1.0.0"))
Expect(err).NotTo(HaveOccurred())
Expect(conf.CNIVersion).To(Equal("1.1.0"))
})
It("fails when invalid versions are specified", func() {
_, err := libcni.NetworkConfFromBytes(makeConfig("1.1.0", "0.4.0", "1.0.f"))
Expect(err).To(HaveOccurred())
})
It("falls back to cniVersion", func() {
conf, err := libcni.NetworkConfFromBytes([]byte(`{"name": "test", "cniVersion": "1.2.3", "plugins": [{"type": "foo"}]}`))
Expect(err).NotTo(HaveOccurred())
Expect(conf.CNIVersion).To(Equal("1.2.3"))
})
It("merges cniVersions and cniVersion", func() {
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(conf.CNIVersion).To(Equal("1.0.0"))
})
It("handles an empty cniVersions array", func() {
conf, err := libcni.NetworkConfFromBytes([]byte(`{"name": "test", "cniVersions": [], "plugins": [{"type": "foo"}]}`))
Expect(err).NotTo(HaveOccurred())
Expect(conf.CNIVersion).To(Equal(""))
})
})
})
var _ = Describe("ConfListFromConf", func() {
var testNetConfig *libcni.PluginConfig
BeforeEach(func() {
pb := []byte(`{"name":"some-plugin","cniVersion":"0.3.1", "type":"foobar"}`)
tc, err := libcni.ConfFromBytes(pb)
Expect(err).NotTo(HaveOccurred())
testNetConfig = tc
})
It("correctly upconverts a NetworkConfig to a NetworkConfigList", func() {
ncl, err := libcni.ConfListFromConf(testNetConfig)
Expect(err).NotTo(HaveOccurred())
bytes := ncl.Bytes
// null out the json - we don't care about the exact marshalling
ncl.Bytes = nil
ncl.Plugins[0].Bytes = nil
testNetConfig.Bytes = nil
Expect(ncl).To(Equal(&libcni.NetworkConfigList{
Name: "some-plugin",
CNIVersion: "0.3.1",
Plugins: []*libcni.PluginConfig{testNetConfig},
}))
// Test that the json unmarshals to the same data
ncl2, err := libcni.NetworkConfFromBytes(bytes)
Expect(err).NotTo(HaveOccurred())
ncl2.Bytes = nil
ncl2.Plugins[0].Bytes = nil
Expect(ncl2).To(Equal(ncl))
})
})

View File

@ -0,0 +1,62 @@
// Copyright 2016 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package libcni_test
import (
"encoding/json"
"path/filepath"
"testing"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/onsi/gomega/gexec"
)
func TestLibcni(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Libcni Suite")
}
var pluginPackages = map[string]string{
"noop": "github.com/containernetworking/cni/plugins/test/noop",
"sleep": "github.com/containernetworking/cni/plugins/test/sleep",
}
var (
pluginPaths map[string]string
pluginDirs []string // array of plugin dirs
)
var _ = SynchronizedBeforeSuite(func() []byte {
paths := map[string]string{}
for name, packagePath := range pluginPackages {
execPath, err := gexec.Build(packagePath)
Expect(err).NotTo(HaveOccurred())
paths[name] = execPath
}
crossNodeData, err := json.Marshal(paths)
Expect(err).NotTo(HaveOccurred())
return crossNodeData
}, func(crossNodeData []byte) {
Expect(json.Unmarshal(crossNodeData, &pluginPaths)).To(Succeed())
for _, pluginPath := range pluginPaths {
pluginDirs = append(pluginDirs, filepath.Dir(pluginPath))
}
})
var _ = SynchronizedAfterSuite(func() {}, func() {
gexec.CleanupBuildArtifacts()
})

BIN
logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

6
mk/dependencies/golangci.sh Executable file
View File

@ -0,0 +1,6 @@
#!/bin/bash
set -e
GOLANGCI_LINT_VERSION="v1.57.1"
go install "github.com/golangci/golangci-lint/cmd/golangci-lint@${GOLANGCI_LINT_VERSION}"

14
mk/lint.mk Normal file
View File

@ -0,0 +1,14 @@
.PHONY: lint
lint: golangci/install golangci/lint
.PHONY: golangci/install
golangci/install:
./mk/dependencies/golangci.sh
.PHONY: golangci/lint
golangci/lint:
golangci-lint run --verbose
.PHONY: golangci/fix
golangci/fix:
golangci-lint run --verbose --fix

View File

@ -15,6 +15,7 @@
package invoke
import (
"fmt"
"os"
"strings"
)
@ -22,6 +23,8 @@ import (
type CNIArgs interface {
// For use with os/exec; i.e., return nil to inherit the
// environment from this process
// For use in delegation; inherit the environment from this
// process and allow overrides
AsEnv() []string
}
@ -29,7 +32,7 @@ type inherited struct{}
var inheritArgsFromEnv inherited
func (_ *inherited) AsEnv() []string {
func (*inherited) AsEnv() []string {
return nil
}
@ -47,6 +50,9 @@ type Args struct {
Path string
}
// Args implements the CNIArgs interface
var _ CNIArgs = &Args{}
func (args *Args) AsEnv() []string {
env := os.Environ()
pluginArgsStr := args.PluginArgsStr
@ -54,14 +60,17 @@ func (args *Args) AsEnv() []string {
pluginArgsStr = stringify(args.PluginArgs)
}
// Duplicated values which come first will be overridden, so we must put the
// custom values in the end to avoid being overridden by the process environments.
env = append(env,
"CNI_COMMAND="+args.Command,
"CNI_CONTAINERID="+args.ContainerID,
"CNI_NETNS="+args.NetNS,
"CNI_ARGS="+pluginArgsStr,
"CNI_IFNAME="+args.IfName,
"CNI_PATH="+args.Path)
return env
"CNI_PATH="+args.Path,
)
return dedupEnv(env)
}
// taken from rkt/networking/net_plugin.go
@ -74,3 +83,46 @@ func stringify(pluginArgs [][2]string) string {
return strings.Join(entries, ";")
}
// DelegateArgs implements the CNIArgs interface
// used for delegation to inherit from environments
// and allow some overrides like CNI_COMMAND
var _ CNIArgs = &DelegateArgs{}
type DelegateArgs struct {
Command string
}
func (d *DelegateArgs) AsEnv() []string {
env := os.Environ()
// The custom values should come in the end to override the existing
// process environment of the same key.
env = append(env,
"CNI_COMMAND="+d.Command,
)
return dedupEnv(env)
}
// dedupEnv returns a copy of env with any duplicates removed, in favor of later values.
// Items not of the normal environment "key=value" form are preserved unchanged.
func dedupEnv(env []string) []string {
out := make([]string, 0, len(env))
envMap := map[string]string{}
for _, kv := range env {
// find the first "=" in environment, if not, just keep it
eq := strings.Index(kv, "=")
if eq < 0 {
out = append(out, kv)
continue
}
envMap[kv[:eq]] = kv[eq+1:]
}
for k, v := range envMap {
out = append(out, fmt.Sprintf("%s=%s", k, v))
}
return out
}

139
pkg/invoke/args_test.go Normal file
View File

@ -0,0 +1,139 @@
// Copyright 2017 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package invoke_test
import (
"os"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/containernetworking/cni/pkg/invoke"
)
var _ = Describe("CNIArgs AsEnv", func() {
Describe("Args AsEnv", func() {
BeforeEach(func() {
os.Setenv("CNI_COMMAND", "DEL")
os.Setenv("CNI_IFNAME", "eth0")
os.Setenv("CNI_CONTAINERID", "id")
os.Setenv("CNI_ARGS", "args")
os.Setenv("CNI_NETNS", "testns")
os.Setenv("CNI_PATH", "testpath")
})
It("places the CNI environment variables in the end to be prepended", func() {
args := invoke.Args{
Command: "ADD",
ContainerID: "some-container-id",
NetNS: "/some/netns/path",
PluginArgs: [][2]string{
{"KEY1", "VALUE1"},
{"KEY2", "VALUE2"},
},
IfName: "eth7",
Path: "/some/cni/path",
}
latentEnvs := os.Environ()
numLatentEnvs := len(latentEnvs)
cniEnvs := args.AsEnv()
Expect(cniEnvs).To(HaveLen(numLatentEnvs))
Expect(inStringSlice("CNI_COMMAND=ADD", cniEnvs)).To(BeTrue())
Expect(inStringSlice("CNI_IFNAME=eth7", cniEnvs)).To(BeTrue())
Expect(inStringSlice("CNI_CONTAINERID=some-container-id", cniEnvs)).To(BeTrue())
Expect(inStringSlice("CNI_NETNS=/some/netns/path", cniEnvs)).To(BeTrue())
Expect(inStringSlice("CNI_ARGS=KEY1=VALUE1;KEY2=VALUE2", cniEnvs)).To(BeTrue())
Expect(inStringSlice("CNI_PATH=/some/cni/path", cniEnvs)).To(BeTrue())
Expect(inStringSlice("CNI_COMMAND=DEL", cniEnvs)).To(BeFalse())
Expect(inStringSlice("CNI_IFNAME=eth0", cniEnvs)).To(BeFalse())
Expect(inStringSlice("CNI_CONTAINERID=id", cniEnvs)).To(BeFalse())
Expect(inStringSlice("CNI_NETNS=testns", cniEnvs)).To(BeFalse())
Expect(inStringSlice("CNI_ARGS=args", cniEnvs)).To(BeFalse())
Expect(inStringSlice("CNI_PATH=testpath", cniEnvs)).To(BeFalse())
})
AfterEach(func() {
os.Unsetenv("CNI_COMMAND")
os.Unsetenv("CNI_IFNAME")
os.Unsetenv("CNI_CONTAINERID")
os.Unsetenv("CNI_ARGS")
os.Unsetenv("CNI_NETNS")
os.Unsetenv("CNI_PATH")
})
})
Describe("DelegateArgs AsEnv", func() {
BeforeEach(func() {
os.Unsetenv("CNI_COMMAND")
})
It("override CNI_COMMAND if it already exists in environment variables", func() {
os.Setenv("CNI_COMMAND", "DEL")
delegateArgs := invoke.DelegateArgs{
Command: "ADD",
}
latentEnvs := os.Environ()
numLatentEnvs := len(latentEnvs)
cniEnvs := delegateArgs.AsEnv()
Expect(cniEnvs).To(HaveLen(numLatentEnvs))
Expect(inStringSlice("CNI_COMMAND=ADD", cniEnvs)).To(BeTrue())
Expect(inStringSlice("CNI_COMMAND=DEL", cniEnvs)).To(BeFalse())
})
It("append CNI_COMMAND if it does not exist in environment variables", func() {
delegateArgs := invoke.DelegateArgs{
Command: "ADD",
}
latentEnvs := os.Environ()
numLatentEnvs := len(latentEnvs)
cniEnvs := delegateArgs.AsEnv()
Expect(cniEnvs).To(HaveLen(numLatentEnvs + 1))
Expect(inStringSlice("CNI_COMMAND=ADD", cniEnvs)).To(BeTrue())
})
AfterEach(func() {
os.Unsetenv("CNI_COMMAND")
})
})
Describe("inherited AsEnv", func() {
It("return nil string slice if we call AsEnv of inherited", func() {
inheritedArgs := invoke.ArgsFromEnv()
var nilSlice []string = nil
Expect(inheritedArgs.AsEnv()).To(Equal(nilSlice))
})
})
})
func inStringSlice(in string, slice []string) bool {
for _, s := range slice {
if in == s {
return true
}
}
return false
}

View File

@ -15,39 +15,75 @@
package invoke
import (
"fmt"
"context"
"os"
"strings"
"path/filepath"
"github.com/containernetworking/cni/pkg/types"
)
func DelegateAdd(delegatePlugin string, netconf []byte) (*types.Result, error) {
if os.Getenv("CNI_COMMAND") != "ADD" {
return nil, fmt.Errorf("CNI_COMMAND is not ADD")
func delegateCommon(delegatePlugin string, exec Exec) (string, Exec, error) {
if exec == nil {
exec = defaultExec
}
paths := strings.Split(os.Getenv("CNI_PATH"), ":")
paths := filepath.SplitList(os.Getenv("CNI_PATH"))
pluginPath, err := exec.FindInPath(delegatePlugin, paths)
if err != nil {
return "", nil, err
}
pluginPath, err := FindInPath(delegatePlugin, paths)
return pluginPath, exec, nil
}
// DelegateAdd calls the given delegate plugin with the CNI ADD action and
// JSON configuration
func DelegateAdd(ctx context.Context, delegatePlugin string, netconf []byte, exec Exec) (types.Result, error) {
pluginPath, realExec, err := delegateCommon(delegatePlugin, exec)
if err != nil {
return nil, err
}
return ExecPluginWithResult(pluginPath, netconf, ArgsFromEnv())
// DelegateAdd will override the original "CNI_COMMAND" env from process with ADD
return ExecPluginWithResult(ctx, pluginPath, netconf, delegateArgs("ADD"), realExec)
}
func DelegateDel(delegatePlugin string, netconf []byte) error {
if os.Getenv("CNI_COMMAND") != "DEL" {
return fmt.Errorf("CNI_COMMAND is not DEL")
}
// DelegateCheck calls the given delegate plugin with the CNI CHECK action and
// JSON configuration
func DelegateCheck(ctx context.Context, delegatePlugin string, netconf []byte, exec Exec) error {
return delegateNoResult(ctx, delegatePlugin, netconf, exec, "CHECK")
}
paths := strings.Split(os.Getenv("CNI_PATH"), ":")
pluginPath, err := FindInPath(delegatePlugin, paths)
func delegateNoResult(ctx context.Context, delegatePlugin string, netconf []byte, exec Exec, verb string) error {
pluginPath, realExec, err := delegateCommon(delegatePlugin, exec)
if err != nil {
return err
}
return ExecPluginWithoutResult(pluginPath, netconf, ArgsFromEnv())
return ExecPluginWithoutResult(ctx, pluginPath, netconf, delegateArgs(verb), realExec)
}
// DelegateDel calls the given delegate plugin with the CNI DEL action and
// JSON configuration
func DelegateDel(ctx context.Context, delegatePlugin string, netconf []byte, exec Exec) error {
return delegateNoResult(ctx, delegatePlugin, netconf, exec, "DEL")
}
// DelegateStatus calls the given delegate plugin with the CNI STATUS action and
// JSON configuration
func DelegateStatus(ctx context.Context, delegatePlugin string, netconf []byte, exec Exec) error {
return delegateNoResult(ctx, delegatePlugin, netconf, exec, "STATUS")
}
// DelegateGC calls the given delegate plugin with the CNI GC action and
// JSON configuration
func DelegateGC(ctx context.Context, delegatePlugin string, netconf []byte, exec Exec) error {
return delegateNoResult(ctx, delegatePlugin, netconf, exec, "GC")
}
// return CNIArgs used by delegation
func delegateArgs(action string) *DelegateArgs {
return &DelegateArgs{
Command: action,
}
}

270
pkg/invoke/delegate_test.go Normal file
View File

@ -0,0 +1,270 @@
// Copyright 2017 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package invoke_test
import (
"context"
"encoding/json"
"net"
"os"
"path/filepath"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/containernetworking/cni/pkg/invoke"
current "github.com/containernetworking/cni/pkg/types/100"
"github.com/containernetworking/cni/pkg/version"
"github.com/containernetworking/cni/plugins/test/noop/debug"
)
var _ = Describe("Delegate", func() {
var (
pluginName string
netConf []byte
debugFileName string
debugBehavior *debug.Debug
expectedResult *current.Result
ctx context.Context
)
BeforeEach(func() {
netConf, _ = json.Marshal(map[string]string{
"name": "delegate-test",
"cniVersion": version.Current(),
})
expectedResult = &current.Result{
CNIVersion: current.ImplementedSpecVersion,
IPs: []*current.IPConfig{
{
Address: net.IPNet{
IP: net.ParseIP("10.1.2.3"),
Mask: net.CIDRMask(24, 32),
},
},
},
}
expectedResultBytes, _ := json.Marshal(expectedResult)
debugFile, err := os.CreateTemp("", "cni_debug")
Expect(err).NotTo(HaveOccurred())
Expect(debugFile.Close()).To(Succeed())
debugFileName = debugFile.Name()
debugBehavior = &debug.Debug{
ReportResult: string(expectedResultBytes),
}
Expect(debugBehavior.WriteDebug(debugFileName)).To(Succeed())
pluginName = "noop"
ctx = context.TODO()
os.Setenv("CNI_ARGS", "DEBUG="+debugFileName)
os.Setenv("CNI_PATH", filepath.Dir(pathToPlugin))
os.Setenv("CNI_NETNS", "/tmp/some/netns/path")
os.Setenv("CNI_IFNAME", "eth7")
os.Setenv("CNI_CONTAINERID", "container")
})
AfterEach(func() {
os.RemoveAll(debugFileName)
for _, k := range []string{"CNI_COMMAND", "CNI_ARGS", "CNI_PATH", "CNI_NETNS", "CNI_IFNAME"} {
os.Unsetenv(k)
}
})
Describe("DelegateAdd", func() {
BeforeEach(func() {
os.Setenv("CNI_COMMAND", "ADD")
})
It("finds and execs the named plugin", func() {
result, err := invoke.DelegateAdd(ctx, pluginName, netConf, nil)
Expect(err).NotTo(HaveOccurred())
Expect(result).To(Equal(expectedResult))
pluginInvocation, err := debug.ReadDebug(debugFileName)
Expect(err).NotTo(HaveOccurred())
Expect(pluginInvocation.Command).To(Equal("ADD"))
Expect(pluginInvocation.CmdArgs.IfName).To(Equal("eth7"))
})
Context("if the ADD delegation runs on an existing non-ADD command, ", func() {
BeforeEach(func() {
os.Setenv("CNI_COMMAND", "NOPE")
})
It("aborts and returns a useful error", func() {
result, err := invoke.DelegateAdd(ctx, pluginName, netConf, nil)
Expect(err).NotTo(HaveOccurred())
Expect(result).To(Equal(expectedResult))
pluginInvocation, err := debug.ReadDebug(debugFileName)
Expect(err).NotTo(HaveOccurred())
Expect(pluginInvocation.Command).To(Equal("ADD"))
Expect(pluginInvocation.CmdArgs.IfName).To(Equal("eth7"))
// check the original env
Expect(os.Getenv("CNI_COMMAND")).To(Equal("NOPE"))
})
})
Context("when the plugin cannot be found", func() {
BeforeEach(func() {
pluginName = "non-existent-plugin"
})
It("returns a useful error", func() {
_, err := invoke.DelegateAdd(ctx, pluginName, netConf, nil)
Expect(err).To(MatchError(HavePrefix("failed to find plugin")))
})
})
})
Describe("DelegateCheck", func() {
BeforeEach(func() {
os.Setenv("CNI_COMMAND", "CHECK")
})
It("finds and execs the named plugin", func() {
err := invoke.DelegateCheck(ctx, pluginName, netConf, nil)
Expect(err).NotTo(HaveOccurred())
pluginInvocation, err := debug.ReadDebug(debugFileName)
Expect(err).NotTo(HaveOccurred())
Expect(pluginInvocation.Command).To(Equal("CHECK"))
Expect(pluginInvocation.CmdArgs.IfName).To(Equal("eth7"))
})
Context("if the CHECK delegation runs on an existing non-CHECK command", func() {
BeforeEach(func() {
os.Setenv("CNI_COMMAND", "NOPE")
})
It("aborts and returns a useful error", func() {
err := invoke.DelegateCheck(ctx, pluginName, netConf, nil)
Expect(err).NotTo(HaveOccurred())
pluginInvocation, err := debug.ReadDebug(debugFileName)
Expect(err).NotTo(HaveOccurred())
Expect(pluginInvocation.Command).To(Equal("CHECK"))
Expect(pluginInvocation.CmdArgs.IfName).To(Equal("eth7"))
// check the original env
Expect(os.Getenv("CNI_COMMAND")).To(Equal("NOPE"))
})
})
Context("when the plugin cannot be found", func() {
BeforeEach(func() {
pluginName = "non-existent-plugin"
})
It("returns a useful error", func() {
err := invoke.DelegateCheck(ctx, pluginName, netConf, nil)
Expect(err).To(MatchError(HavePrefix("failed to find plugin")))
})
})
})
Describe("DelegateDel", func() {
BeforeEach(func() {
os.Setenv("CNI_COMMAND", "DEL")
})
It("finds and execs the named plugin", func() {
err := invoke.DelegateDel(ctx, pluginName, netConf, nil)
Expect(err).NotTo(HaveOccurred())
pluginInvocation, err := debug.ReadDebug(debugFileName)
Expect(err).NotTo(HaveOccurred())
Expect(pluginInvocation.Command).To(Equal("DEL"))
Expect(pluginInvocation.CmdArgs.IfName).To(Equal("eth7"))
})
Context("if the DEL delegation runs on an existing non-DEL command", func() {
BeforeEach(func() {
os.Setenv("CNI_COMMAND", "NOPE")
})
It("aborts and returns a useful error", func() {
err := invoke.DelegateDel(ctx, pluginName, netConf, nil)
Expect(err).NotTo(HaveOccurred())
pluginInvocation, err := debug.ReadDebug(debugFileName)
Expect(err).NotTo(HaveOccurred())
Expect(pluginInvocation.Command).To(Equal("DEL"))
Expect(pluginInvocation.CmdArgs.IfName).To(Equal("eth7"))
// check the original env
Expect(os.Getenv("CNI_COMMAND")).To(Equal("NOPE"))
})
})
Context("when the plugin cannot be found", func() {
BeforeEach(func() {
pluginName = "non-existent-plugin"
})
It("returns a useful error", func() {
err := invoke.DelegateDel(ctx, pluginName, netConf, nil)
Expect(err).To(MatchError(HavePrefix("failed to find plugin")))
})
})
})
Describe("DelegateStatus", func() {
BeforeEach(func() {
os.Setenv("CNI_COMMAND", "STATUS")
})
It("finds and execs the named plugin", func() {
err := invoke.DelegateStatus(ctx, pluginName, netConf, nil)
Expect(err).NotTo(HaveOccurred())
pluginInvocation, err := debug.ReadDebug(debugFileName)
Expect(err).NotTo(HaveOccurred())
Expect(pluginInvocation.Command).To(Equal("STATUS"))
})
Context("if the STATUS delegation runs on an existing non-STATUS command", func() {
BeforeEach(func() {
os.Setenv("CNI_COMMAND", "NOPE")
})
It("aborts and returns a useful error", func() {
err := invoke.DelegateStatus(ctx, pluginName, netConf, nil)
Expect(err).NotTo(HaveOccurred())
pluginInvocation, err := debug.ReadDebug(debugFileName)
Expect(err).NotTo(HaveOccurred())
Expect(pluginInvocation.Command).To(Equal("STATUS"))
// check the original env
Expect(os.Getenv("CNI_COMMAND")).To(Equal("NOPE"))
})
})
Context("when the plugin cannot be found", func() {
BeforeEach(func() {
pluginName = "non-existent-plugin"
})
It("returns a useful error", func() {
err := invoke.DelegateStatus(ctx, pluginName, netConf, nil)
Expect(err).To(MatchError(HavePrefix("failed to find plugin")))
})
})
})
})

View File

@ -15,61 +15,173 @@
package invoke
import (
"bytes"
"context"
"encoding/json"
"fmt"
"os"
"os/exec"
"github.com/containernetworking/cni/pkg/types"
"github.com/containernetworking/cni/pkg/types/create"
"github.com/containernetworking/cni/pkg/version"
)
func pluginErr(err error, output []byte) error {
if _, ok := err.(*exec.ExitError); ok {
emsg := types.Error{}
if perr := json.Unmarshal(output, &emsg); perr != nil {
return fmt.Errorf("netplugin failed but error parsing its diagnostic message %q: %v", string(output), perr)
}
details := ""
if emsg.Details != "" {
details = fmt.Sprintf("; %v", emsg.Details)
}
return fmt.Errorf("%v%v", emsg.Msg, details)
}
return err
// Exec is an interface encapsulates all operations that deal with finding
// and executing a CNI plugin. Tests may provide a fake implementation
// to avoid writing fake plugins to temporary directories during the test.
type Exec interface {
ExecPlugin(ctx context.Context, pluginPath string, stdinData []byte, environ []string) ([]byte, error)
FindInPath(plugin string, paths []string) (string, error)
Decode(jsonBytes []byte) (version.PluginInfo, error)
}
func ExecPluginWithResult(pluginPath string, netconf []byte, args CNIArgs) (*types.Result, error) {
stdoutBytes, err := execPlugin(pluginPath, netconf, args)
// Plugin must return result in same version as specified in netconf; but
// for backwards compatibility reasons if the result version is empty use
// config version (rather than technically correct 0.1.0).
// https://github.com/containernetworking/cni/issues/895
func fixupResultVersion(netconf, result []byte) (string, []byte, error) {
versionDecoder := &version.ConfigDecoder{}
confVersion, err := versionDecoder.Decode(netconf)
if err != nil {
return "", nil, err
}
var rawResult map[string]interface{}
if err := json.Unmarshal(result, &rawResult); err != nil {
return "", nil, fmt.Errorf("failed to unmarshal raw result: %w", err)
}
// plugin output of "null" is successfully unmarshalled, but results in a nil
// map which causes a panic when the confVersion is assigned below.
if rawResult == nil {
rawResult = make(map[string]interface{})
}
// Manually decode Result version; we need to know whether its cniVersion
// is empty, while built-in decoders (correctly) substitute 0.1.0 for an
// empty version per the CNI spec.
if resultVerRaw, ok := rawResult["cniVersion"]; ok {
resultVer, ok := resultVerRaw.(string)
if ok && resultVer != "" {
return resultVer, result, nil
}
}
// If the cniVersion is not present or empty, assume the result is
// the same CNI spec version as the config
rawResult["cniVersion"] = confVersion
newBytes, err := json.Marshal(rawResult)
if err != nil {
return "", nil, fmt.Errorf("failed to remarshal fixed result: %w", err)
}
return confVersion, newBytes, nil
}
// For example, a testcase could pass an instance of the following fakeExec
// object to ExecPluginWithResult() to verify the incoming stdin and environment
// and provide a tailored response:
//
// import (
// "encoding/json"
// "path"
// "strings"
// )
//
// type fakeExec struct {
// version.PluginDecoder
// }
//
// func (f *fakeExec) ExecPlugin(pluginPath string, stdinData []byte, environ []string) ([]byte, error) {
// net := &types.NetConf{}
// err := json.Unmarshal(stdinData, net)
// if err != nil {
// return nil, fmt.Errorf("failed to unmarshal configuration: %v", err)
// }
// pluginName := path.Base(pluginPath)
// if pluginName != net.Type {
// return nil, fmt.Errorf("plugin name %q did not match config type %q", pluginName, net.Type)
// }
// for _, e := range environ {
// // Check environment for forced failure request
// parts := strings.Split(e, "=")
// if len(parts) > 0 && parts[0] == "FAIL" {
// return nil, fmt.Errorf("failed to execute plugin %s", pluginName)
// }
// }
// return []byte("{\"CNIVersion\":\"0.4.0\"}"), nil
// }
//
// func (f *fakeExec) FindInPath(plugin string, paths []string) (string, error) {
// if len(paths) > 0 {
// return path.Join(paths[0], plugin), nil
// }
// return "", fmt.Errorf("failed to find plugin %s in paths %v", plugin, paths)
// }
func ExecPluginWithResult(ctx context.Context, pluginPath string, netconf []byte, args CNIArgs, exec Exec) (types.Result, error) {
if exec == nil {
exec = defaultExec
}
stdoutBytes, err := exec.ExecPlugin(ctx, pluginPath, netconf, args.AsEnv())
if err != nil {
return nil, err
}
res := &types.Result{}
err = json.Unmarshal(stdoutBytes, res)
return res, err
resultVersion, fixedBytes, err := fixupResultVersion(netconf, stdoutBytes)
if err != nil {
return nil, err
}
return create.Create(resultVersion, fixedBytes)
}
func ExecPluginWithoutResult(pluginPath string, netconf []byte, args CNIArgs) error {
_, err := execPlugin(pluginPath, netconf, args)
func ExecPluginWithoutResult(ctx context.Context, pluginPath string, netconf []byte, args CNIArgs, exec Exec) error {
if exec == nil {
exec = defaultExec
}
_, err := exec.ExecPlugin(ctx, pluginPath, netconf, args.AsEnv())
return err
}
func execPlugin(pluginPath string, netconf []byte, args CNIArgs) ([]byte, error) {
stdout := &bytes.Buffer{}
c := exec.Cmd{
Env: args.AsEnv(),
Path: pluginPath,
Args: []string{pluginPath},
Stdin: bytes.NewBuffer(netconf),
Stdout: stdout,
Stderr: os.Stderr,
// GetVersionInfo returns the version information available about the plugin.
// For recent-enough plugins, it uses the information returned by the VERSION
// command. For older plugins which do not recognize that command, it reports
// version 0.1.0
func GetVersionInfo(ctx context.Context, pluginPath string, exec Exec) (version.PluginInfo, error) {
if exec == nil {
exec = defaultExec
}
if err := c.Run(); err != nil {
return nil, pluginErr(err, stdout.Bytes())
args := &Args{
Command: "VERSION",
// set fake values required by plugins built against an older version of skel
NetNS: "dummy",
IfName: "dummy",
Path: "dummy",
}
stdin := []byte(fmt.Sprintf(`{"cniVersion":%q}`, version.Current()))
stdoutBytes, err := exec.ExecPlugin(ctx, pluginPath, stdin, args.AsEnv())
if err != nil {
if err.Error() == "unknown CNI_COMMAND: VERSION" {
return version.PluginSupports("0.1.0"), nil
}
return nil, err
}
return stdout.Bytes(), nil
return exec.Decode(stdoutBytes)
}
// DefaultExec is an object that implements the Exec interface which looks
// for and executes plugins from disk.
type DefaultExec struct {
*RawExec
version.PluginDecoder
}
// DefaultExec implements the Exec interface
var _ Exec = &DefaultExec{}
var defaultExec = &DefaultExec{
RawExec: &RawExec{Stderr: os.Stderr},
}

215
pkg/invoke/exec_test.go Normal file
View File

@ -0,0 +1,215 @@
// Copyright 2016 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package invoke_test
import (
"context"
"encoding/json"
"errors"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/containernetworking/cni/pkg/invoke"
"github.com/containernetworking/cni/pkg/invoke/fakes"
current "github.com/containernetworking/cni/pkg/types/100"
"github.com/containernetworking/cni/pkg/version"
)
var _ = Describe("Executing a plugin, unit tests", func() {
var (
pluginExec invoke.Exec
rawExec *fakes.RawExec
versionDecoder *fakes.VersionDecoder
pluginPath string
netconf []byte
cniargs *fakes.CNIArgs
ctx context.Context
)
BeforeEach(func() {
rawExec = &fakes.RawExec{}
rawExec.ExecPluginCall.Returns.ResultBytes = []byte(`{ "cniVersion": "0.3.1", "ips": [ { "version": "4", "address": "1.2.3.4/24" } ] }`)
versionDecoder = &fakes.VersionDecoder{}
versionDecoder.DecodeCall.Returns.PluginInfo = version.PluginSupports("0.42.0")
pluginExec = &struct {
*fakes.RawExec
*fakes.VersionDecoder
}{
RawExec: rawExec,
VersionDecoder: versionDecoder,
}
pluginPath = "/some/plugin/path"
netconf = []byte(`{ "some": "stdin", "cniVersion": "0.3.1" }`)
cniargs = &fakes.CNIArgs{}
cniargs.AsEnvCall.Returns.Env = []string{"SOME=ENV"}
ctx = context.TODO()
})
Describe("returning a result", func() {
It("unmarshals the result bytes into the Result type", func() {
r, err := invoke.ExecPluginWithResult(ctx, pluginPath, netconf, cniargs, pluginExec)
Expect(err).NotTo(HaveOccurred())
result, err := current.GetResult(r)
Expect(err).NotTo(HaveOccurred())
Expect(result.IPs).To(HaveLen(1))
Expect(result.IPs[0].Address.IP.String()).To(Equal("1.2.3.4"))
})
It("passes its arguments through to the rawExec", func() {
_, err := invoke.ExecPluginWithResult(ctx, pluginPath, netconf, cniargs, pluginExec)
Expect(err).NotTo(HaveOccurred())
Expect(rawExec.ExecPluginCall.Received.PluginPath).To(Equal(pluginPath))
Expect(rawExec.ExecPluginCall.Received.StdinData).To(Equal(netconf))
Expect(rawExec.ExecPluginCall.Received.Environ).To(Equal([]string{"SOME=ENV"}))
})
Context("when the rawExec fails", func() {
BeforeEach(func() {
rawExec.ExecPluginCall.Returns.Error = errors.New("banana")
})
It("returns the error", func() {
_, err := invoke.ExecPluginWithResult(ctx, pluginPath, netconf, cniargs, pluginExec)
Expect(err).To(MatchError("banana"))
})
})
It("returns an error using the default exec interface", func() {
// pluginPath should not exist on-disk so we expect an error.
// This test simply tests that the default exec handler
// is run when the exec interface is nil.
_, err := invoke.ExecPluginWithResult(ctx, pluginPath, netconf, cniargs, nil)
Expect(err).To(HaveOccurred())
})
It("assumes config version if result version is missing", func() {
rawExec.ExecPluginCall.Returns.ResultBytes = []byte(`{ "ips": [ { "version": "4", "address": "1.2.3.4/24" } ] }`)
r, err := invoke.ExecPluginWithResult(ctx, pluginPath, netconf, cniargs, pluginExec)
Expect(err).NotTo(HaveOccurred())
Expect(r.Version()).To(Equal("0.3.1"))
result, err := current.GetResult(r)
Expect(err).NotTo(HaveOccurred())
Expect(result.IPs).To(HaveLen(1))
Expect(result.IPs[0].Address.IP.String()).To(Equal("1.2.3.4"))
})
It("assumes config version if result version is empty", func() {
rawExec.ExecPluginCall.Returns.ResultBytes = []byte(`{ "cniVersion": "", "ips": [ { "version": "4", "address": "1.2.3.4/24" } ] }`)
r, err := invoke.ExecPluginWithResult(ctx, pluginPath, netconf, cniargs, pluginExec)
Expect(err).NotTo(HaveOccurred())
Expect(r.Version()).To(Equal("0.3.1"))
result, err := current.GetResult(r)
Expect(err).NotTo(HaveOccurred())
Expect(result.IPs).To(HaveLen(1))
Expect(result.IPs[0].Address.IP.String()).To(Equal("1.2.3.4"))
})
It("assumes 0.1.0 if config and result version are empty", func() {
netconf = []byte(`{ "some": "stdin" }`)
rawExec.ExecPluginCall.Returns.ResultBytes = []byte(`{ "some": "version-info" }`)
r, err := invoke.ExecPluginWithResult(ctx, pluginPath, netconf, cniargs, pluginExec)
Expect(err).NotTo(HaveOccurred())
Expect(r.Version()).To(Equal("0.1.0"))
})
})
Describe("without returning a result", func() {
It("passes its arguments through to the rawExec", func() {
err := invoke.ExecPluginWithoutResult(ctx, pluginPath, netconf, cniargs, pluginExec)
Expect(err).NotTo(HaveOccurred())
Expect(rawExec.ExecPluginCall.Received.PluginPath).To(Equal(pluginPath))
Expect(rawExec.ExecPluginCall.Received.StdinData).To(Equal(netconf))
Expect(rawExec.ExecPluginCall.Received.Environ).To(Equal([]string{"SOME=ENV"}))
})
Context("when the rawExec fails", func() {
BeforeEach(func() {
rawExec.ExecPluginCall.Returns.Error = errors.New("banana")
})
It("returns the error", func() {
err := invoke.ExecPluginWithoutResult(ctx, pluginPath, netconf, cniargs, pluginExec)
Expect(err).To(MatchError("banana"))
})
})
It("returns an error using the default exec interface", func() {
// pluginPath should not exist on-disk so we expect an error.
// This test simply tests that the default exec handler
// is run when the exec interface is nil.
err := invoke.ExecPluginWithoutResult(ctx, pluginPath, netconf, cniargs, nil)
Expect(err).To(HaveOccurred())
})
})
Describe("discovering the plugin version", func() {
BeforeEach(func() {
rawExec.ExecPluginCall.Returns.ResultBytes = []byte(`{ "some": "version-info" }`)
})
It("execs the plugin with the command VERSION", func() {
_, err := invoke.GetVersionInfo(ctx, pluginPath, pluginExec)
Expect(err).NotTo(HaveOccurred())
Expect(rawExec.ExecPluginCall.Received.PluginPath).To(Equal(pluginPath))
Expect(rawExec.ExecPluginCall.Received.Environ).To(ContainElement("CNI_COMMAND=VERSION"))
expectedStdin, _ := json.Marshal(map[string]string{"cniVersion": version.Current()})
Expect(rawExec.ExecPluginCall.Received.StdinData).To(MatchJSON(expectedStdin))
})
It("decodes and returns the version info", func() {
versionInfo, err := invoke.GetVersionInfo(ctx, pluginPath, pluginExec)
Expect(err).NotTo(HaveOccurred())
Expect(versionInfo.SupportedVersions()).To(Equal([]string{"0.42.0"}))
Expect(versionDecoder.DecodeCall.Received.JSONBytes).To(MatchJSON(`{ "some": "version-info" }`))
})
Context("when the rawExec fails", func() {
BeforeEach(func() {
rawExec.ExecPluginCall.Returns.Error = errors.New("banana")
})
It("returns the error", func() {
_, err := invoke.GetVersionInfo(ctx, pluginPath, pluginExec)
Expect(err).To(MatchError("banana"))
})
})
Context("when the plugin is too old to recognize the VERSION command", func() {
BeforeEach(func() {
rawExec.ExecPluginCall.Returns.Error = errors.New("unknown CNI_COMMAND: VERSION")
})
It("interprets the error as a 0.1.0 version", func() {
versionInfo, err := invoke.GetVersionInfo(ctx, pluginPath, pluginExec)
Expect(err).NotTo(HaveOccurred())
Expect(versionInfo.SupportedVersions()).To(ConsistOf("0.1.0"))
})
It("sets dummy values for env vars required by very old plugins", func() {
_, err := invoke.GetVersionInfo(ctx, pluginPath, pluginExec)
Expect(err).NotTo(HaveOccurred())
env := rawExec.ExecPluginCall.Received.Environ
Expect(env).To(ContainElement("CNI_NETNS=dummy"))
Expect(env).To(ContainElement("CNI_IFNAME=dummy"))
Expect(env).To(ContainElement("CNI_PATH=dummy"))
})
})
})
})

View File

@ -0,0 +1,27 @@
// Copyright 2016 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package fakes
type CNIArgs struct {
AsEnvCall struct {
Returns struct {
Env []string
}
}
}
func (a *CNIArgs) AsEnv() []string {
return a.AsEnvCall.Returns.Env
}

View File

@ -0,0 +1,54 @@
// Copyright 2016 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package fakes
import "context"
type RawExec struct {
ExecPluginCall struct {
Received struct {
PluginPath string
StdinData []byte
Environ []string
}
Returns struct {
ResultBytes []byte
Error error
}
}
FindInPathCall struct {
Received struct {
Plugin string
Paths []string
}
Returns struct {
Path string
Error error
}
}
}
func (e *RawExec) ExecPlugin(ctx context.Context, pluginPath string, stdinData []byte, environ []string) ([]byte, error) {
e.ExecPluginCall.Received.PluginPath = pluginPath
e.ExecPluginCall.Received.StdinData = stdinData
e.ExecPluginCall.Received.Environ = environ
return e.ExecPluginCall.Returns.ResultBytes, e.ExecPluginCall.Returns.Error
}
func (e *RawExec) FindInPath(plugin string, paths []string) (string, error) {
e.FindInPathCall.Received.Plugin = plugin
e.FindInPathCall.Received.Paths = paths
return e.FindInPathCall.Returns.Path, e.FindInPathCall.Returns.Error
}

View File

@ -12,23 +12,23 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package ns_test
package fakes
import (
"math/rand"
"runtime"
import "github.com/containernetworking/cni/pkg/version"
. "github.com/onsi/ginkgo"
"github.com/onsi/ginkgo/config"
. "github.com/onsi/gomega"
"testing"
)
func TestNs(t *testing.T) {
rand.Seed(config.GinkgoConfig.RandomSeed)
runtime.LockOSThread()
RegisterFailHandler(Fail)
RunSpecs(t, "pkg/ns Suite")
type VersionDecoder struct {
DecodeCall struct {
Received struct {
JSONBytes []byte
}
Returns struct {
PluginInfo version.PluginInfo
Error error
}
}
}
func (e *VersionDecoder) Decode(jsonData []byte) (version.PluginInfo, error) {
e.DecodeCall.Received.JSONBytes = jsonData
return e.DecodeCall.Returns.PluginInfo, e.DecodeCall.Returns.Error
}

View File

@ -18,6 +18,7 @@ import (
"fmt"
"os"
"path/filepath"
"strings"
)
// FindInPath returns the full path of the plugin by searching in the provided path
@ -26,22 +27,22 @@ func FindInPath(plugin string, paths []string) (string, error) {
return "", fmt.Errorf("no plugin name provided")
}
if strings.ContainsRune(plugin, os.PathSeparator) {
return "", fmt.Errorf("invalid plugin name: %s", plugin)
}
if len(paths) == 0 {
return "", fmt.Errorf("no paths provided")
}
var fullpath string
for _, path := range paths {
full := filepath.Join(path, plugin)
if fi, err := os.Stat(full); err == nil && fi.Mode().IsRegular() {
fullpath = full
break
for _, fe := range ExecutableFileExtensions {
fullpath := filepath.Join(path, plugin) + fe
if fi, err := os.Stat(fullpath); err == nil && fi.Mode().IsRegular() {
return fullpath, nil
}
}
}
if fullpath == "" {
return "", fmt.Errorf("failed to find plugin %q in path %s", plugin, paths)
}
return fullpath, nil
return "", fmt.Errorf("failed to find plugin %q in path %s", plugin, paths)
}

View File

@ -16,32 +16,49 @@ package invoke_test
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/containernetworking/cni/pkg/invoke"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var _ = Describe("FindInPath", func() {
var (
multiplePaths []string
pluginName string
pluginDir string
anotherTempDir string
multiplePaths []string
pluginName string
plugin2NameWithExt string
plugin2NameWithoutExt string
pluginDir string
anotherTempDir string
)
BeforeEach(func() {
tempDir, err := ioutil.TempDir("", "cni-find")
tempDir, err := os.MkdirTemp("", "cni-find")
Expect(err).NotTo(HaveOccurred())
plugin, err := ioutil.TempFile(tempDir, "a-cni-plugin")
plugin, err := os.CreateTemp(tempDir, "a-cni-plugin")
Expect(err).NotTo(HaveOccurred())
anotherTempDir, err = ioutil.TempDir("", "nothing-here")
plugin2Name := "a-plugin-with-extension" + invoke.ExecutableFileExtensions[0]
plugin2, err := os.Create(filepath.Join(tempDir, plugin2Name))
Expect(err).NotTo(HaveOccurred())
anotherTempDir, err = os.MkdirTemp("", "nothing-here")
Expect(err).NotTo(HaveOccurred())
multiplePaths = []string{anotherTempDir, tempDir}
pluginDir, pluginName = filepath.Split(plugin.Name())
_, plugin2NameWithExt = filepath.Split(plugin2.Name())
plugin2NameWithoutExt = strings.Split(plugin2NameWithExt, ".")[0]
})
AfterEach(func() {
os.RemoveAll(pluginDir)
os.RemoveAll(anotherTempDir)
})
Context("when multiple paths are provided", func() {
@ -52,6 +69,14 @@ var _ = Describe("FindInPath", func() {
})
})
Context("when a plugin name without its file name extension is provided", func() {
It("returns the path to the plugin, including its extension", func() {
pluginPath, err := invoke.FindInPath(plugin2NameWithoutExt, multiplePaths)
Expect(err).NotTo(HaveOccurred())
Expect(pluginPath).To(Equal(filepath.Join(pluginDir, plugin2NameWithExt)))
})
})
Context("when an error occurs", func() {
Context("when no paths are provided", func() {
It("returns an error noting no paths were provided", func() {
@ -74,5 +99,13 @@ var _ = Describe("FindInPath", func() {
Expect(err).To(MatchError(fmt.Sprintf("failed to find plugin %q in path %s", pluginName, pathsWithNothing)))
})
})
Context("When the plugin contains a directory separator", func() {
It("returns an error", func() {
bogusPlugin := ".." + string(os.PathSeparator) + "pluginname"
_, err := invoke.FindInPath(bogusPlugin, []string{anotherTempDir})
Expect(err).To(MatchError("invalid plugin name: " + bogusPlugin))
})
})
})
})

View File

@ -0,0 +1,133 @@
// Copyright 2016 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package invoke_test
import (
"context"
"os"
"path/filepath"
"runtime"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/containernetworking/cni/pkg/invoke"
"github.com/containernetworking/cni/pkg/version"
"github.com/containernetworking/cni/pkg/version/testhelpers"
)
var _ = Describe("GetVersion, integration tests", func() {
var (
pluginDir string
pluginPath string
)
BeforeEach(func() {
pluginDir, err := os.MkdirTemp("", "plugins")
Expect(err).NotTo(HaveOccurred())
pluginPath = filepath.Join(pluginDir, "test-plugin")
if runtime.GOOS == "windows" {
pluginPath += ".exe"
}
})
AfterEach(func() {
Expect(os.RemoveAll(pluginDir)).To(Succeed())
})
DescribeTable("correctly reporting plugin versions",
func(gitRef string, pluginSource string, expectedVersions version.PluginInfo) {
Expect(testhelpers.BuildAt([]byte(pluginSource), gitRef, pluginPath)).To(Succeed())
versionInfo, err := invoke.GetVersionInfo(context.TODO(), pluginPath, nil)
Expect(err).NotTo(HaveOccurred())
Expect(versionInfo.SupportedVersions()).To(ConsistOf(expectedVersions.SupportedVersions()))
},
Entry("historical: before VERSION was introduced",
git_ref_v010, plugin_source_no_custom_versions,
version.PluginSupports("0.1.0"),
),
Entry("historical: when VERSION was introduced but plugins couldn't customize it",
git_ref_v020_no_custom_versions, plugin_source_no_custom_versions,
version.PluginSupports("0.1.0", "0.2.0"),
),
Entry("historical: when plugins started reporting their own version list",
git_ref_v020_custom_versions, plugin_source_v020_custom_versions,
version.PluginSupports("0.2.0", "0.999.0"),
),
Entry("historical: before CHECK was introduced",
git_ref_v031, plugin_source_v020_custom_versions,
version.PluginSupports("0.2.0", "0.999.0"),
),
// this entry tracks the current behavior. Before you change it, ensure
// that its previous behavior is captured in the most recent "historical" entry
Entry("current",
"HEAD", plugin_source_v040_check,
version.PluginSupports("0.2.0", "0.4.0", "0.999.0"),
),
)
})
// A 0.4.0 plugin that supports CHECK
const plugin_source_v040_check = `package main
import (
"github.com/containernetworking/cni/pkg/skel"
"github.com/containernetworking/cni/pkg/version"
"fmt"
)
func c(_ *skel.CmdArgs) error { fmt.Println("{}"); return nil }
func main() { skel.PluginMain(c, c, c, version.PluginSupports("0.2.0", "0.4.0", "0.999.0"), "") }
`
const git_ref_v031 = "909fe7d"
// a 0.2.0 plugin that can report its own versions
const plugin_source_v020_custom_versions = `package main
import (
"github.com/containernetworking/cni/pkg/skel"
"github.com/containernetworking/cni/pkg/version"
"fmt"
)
func c(_ *skel.CmdArgs) error { fmt.Println("{}"); return nil }
func main() { skel.PluginMain(c, c, version.PluginSupports("0.2.0", "0.999.0")) }
`
const git_ref_v020_custom_versions = "bf31ed15"
// a minimal 0.1.0 / 0.2.0 plugin that cannot report it's own version support
const plugin_source_no_custom_versions = `package main
import "github.com/containernetworking/cni/pkg/skel"
import "fmt"
func c(_ *skel.CmdArgs) error { fmt.Println("{}"); return nil }
func main() { skel.PluginMain(c, c) }
`
const (
git_ref_v010 = "2c482f4"
git_ref_v020_no_custom_versions = "349d66d"
)

View File

@ -15,13 +15,31 @@
package invoke_test
import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"testing"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/onsi/gomega/gexec"
)
func TestInvoke(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Invoke Suite")
}
const packagePath = "github.com/containernetworking/cni/plugins/test/noop"
var pathToPlugin string
var _ = SynchronizedBeforeSuite(func() []byte {
var err error
pathToPlugin, err = gexec.Build(packagePath)
Expect(err).NotTo(HaveOccurred())
return []byte(pathToPlugin)
}, func(crossNodeData []byte) {
pathToPlugin = string(crossNodeData)
})
var _ = SynchronizedAfterSuite(func() {}, func() {
gexec.CleanupBuildArtifacts()
})

21
pkg/invoke/os_unix.go Normal file
View File

@ -0,0 +1,21 @@
// Copyright 2016 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
// +build darwin dragonfly freebsd linux netbsd openbsd solaris
package invoke
// Valid file extensions for plugin executables.
var ExecutableFileExtensions = []string{""}

18
pkg/invoke/os_windows.go Normal file
View File

@ -0,0 +1,18 @@
// Copyright 2016 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package invoke
// Valid file extensions for plugin executables.
var ExecutableFileExtensions = []string{".exe", ""}

88
pkg/invoke/raw_exec.go Normal file
View File

@ -0,0 +1,88 @@
// Copyright 2016 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package invoke
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"os/exec"
"strings"
"time"
"github.com/containernetworking/cni/pkg/types"
)
type RawExec struct {
Stderr io.Writer
}
func (e *RawExec) ExecPlugin(ctx context.Context, pluginPath string, stdinData []byte, environ []string) ([]byte, error) {
stdout := &bytes.Buffer{}
stderr := &bytes.Buffer{}
c := exec.CommandContext(ctx, pluginPath)
c.Env = environ
c.Stdin = bytes.NewBuffer(stdinData)
c.Stdout = stdout
c.Stderr = stderr
// Retry the command on "text file busy" errors
for i := 0; i <= 5; i++ {
err := c.Run()
// Command succeeded
if err == nil {
break
}
// If the plugin is currently about to be written, then we wait a
// second and try it again
if strings.Contains(err.Error(), "text file busy") {
time.Sleep(time.Second)
continue
}
// All other errors except than the busy text file
return nil, e.pluginErr(err, stdout.Bytes(), stderr.Bytes())
}
// Copy stderr to caller's buffer in case plugin printed to both
// stdout and stderr for some reason. Ignore failures as stderr is
// only informational.
if e.Stderr != nil && stderr.Len() > 0 {
_, _ = stderr.WriteTo(e.Stderr)
}
return stdout.Bytes(), nil
}
func (e *RawExec) pluginErr(err error, stdout, stderr []byte) error {
emsg := types.Error{}
if len(stdout) == 0 {
if len(stderr) == 0 {
emsg.Msg = fmt.Sprintf("netplugin failed with no error message: %v", err)
} else {
emsg.Msg = fmt.Sprintf("netplugin failed: %q", string(stderr))
}
} else if perr := json.Unmarshal(stdout, &emsg); perr != nil {
emsg.Msg = fmt.Sprintf("netplugin failed but error parsing its diagnostic message %q: %v", string(stdout), perr)
}
return &emsg
}
func (e *RawExec) FindInPath(plugin string, paths []string) (string, error) {
return FindInPath(plugin, paths)
}

149
pkg/invoke/raw_exec_test.go Normal file
View File

@ -0,0 +1,149 @@
// Copyright 2016 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package invoke_test
import (
"bytes"
"context"
"os"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/containernetworking/cni/pkg/invoke"
noop_debug "github.com/containernetworking/cni/plugins/test/noop/debug"
)
var _ = Describe("RawExec", func() {
var (
debugFileName string
debug *noop_debug.Debug
environ []string
stdin []byte
execer *invoke.RawExec
ctx context.Context
)
const reportResult = `{ "some": "result" }`
BeforeEach(func() {
debugFile, err := os.CreateTemp("", "cni_debug")
Expect(err).NotTo(HaveOccurred())
Expect(debugFile.Close()).To(Succeed())
debugFileName = debugFile.Name()
debug = &noop_debug.Debug{
ReportResult: reportResult,
ReportStderr: "some stderr message",
}
Expect(debug.WriteDebug(debugFileName)).To(Succeed())
environ = []string{
"CNI_COMMAND=ADD",
"CNI_CONTAINERID=some-container-id",
"CNI_ARGS=DEBUG=" + debugFileName,
"CNI_NETNS=/some/netns/path",
"CNI_PATH=/some/bin/path",
"CNI_IFNAME=some-eth0",
}
stdin = []byte(`{"name": "raw-exec-test", "some":"stdin-json", "cniVersion": "0.3.1"}`)
execer = &invoke.RawExec{}
ctx = context.TODO()
})
AfterEach(func() {
Expect(os.Remove(debugFileName)).To(Succeed())
})
It("runs the plugin with the given stdin and environment", func() {
_, err := execer.ExecPlugin(ctx, pathToPlugin, stdin, environ)
Expect(err).NotTo(HaveOccurred())
debug, err := noop_debug.ReadDebug(debugFileName)
Expect(err).NotTo(HaveOccurred())
Expect(debug.Command).To(Equal("ADD"))
Expect(debug.CmdArgs.StdinData).To(Equal(stdin))
Expect(debug.CmdArgs.Netns).To(Equal("/some/netns/path"))
})
It("returns the resulting stdout as bytes", func() {
resultBytes, err := execer.ExecPlugin(ctx, pathToPlugin, stdin, environ)
Expect(err).NotTo(HaveOccurred())
Expect(resultBytes).To(BeEquivalentTo(reportResult))
})
Context("when the Stderr writer is set", func() {
var stderrBuffer *bytes.Buffer
BeforeEach(func() {
stderrBuffer = &bytes.Buffer{}
execer.Stderr = stderrBuffer
})
It("forwards any stderr bytes to the Stderr writer", func() {
_, err := execer.ExecPlugin(ctx, pathToPlugin, stdin, environ)
Expect(err).NotTo(HaveOccurred())
Expect(stderrBuffer.String()).To(Equal("some stderr message"))
})
})
Context("when the plugin errors", func() {
BeforeEach(func() {
debug.ReportResult = ""
})
Context("and writes valid error JSON to stdout", func() {
It("wraps and returns the error", func() {
debug.ReportError = "banana"
Expect(debug.WriteDebug(debugFileName)).To(Succeed())
_, err := execer.ExecPlugin(ctx, pathToPlugin, stdin, environ)
Expect(err).To(HaveOccurred())
Expect(err).To(MatchError("banana"))
})
})
Context("and writes to stderr", func() {
It("returns an error message with stderr output", func() {
debug.ExitWithCode = 1
Expect(debug.WriteDebug(debugFileName)).To(Succeed())
_, err := execer.ExecPlugin(ctx, pathToPlugin, stdin, environ)
Expect(err).To(HaveOccurred())
Expect(err).To(MatchError(`netplugin failed: "some stderr message"`))
})
})
})
Context("when the plugin errors with no output on stdout or stderr", func() {
It("returns the exec error message", func() {
debug.ExitWithCode = 1
debug.ReportResult = ""
debug.ReportStderr = ""
Expect(debug.WriteDebug(debugFileName)).To(Succeed())
_, err := execer.ExecPlugin(ctx, pathToPlugin, stdin, environ)
Expect(err).To(HaveOccurred())
Expect(err).To(MatchError("netplugin failed with no error message: exit status 1"))
})
})
Context("when the system is unable to execute the plugin", func() {
It("returns the error", func() {
_, err := execer.ExecPlugin(ctx, "/tmp/some/invalid/plugin/path", stdin, environ)
Expect(err).To(HaveOccurred())
Expect(err).To(MatchError(ContainSubstring("/tmp/some/invalid/plugin/path")))
})
})
})

View File

@ -1,51 +0,0 @@
// Copyright 2015 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package ip
import (
"math/big"
"net"
)
// NextIP returns IP incremented by 1
func NextIP(ip net.IP) net.IP {
i := ipToInt(ip)
return intToIP(i.Add(i, big.NewInt(1)))
}
// PrevIP returns IP decremented by 1
func PrevIP(ip net.IP) net.IP {
i := ipToInt(ip)
return intToIP(i.Sub(i, big.NewInt(1)))
}
func ipToInt(ip net.IP) *big.Int {
if v := ip.To4(); v != nil {
return big.NewInt(0).SetBytes(v)
}
return big.NewInt(0).SetBytes(ip.To16())
}
func intToIP(i *big.Int) net.IP {
return net.IP(i.Bytes())
}
// Network masks off the host portion of the IP
func Network(ipn *net.IPNet) *net.IPNet {
return &net.IPNet{
IP: ipn.IP.Mask(ipn.Mask),
Mask: ipn.Mask,
}
}

View File

@ -1,31 +0,0 @@
// Copyright 2015 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package ip
import (
"io/ioutil"
)
func EnableIP4Forward() error {
return echo1("/proc/sys/net/ipv4/ip_forward")
}
func EnableIP6Forward() error {
return echo1("/proc/sys/net/ipv6/conf/all/forwarding")
}
func echo1(f string) error {
return ioutil.WriteFile(f, []byte("1"), 0644)
}

View File

@ -1,66 +0,0 @@
// Copyright 2015 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package ip
import (
"fmt"
"net"
"github.com/coreos/go-iptables/iptables"
)
// SetupIPMasq installs iptables rules to masquerade traffic
// coming from ipn and going outside of it
func SetupIPMasq(ipn *net.IPNet, chain string, comment string) error {
ipt, err := iptables.New()
if err != nil {
return fmt.Errorf("failed to locate iptables: %v", err)
}
if err = ipt.NewChain("nat", chain); err != nil {
if err.(*iptables.Error).ExitStatus() != 1 {
// TODO(eyakubovich): assumes exit status 1 implies chain exists
return err
}
}
if err = ipt.AppendUnique("nat", chain, "-d", ipn.String(), "-j", "ACCEPT", "-m", "comment", "--comment", comment); err != nil {
return err
}
if err = ipt.AppendUnique("nat", chain, "!", "-d", "224.0.0.0/4", "-j", "MASQUERADE", "-m", "comment", "--comment", comment); err != nil {
return err
}
return ipt.AppendUnique("nat", "POSTROUTING", "-s", ipn.String(), "-j", chain, "-m", "comment", "--comment", comment)
}
// TeardownIPMasq undoes the effects of SetupIPMasq
func TeardownIPMasq(ipn *net.IPNet, chain string, comment string) error {
ipt, err := iptables.New()
if err != nil {
return fmt.Errorf("failed to locate iptables: %v", err)
}
if err = ipt.Delete("nat", "POSTROUTING", "-s", ipn.String(), "-j", chain, "-m", "comment", "--comment", comment); err != nil {
return err
}
if err = ipt.ClearChain("nat", chain); err != nil {
return err
}
return ipt.DeleteChain("nat", chain)
}

View File

@ -1,153 +0,0 @@
// Copyright 2015 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package ip
import (
"crypto/rand"
"fmt"
"net"
"os"
"github.com/containernetworking/cni/pkg/ns"
"github.com/vishvananda/netlink"
)
func makeVethPair(name, peer string, mtu int) (netlink.Link, error) {
veth := &netlink.Veth{
LinkAttrs: netlink.LinkAttrs{
Name: name,
Flags: net.FlagUp,
MTU: mtu,
},
PeerName: peer,
}
if err := netlink.LinkAdd(veth); err != nil {
return nil, err
}
return veth, nil
}
func makeVeth(name string, mtu int) (peerName string, veth netlink.Link, err error) {
for i := 0; i < 10; i++ {
peerName, err = RandomVethName()
if err != nil {
return
}
veth, err = makeVethPair(name, peerName, mtu)
switch {
case err == nil:
return
case os.IsExist(err):
continue
default:
err = fmt.Errorf("failed to make veth pair: %v", err)
return
}
}
// should really never be hit
err = fmt.Errorf("failed to find a unique veth name")
return
}
// RandomVethName returns string "veth" with random prefix (hashed from entropy)
func RandomVethName() (string, error) {
entropy := make([]byte, 4)
_, err := rand.Reader.Read(entropy)
if err != nil {
return "", fmt.Errorf("failed to generate random veth name: %v", err)
}
// NetworkManager (recent versions) will ignore veth devices that start with "veth"
return fmt.Sprintf("veth%x", entropy), nil
}
// SetupVeth sets up a virtual ethernet link.
// Should be in container netns, and will switch back to hostNS to set the host
// veth end up.
func SetupVeth(contVethName string, mtu int, hostNS ns.NetNS) (hostVeth, contVeth netlink.Link, err error) {
var hostVethName string
hostVethName, contVeth, err = makeVeth(contVethName, mtu)
if err != nil {
return
}
if err = netlink.LinkSetUp(contVeth); err != nil {
err = fmt.Errorf("failed to set %q up: %v", contVethName, err)
return
}
hostVeth, err = netlink.LinkByName(hostVethName)
if err != nil {
err = fmt.Errorf("failed to lookup %q: %v", hostVethName, err)
return
}
if err = netlink.LinkSetNsFd(hostVeth, int(hostNS.Fd())); err != nil {
err = fmt.Errorf("failed to move veth to host netns: %v", err)
return
}
err = hostNS.Do(func(_ ns.NetNS) error {
hostVeth, err := netlink.LinkByName(hostVethName)
if err != nil {
return fmt.Errorf("failed to lookup %q in %q: %v", hostVethName, hostNS.Path(), err)
}
if err = netlink.LinkSetUp(hostVeth); err != nil {
return fmt.Errorf("failed to set %q up: %v", hostVethName, err)
}
return nil
})
return
}
// DelLinkByName removes an interface link.
func DelLinkByName(ifName string) error {
iface, err := netlink.LinkByName(ifName)
if err != nil {
return fmt.Errorf("failed to lookup %q: %v", ifName, err)
}
if err = netlink.LinkDel(iface); err != nil {
return fmt.Errorf("failed to delete %q: %v", ifName, err)
}
return nil
}
// DelLinkByNameAddr remove an interface returns its IP address
// of the specified family
func DelLinkByNameAddr(ifName string, family int) (*net.IPNet, error) {
iface, err := netlink.LinkByName(ifName)
if err != nil {
return nil, fmt.Errorf("failed to lookup %q: %v", ifName, err)
}
addrs, err := netlink.AddrList(iface, family)
if err != nil || len(addrs) == 0 {
return nil, fmt.Errorf("failed to get IP addresses for %q: %v", ifName, err)
}
if err = netlink.LinkDel(iface); err != nil {
return nil, fmt.Errorf("failed to delete %q: %v", ifName, err)
}
return addrs[0].IPNet, nil
}

View File

@ -1,47 +0,0 @@
// Copyright 2015 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package ip
import (
"net"
"github.com/vishvananda/netlink"
)
// AddDefaultRoute sets the default route on the given gateway.
func AddDefaultRoute(gw net.IP, dev netlink.Link) error {
_, defNet, _ := net.ParseCIDR("0.0.0.0/0")
return AddRoute(defNet, gw, dev)
}
// AddRoute adds a universally-scoped route to a device.
func AddRoute(ipn *net.IPNet, gw net.IP, dev netlink.Link) error {
return netlink.RouteAdd(&netlink.Route{
LinkIndex: dev.Attrs().Index,
Scope: netlink.SCOPE_UNIVERSE,
Dst: ipn,
Gw: gw,
})
}
// AddHostRoute adds a host-scoped route to a device.
func AddHostRoute(ipn *net.IPNet, gw net.IP, dev netlink.Link) error {
return netlink.RouteAdd(&netlink.Route{
LinkIndex: dev.Attrs().Index,
Scope: netlink.SCOPE_HOST,
Dst: ipn,
Gw: gw,
})
}

View File

@ -1,68 +0,0 @@
// Copyright 2015 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package ipam
import (
"fmt"
"os"
"github.com/containernetworking/cni/pkg/invoke"
"github.com/containernetworking/cni/pkg/ip"
"github.com/containernetworking/cni/pkg/types"
"github.com/vishvananda/netlink"
)
func ExecAdd(plugin string, netconf []byte) (*types.Result, error) {
return invoke.DelegateAdd(plugin, netconf)
}
func ExecDel(plugin string, netconf []byte) error {
return invoke.DelegateDel(plugin, netconf)
}
// ConfigureIface takes the result of IPAM plugin and
// applies to the ifName interface
func ConfigureIface(ifName string, res *types.Result) error {
link, err := netlink.LinkByName(ifName)
if err != nil {
return fmt.Errorf("failed to lookup %q: %v", ifName, err)
}
if err := netlink.LinkSetUp(link); err != nil {
return fmt.Errorf("failed to set %q UP: %v", ifName, err)
}
// TODO(eyakubovich): IPv6
addr := &netlink.Addr{IPNet: &res.IP4.IP, Label: ""}
if err = netlink.AddrAdd(link, addr); err != nil {
return fmt.Errorf("failed to add IP addr to %q: %v", ifName, err)
}
for _, r := range res.IP4.Routes {
gw := r.GW
if gw == nil {
gw = res.IP4.Gateway
}
if err = ip.AddRoute(&r.Dst, gw, link); err != nil {
// we skip over duplicate routes as we assume the first one wins
if !os.IsExist(err) {
return fmt.Errorf("failed to add route '%v via %v dev %v': %v", r.Dst, gw, ifName, err)
}
}
}
return nil
}

View File

@ -1,31 +0,0 @@
### Namespaces, Threads, and Go
On Linux each OS thread can have a different network namespace. Go's thread scheduling model switches goroutines between OS threads based on OS thread load and whether the goroutine would block other goroutines. This can result in a goroutine switching network namespaces without notice and lead to errors in your code.
### Namespace Switching
Switching namespaces with the `ns.Set()` method is not recommended without additional strategies to prevent unexpected namespace changes when your goroutines switch OS threads.
Go provides the `runtime.LockOSThread()` function to ensure a specific goroutine executes on its current OS thread and prevents any other goroutine from running in that thread until the locked one exits. Careful usage of `LockOSThread()` and goroutines can provide good control over which network namespace a given goroutine executes in.
For example, you cannot rely on the `ns.Set()` namespace being the current namespace after the `Set()` call unless you do two things. First, the goroutine calling `Set()` must have previously called `LockOSThread()`. Second, you must ensure `runtime.UnlockOSThread()` is not called somewhere in-between. You also cannot rely on the initial network namespace remaining the current network namespace if any other code in your program switches namespaces, unless you have already called `LockOSThread()` in that goroutine. Note that `LockOSThread()` prevents the Go scheduler from optimally scheduling goroutines for best performance, so `LockOSThread()` should only be used in small, isolated goroutines that release the lock quickly.
### Do() The Recommended Thing
The `ns.Do()` method provides control over network namespaces for you by implementing these strategies. All code dependent on a particular network namespace should be wrapped in the `ns.Do()` method to ensure the correct namespace is selected for the duration of your code. For example:
```go
targetNs, err := ns.NewNS()
if err != nil {
return err
}
err = targetNs.Do(func(hostNs ns.NetNS) error {
dummy := &netlink.Dummy{
LinkAttrs: netlink.LinkAttrs{
Name: "dummy0",
},
}
return netlink.LinkAdd(dummy)
})
```
### Further Reading
- https://github.com/golang/go/wiki/LockOSThread
- http://morsmachine.dk/go-scheduler

View File

@ -1,237 +0,0 @@
// Copyright 2015 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package ns
import (
"crypto/rand"
"fmt"
"os"
"path"
"runtime"
"sync"
"golang.org/x/sys/unix"
)
type NetNS interface {
// Executes the passed closure in this object's network namespace,
// attemtping to restore the original namespace before returning.
// However, since each OS thread can have a different network namespace,
// and Go's thread scheduling is highly variable, callers cannot
// guarantee any specific namespace is set unless operations that
// require that namespace are wrapped with Do(). Also, no code called
// from Do() should call runtime.UnlockOSThread(), or the risk
// of executing code in an incorrect namespace will be greater. See
// https://github.com/golang/go/wiki/LockOSThread for further details.
Do(toRun func(NetNS) error) error
// Sets the current network namespace to this object's network namespace.
// Note that since Go's thread scheduling is highly variable, callers
// cannot guarantee the requested namespace will be the current namespace
// after this function is called; to ensure this wrap operations that
// require the namespace with Do() instead.
Set() error
// Returns the filesystem path representing this object's network namespace
Path() string
// Returns a file descriptor representing this object's network namespace
Fd() uintptr
// Cleans up this instance of the network namespace; if this instance
// is the last user the namespace will be destroyed
Close() error
}
type netNS struct {
file *os.File
mounted bool
}
func getCurrentThreadNetNSPath() string {
// /proc/self/ns/net returns the namespace of the main thread, not
// of whatever thread this goroutine is running on. Make sure we
// use the thread's net namespace since the thread is switching around
return fmt.Sprintf("/proc/%d/task/%d/ns/net", os.Getpid(), unix.Gettid())
}
// Returns an object representing the current OS thread's network namespace
func GetCurrentNS() (NetNS, error) {
return GetNS(getCurrentThreadNetNSPath())
}
// Returns an object representing the namespace referred to by @path
func GetNS(nspath string) (NetNS, error) {
fd, err := os.Open(nspath)
if err != nil {
return nil, err
}
return &netNS{file: fd}, nil
}
// Creates a new persistent network namespace and returns an object
// representing that namespace, without switching to it
func NewNS() (NetNS, error) {
const nsRunDir = "/var/run/netns"
b := make([]byte, 16)
_, err := rand.Reader.Read(b)
if err != nil {
return nil, fmt.Errorf("failed to generate random netns name: %v", err)
}
err = os.MkdirAll(nsRunDir, 0755)
if err != nil {
return nil, err
}
// create an empty file at the mount point
nsName := fmt.Sprintf("cni-%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:])
nsPath := path.Join(nsRunDir, nsName)
mountPointFd, err := os.Create(nsPath)
if err != nil {
return nil, err
}
mountPointFd.Close()
// Ensure the mount point is cleaned up on errors; if the namespace
// was successfully mounted this will have no effect because the file
// is in-use
defer os.RemoveAll(nsPath)
var wg sync.WaitGroup
wg.Add(1)
// do namespace work in a dedicated goroutine, so that we can safely
// Lock/Unlock OSThread without upsetting the lock/unlock state of
// the caller of this function
var fd *os.File
go (func() {
defer wg.Done()
runtime.LockOSThread()
var origNS NetNS
origNS, err = GetNS(getCurrentThreadNetNSPath())
if err != nil {
return
}
defer origNS.Close()
// create a new netns on the current thread
err = unix.Unshare(unix.CLONE_NEWNET)
if err != nil {
return
}
defer origNS.Set()
// bind mount the new netns from the current thread onto the mount point
err = unix.Mount(getCurrentThreadNetNSPath(), nsPath, "none", unix.MS_BIND, "")
if err != nil {
return
}
fd, err = os.Open(nsPath)
if err != nil {
return
}
})()
wg.Wait()
if err != nil {
unix.Unmount(nsPath, unix.MNT_DETACH)
return nil, fmt.Errorf("failed to create namespace: %v", err)
}
return &netNS{file: fd, mounted: true}, nil
}
func (ns *netNS) Path() string {
return ns.file.Name()
}
func (ns *netNS) Fd() uintptr {
return ns.file.Fd()
}
func (ns *netNS) Close() error {
ns.file.Close()
if ns.mounted {
if err := unix.Unmount(ns.file.Name(), unix.MNT_DETACH); err != nil {
return fmt.Errorf("Failed to unmount namespace %s: %v", ns.file.Name(), err)
}
if err := os.RemoveAll(ns.file.Name()); err != nil {
return fmt.Errorf("Failed to clean up namespace %s: %v", ns.file.Name(), err)
}
}
return nil
}
func (ns *netNS) Do(toRun func(NetNS) error) error {
containedCall := func(hostNS NetNS) error {
threadNS, err := GetNS(getCurrentThreadNetNSPath())
if err != nil {
return fmt.Errorf("failed to open current netns: %v", err)
}
defer threadNS.Close()
// switch to target namespace
if err = ns.Set(); err != nil {
return fmt.Errorf("error switching to ns %v: %v", ns.file.Name(), err)
}
defer threadNS.Set() // switch back
return toRun(hostNS)
}
// save a handle to current network namespace
hostNS, err := GetNS(getCurrentThreadNetNSPath())
if err != nil {
return fmt.Errorf("Failed to open current namespace: %v", err)
}
defer hostNS.Close()
var wg sync.WaitGroup
wg.Add(1)
var innerError error
go func() {
defer wg.Done()
runtime.LockOSThread()
innerError = containedCall(hostNS)
}()
wg.Wait()
return innerError
}
func (ns *netNS) Set() error {
if _, _, err := unix.Syscall(unix.SYS_SETNS, ns.Fd(), uintptr(unix.CLONE_NEWNET), 0); err != 0 {
return fmt.Errorf("Error switching to ns %v: %v", ns.file.Name(), err)
}
return nil
}
// WithNetNSPath executes the passed closure under the given network
// namespace, restoring the original namespace afterwards.
func WithNetNSPath(nspath string, toRun func(NetNS) error) error {
ns, err := GetNS(nspath)
if err != nil {
return fmt.Errorf("Failed to open %v: %v", nspath, err)
}
defer ns.Close()
return ns.Do(toRun)
}

21
pkg/ns/ns_darwin.go Normal file
View File

@ -0,0 +1,21 @@
// Copyright 2022 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package ns
import "github.com/containernetworking/cni/pkg/types"
func CheckNetNS(nsPath string) (bool, *types.Error) {
return false, nil
}

50
pkg/ns/ns_linux.go Normal file
View File

@ -0,0 +1,50 @@
// Copyright 2022 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package ns
import (
"runtime"
"github.com/vishvananda/netns"
"github.com/containernetworking/cni/pkg/types"
)
// Returns an object representing the current OS thread's network namespace
func getCurrentNS() (netns.NsHandle, error) {
// Lock the thread in case other goroutine executes in it and changes its
// network namespace after getCurrentThreadNetNSPath(), otherwise it might
// return an unexpected network namespace.
runtime.LockOSThread()
defer runtime.UnlockOSThread()
return netns.Get()
}
func CheckNetNS(nsPath string) (bool, *types.Error) {
ns, err := netns.GetFromPath(nsPath)
// Let plugins check whether nsPath from args is valid. Also support CNI DEL for empty nsPath as already-deleted nsPath.
if err != nil {
return false, nil
}
defer ns.Close()
pluginNS, err := getCurrentNS()
if err != nil {
return false, types.NewError(types.ErrInvalidNetNS, "get plugin's netns failed", "")
}
defer pluginNS.Close()
return pluginNS.Equal(ns), nil
}

View File

@ -1,181 +0,0 @@
// Copyright 2016 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package ns_test
import (
"errors"
"fmt"
"os"
"path/filepath"
"github.com/containernetworking/cni/pkg/ns"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"golang.org/x/sys/unix"
)
func getInodeCurNetNS() (uint64, error) {
curNS, err := ns.GetCurrentNS()
if err != nil {
return 0, err
}
defer curNS.Close()
return getInodeNS(curNS)
}
func getInodeNS(netns ns.NetNS) (uint64, error) {
return getInodeFd(int(netns.Fd()))
}
func getInode(path string) (uint64, error) {
file, err := os.Open(path)
if err != nil {
return 0, err
}
defer file.Close()
return getInodeFd(int(file.Fd()))
}
func getInodeFd(fd int) (uint64, error) {
stat := &unix.Stat_t{}
err := unix.Fstat(fd, stat)
return stat.Ino, err
}
var _ = Describe("Linux namespace operations", func() {
Describe("WithNetNS", func() {
var (
originalNetNS ns.NetNS
targetNetNS ns.NetNS
)
BeforeEach(func() {
var err error
originalNetNS, err = ns.NewNS()
Expect(err).NotTo(HaveOccurred())
targetNetNS, err = ns.NewNS()
Expect(err).NotTo(HaveOccurred())
})
AfterEach(func() {
Expect(targetNetNS.Close()).To(Succeed())
Expect(originalNetNS.Close()).To(Succeed())
})
It("executes the callback within the target network namespace", func() {
expectedInode, err := getInodeNS(targetNetNS)
Expect(err).NotTo(HaveOccurred())
err = targetNetNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
actualInode, err := getInodeCurNetNS()
Expect(err).NotTo(HaveOccurred())
Expect(actualInode).To(Equal(expectedInode))
return nil
})
Expect(err).NotTo(HaveOccurred())
})
It("provides the original namespace as the argument to the callback", func() {
// Ensure we start in originalNetNS
err := originalNetNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
origNSInode, err := getInodeNS(originalNetNS)
Expect(err).NotTo(HaveOccurred())
err = targetNetNS.Do(func(hostNS ns.NetNS) error {
defer GinkgoRecover()
hostNSInode, err := getInodeNS(hostNS)
Expect(err).NotTo(HaveOccurred())
Expect(hostNSInode).To(Equal(origNSInode))
return nil
})
return nil
})
Expect(err).NotTo(HaveOccurred())
})
Context("when the callback returns an error", func() {
It("restores the calling thread to the original namespace before returning", func() {
err := originalNetNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
preTestInode, err := getInodeCurNetNS()
Expect(err).NotTo(HaveOccurred())
_ = targetNetNS.Do(func(ns.NetNS) error {
return errors.New("potato")
})
postTestInode, err := getInodeCurNetNS()
Expect(err).NotTo(HaveOccurred())
Expect(postTestInode).To(Equal(preTestInode))
return nil
})
Expect(err).NotTo(HaveOccurred())
})
It("returns the error from the callback", func() {
err := targetNetNS.Do(func(ns.NetNS) error {
return errors.New("potato")
})
Expect(err).To(MatchError("potato"))
})
})
Describe("validating inode mapping to namespaces", func() {
It("checks that different namespaces have different inodes", func() {
origNSInode, err := getInodeNS(originalNetNS)
Expect(err).NotTo(HaveOccurred())
testNsInode, err := getInodeNS(targetNetNS)
Expect(err).NotTo(HaveOccurred())
Expect(testNsInode).NotTo(Equal(0))
Expect(testNsInode).NotTo(Equal(origNSInode))
})
It("should not leak a closed netns onto any threads in the process", func() {
By("creating a new netns")
createdNetNS, err := ns.NewNS()
Expect(err).NotTo(HaveOccurred())
By("discovering the inode of the created netns")
createdNetNSInode, err := getInodeNS(createdNetNS)
Expect(err).NotTo(HaveOccurred())
createdNetNS.Close()
By("comparing against the netns inode of every thread in the process")
for _, netnsPath := range allNetNSInCurrentProcess() {
netnsInode, err := getInode(netnsPath)
Expect(err).NotTo(HaveOccurred())
Expect(netnsInode).NotTo(Equal(createdNetNSInode))
}
})
})
})
})
func allNetNSInCurrentProcess() []string {
pid := unix.Getpid()
paths, err := filepath.Glob(fmt.Sprintf("/proc/%d/task/*/ns/net", pid))
Expect(err).NotTo(HaveOccurred())
return paths
}

21
pkg/ns/ns_windows.go Normal file
View File

@ -0,0 +1,21 @@
// Copyright 2022 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package ns
import "github.com/containernetworking/cni/pkg/types"
func CheckNetNS(nsPath string) (bool, *types.Error) {
return false, nil
}

View File

@ -1,4 +1,4 @@
// Copyright 2014 CNI authors
// Copyright 2014-2016 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -17,101 +17,423 @@
package skel
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"io"
"log"
"os"
"strings"
"github.com/containernetworking/cni/pkg/ns"
"github.com/containernetworking/cni/pkg/types"
"github.com/containernetworking/cni/pkg/utils"
"github.com/containernetworking/cni/pkg/version"
)
// CmdArgs captures all the arguments passed in to the plugin
// via both env vars and stdin
type CmdArgs struct {
ContainerID string
Netns string
IfName string
Args string
Path string
StdinData []byte
ContainerID string
Netns string
IfName string
Args string
Path string
NetnsOverride string
StdinData []byte
}
// PluginMain is the "main" for a plugin. It accepts
// two callback functions for add and del commands.
func PluginMain(cmdAdd, cmdDel func(_ *CmdArgs) error) {
var cmd, contID, netns, ifName, args, path string
type dispatcher struct {
Getenv func(string) string
Stdin io.Reader
Stdout io.Writer
Stderr io.Writer
ConfVersionDecoder version.ConfigDecoder
VersionReconciler version.Reconciler
}
type reqForCmdEntry map[string]bool
func (t *dispatcher) getCmdArgsFromEnv() (string, *CmdArgs, *types.Error) {
var cmd, contID, netns, ifName, args, path, netnsOverride string
vars := []struct {
name string
val *string
req bool
name string
val *string
reqForCmd reqForCmdEntry
validateFn func(string) *types.Error
}{
{"CNI_COMMAND", &cmd, true},
{"CNI_CONTAINERID", &contID, false},
{"CNI_NETNS", &netns, true},
{"CNI_IFNAME", &ifName, true},
{"CNI_ARGS", &args, false},
{"CNI_PATH", &path, true},
{
"CNI_COMMAND",
&cmd,
reqForCmdEntry{
"ADD": true,
"CHECK": true,
"DEL": true,
"GC": true,
"STATUS": true,
},
nil,
},
{
"CNI_CONTAINERID",
&contID,
reqForCmdEntry{
"ADD": true,
"CHECK": true,
"DEL": true,
},
utils.ValidateContainerID,
},
{
"CNI_NETNS",
&netns,
reqForCmdEntry{
"ADD": true,
"CHECK": true,
"DEL": false,
},
nil,
},
{
"CNI_IFNAME",
&ifName,
reqForCmdEntry{
"ADD": true,
"CHECK": true,
"DEL": true,
},
utils.ValidateInterfaceName,
},
{
"CNI_ARGS",
&args,
reqForCmdEntry{
"ADD": false,
"CHECK": false,
"DEL": false,
},
nil,
},
{
"CNI_PATH",
&path,
reqForCmdEntry{
"ADD": true,
"CHECK": true,
"DEL": true,
"GC": true,
"STATUS": true,
},
nil,
},
{
"CNI_NETNS_OVERRIDE",
&netnsOverride,
reqForCmdEntry{
"ADD": false,
"CHECK": false,
"DEL": false,
},
nil,
},
}
argsMissing := false
argsMissing := make([]string, 0)
for _, v := range vars {
*v.val = os.Getenv(v.name)
if v.req && *v.val == "" {
log.Printf("%v env variable missing", v.name)
argsMissing = true
*v.val = t.Getenv(v.name)
if *v.val == "" {
if v.reqForCmd[cmd] || v.name == "CNI_COMMAND" {
argsMissing = append(argsMissing, v.name)
}
} else if v.reqForCmd[cmd] && v.validateFn != nil {
if err := v.validateFn(*v.val); err != nil {
return "", nil, err
}
}
}
if argsMissing {
dieMsg("required env variables missing")
if len(argsMissing) > 0 {
joined := strings.Join(argsMissing, ",")
return "", nil, types.NewError(types.ErrInvalidEnvironmentVariables, fmt.Sprintf("required env variables [%s] missing", joined), "")
}
stdinData, err := ioutil.ReadAll(os.Stdin)
if cmd == "VERSION" {
t.Stdin = bytes.NewReader(nil)
}
stdinData, err := io.ReadAll(t.Stdin)
if err != nil {
dieMsg("error reading from stdin: %v", err)
return "", nil, types.NewError(types.ErrIOFailure, fmt.Sprintf("error reading from stdin: %v", err), "")
}
if cmd != "VERSION" {
if err := validateConfig(stdinData); err != nil {
return "", nil, err
}
}
cmdArgs := &CmdArgs{
ContainerID: contID,
Netns: netns,
IfName: ifName,
Args: args,
Path: path,
StdinData: stdinData,
ContainerID: contID,
Netns: netns,
IfName: ifName,
Args: args,
Path: path,
StdinData: stdinData,
NetnsOverride: netnsOverride,
}
return cmd, cmdArgs, nil
}
func (t *dispatcher) checkVersionAndCall(cmdArgs *CmdArgs, pluginVersionInfo version.PluginInfo, toCall func(*CmdArgs) error) *types.Error {
configVersion, err := t.ConfVersionDecoder.Decode(cmdArgs.StdinData)
if err != nil {
return types.NewError(types.ErrDecodingFailure, err.Error(), "")
}
verErr := t.VersionReconciler.Check(configVersion, pluginVersionInfo)
if verErr != nil {
return types.NewError(types.ErrIncompatibleCNIVersion, "incompatible CNI versions", verErr.Details())
}
if toCall == nil {
return nil
}
if err = toCall(cmdArgs); err != nil {
var e *types.Error
if errors.As(err, &e) {
// don't wrap Error in Error
return e
}
return types.NewError(types.ErrInternal, err.Error(), "")
}
return nil
}
func validateConfig(jsonBytes []byte) *types.Error {
var conf struct {
Name string `json:"name"`
}
if err := json.Unmarshal(jsonBytes, &conf); err != nil {
return types.NewError(types.ErrDecodingFailure, fmt.Sprintf("error unmarshall network config: %v", err), "")
}
if conf.Name == "" {
return types.NewError(types.ErrInvalidNetworkConfig, "missing network name", "")
}
if err := utils.ValidateNetworkName(conf.Name); err != nil {
return err
}
return nil
}
func (t *dispatcher) pluginMain(funcs CNIFuncs, versionInfo version.PluginInfo, about string) *types.Error {
cmd, cmdArgs, err := t.getCmdArgsFromEnv()
if err != nil {
// Print the about string to stderr when no command is set
if err.Code == types.ErrInvalidEnvironmentVariables && t.Getenv("CNI_COMMAND") == "" && about != "" {
_, _ = fmt.Fprintln(t.Stderr, about)
_, _ = fmt.Fprintf(t.Stderr, "CNI protocol versions supported: %s\n", strings.Join(versionInfo.SupportedVersions(), ", "))
return nil
}
return err
}
switch cmd {
case "ADD":
err = cmdAdd(cmdArgs)
case "DEL":
err = cmdDel(cmdArgs)
default:
dieMsg("unknown CNI_COMMAND: %v", cmd)
}
if err != nil {
if e, ok := err.(*types.Error); ok {
// don't wrap Error in Error
dieErr(e)
err = t.checkVersionAndCall(cmdArgs, versionInfo, funcs.Add)
if err != nil {
return err
}
dieMsg(err.Error())
if strings.ToUpper(cmdArgs.NetnsOverride) != "TRUE" && cmdArgs.NetnsOverride != "1" {
isPluginNetNS, checkErr := ns.CheckNetNS(cmdArgs.Netns)
if checkErr != nil {
return checkErr
} else if isPluginNetNS {
return types.NewError(types.ErrInvalidNetNS, "plugin's netns and netns from CNI_NETNS should not be the same", "")
}
}
case "CHECK":
configVersion, err := t.ConfVersionDecoder.Decode(cmdArgs.StdinData)
if err != nil {
return types.NewError(types.ErrDecodingFailure, err.Error(), "")
}
if gtet, err := version.GreaterThanOrEqualTo(configVersion, "0.4.0"); err != nil {
return types.NewError(types.ErrDecodingFailure, err.Error(), "")
} else if !gtet {
return types.NewError(types.ErrIncompatibleCNIVersion, "config version does not allow CHECK", "")
}
for _, pluginVersion := range versionInfo.SupportedVersions() {
gtet, err := version.GreaterThanOrEqualTo(pluginVersion, configVersion)
if err != nil {
return types.NewError(types.ErrDecodingFailure, err.Error(), "")
} else if gtet {
if err := t.checkVersionAndCall(cmdArgs, versionInfo, funcs.Check); err != nil {
return err
}
return nil
}
}
return types.NewError(types.ErrIncompatibleCNIVersion, "plugin version does not allow CHECK", "")
case "DEL":
err = t.checkVersionAndCall(cmdArgs, versionInfo, funcs.Del)
if err != nil {
return err
}
if strings.ToUpper(cmdArgs.NetnsOverride) != "TRUE" && cmdArgs.NetnsOverride != "1" {
isPluginNetNS, checkErr := ns.CheckNetNS(cmdArgs.Netns)
if checkErr != nil {
return checkErr
} else if isPluginNetNS {
return types.NewError(types.ErrInvalidNetNS, "plugin's netns and netns from CNI_NETNS should not be the same", "")
}
}
case "GC":
configVersion, err := t.ConfVersionDecoder.Decode(cmdArgs.StdinData)
if err != nil {
return types.NewError(types.ErrDecodingFailure, err.Error(), "")
}
if gtet, err := version.GreaterThanOrEqualTo(configVersion, "1.1.0"); err != nil {
return types.NewError(types.ErrDecodingFailure, err.Error(), "")
} else if !gtet {
return types.NewError(types.ErrIncompatibleCNIVersion, "config version does not allow GC", "")
}
for _, pluginVersion := range versionInfo.SupportedVersions() {
gtet, err := version.GreaterThanOrEqualTo(pluginVersion, configVersion)
if err != nil {
return types.NewError(types.ErrDecodingFailure, err.Error(), "")
} else if gtet {
if err := t.checkVersionAndCall(cmdArgs, versionInfo, funcs.GC); err != nil {
return err
}
return nil
}
}
return types.NewError(types.ErrIncompatibleCNIVersion, "plugin version does not allow GC", "")
case "STATUS":
configVersion, err := t.ConfVersionDecoder.Decode(cmdArgs.StdinData)
if err != nil {
return types.NewError(types.ErrDecodingFailure, err.Error(), "")
}
if gtet, err := version.GreaterThanOrEqualTo(configVersion, "1.1.0"); err != nil {
return types.NewError(types.ErrDecodingFailure, err.Error(), "")
} else if !gtet {
return types.NewError(types.ErrIncompatibleCNIVersion, "config version does not allow STATUS", "")
}
for _, pluginVersion := range versionInfo.SupportedVersions() {
gtet, err := version.GreaterThanOrEqualTo(pluginVersion, configVersion)
if err != nil {
return types.NewError(types.ErrDecodingFailure, err.Error(), "")
} else if gtet {
if err := t.checkVersionAndCall(cmdArgs, versionInfo, funcs.Status); err != nil {
return err
}
return nil
}
}
return types.NewError(types.ErrIncompatibleCNIVersion, "plugin version does not allow STATUS", "")
case "VERSION":
if err := versionInfo.Encode(t.Stdout); err != nil {
return types.NewError(types.ErrIOFailure, err.Error(), "")
}
default:
return types.NewError(types.ErrInvalidEnvironmentVariables, fmt.Sprintf("unknown CNI_COMMAND: %v", cmd), "")
}
return err
}
// PluginMainWithError is the core "main" for a plugin. It accepts
// callback functions for add, check, and del CNI commands and returns an error.
//
// The caller must also specify what CNI spec versions the plugin supports.
//
// It is the responsibility of the caller to check for non-nil error return.
//
// For a plugin to comply with the CNI spec, it must print any error to stdout
// as JSON and then exit with nonzero status code.
//
// To let this package automatically handle errors and call os.Exit(1) for you,
// use PluginMain() instead.
//
// Deprecated: Use github.com/containernetworking/cni/pkg/skel.PluginMainFuncsWithError instead.
func PluginMainWithError(cmdAdd, cmdCheck, cmdDel func(_ *CmdArgs) error, versionInfo version.PluginInfo, about string) *types.Error {
return PluginMainFuncsWithError(CNIFuncs{Add: cmdAdd, Check: cmdCheck, Del: cmdDel}, versionInfo, about)
}
// CNIFuncs contains a group of callback command funcs to be passed in as
// parameters to the core "main" for a plugin.
type CNIFuncs struct {
Add func(_ *CmdArgs) error
Del func(_ *CmdArgs) error
Check func(_ *CmdArgs) error
GC func(_ *CmdArgs) error
Status func(_ *CmdArgs) error
}
// PluginMainFuncsWithError is the core "main" for a plugin. It accepts
// callback functions defined within CNIFuncs and returns an error.
//
// The caller must also specify what CNI spec versions the plugin supports.
//
// It is the responsibility of the caller to check for non-nil error return.
//
// For a plugin to comply with the CNI spec, it must print any error to stdout
// as JSON and then exit with nonzero status code.
//
// To let this package automatically handle errors and call os.Exit(1) for you,
// use PluginMainFuncs() instead.
func PluginMainFuncsWithError(funcs CNIFuncs, versionInfo version.PluginInfo, about string) *types.Error {
return (&dispatcher{
Getenv: os.Getenv,
Stdin: os.Stdin,
Stdout: os.Stdout,
Stderr: os.Stderr,
}).pluginMain(funcs, versionInfo, about)
}
// PluginMainFuncs is the core "main" for a plugin which includes automatic error handling.
// This is a newer alternative func to PluginMain which abstracts CNI commands within a
// CNIFuncs interface.
//
// The caller must also specify what CNI spec versions the plugin supports.
//
// The caller can specify an "about" string, which is printed on stderr
// when no CNI_COMMAND is specified. The recommended output is "CNI plugin <foo> v<version>"
//
// When an error occurs in any func in CNIFuncs, PluginMainFuncs will print the error
// as JSON to stdout and call os.Exit(1).
//
// To have more control over error handling, use PluginMainFuncsWithError() instead.
func PluginMainFuncs(funcs CNIFuncs, versionInfo version.PluginInfo, about string) {
if e := PluginMainFuncsWithError(funcs, versionInfo, about); e != nil {
if err := e.Print(); err != nil {
log.Print("Error writing error JSON to stdout: ", err)
}
os.Exit(1)
}
}
func dieMsg(f string, args ...interface{}) {
e := &types.Error{
Code: 100,
Msg: fmt.Sprintf(f, args...),
// PluginMain is the core "main" for a plugin which includes automatic error handling.
//
// The caller must also specify what CNI spec versions the plugin supports.
//
// The caller can specify an "about" string, which is printed on stderr
// when no CNI_COMMAND is specified. The recommended output is "CNI plugin <foo> v<version>"
//
// When an error occurs in either cmdAdd, cmdCheck, or cmdDel, PluginMain will print the error
// as JSON to stdout and call os.Exit(1).
//
// To have more control over error handling, use PluginMainWithError() instead.
//
// Deprecated: Use github.com/containernetworking/cni/pkg/skel.PluginMainFuncs instead.
func PluginMain(cmdAdd, cmdCheck, cmdDel func(_ *CmdArgs) error, versionInfo version.PluginInfo, about string) {
if e := PluginMainWithError(cmdAdd, cmdCheck, cmdDel, versionInfo, about); e != nil {
if err := e.Print(); err != nil {
log.Print("Error writing error JSON to stdout: ", err)
}
os.Exit(1)
}
dieErr(e)
}
func dieErr(e *types.Error) {
if err := e.Print(); err != nil {
log.Print("Error writing error JSON to stdout: ", err)
}
os.Exit(1)
}

View File

@ -15,10 +15,10 @@
package skel
import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"testing"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
func TestSkel(t *testing.T) {

View File

@ -15,61 +15,719 @@
package skel
import (
"os"
"bytes"
"errors"
"fmt"
"strings"
. "github.com/onsi/ginkgo"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/containernetworking/cni/pkg/types"
"github.com/containernetworking/cni/pkg/version"
)
var _ = Describe("Skel", func() {
type fakeCmd struct {
CallCount int
Returns struct {
Error error
}
Received struct {
CmdArgs *CmdArgs
}
}
func (c *fakeCmd) Func(args *CmdArgs) error {
c.CallCount++
c.Received.CmdArgs = args
return c.Returns.Error
}
var _ = Describe("dispatching to the correct callback", func() {
var (
fNoop = func(_ *CmdArgs) error { return nil }
// fErr = func(_ *CmdArgs) error { return errors.New("dummy") }
envVars = []struct {
name string
val string
}{
{"CNI_CONTAINERID", "dummy"},
{"CNI_NETNS", "dummy"},
{"CNI_IFNAME", "dummy"},
{"CNI_ARGS", "dummy"},
{"CNI_PATH", "dummy"},
}
environment map[string]string
stdinData string
stdout, stderr *bytes.Buffer
cmdAdd, cmdCheck, cmdDel, cmdGC *fakeCmd
dispatch *dispatcher
expectedCmdArgs *CmdArgs
versionInfo version.PluginInfo
funcs CNIFuncs
)
It("Must be possible to set the env vars", func() {
for _, v := range envVars {
err := os.Setenv(v.name, v.val)
Expect(err).NotTo(HaveOccurred())
BeforeEach(func() {
environment = map[string]string{
"CNI_COMMAND": "ADD",
"CNI_CONTAINERID": "some-container-id",
"CNI_NETNS": "/some/netns/path",
"CNI_IFNAME": "eth0",
"CNI_ARGS": "some;extra;args",
"CNI_PATH": "/some/cni/path",
}
stdinData = `{ "name":"skel-test", "some": "config", "cniVersion": "9.8.7" }`
stdout = &bytes.Buffer{}
stderr = &bytes.Buffer{}
versionInfo = version.PluginSupports("9.8.7", "10.0.0")
dispatch = &dispatcher{
Getenv: func(key string) string { return environment[key] },
Stdin: strings.NewReader(stdinData),
Stdout: stdout,
Stderr: stderr,
}
cmdAdd = &fakeCmd{}
cmdCheck = &fakeCmd{}
cmdDel = &fakeCmd{}
cmdGC = &fakeCmd{}
funcs = CNIFuncs{
Add: cmdAdd.Func,
Del: cmdDel.Func,
Check: cmdCheck.Func,
GC: cmdGC.Func,
}
expectedCmdArgs = &CmdArgs{
ContainerID: "some-container-id",
Netns: "/some/netns/path",
IfName: "eth0",
Args: "some;extra;args",
Path: "/some/cni/path",
StdinData: []byte(stdinData),
}
})
Context("When dummy environment variables are passed", func() {
envVarChecker := func(envVar string, isRequired bool) {
delete(environment, envVar)
It("should not fail with ADD and noop callback", func() {
err := os.Setenv("CNI_COMMAND", "ADD")
err := dispatch.pluginMain(funcs, versionInfo, "")
if isRequired {
Expect(err).To(Equal(&types.Error{
Code: types.ErrInvalidEnvironmentVariables,
Msg: "required env variables [" + envVar + "] missing",
}))
} else {
Expect(err).NotTo(HaveOccurred())
PluginMain(fNoop, nil)
}
}
Context("when the CNI_COMMAND is ADD", func() {
expectedCmdArgs = &CmdArgs{
ContainerID: "some-container-id",
Netns: "/some/netns/path",
IfName: "eth0",
Args: "some;extra;args",
Path: "/some/cni/path",
StdinData: []byte(stdinData),
}
It("extracts env vars and stdin data and calls cmdAdd", func() {
err := dispatch.pluginMain(funcs, versionInfo, "")
Expect(err).NotTo(HaveOccurred())
Expect(cmdAdd.CallCount).To(Equal(1))
Expect(cmdCheck.CallCount).To(Equal(0))
Expect(cmdDel.CallCount).To(Equal(0))
Expect(cmdAdd.Received.CmdArgs).To(Equal(expectedCmdArgs))
})
// TODO: figure out howto mock printing and os.Exit()
// It("should fail with ADD and error callback", func() {
// err := os.Setenv("CNI_COMMAND", "ADD")
// Expect(err).NotTo(HaveOccurred())
// PluginMain(fErr, nil)
// })
It("should not fail with DEL and noop callback", func() {
err := os.Setenv("CNI_COMMAND", "DEL")
Expect(err).NotTo(HaveOccurred())
PluginMain(nil, fNoop)
It("returns an error when containerID has invalid characters", func() {
environment["CNI_CONTAINERID"] = "some-%%container-id"
err := dispatch.pluginMain(funcs, versionInfo, "")
Expect(err).To(HaveOccurred())
Expect(err).To(Equal(&types.Error{
Code: types.ErrInvalidEnvironmentVariables,
Msg: "invalid characters in containerID",
Details: "some-%%container-id",
}))
})
// TODO: figure out howto mock printing and os.Exit()
// It("should fail with DEL and error callback", func() {
// err := os.Setenv("CNI_COMMAND", "DEL")
// Expect(err).NotTo(HaveOccurred())
// PluginMain(fErr, nil)
// })
Context("return errors when interface name is invalid", func() {
It("interface name is too long", func() {
environment["CNI_IFNAME"] = "1234567890123456"
err := dispatch.pluginMain(funcs, versionInfo, "")
Expect(err).To(HaveOccurred())
Expect(err).To(Equal(&types.Error{
Code: types.ErrInvalidEnvironmentVariables,
Msg: "interface name is too long",
Details: "interface name should be less than 16 characters",
}))
})
It("interface name is .", func() {
environment["CNI_IFNAME"] = "."
err := dispatch.pluginMain(funcs, versionInfo, "")
Expect(err).To(HaveOccurred())
Expect(err).To(Equal(&types.Error{
Code: types.ErrInvalidEnvironmentVariables,
Msg: "interface name is . or ..",
Details: "",
}))
})
It("interface name is ..", func() {
environment["CNI_IFNAME"] = ".."
err := dispatch.pluginMain(funcs, versionInfo, "")
Expect(err).To(HaveOccurred())
Expect(err).To(Equal(&types.Error{
Code: types.ErrInvalidEnvironmentVariables,
Msg: "interface name is . or ..",
Details: "",
}))
})
It("interface name contains invalid characters /", func() {
environment["CNI_IFNAME"] = "test/test"
err := dispatch.pluginMain(funcs, versionInfo, "")
Expect(err).To(HaveOccurred())
Expect(err).To(Equal(&types.Error{
Code: types.ErrInvalidEnvironmentVariables,
Msg: "interface name contains / or : or whitespace characters",
Details: "",
}))
})
It("interface name contains invalid characters :", func() {
environment["CNI_IFNAME"] = "test:test"
err := dispatch.pluginMain(funcs, versionInfo, "")
Expect(err).To(HaveOccurred())
Expect(err).To(Equal(&types.Error{
Code: types.ErrInvalidEnvironmentVariables,
Msg: "interface name contains / or : or whitespace characters",
Details: "",
}))
})
It("interface name contains invalid characters whitespace", func() {
environment["CNI_IFNAME"] = "test test"
err := dispatch.pluginMain(funcs, versionInfo, "")
Expect(err).To(HaveOccurred())
Expect(err).To(Equal(&types.Error{
Code: types.ErrInvalidEnvironmentVariables,
Msg: "interface name contains / or : or whitespace characters",
Details: "",
}))
})
})
It("does not call cmdCheck or cmdDel", func() {
err := dispatch.pluginMain(funcs, versionInfo, "")
Expect(err).NotTo(HaveOccurred())
Expect(cmdCheck.CallCount).To(Equal(0))
Expect(cmdDel.CallCount).To(Equal(0))
})
DescribeTable("required / optional env vars", envVarChecker,
Entry("command", "CNI_COMMAND", true),
Entry("container id", "CNI_CONTAINERID", true),
Entry("net ns", "CNI_NETNS", true),
Entry("if name", "CNI_IFNAME", true),
Entry("args", "CNI_ARGS", false),
Entry("path", "CNI_PATH", true),
)
Context("when multiple required env vars are missing", func() {
BeforeEach(func() {
delete(environment, "CNI_NETNS")
delete(environment, "CNI_IFNAME")
delete(environment, "CNI_PATH")
})
It("reports that all of them are missing, not just the first", func() {
err := dispatch.pluginMain(funcs, versionInfo, "")
Expect(err).To(HaveOccurred())
Expect(err).To(Equal(&types.Error{
Code: types.ErrInvalidEnvironmentVariables,
Msg: "required env variables [CNI_NETNS,CNI_IFNAME,CNI_PATH] missing",
}))
})
})
Context("when the stdin data is missing the required cniVersion config", func() {
BeforeEach(func() {
dispatch.Stdin = strings.NewReader(`{ "name": "skel-test", "some": "config" }`)
})
Context("when the plugin supports version 0.1.0", func() {
BeforeEach(func() {
versionInfo = version.PluginSupports("0.1.0")
expectedCmdArgs.StdinData = []byte(`{ "name": "skel-test", "some": "config" }`)
})
It("infers the config is 0.1.0 and calls the cmdAdd callback", func() {
err := dispatch.pluginMain(funcs, versionInfo, "")
Expect(err).NotTo(HaveOccurred())
Expect(cmdAdd.CallCount).To(Equal(1))
Expect(cmdAdd.Received.CmdArgs).To(Equal(expectedCmdArgs))
})
})
Context("when the plugin does not support 0.1.0", func() {
BeforeEach(func() {
versionInfo = version.PluginSupports("4.3.2")
})
It("immediately returns a useful error", func() {
err := dispatch.pluginMain(funcs, versionInfo, "")
Expect(err.Code).To(Equal(types.ErrIncompatibleCNIVersion)) // see https://github.com/containernetworking/cni/blob/main/SPEC.md#well-known-error-codes
Expect(err.Msg).To(Equal("incompatible CNI versions"))
Expect(err.Details).To(Equal(`config is "0.1.0", plugin supports ["4.3.2"]`))
})
It("does not call either callback", func() {
err := dispatch.pluginMain(funcs, versionInfo, "")
Expect(err).To(HaveOccurred())
Expect(cmdAdd.CallCount).To(Equal(0))
Expect(cmdCheck.CallCount).To(Equal(0))
Expect(cmdDel.CallCount).To(Equal(0))
})
})
})
})
Context("when the CNI_COMMAND is CHECK", func() {
BeforeEach(func() {
environment["CNI_COMMAND"] = "CHECK"
})
It("extracts env vars and stdin data and calls cmdCheck", func() {
err := dispatch.pluginMain(funcs, versionInfo, "")
Expect(err).NotTo(HaveOccurred())
Expect(cmdAdd.CallCount).To(Equal(0))
Expect(cmdCheck.CallCount).To(Equal(1))
Expect(cmdDel.CallCount).To(Equal(0))
Expect(cmdCheck.Received.CmdArgs).To(Equal(expectedCmdArgs))
})
It("does not call cmdAdd or cmdDel", func() {
err := dispatch.pluginMain(funcs, versionInfo, "")
Expect(err).NotTo(HaveOccurred())
Expect(cmdAdd.CallCount).To(Equal(0))
Expect(cmdDel.CallCount).To(Equal(0))
})
DescribeTable("required / optional env vars", envVarChecker,
Entry("command", "CNI_COMMAND", true),
Entry("container id", "CNI_CONTAINERID", true),
Entry("net ns", "CNI_NETNS", true),
Entry("if name", "CNI_IFNAME", true),
Entry("args", "CNI_ARGS", false),
Entry("path", "CNI_PATH", true),
)
Context("when multiple required env vars are missing", func() {
BeforeEach(func() {
delete(environment, "CNI_NETNS")
delete(environment, "CNI_IFNAME")
delete(environment, "CNI_PATH")
})
It("reports that all of them are missing, not just the first", func() {
err := dispatch.pluginMain(funcs, versionInfo, "")
Expect(err).To(HaveOccurred())
Expect(err).To(Equal(&types.Error{
Code: types.ErrInvalidEnvironmentVariables,
Msg: "required env variables [CNI_NETNS,CNI_IFNAME,CNI_PATH] missing",
}))
})
})
Context("when cniVersion is less than 0.4.0", func() {
It("immediately returns a useful error", func() {
dispatch.Stdin = strings.NewReader(`{ "name": "skel-test", "cniVersion": "0.3.0", "some": "config" }`)
err := dispatch.pluginMain(funcs, versionInfo, "")
Expect(err.Code).To(Equal(types.ErrIncompatibleCNIVersion)) // see https://github.com/containernetworking/cni/blob/main/SPEC.md#well-known-error-codes
Expect(err.Msg).To(Equal("config version does not allow CHECK"))
Expect(cmdAdd.CallCount).To(Equal(0))
Expect(cmdCheck.CallCount).To(Equal(0))
Expect(cmdDel.CallCount).To(Equal(0))
})
})
Context("when plugin does not support 0.4.0", func() {
It("immediately returns a useful error", func() {
dispatch.Stdin = strings.NewReader(`{ "name": "skel-test", "cniVersion": "0.4.0", "some": "config" }`)
versionInfo = version.PluginSupports("0.1.0", "0.2.0", "0.3.0")
err := dispatch.pluginMain(funcs, versionInfo, "")
Expect(err.Code).To(Equal(types.ErrIncompatibleCNIVersion)) // see https://github.com/containernetworking/cni/blob/main/SPEC.md#well-known-error-codes
Expect(err.Msg).To(Equal("plugin version does not allow CHECK"))
Expect(cmdAdd.CallCount).To(Equal(0))
Expect(cmdCheck.CallCount).To(Equal(0))
Expect(cmdDel.CallCount).To(Equal(0))
})
})
Context("when the config has a bad version", func() {
It("immediately returns a useful error", func() {
dispatch.Stdin = strings.NewReader(`{ "cniVersion": "adsfsadf", "some": "config", "name": "test" }`)
versionInfo = version.PluginSupports("0.1.0", "0.2.0", "0.3.0")
err := dispatch.pluginMain(funcs, versionInfo, "")
Expect(err.Code).To(Equal(types.ErrDecodingFailure))
Expect(cmdAdd.CallCount).To(Equal(0))
Expect(cmdCheck.CallCount).To(Equal(0))
Expect(cmdDel.CallCount).To(Equal(0))
})
})
Context("when the config has a bad name", func() {
It("immediately returns invalid network config", func() {
dispatch.Stdin = strings.NewReader(`{ "cniVersion": "0.4.0", "some": "config", "name": "te%%st" }`)
versionInfo = version.PluginSupports("0.1.0", "0.2.0", "0.3.0", "0.4.0")
err := dispatch.pluginMain(funcs, versionInfo, "")
Expect(err.Code).To(Equal(types.ErrInvalidNetworkConfig))
Expect(cmdAdd.CallCount).To(Equal(0))
Expect(cmdCheck.CallCount).To(Equal(0))
Expect(cmdDel.CallCount).To(Equal(0))
})
})
Context("when the plugin has a bad version", func() {
It("immediately returns a useful error", func() {
dispatch.Stdin = strings.NewReader(`{ "cniVersion": "0.4.0", "some": "config", "name": "test" }`)
versionInfo = version.PluginSupports("0.1.0", "0.2.0", "adsfasdf")
err := dispatch.pluginMain(funcs, versionInfo, "")
Expect(err.Code).To(Equal(types.ErrDecodingFailure))
Expect(cmdAdd.CallCount).To(Equal(0))
Expect(cmdCheck.CallCount).To(Equal(0))
Expect(cmdDel.CallCount).To(Equal(0))
})
})
})
Context("when the CNI_COMMAND is GC", func() {
BeforeEach(func() {
environment["CNI_COMMAND"] = "GC"
delete(environment, "CNI_NETNS")
delete(environment, "CNI_IFNAME")
delete(environment, "CNI_CONTAINERID")
delete(environment, "CNI_ARGS")
expectedCmdArgs = &CmdArgs{
Path: "/some/cni/path",
StdinData: []byte(stdinData),
}
dispatch = &dispatcher{
Getenv: func(key string) string {
return environment[key]
},
Stdin: strings.NewReader(stdinData),
Stdout: stdout,
Stderr: stderr,
}
})
It("extracts env vars and stdin data and calls cmdGC", func() {
err := dispatch.pluginMain(funcs, versionInfo, "")
Expect(err).NotTo(HaveOccurred())
Expect(cmdAdd.CallCount).To(Equal(0))
Expect(cmdCheck.CallCount).To(Equal(0))
Expect(cmdDel.CallCount).To(Equal(0))
Expect(cmdGC.CallCount).To(Equal(1))
Expect(cmdGC.Received.CmdArgs).To(Equal(expectedCmdArgs))
})
It("does not call cmdAdd or cmdDel", func() {
err := dispatch.pluginMain(funcs, versionInfo, "")
Expect(err).NotTo(HaveOccurred())
Expect(cmdAdd.CallCount).To(Equal(0))
Expect(cmdDel.CallCount).To(Equal(0))
})
DescribeTable("required / optional env vars", envVarChecker,
Entry("command", "CNI_COMMAND", true),
Entry("container id", "CNI_CONTAINERID", false),
Entry("net ns", "CNI_NETNS", false),
Entry("if name", "CNI_IFNAME", false),
Entry("args", "CNI_ARGS", false),
Entry("path", "CNI_PATH", true),
)
Context("when multiple required env vars are missing", func() {
BeforeEach(func() {
delete(environment, "CNI_PATH")
})
It("reports that all of them are missing, not just the first", func() {
err := dispatch.pluginMain(funcs, versionInfo, "")
Expect(err).To(HaveOccurred())
Expect(err).To(Equal(&types.Error{
Code: types.ErrInvalidEnvironmentVariables,
Msg: "required env variables [CNI_PATH] missing",
}))
})
})
Context("when cniVersion is less than 1.1.0", func() {
It("immediately returns a useful error", func() {
dispatch.Stdin = strings.NewReader(`{ "name": "skel-test", "cniVersion": "0.3.0", "some": "config" }`)
err := dispatch.pluginMain(funcs, versionInfo, "")
Expect(err.Code).To(Equal(types.ErrIncompatibleCNIVersion)) // see https://github.com/containernetworking/cni/blob/main/SPEC.md#well-known-error-codes
Expect(err.Msg).To(Equal("config version does not allow GC"))
Expect(cmdGC.CallCount).To(Equal(0))
})
})
Context("when plugin does not support 1.1.0", func() {
It("immediately returns a useful error", func() {
dispatch.Stdin = strings.NewReader(`{ "name": "skel-test", "cniVersion": "1.1.0", "some": "config" }`)
versionInfo = version.PluginSupports("0.1.0", "0.2.0", "0.3.0")
err := dispatch.pluginMain(funcs, versionInfo, "")
Expect(err.Code).To(Equal(types.ErrIncompatibleCNIVersion)) // see https://github.com/containernetworking/cni/blob/main/SPEC.md#well-known-error-codes
Expect(err.Msg).To(Equal("plugin version does not allow GC"))
Expect(cmdGC.CallCount).To(Equal(0))
})
})
Context("when the config has a bad version", func() {
It("immediately returns a useful error", func() {
dispatch.Stdin = strings.NewReader(`{ "cniVersion": "adsfsadf", "some": "config", "name": "test" }`)
versionInfo = version.PluginSupports("1.1.0")
err := dispatch.pluginMain(funcs, versionInfo, "")
Expect(err.Code).To(Equal(types.ErrDecodingFailure))
Expect(cmdGC.CallCount).To(Equal(0))
})
})
Context("when the config has a bad name", func() {
It("immediately returns invalid network config", func() {
dispatch.Stdin = strings.NewReader(`{ "cniVersion": "0.4.0", "some": "config", "name": "te%%st" }`)
versionInfo = version.PluginSupports("1.1.0")
err := dispatch.pluginMain(funcs, versionInfo, "")
Expect(err.Code).To(Equal(types.ErrInvalidNetworkConfig))
Expect(cmdGC.CallCount).To(Equal(0))
})
})
Context("when the plugin has a bad version", func() {
It("immediately returns a useful error", func() {
dispatch.Stdin = strings.NewReader(`{ "cniVersion": "1.1.0", "some": "config", "name": "test" }`)
versionInfo = version.PluginSupports("0.1.0", "0.2.0", "adsfasdf")
err := dispatch.pluginMain(funcs, versionInfo, "")
Expect(err.Code).To(Equal(types.ErrDecodingFailure))
Expect(cmdGC.CallCount).To(Equal(0))
})
})
})
Context("when the CNI_COMMAND is DEL", func() {
BeforeEach(func() {
environment["CNI_COMMAND"] = "DEL"
})
It("calls cmdDel with the env vars and stdin data", func() {
err := dispatch.pluginMain(funcs, versionInfo, "")
Expect(err).NotTo(HaveOccurred())
Expect(cmdDel.CallCount).To(Equal(1))
Expect(cmdDel.Received.CmdArgs).To(Equal(expectedCmdArgs))
})
It("does not call cmdAdd", func() {
err := dispatch.pluginMain(funcs, versionInfo, "")
Expect(err).NotTo(HaveOccurred())
Expect(cmdAdd.CallCount).To(Equal(0))
})
DescribeTable("required / optional env vars", envVarChecker,
Entry("command", "CNI_COMMAND", true),
Entry("container id", "CNI_CONTAINERID", true),
Entry("net ns", "CNI_NETNS", false),
Entry("if name", "CNI_IFNAME", true),
Entry("args", "CNI_ARGS", false),
Entry("path", "CNI_PATH", true),
)
})
Context("when the CNI_COMMAND is VERSION", func() {
BeforeEach(func() {
environment["CNI_COMMAND"] = "VERSION"
})
It("prints the version to stdout", func() {
err := dispatch.pluginMain(funcs, versionInfo, "")
Expect(err).NotTo(HaveOccurred())
Expect(stdout).To(MatchJSON(fmt.Sprintf(`{
"cniVersion": "%s",
"supportedVersions": ["9.8.7", "10.0.0"]
}`, version.Current())))
})
It("does not call cmdAdd or cmdDel", func() {
err := dispatch.pluginMain(funcs, versionInfo, "")
Expect(err).NotTo(HaveOccurred())
Expect(cmdAdd.CallCount).To(Equal(0))
Expect(cmdDel.CallCount).To(Equal(0))
})
DescribeTable("VERSION does not need the usual env vars", envVarChecker,
Entry("command", "CNI_COMMAND", true),
Entry("container id", "CNI_CONTAINERID", false),
Entry("net ns", "CNI_NETNS", false),
Entry("if name", "CNI_IFNAME", false),
Entry("args", "CNI_ARGS", false),
Entry("path", "CNI_PATH", false),
)
It("does not read from Stdin", func() {
r := &BadReader{}
dispatch.Stdin = r
err := dispatch.pluginMain(funcs, versionInfo, "")
Expect(err).NotTo(HaveOccurred())
Expect(r.ReadCount).To(Equal(0))
Expect(stdout).To(MatchJSON(fmt.Sprintf(`{
"cniVersion": "%s",
"supportedVersions": ["9.8.7", "10.0.0"]
}`, version.Current())))
})
})
Context("when the CNI_COMMAND is unrecognized", func() {
BeforeEach(func() {
environment["CNI_COMMAND"] = "NOPE"
})
It("does not call any cmd callback", func() {
err := dispatch.pluginMain(funcs, versionInfo, "")
Expect(err).To(HaveOccurred())
Expect(cmdAdd.CallCount).To(Equal(0))
Expect(cmdDel.CallCount).To(Equal(0))
})
It("returns an error", func() {
err := dispatch.pluginMain(funcs, versionInfo, "")
Expect(err).To(Equal(&types.Error{
Code: types.ErrInvalidEnvironmentVariables,
Msg: "unknown CNI_COMMAND: NOPE",
}))
})
It("prints the about string when the command is blank", func() {
environment["CNI_COMMAND"] = ""
err := dispatch.pluginMain(funcs, versionInfo, "test framework v42")
Expect(err).NotTo(HaveOccurred())
Expect(stderr.String()).To(ContainSubstring("test framework v42"))
})
})
Context("when the CNI_COMMAND is missing", func() {
It("prints the about string to stderr", func() {
environment = map[string]string{}
err := dispatch.pluginMain(funcs, versionInfo, "AWESOME PLUGIN")
Expect(err).NotTo(HaveOccurred())
Expect(cmdAdd.CallCount).To(Equal(0))
Expect(cmdDel.CallCount).To(Equal(0))
log := stderr.String()
Expect(log).To(Equal("AWESOME PLUGIN\nCNI protocol versions supported: 9.8.7, 10.0.0\n"))
})
It("fails if there is no about string", func() {
environment = map[string]string{}
err := dispatch.pluginMain(funcs, versionInfo, "")
Expect(err).To(HaveOccurred())
Expect(cmdAdd.CallCount).To(Equal(0))
Expect(cmdDel.CallCount).To(Equal(0))
Expect(err).To(Equal(&types.Error{
Code: types.ErrInvalidEnvironmentVariables,
Msg: "required env variables [CNI_COMMAND] missing",
}))
})
})
Context("when stdin cannot be read", func() {
BeforeEach(func() {
dispatch.Stdin = &BadReader{}
})
It("does not call any cmd callback", func() {
err := dispatch.pluginMain(funcs, versionInfo, "")
Expect(err).To(HaveOccurred())
Expect(cmdAdd.CallCount).To(Equal(0))
Expect(cmdDel.CallCount).To(Equal(0))
})
It("wraps and returns the error", func() {
err := dispatch.pluginMain(funcs, versionInfo, "")
Expect(err).To(Equal(&types.Error{
Code: types.ErrIOFailure,
Msg: "error reading from stdin: banana",
}))
})
})
Context("when the callback returns an error", func() {
Context("when it is a typed Error", func() {
BeforeEach(func() {
cmdAdd.Returns.Error = &types.Error{
Code: 1234,
Msg: "insufficient something",
}
})
It("returns the error as-is", func() {
err := dispatch.pluginMain(funcs, versionInfo, "")
Expect(err).To(Equal(&types.Error{
Code: 1234,
Msg: "insufficient something",
}))
})
})
Context("when it is an unknown error", func() {
BeforeEach(func() {
cmdAdd.Returns.Error = errors.New("potato")
})
It("wraps and returns the error", func() {
err := dispatch.pluginMain(funcs, versionInfo, "")
Expect(err).To(Equal(&types.Error{
Code: types.ErrInternal,
Msg: "potato",
}))
})
})
})
})
// BadReader is an io.Reader which always errors
type BadReader struct {
Error error
ReadCount int
}
func (r *BadReader) Read(buffer []byte) (int, error) {
r.ReadCount++
if r.Error != nil {
return 0, r.Error
}
return 0, errors.New("banana")
}
func (r *BadReader) Close() error {
return nil
}

View File

@ -1,77 +0,0 @@
// Copyright 2016 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package testutils
import (
"encoding/json"
"io/ioutil"
"os"
"github.com/containernetworking/cni/pkg/types"
)
func envCleanup() {
os.Unsetenv("CNI_COMMAND")
os.Unsetenv("CNI_PATH")
os.Unsetenv("CNI_NETNS")
os.Unsetenv("CNI_IFNAME")
}
func CmdAddWithResult(cniNetns, cniIfname string, f func() error) (*types.Result, error) {
os.Setenv("CNI_COMMAND", "ADD")
os.Setenv("CNI_PATH", os.Getenv("PATH"))
os.Setenv("CNI_NETNS", cniNetns)
os.Setenv("CNI_IFNAME", cniIfname)
defer envCleanup()
// Redirect stdout to capture plugin result
oldStdout := os.Stdout
r, w, err := os.Pipe()
if err != nil {
return nil, err
}
os.Stdout = w
err = f()
w.Close()
if err != nil {
return nil, err
}
// parse the result
out, err := ioutil.ReadAll(r)
os.Stdout = oldStdout
if err != nil {
return nil, err
}
result := types.Result{}
err = json.Unmarshal(out, &result)
if err != nil {
return nil, err
}
return &result, nil
}
func CmdDelWithResult(cniNetns, cniIfname string, f func() error) error {
os.Setenv("CNI_COMMAND", "DEL")
os.Setenv("CNI_PATH", os.Getenv("PATH"))
os.Setenv("CNI_NETNS", cniNetns)
os.Setenv("CNI_IFNAME", cniIfname)
defer envCleanup()
return f()
}

189
pkg/types/020/types.go Normal file
View File

@ -0,0 +1,189 @@
// Copyright 2016 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package types020
import (
"encoding/json"
"fmt"
"io"
"net"
"os"
"github.com/containernetworking/cni/pkg/types"
convert "github.com/containernetworking/cni/pkg/types/internal"
)
const ImplementedSpecVersion string = "0.2.0"
var supportedVersions = []string{"", "0.1.0", ImplementedSpecVersion}
// Register converters for all versions less than the implemented spec version
func init() {
convert.RegisterConverter("0.1.0", []string{ImplementedSpecVersion}, convertFrom010)
convert.RegisterConverter(ImplementedSpecVersion, []string{"0.1.0"}, convertTo010)
// Creator
convert.RegisterCreator(supportedVersions, NewResult)
}
// Compatibility types for CNI version 0.1.0 and 0.2.0
// NewResult creates a new Result object from JSON data. The JSON data
// must be compatible with the CNI versions implemented by this type.
func NewResult(data []byte) (types.Result, error) {
result := &Result{}
if err := json.Unmarshal(data, result); err != nil {
return nil, err
}
for _, v := range supportedVersions {
if result.CNIVersion == v {
if result.CNIVersion == "" {
result.CNIVersion = "0.1.0"
}
return result, nil
}
}
return nil, fmt.Errorf("result type supports %v but unmarshalled CNIVersion is %q",
supportedVersions, result.CNIVersion)
}
// GetResult converts the given Result object to the ImplementedSpecVersion
// and returns the concrete type or an error
func GetResult(r types.Result) (*Result, error) {
result020, err := convert.Convert(r, ImplementedSpecVersion)
if err != nil {
return nil, err
}
result, ok := result020.(*Result)
if !ok {
return nil, fmt.Errorf("failed to convert result")
}
return result, nil
}
func convertFrom010(from types.Result, toVersion string) (types.Result, error) {
if toVersion != "0.2.0" {
panic("only converts to version 0.2.0")
}
fromResult := from.(*Result)
return &Result{
CNIVersion: ImplementedSpecVersion,
IP4: fromResult.IP4.Copy(),
IP6: fromResult.IP6.Copy(),
DNS: *fromResult.DNS.Copy(),
}, nil
}
func convertTo010(from types.Result, toVersion string) (types.Result, error) {
if toVersion != "0.1.0" {
panic("only converts to version 0.1.0")
}
fromResult := from.(*Result)
return &Result{
CNIVersion: "0.1.0",
IP4: fromResult.IP4.Copy(),
IP6: fromResult.IP6.Copy(),
DNS: *fromResult.DNS.Copy(),
}, nil
}
// Result is what gets returned from the plugin (via stdout) to the caller
type Result struct {
CNIVersion string `json:"cniVersion,omitempty"`
IP4 *IPConfig `json:"ip4,omitempty"`
IP6 *IPConfig `json:"ip6,omitempty"`
DNS types.DNS `json:"dns,omitempty"`
}
func (r *Result) Version() string {
return r.CNIVersion
}
func (r *Result) GetAsVersion(version string) (types.Result, error) {
// If the creator of the result did not set the CNIVersion, assume it
// should be the highest spec version implemented by this Result
if r.CNIVersion == "" {
r.CNIVersion = ImplementedSpecVersion
}
return convert.Convert(r, version)
}
func (r *Result) Print() error {
return r.PrintTo(os.Stdout)
}
func (r *Result) PrintTo(writer io.Writer) error {
data, err := json.MarshalIndent(r, "", " ")
if err != nil {
return err
}
_, err = writer.Write(data)
return err
}
// IPConfig contains values necessary to configure an interface
type IPConfig struct {
IP net.IPNet
Gateway net.IP
Routes []types.Route
}
func (i *IPConfig) Copy() *IPConfig {
if i == nil {
return nil
}
var routes []types.Route
for _, fromRoute := range i.Routes {
routes = append(routes, *fromRoute.Copy())
}
return &IPConfig{
IP: i.IP,
Gateway: i.Gateway,
Routes: routes,
}
}
// net.IPNet is not JSON (un)marshallable so this duality is needed
// for our custom IPNet type
// JSON (un)marshallable types
type ipConfig struct {
IP types.IPNet `json:"ip"`
Gateway net.IP `json:"gateway,omitempty"`
Routes []types.Route `json:"routes,omitempty"`
}
func (c *IPConfig) MarshalJSON() ([]byte, error) {
ipc := ipConfig{
IP: types.IPNet(c.IP),
Gateway: c.Gateway,
Routes: c.Routes,
}
return json.Marshal(ipc)
}
func (c *IPConfig) UnmarshalJSON(data []byte) error {
ipc := ipConfig{}
if err := json.Unmarshal(data, &ipc); err != nil {
return err
}
c.IP = net.IPNet(ipc.IP)
c.Gateway = ipc.Gateway
c.Routes = ipc.Routes
return nil
}

View File

@ -12,16 +12,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package utils_test
package types020_test
import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"testing"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
func TestUtils(t *testing.T) {
func TestTypes010(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Utils Suite")
RunSpecs(t, "0.1.0/0.2.0 Types Suite")
}

157
pkg/types/020/types_test.go Normal file
View File

@ -0,0 +1,157 @@
// Copyright 2016 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package types020_test
import (
"encoding/json"
"fmt"
"net"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/containernetworking/cni/pkg/types"
"github.com/containernetworking/cni/pkg/types/020"
"github.com/containernetworking/cni/pkg/types/create"
)
func testResult(resultCNIVersion, jsonCNIVersion string) (*types020.Result, string) {
ipv4, err := types.ParseCIDR("1.2.3.30/24")
Expect(err).NotTo(HaveOccurred())
Expect(ipv4).NotTo(BeNil())
routegwv4, routev4, err := net.ParseCIDR("15.5.6.8/24")
Expect(err).NotTo(HaveOccurred())
Expect(routev4).NotTo(BeNil())
Expect(routegwv4).NotTo(BeNil())
ipv6, err := types.ParseCIDR("abcd:1234:ffff::cdde/64")
Expect(err).NotTo(HaveOccurred())
Expect(ipv6).NotTo(BeNil())
routegwv6, routev6, err := net.ParseCIDR("1111:dddd::aaaa/80")
Expect(err).NotTo(HaveOccurred())
Expect(routev6).NotTo(BeNil())
Expect(routegwv6).NotTo(BeNil())
// Set every field of the struct to ensure source compatibility
res := &types020.Result{
CNIVersion: resultCNIVersion,
IP4: &types020.IPConfig{
IP: *ipv4,
Gateway: net.ParseIP("1.2.3.1"),
Routes: []types.Route{
{Dst: *routev4, GW: routegwv4},
},
},
IP6: &types020.IPConfig{
IP: *ipv6,
Gateway: net.ParseIP("abcd:1234:ffff::1"),
Routes: []types.Route{
{Dst: *routev6, GW: routegwv6},
},
},
DNS: types.DNS{
Nameservers: []string{"1.2.3.4", "1::cafe"},
Domain: "acompany.com",
Search: []string{"somedomain.com", "otherdomain.net"},
Options: []string{"foo", "bar"},
},
}
json := fmt.Sprintf(`{
"cniVersion": "%s",
"ip4": {
"ip": "1.2.3.30/24",
"gateway": "1.2.3.1",
"routes": [
{
"dst": "15.5.6.0/24",
"gw": "15.5.6.8"
}
]
},
"ip6": {
"ip": "abcd:1234:ffff::cdde/64",
"gateway": "abcd:1234:ffff::1",
"routes": [
{
"dst": "1111:dddd::/80",
"gw": "1111:dddd::aaaa"
}
]
},
"dns": {
"nameservers": [
"1.2.3.4",
"1::cafe"
],
"domain": "acompany.com",
"search": [
"somedomain.com",
"otherdomain.net"
],
"options": [
"foo",
"bar"
]
}
}`, jsonCNIVersion)
return res, json
}
var _ = Describe("Ensures compatibility with the 0.1.0/0.2.0 spec", func() {
It("correctly encodes a 0.2.0 Result", func() {
res, expectedJSON := testResult(types020.ImplementedSpecVersion, types020.ImplementedSpecVersion)
out, err := json.Marshal(res)
Expect(err).NotTo(HaveOccurred())
Expect(out).To(MatchJSON(expectedJSON))
})
It("correctly encodes a 0.1.0 Result", func() {
res, expectedJSON := testResult("0.1.0", "0.1.0")
out, err := json.Marshal(res)
Expect(err).NotTo(HaveOccurred())
Expect(out).To(MatchJSON(expectedJSON))
})
It("converts a 0.2.0 result to 0.1.0", func() {
res, expectedJSON := testResult(types020.ImplementedSpecVersion, "0.1.0")
res010, err := res.GetAsVersion("0.1.0")
Expect(err).NotTo(HaveOccurred())
out, err := json.Marshal(res010)
Expect(err).NotTo(HaveOccurred())
Expect(out).To(MatchJSON(expectedJSON))
})
It("converts a 0.1.0 result to 0.2.0", func() {
res, expectedJSON := testResult("0.1.0", types020.ImplementedSpecVersion)
res020, err := res.GetAsVersion(types020.ImplementedSpecVersion)
Expect(err).NotTo(HaveOccurred())
out, err := json.Marshal(res020)
Expect(err).NotTo(HaveOccurred())
Expect(out).To(MatchJSON(expectedJSON))
})
It("creates a 0.1.0 result passing CNIVersion ''", func() {
_, expectedJSON := testResult("", "")
resT, err := create.Create("", []byte(expectedJSON))
Expect(err).NotTo(HaveOccurred())
res010, ok := resT.(*types020.Result)
Expect(ok).To(BeTrue())
Expect(res010.CNIVersion).To(Equal("0.1.0"))
})
})

306
pkg/types/040/types.go Normal file
View File

@ -0,0 +1,306 @@
// Copyright 2016 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package types040
import (
"encoding/json"
"fmt"
"io"
"net"
"os"
"github.com/containernetworking/cni/pkg/types"
types020 "github.com/containernetworking/cni/pkg/types/020"
convert "github.com/containernetworking/cni/pkg/types/internal"
)
const ImplementedSpecVersion string = "0.4.0"
var supportedVersions = []string{"0.3.0", "0.3.1", ImplementedSpecVersion}
// Register converters for all versions less than the implemented spec version
func init() {
// Up-converters
convert.RegisterConverter("0.1.0", supportedVersions, convertFrom02x)
convert.RegisterConverter("0.2.0", supportedVersions, convertFrom02x)
convert.RegisterConverter("0.3.0", supportedVersions, convertInternal)
convert.RegisterConverter("0.3.1", supportedVersions, convertInternal)
// Down-converters
convert.RegisterConverter("0.4.0", []string{"0.3.0", "0.3.1"}, convertInternal)
convert.RegisterConverter("0.4.0", []string{"0.1.0", "0.2.0"}, convertTo02x)
convert.RegisterConverter("0.3.1", []string{"0.1.0", "0.2.0"}, convertTo02x)
convert.RegisterConverter("0.3.0", []string{"0.1.0", "0.2.0"}, convertTo02x)
// Creator
convert.RegisterCreator(supportedVersions, NewResult)
}
func NewResult(data []byte) (types.Result, error) {
result := &Result{}
if err := json.Unmarshal(data, result); err != nil {
return nil, err
}
for _, v := range supportedVersions {
if result.CNIVersion == v {
return result, nil
}
}
return nil, fmt.Errorf("result type supports %v but unmarshalled CNIVersion is %q",
supportedVersions, result.CNIVersion)
}
func GetResult(r types.Result) (*Result, error) {
resultCurrent, err := r.GetAsVersion(ImplementedSpecVersion)
if err != nil {
return nil, err
}
result, ok := resultCurrent.(*Result)
if !ok {
return nil, fmt.Errorf("failed to convert result")
}
return result, nil
}
func NewResultFromResult(result types.Result) (*Result, error) {
newResult, err := convert.Convert(result, ImplementedSpecVersion)
if err != nil {
return nil, err
}
return newResult.(*Result), nil
}
// Result is what gets returned from the plugin (via stdout) to the caller
type Result struct {
CNIVersion string `json:"cniVersion,omitempty"`
Interfaces []*Interface `json:"interfaces,omitempty"`
IPs []*IPConfig `json:"ips,omitempty"`
Routes []*types.Route `json:"routes,omitempty"`
DNS types.DNS `json:"dns,omitempty"`
}
func convert020IPConfig(from *types020.IPConfig, ipVersion string) *IPConfig {
return &IPConfig{
Version: ipVersion,
Address: from.IP,
Gateway: from.Gateway,
}
}
func convertFrom02x(from types.Result, toVersion string) (types.Result, error) {
fromResult := from.(*types020.Result)
toResult := &Result{
CNIVersion: toVersion,
DNS: *fromResult.DNS.Copy(),
Routes: []*types.Route{},
}
if fromResult.IP4 != nil {
toResult.IPs = append(toResult.IPs, convert020IPConfig(fromResult.IP4, "4"))
for _, fromRoute := range fromResult.IP4.Routes {
toResult.Routes = append(toResult.Routes, fromRoute.Copy())
}
}
if fromResult.IP6 != nil {
toResult.IPs = append(toResult.IPs, convert020IPConfig(fromResult.IP6, "6"))
for _, fromRoute := range fromResult.IP6.Routes {
toResult.Routes = append(toResult.Routes, fromRoute.Copy())
}
}
return toResult, nil
}
func convertInternal(from types.Result, toVersion string) (types.Result, error) {
fromResult := from.(*Result)
toResult := &Result{
CNIVersion: toVersion,
DNS: *fromResult.DNS.Copy(),
Routes: []*types.Route{},
}
for _, fromIntf := range fromResult.Interfaces {
toResult.Interfaces = append(toResult.Interfaces, fromIntf.Copy())
}
for _, fromIPC := range fromResult.IPs {
toResult.IPs = append(toResult.IPs, fromIPC.Copy())
}
for _, fromRoute := range fromResult.Routes {
toResult.Routes = append(toResult.Routes, fromRoute.Copy())
}
return toResult, nil
}
func convertTo02x(from types.Result, toVersion string) (types.Result, error) {
fromResult := from.(*Result)
toResult := &types020.Result{
CNIVersion: toVersion,
DNS: *fromResult.DNS.Copy(),
}
for _, fromIP := range fromResult.IPs {
// Only convert the first IP address of each version as 0.2.0
// and earlier cannot handle multiple IP addresses
if fromIP.Version == "4" && toResult.IP4 == nil {
toResult.IP4 = &types020.IPConfig{
IP: fromIP.Address,
Gateway: fromIP.Gateway,
}
} else if fromIP.Version == "6" && toResult.IP6 == nil {
toResult.IP6 = &types020.IPConfig{
IP: fromIP.Address,
Gateway: fromIP.Gateway,
}
}
if toResult.IP4 != nil && toResult.IP6 != nil {
break
}
}
for _, fromRoute := range fromResult.Routes {
is4 := fromRoute.Dst.IP.To4() != nil
if is4 && toResult.IP4 != nil {
toResult.IP4.Routes = append(toResult.IP4.Routes, types.Route{
Dst: fromRoute.Dst,
GW: fromRoute.GW,
})
} else if !is4 && toResult.IP6 != nil {
toResult.IP6.Routes = append(toResult.IP6.Routes, types.Route{
Dst: fromRoute.Dst,
GW: fromRoute.GW,
})
}
}
// 0.2.0 and earlier require at least one IP address in the Result
if toResult.IP4 == nil && toResult.IP6 == nil {
return nil, fmt.Errorf("cannot convert: no valid IP addresses")
}
return toResult, nil
}
func (r *Result) Version() string {
return r.CNIVersion
}
func (r *Result) GetAsVersion(version string) (types.Result, error) {
// If the creator of the result did not set the CNIVersion, assume it
// should be the highest spec version implemented by this Result
if r.CNIVersion == "" {
r.CNIVersion = ImplementedSpecVersion
}
return convert.Convert(r, version)
}
func (r *Result) Print() error {
return r.PrintTo(os.Stdout)
}
func (r *Result) PrintTo(writer io.Writer) error {
data, err := json.MarshalIndent(r, "", " ")
if err != nil {
return err
}
_, err = writer.Write(data)
return err
}
// Interface contains values about the created interfaces
type Interface struct {
Name string `json:"name"`
Mac string `json:"mac,omitempty"`
Sandbox string `json:"sandbox,omitempty"`
}
func (i *Interface) String() string {
return fmt.Sprintf("%+v", *i)
}
func (i *Interface) Copy() *Interface {
if i == nil {
return nil
}
newIntf := *i
return &newIntf
}
// Int returns a pointer to the int value passed in. Used to
// set the IPConfig.Interface field.
func Int(v int) *int {
return &v
}
// IPConfig contains values necessary to configure an IP address on an interface
type IPConfig struct {
// IP version, either "4" or "6"
Version string
// Index into Result structs Interfaces list
Interface *int
Address net.IPNet
Gateway net.IP
}
func (i *IPConfig) String() string {
return fmt.Sprintf("%+v", *i)
}
func (i *IPConfig) Copy() *IPConfig {
if i == nil {
return nil
}
ipc := &IPConfig{
Version: i.Version,
Address: i.Address,
Gateway: i.Gateway,
}
if i.Interface != nil {
intf := *i.Interface
ipc.Interface = &intf
}
return ipc
}
// JSON (un)marshallable types
type ipConfig struct {
Version string `json:"version"`
Interface *int `json:"interface,omitempty"`
Address types.IPNet `json:"address"`
Gateway net.IP `json:"gateway,omitempty"`
}
func (c *IPConfig) MarshalJSON() ([]byte, error) {
ipc := ipConfig{
Version: c.Version,
Interface: c.Interface,
Address: types.IPNet(c.Address),
Gateway: c.Gateway,
}
return json.Marshal(ipc)
}
func (c *IPConfig) UnmarshalJSON(data []byte) error {
ipc := ipConfig{}
if err := json.Unmarshal(data, &ipc); err != nil {
return err
}
c.Version = ipc.Version
c.Interface = ipc.Interface
c.Address = net.IPNet(ipc.Address)
c.Gateway = ipc.Gateway
return nil
}

View File

@ -12,16 +12,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package main
package types040_test
import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"testing"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
func TestMacvlan(t *testing.T) {
func TestTypesCurrent(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "macvlan Suite")
RunSpecs(t, "Current Types Suite")
}

387
pkg/types/040/types_test.go Normal file
View File

@ -0,0 +1,387 @@
// Copyright 2016 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package types040_test
import (
"encoding/json"
"io"
"net"
"os"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/containernetworking/cni/pkg/types"
types020 "github.com/containernetworking/cni/pkg/types/020"
types040 "github.com/containernetworking/cni/pkg/types/040"
)
func testResult() *types040.Result {
ipv4, err := types.ParseCIDR("1.2.3.30/24")
Expect(err).NotTo(HaveOccurred())
Expect(ipv4).NotTo(BeNil())
routegwv4, routev4, err := net.ParseCIDR("15.5.6.8/24")
Expect(err).NotTo(HaveOccurred())
Expect(routev4).NotTo(BeNil())
Expect(routegwv4).NotTo(BeNil())
ipv6, err := types.ParseCIDR("abcd:1234:ffff::cdde/64")
Expect(err).NotTo(HaveOccurred())
Expect(ipv6).NotTo(BeNil())
routegwv6, routev6, err := net.ParseCIDR("1111:dddd::aaaa/80")
Expect(err).NotTo(HaveOccurred())
Expect(routev6).NotTo(BeNil())
Expect(routegwv6).NotTo(BeNil())
// Set every field of the struct to ensure source compatibility
return &types040.Result{
CNIVersion: "0.3.1",
Interfaces: []*types040.Interface{
{
Name: "eth0",
Mac: "00:11:22:33:44:55",
Sandbox: "/proc/3553/ns/net",
},
},
IPs: []*types040.IPConfig{
{
Version: "4",
Interface: types040.Int(0),
Address: *ipv4,
Gateway: net.ParseIP("1.2.3.1"),
},
{
Version: "6",
Interface: types040.Int(0),
Address: *ipv6,
Gateway: net.ParseIP("abcd:1234:ffff::1"),
},
},
Routes: []*types.Route{
{Dst: *routev4, GW: routegwv4},
{Dst: *routev6, GW: routegwv6},
},
DNS: types.DNS{
Nameservers: []string{"1.2.3.4", "1::cafe"},
Domain: "acompany.com",
Search: []string{"somedomain.com", "otherdomain.net"},
Options: []string{"foo", "bar"},
},
}
}
var _ = Describe("040 types operations", func() {
It("correctly encodes a 0.3.x Result", func() {
res := testResult()
// Redirect stdout to capture JSON result
oldStdout := os.Stdout
r, w, err := os.Pipe()
Expect(err).NotTo(HaveOccurred())
os.Stdout = w
err = res.Print()
w.Close()
Expect(err).NotTo(HaveOccurred())
// parse the result
out, err := io.ReadAll(r)
os.Stdout = oldStdout
Expect(err).NotTo(HaveOccurred())
Expect(string(out)).To(MatchJSON(`{
"cniVersion": "0.3.1",
"interfaces": [
{
"name": "eth0",
"mac": "00:11:22:33:44:55",
"sandbox": "/proc/3553/ns/net"
}
],
"ips": [
{
"version": "4",
"interface": 0,
"address": "1.2.3.30/24",
"gateway": "1.2.3.1"
},
{
"version": "6",
"interface": 0,
"address": "abcd:1234:ffff::cdde/64",
"gateway": "abcd:1234:ffff::1"
}
],
"routes": [
{
"dst": "15.5.6.0/24",
"gw": "15.5.6.8"
},
{
"dst": "1111:dddd::/80",
"gw": "1111:dddd::aaaa"
}
],
"dns": {
"nameservers": [
"1.2.3.4",
"1::cafe"
],
"domain": "acompany.com",
"search": [
"somedomain.com",
"otherdomain.net"
],
"options": [
"foo",
"bar"
]
}
}`))
})
It("correctly encodes a 0.1.0 Result", func() {
res, err := testResult().GetAsVersion("0.1.0")
Expect(err).NotTo(HaveOccurred())
// Redirect stdout to capture JSON result
oldStdout := os.Stdout
r, w, err := os.Pipe()
Expect(err).NotTo(HaveOccurred())
os.Stdout = w
err = res.Print()
w.Close()
Expect(err).NotTo(HaveOccurred())
// parse the result
out, err := io.ReadAll(r)
os.Stdout = oldStdout
Expect(err).NotTo(HaveOccurred())
Expect(string(out)).To(MatchJSON(`{
"cniVersion": "0.1.0",
"ip4": {
"ip": "1.2.3.30/24",
"gateway": "1.2.3.1",
"routes": [
{
"dst": "15.5.6.0/24",
"gw": "15.5.6.8"
}
]
},
"ip6": {
"ip": "abcd:1234:ffff::cdde/64",
"gateway": "abcd:1234:ffff::1",
"routes": [
{
"dst": "1111:dddd::/80",
"gw": "1111:dddd::aaaa"
}
]
},
"dns": {
"nameservers": [
"1.2.3.4",
"1::cafe"
],
"domain": "acompany.com",
"search": [
"somedomain.com",
"otherdomain.net"
],
"options": [
"foo",
"bar"
]
}
}`))
})
It("correctly round-trips a 0.2.0 Result with route gateways", func() {
ipv4, err := types.ParseCIDR("1.2.3.30/24")
Expect(err).NotTo(HaveOccurred())
Expect(ipv4).NotTo(BeNil())
routegwv4, routev4, err := net.ParseCIDR("15.5.6.8/24")
Expect(err).NotTo(HaveOccurred())
Expect(routev4).NotTo(BeNil())
Expect(routegwv4).NotTo(BeNil())
ipv6, err := types.ParseCIDR("abcd:1234:ffff::cdde/64")
Expect(err).NotTo(HaveOccurred())
Expect(ipv6).NotTo(BeNil())
routegwv6, routev6, err := net.ParseCIDR("1111:dddd::aaaa/80")
Expect(err).NotTo(HaveOccurred())
Expect(routev6).NotTo(BeNil())
Expect(routegwv6).NotTo(BeNil())
// Set every field of the struct to ensure source compatibility
res := &types020.Result{
CNIVersion: types020.ImplementedSpecVersion,
IP4: &types020.IPConfig{
IP: *ipv4,
Gateway: net.ParseIP("1.2.3.1"),
Routes: []types.Route{
{Dst: *routev4, GW: routegwv4},
},
},
IP6: &types020.IPConfig{
IP: *ipv6,
Gateway: net.ParseIP("abcd:1234:ffff::1"),
Routes: []types.Route{
{Dst: *routev6, GW: routegwv6},
},
},
DNS: types.DNS{
Nameservers: []string{"1.2.3.4", "1::cafe"},
Domain: "acompany.com",
Search: []string{"somedomain.com", "otherdomain.net"},
Options: []string{"foo", "bar"},
},
}
// Convert to 040
newRes, err := types040.NewResultFromResult(res)
Expect(err).NotTo(HaveOccurred())
// Convert back to 0.2.0
oldRes, err := newRes.GetAsVersion("0.2.0")
Expect(err).NotTo(HaveOccurred())
// Match JSON so we can figure out what's wrong if something fails the test
origJson, err := json.Marshal(res)
Expect(err).NotTo(HaveOccurred())
oldJson, err := json.Marshal(oldRes)
Expect(err).NotTo(HaveOccurred())
Expect(oldJson).To(MatchJSON(origJson))
})
It("correctly round-trips a 0.2.0 Result without route gateways", func() {
ipv4, err := types.ParseCIDR("1.2.3.30/24")
Expect(err).NotTo(HaveOccurred())
Expect(ipv4).NotTo(BeNil())
_, routev4, err := net.ParseCIDR("15.5.6.0/24")
Expect(err).NotTo(HaveOccurred())
Expect(routev4).NotTo(BeNil())
ipv6, err := types.ParseCIDR("abcd:1234:ffff::cdde/64")
Expect(err).NotTo(HaveOccurred())
Expect(ipv6).NotTo(BeNil())
_, routev6, err := net.ParseCIDR("1111:dddd::aaaa/80")
Expect(err).NotTo(HaveOccurred())
Expect(routev6).NotTo(BeNil())
// Set every field of the struct to ensure source compatibility
res := &types020.Result{
CNIVersion: types020.ImplementedSpecVersion,
IP4: &types020.IPConfig{
IP: *ipv4,
Gateway: net.ParseIP("1.2.3.1"),
Routes: []types.Route{
{Dst: *routev4},
},
},
IP6: &types020.IPConfig{
IP: *ipv6,
Gateway: net.ParseIP("abcd:1234:ffff::1"),
Routes: []types.Route{
{Dst: *routev6},
},
},
DNS: types.DNS{
Nameservers: []string{"1.2.3.4", "1::cafe"},
Domain: "acompany.com",
Search: []string{"somedomain.com", "otherdomain.net"},
Options: []string{"foo", "bar"},
},
}
// Convert to 040
newRes, err := types040.NewResultFromResult(res)
Expect(err).NotTo(HaveOccurred())
// Convert back to 0.2.0
oldRes, err := newRes.GetAsVersion("0.2.0")
Expect(err).NotTo(HaveOccurred())
// Match JSON so we can figure out what's wrong if something fails the test
origJson, err := json.Marshal(res)
Expect(err).NotTo(HaveOccurred())
oldJson, err := json.Marshal(oldRes)
Expect(err).NotTo(HaveOccurred())
Expect(oldJson).To(MatchJSON(origJson))
})
It("correctly marshals and unmarshals interface index 0", func() {
ipc := &types040.IPConfig{
Version: "4",
Interface: types040.Int(0),
Address: net.IPNet{
IP: net.ParseIP("10.1.2.3"),
Mask: net.IPv4Mask(255, 255, 255, 0),
},
}
jsonBytes, err := json.Marshal(ipc)
Expect(err).NotTo(HaveOccurred())
Expect(jsonBytes).To(MatchJSON(`{
"version": "4",
"interface": 0,
"address": "10.1.2.3/24"
}`))
recovered := &types040.IPConfig{}
Expect(json.Unmarshal(jsonBytes, recovered)).To(Succeed())
Expect(recovered).To(Equal(ipc))
})
It("fails when downconverting a config to 0.2.0 that has no IPs", func() {
res := testResult()
res.IPs = nil
res.Routes = nil
_, err := res.GetAsVersion("0.2.0")
Expect(err).To(MatchError("cannot convert: no valid IP addresses"))
})
Context("when unmarshalling json fails", func() {
It("returns an error", func() {
recovered := &types040.IPConfig{}
err := json.Unmarshal([]byte(`{"address": 5}`), recovered)
Expect(err).To(MatchError(HavePrefix("json: cannot unmarshal")))
})
})
It("correctly marshals a missing interface index", func() {
ipc := &types040.IPConfig{
Version: "4",
Address: net.IPNet{
IP: net.ParseIP("10.1.2.3"),
Mask: net.IPv4Mask(255, 255, 255, 0),
},
}
json, err := json.Marshal(ipc)
Expect(err).NotTo(HaveOccurred())
Expect(json).To(MatchJSON(`{
"version": "4",
"address": "10.1.2.3/24"
}`))
})
})

352
pkg/types/100/types.go Normal file
View File

@ -0,0 +1,352 @@
// Copyright 2016 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package types100
import (
"encoding/json"
"fmt"
"io"
"net"
"os"
"github.com/containernetworking/cni/pkg/types"
types040 "github.com/containernetworking/cni/pkg/types/040"
convert "github.com/containernetworking/cni/pkg/types/internal"
)
// The types did not change between v1.0 and v1.1
const ImplementedSpecVersion string = "1.1.0"
var supportedVersions = []string{"1.0.0", "1.1.0"}
// Register converters for all versions less than the implemented spec version
func init() {
// Up-converters
convert.RegisterConverter("0.1.0", supportedVersions, convertFrom02x)
convert.RegisterConverter("0.2.0", supportedVersions, convertFrom02x)
convert.RegisterConverter("0.3.0", supportedVersions, convertFrom04x)
convert.RegisterConverter("0.3.1", supportedVersions, convertFrom04x)
convert.RegisterConverter("0.4.0", supportedVersions, convertFrom04x)
convert.RegisterConverter("1.0.0", []string{"1.1.0"}, convertFrom100)
// Down-converters
convert.RegisterConverter("1.0.0", []string{"0.3.0", "0.3.1", "0.4.0"}, convertTo04x)
convert.RegisterConverter("1.0.0", []string{"0.1.0", "0.2.0"}, convertTo02x)
convert.RegisterConverter("1.1.0", []string{"0.3.0", "0.3.1", "0.4.0"}, convertTo04x)
convert.RegisterConverter("1.1.0", []string{"0.1.0", "0.2.0"}, convertTo02x)
convert.RegisterConverter("1.1.0", []string{"1.0.0"}, convertFrom100)
// Creator
convert.RegisterCreator(supportedVersions, NewResult)
}
func NewResult(data []byte) (types.Result, error) {
result := &Result{}
if err := json.Unmarshal(data, result); err != nil {
return nil, err
}
for _, v := range supportedVersions {
if result.CNIVersion == v {
return result, nil
}
}
return nil, fmt.Errorf("result type supports %v but unmarshalled CNIVersion is %q",
supportedVersions, result.CNIVersion)
}
func GetResult(r types.Result) (*Result, error) {
resultCurrent, err := r.GetAsVersion(ImplementedSpecVersion)
if err != nil {
return nil, err
}
result, ok := resultCurrent.(*Result)
if !ok {
return nil, fmt.Errorf("failed to convert result")
}
return result, nil
}
func NewResultFromResult(result types.Result) (*Result, error) {
newResult, err := convert.Convert(result, ImplementedSpecVersion)
if err != nil {
return nil, err
}
return newResult.(*Result), nil
}
// Result is what gets returned from the plugin (via stdout) to the caller
type Result struct {
CNIVersion string `json:"cniVersion,omitempty"`
Interfaces []*Interface `json:"interfaces,omitempty"`
IPs []*IPConfig `json:"ips,omitempty"`
Routes []*types.Route `json:"routes,omitempty"`
DNS types.DNS `json:"dns,omitempty"`
}
// Note: DNS should be omit if DNS is empty but default Marshal function
// will output empty structure hence need to write a Marshal function
func (r *Result) MarshalJSON() ([]byte, error) {
// use type alias to escape recursion for json.Marshal() to MarshalJSON()
type fixObjType = Result
bytes, err := json.Marshal(fixObjType(*r)) //nolint:all
if err != nil {
return nil, err
}
fixupObj := make(map[string]interface{})
if err := json.Unmarshal(bytes, &fixupObj); err != nil {
return nil, err
}
if r.DNS.IsEmpty() {
delete(fixupObj, "dns")
}
return json.Marshal(fixupObj)
}
// convertFrom100 does nothing except set the version; the types are the same
func convertFrom100(from types.Result, toVersion string) (types.Result, error) {
fromResult := from.(*Result)
result := &Result{
CNIVersion: toVersion,
Interfaces: fromResult.Interfaces,
IPs: fromResult.IPs,
Routes: fromResult.Routes,
DNS: fromResult.DNS,
}
return result, nil
}
func convertFrom02x(from types.Result, toVersion string) (types.Result, error) {
result040, err := convert.Convert(from, "0.4.0")
if err != nil {
return nil, err
}
result100, err := convertFrom04x(result040, toVersion)
if err != nil {
return nil, err
}
return result100, nil
}
func convertIPConfigFrom040(from *types040.IPConfig) *IPConfig {
to := &IPConfig{
Address: from.Address,
Gateway: from.Gateway,
}
if from.Interface != nil {
intf := *from.Interface
to.Interface = &intf
}
return to
}
func convertInterfaceFrom040(from *types040.Interface) *Interface {
return &Interface{
Name: from.Name,
Mac: from.Mac,
Sandbox: from.Sandbox,
}
}
func convertFrom04x(from types.Result, toVersion string) (types.Result, error) {
fromResult := from.(*types040.Result)
toResult := &Result{
CNIVersion: toVersion,
DNS: *fromResult.DNS.Copy(),
Routes: []*types.Route{},
}
for _, fromIntf := range fromResult.Interfaces {
toResult.Interfaces = append(toResult.Interfaces, convertInterfaceFrom040(fromIntf))
}
for _, fromIPC := range fromResult.IPs {
toResult.IPs = append(toResult.IPs, convertIPConfigFrom040(fromIPC))
}
for _, fromRoute := range fromResult.Routes {
toResult.Routes = append(toResult.Routes, fromRoute.Copy())
}
return toResult, nil
}
func convertIPConfigTo040(from *IPConfig) *types040.IPConfig {
version := "6"
if from.Address.IP.To4() != nil {
version = "4"
}
to := &types040.IPConfig{
Version: version,
Address: from.Address,
Gateway: from.Gateway,
}
if from.Interface != nil {
intf := *from.Interface
to.Interface = &intf
}
return to
}
func convertInterfaceTo040(from *Interface) *types040.Interface {
return &types040.Interface{
Name: from.Name,
Mac: from.Mac,
Sandbox: from.Sandbox,
}
}
func convertTo04x(from types.Result, toVersion string) (types.Result, error) {
fromResult := from.(*Result)
toResult := &types040.Result{
CNIVersion: toVersion,
DNS: *fromResult.DNS.Copy(),
Routes: []*types.Route{},
}
for _, fromIntf := range fromResult.Interfaces {
toResult.Interfaces = append(toResult.Interfaces, convertInterfaceTo040(fromIntf))
}
for _, fromIPC := range fromResult.IPs {
toResult.IPs = append(toResult.IPs, convertIPConfigTo040(fromIPC))
}
for _, fromRoute := range fromResult.Routes {
toResult.Routes = append(toResult.Routes, fromRoute.Copy())
}
return toResult, nil
}
func convertTo02x(from types.Result, toVersion string) (types.Result, error) {
// First convert to 0.4.0
result040, err := convertTo04x(from, "0.4.0")
if err != nil {
return nil, err
}
result02x, err := convert.Convert(result040, toVersion)
if err != nil {
return nil, err
}
return result02x, nil
}
func (r *Result) Version() string {
return r.CNIVersion
}
func (r *Result) GetAsVersion(version string) (types.Result, error) {
// If the creator of the result did not set the CNIVersion, assume it
// should be the highest spec version implemented by this Result
if r.CNIVersion == "" {
r.CNIVersion = ImplementedSpecVersion
}
return convert.Convert(r, version)
}
func (r *Result) Print() error {
return r.PrintTo(os.Stdout)
}
func (r *Result) PrintTo(writer io.Writer) error {
data, err := json.MarshalIndent(r, "", " ")
if err != nil {
return err
}
_, err = writer.Write(data)
return err
}
// Interface contains values about the created interfaces
type Interface struct {
Name string `json:"name"`
Mac string `json:"mac,omitempty"`
Mtu int `json:"mtu,omitempty"`
Sandbox string `json:"sandbox,omitempty"`
SocketPath string `json:"socketPath,omitempty"`
PciID string `json:"pciID,omitempty"`
}
func (i *Interface) String() string {
return fmt.Sprintf("%+v", *i)
}
func (i *Interface) Copy() *Interface {
if i == nil {
return nil
}
newIntf := *i
return &newIntf
}
// Int returns a pointer to the int value passed in. Used to
// set the IPConfig.Interface field.
func Int(v int) *int {
return &v
}
// IPConfig contains values necessary to configure an IP address on an interface
type IPConfig struct {
// Index into Result structs Interfaces list
Interface *int
Address net.IPNet
Gateway net.IP
}
func (i *IPConfig) String() string {
return fmt.Sprintf("%+v", *i)
}
func (i *IPConfig) Copy() *IPConfig {
if i == nil {
return nil
}
ipc := &IPConfig{
Address: i.Address,
Gateway: i.Gateway,
}
if i.Interface != nil {
intf := *i.Interface
ipc.Interface = &intf
}
return ipc
}
// JSON (un)marshallable types
type ipConfig struct {
Interface *int `json:"interface,omitempty"`
Address types.IPNet `json:"address"`
Gateway net.IP `json:"gateway,omitempty"`
}
func (c *IPConfig) MarshalJSON() ([]byte, error) {
ipc := ipConfig{
Interface: c.Interface,
Address: types.IPNet(c.Address),
Gateway: c.Gateway,
}
return json.Marshal(ipc)
}
func (c *IPConfig) UnmarshalJSON(data []byte) error {
ipc := ipConfig{}
if err := json.Unmarshal(data, &ipc); err != nil {
return err
}
c.Interface = ipc.Interface
c.Address = net.IPNet(ipc.Address)
c.Gateway = ipc.Gateway
return nil
}

View File

@ -12,16 +12,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package main
package types100_test
import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"testing"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
func TestBridge(t *testing.T) {
func TestTypesCurrent(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "bridge Suite")
RunSpecs(t, "Current Types Suite")
}

345
pkg/types/100/types_test.go Normal file
View File

@ -0,0 +1,345 @@
// Copyright 2016 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package types100_test
import (
"encoding/json"
"io"
"net"
"os"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/containernetworking/cni/pkg/types"
current "github.com/containernetworking/cni/pkg/types/100"
)
func testResult() *current.Result {
ipv4, err := types.ParseCIDR("1.2.3.30/24")
Expect(err).NotTo(HaveOccurred())
Expect(ipv4).NotTo(BeNil())
routegwv4, routev4, err := net.ParseCIDR("15.5.6.8/24")
Expect(err).NotTo(HaveOccurred())
Expect(routev4).NotTo(BeNil())
Expect(routegwv4).NotTo(BeNil())
ipv6, err := types.ParseCIDR("abcd:1234:ffff::cdde/64")
Expect(err).NotTo(HaveOccurred())
Expect(ipv6).NotTo(BeNil())
routegwv6, routev6, err := net.ParseCIDR("1111:dddd::aaaa/80")
Expect(err).NotTo(HaveOccurred())
Expect(routev6).NotTo(BeNil())
Expect(routegwv6).NotTo(BeNil())
// Set every field of the struct to ensure source compatibility
return &current.Result{
CNIVersion: current.ImplementedSpecVersion,
Interfaces: []*current.Interface{
{
Name: "eth0",
Mac: "00:11:22:33:44:55",
Mtu: 1500,
Sandbox: "/proc/3553/ns/net",
PciID: "8086:9a01",
SocketPath: "/path/to/vhost/fd",
},
},
IPs: []*current.IPConfig{
{
Interface: current.Int(0),
Address: *ipv4,
Gateway: net.ParseIP("1.2.3.1"),
},
{
Interface: current.Int(0),
Address: *ipv6,
Gateway: net.ParseIP("abcd:1234:ffff::1"),
},
},
Routes: []*types.Route{
{Dst: *routev4, GW: routegwv4},
{Dst: *routev6, GW: routegwv6},
},
DNS: types.DNS{
Nameservers: []string{"1.2.3.4", "1::cafe"},
Domain: "acompany.com",
Search: []string{"somedomain.com", "otherdomain.net"},
Options: []string{"foo", "bar"},
},
}
}
var _ = Describe("Current types operations", func() {
It("correctly encodes a 1.1.0 Result", func() {
res := testResult()
// Redirect stdout to capture JSON result
oldStdout := os.Stdout
r, w, err := os.Pipe()
Expect(err).NotTo(HaveOccurred())
os.Stdout = w
err = res.Print()
w.Close()
Expect(err).NotTo(HaveOccurred())
// parse the result
out, err := io.ReadAll(r)
os.Stdout = oldStdout
Expect(err).NotTo(HaveOccurred())
Expect(string(out)).To(MatchJSON(`{
"cniVersion": "` + current.ImplementedSpecVersion + `",
"interfaces": [
{
"name": "eth0",
"mac": "00:11:22:33:44:55",
"mtu": 1500,
"sandbox": "/proc/3553/ns/net",
"pciID": "8086:9a01",
"socketPath": "/path/to/vhost/fd"
}
],
"ips": [
{
"interface": 0,
"address": "1.2.3.30/24",
"gateway": "1.2.3.1"
},
{
"interface": 0,
"address": "abcd:1234:ffff::cdde/64",
"gateway": "abcd:1234:ffff::1"
}
],
"routes": [
{
"dst": "15.5.6.0/24",
"gw": "15.5.6.8"
},
{
"dst": "1111:dddd::/80",
"gw": "1111:dddd::aaaa"
}
],
"dns": {
"nameservers": [
"1.2.3.4",
"1::cafe"
],
"domain": "acompany.com",
"search": [
"somedomain.com",
"otherdomain.net"
],
"options": [
"foo",
"bar"
]
}
}`))
})
It("correctly converts a 1.0.0 Result to 1.1.0", func() {
tr, err := testResult().GetAsVersion("1.0.0")
Expect(err).NotTo(HaveOccurred())
trv1, ok := tr.(*current.Result)
Expect(ok).To(BeTrue())
// 1.0.0 and 1.1.0 should be the same except for CNI version
Expect(trv1.CNIVersion).To(Equal("1.0.0"))
// If we convert 1.0.0 back to 1.1.0 it should be identical
trv11, err := trv1.GetAsVersion("1.1.0")
Expect(err).NotTo(HaveOccurred())
Expect(trv11).To(Equal(testResult()))
})
It("correctly encodes a 0.1.0 Result", func() {
res, err := testResult().GetAsVersion("0.1.0")
Expect(err).NotTo(HaveOccurred())
// Redirect stdout to capture JSON result
oldStdout := os.Stdout
r, w, err := os.Pipe()
Expect(err).NotTo(HaveOccurred())
os.Stdout = w
err = res.Print()
w.Close()
Expect(err).NotTo(HaveOccurred())
// parse the result
out, err := io.ReadAll(r)
os.Stdout = oldStdout
Expect(err).NotTo(HaveOccurred())
Expect(string(out)).To(MatchJSON(`{
"cniVersion": "0.1.0",
"ip4": {
"ip": "1.2.3.30/24",
"gateway": "1.2.3.1",
"routes": [
{
"dst": "15.5.6.0/24",
"gw": "15.5.6.8"
}
]
},
"ip6": {
"ip": "abcd:1234:ffff::cdde/64",
"gateway": "abcd:1234:ffff::1",
"routes": [
{
"dst": "1111:dddd::/80",
"gw": "1111:dddd::aaaa"
}
]
},
"dns": {
"nameservers": [
"1.2.3.4",
"1::cafe"
],
"domain": "acompany.com",
"search": [
"somedomain.com",
"otherdomain.net"
],
"options": [
"foo",
"bar"
]
}
}`))
})
It("correctly encodes a 0.4.0 Result", func() {
res, err := testResult().GetAsVersion("0.4.0")
Expect(err).NotTo(HaveOccurred())
// Redirect stdout to capture JSON result
oldStdout := os.Stdout
r, w, err := os.Pipe()
Expect(err).NotTo(HaveOccurred())
os.Stdout = w
err = res.Print()
w.Close()
Expect(err).NotTo(HaveOccurred())
// parse the result
out, err := io.ReadAll(r)
os.Stdout = oldStdout
Expect(err).NotTo(HaveOccurred())
Expect(string(out)).To(MatchJSON(`{
"cniVersion": "0.4.0",
"interfaces": [
{
"name": "eth0",
"mac": "00:11:22:33:44:55",
"sandbox": "/proc/3553/ns/net"
}
],
"ips": [
{
"interface": 0,
"version": "4",
"address": "1.2.3.30/24",
"gateway": "1.2.3.1"
},
{
"interface": 0,
"version": "6",
"address": "abcd:1234:ffff::cdde/64",
"gateway": "abcd:1234:ffff::1"
}
],
"routes": [
{
"dst": "15.5.6.0/24",
"gw": "15.5.6.8"
},
{
"dst": "1111:dddd::/80",
"gw": "1111:dddd::aaaa"
}
],
"dns": {
"nameservers": [
"1.2.3.4",
"1::cafe"
],
"domain": "acompany.com",
"search": [
"somedomain.com",
"otherdomain.net"
],
"options": [
"foo",
"bar"
]
}
}`))
})
It("correctly marshals and unmarshals interface index 0", func() {
ipc := &current.IPConfig{
Interface: current.Int(0),
Address: net.IPNet{
IP: net.ParseIP("10.1.2.3"),
Mask: net.IPv4Mask(255, 255, 255, 0),
},
}
jsonBytes, err := json.Marshal(ipc)
Expect(err).NotTo(HaveOccurred())
Expect(jsonBytes).To(MatchJSON(`{
"interface": 0,
"address": "10.1.2.3/24"
}`))
recovered := &current.IPConfig{}
Expect(json.Unmarshal(jsonBytes, recovered)).To(Succeed())
Expect(recovered).To(Equal(ipc))
})
Context("when unmarshalling json fails", func() {
It("returns an error", func() {
recovered := &current.IPConfig{}
err := json.Unmarshal([]byte(`{"address": 5}`), recovered)
Expect(err).To(MatchError(HavePrefix("json: cannot unmarshal")))
})
})
It("correctly marshals a missing interface index", func() {
ipc := &current.IPConfig{
Address: net.IPNet{
IP: net.ParseIP("10.1.2.3"),
Mask: net.IPv4Mask(255, 255, 255, 0),
},
}
json, err := json.Marshal(ipc)
Expect(err).NotTo(HaveOccurred())
Expect(json).To(MatchJSON(`{
"address": "10.1.2.3/24"
}`))
})
})

View File

@ -26,8 +26,8 @@ import (
type UnmarshallableBool bool
// UnmarshalText implements the encoding.TextUnmarshaler interface.
// Returns boolean true if the string is "1" or "[Tt]rue"
// Returns boolean false if the string is "0" or "[Ff]alse"
// Returns boolean true if the string is "1" or "true" or "True"
// Returns boolean false if the string is "0" or "false" or "False”
func (b *UnmarshallableBool) UnmarshalText(data []byte) error {
s := strings.ToLower(string(data))
switch s {
@ -36,11 +36,21 @@ func (b *UnmarshallableBool) UnmarshalText(data []byte) error {
case "0", "false":
*b = false
default:
return fmt.Errorf("Boolean unmarshal error: invalid input %s", s)
return fmt.Errorf("boolean unmarshal error: invalid input %s", s)
}
return nil
}
// UnmarshallableString typedef for builtin string
type UnmarshallableString string
// UnmarshalText implements the encoding.TextUnmarshaler interface.
// Returns the string
func (s *UnmarshallableString) UnmarshalText(data []byte) error {
*s = UnmarshallableString(data)
return nil
}
// CommonArgs contains the IgnoreUnknown argument
// and must be embedded by all Arg structs
type CommonArgs struct {
@ -53,6 +63,12 @@ func GetKeyField(keyString string, v reflect.Value) reflect.Value {
return v.Elem().FieldByName(keyString)
}
// UnmarshalableArgsError is used to indicate error unmarshalling args
// from the args-string in the form "K=V;K2=V2;..."
type UnmarshalableArgsError struct {
error
}
// LoadArgs parses args from a string in the form "K=V;K2=V2;..."
func LoadArgs(args string, container interface{}) error {
if args == "" {
@ -76,10 +92,25 @@ func LoadArgs(args string, container interface{}) error {
continue
}
u := keyField.Addr().Interface().(encoding.TextUnmarshaler)
var keyFieldInterface interface{}
switch {
case keyField.Kind() == reflect.Ptr:
keyField.Set(reflect.New(keyField.Type().Elem()))
keyFieldInterface = keyField.Interface()
case keyField.CanAddr() && keyField.Addr().CanInterface():
keyFieldInterface = keyField.Addr().Interface()
default:
return UnmarshalableArgsError{fmt.Errorf("field '%s' has no valid interface", keyString)}
}
u, ok := keyFieldInterface.(encoding.TextUnmarshaler)
if !ok {
return UnmarshalableArgsError{fmt.Errorf(
"ARGS: cannot unmarshal into field '%s' - type '%s' does not implement encoding.TextUnmarshaler",
keyString, reflect.TypeOf(keyFieldInterface))}
}
err := u.UnmarshalText([]byte(valueString))
if err != nil {
return fmt.Errorf("ARGS: error parsing value of pair %q: %v)", pair, err)
return fmt.Errorf("ARGS: error parsing value of pair %q: %w", pair, err)
}
}

View File

@ -15,13 +15,13 @@
package types_test
import (
"net"
"reflect"
. "github.com/containernetworking/cni/pkg/types"
. "github.com/onsi/ginkgo"
. "github.com/onsi/ginkgo/extensions/table"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
. "github.com/containernetworking/cni/pkg/types"
)
var _ = Describe("UnmarshallableBool UnmarshalText", func() {
@ -47,6 +47,21 @@ var _ = Describe("UnmarshallableBool UnmarshalText", func() {
})
})
var _ = Describe("UnmarshallableString UnmarshalText", func() {
DescribeTable("string to string detection should succeed in all cases",
func(inputs []string, expected string) {
for _, s := range inputs {
var us UnmarshallableString
err := us.UnmarshalText([]byte(s))
Expect(err).ToNot(HaveOccurred())
Expect(string(us)).To(Equal(expected))
}
},
Entry("parse empty string", []string{""}, ""),
Entry("parse non-empty string", []string{"notempty"}, "notempty"),
)
})
var _ = Describe("GetKeyField", func() {
type testcontainer struct {
Valid string `json:"valid,omitempty"`
@ -103,4 +118,45 @@ var _ = Describe("LoadArgs", func() {
Expect(err).NotTo(HaveOccurred())
})
})
Context("When known arguments are passed and cannot be unmarshalled", func() {
It("LoadArgs should fail", func() {
conf := struct {
IP IPNet
}{}
err := LoadArgs("IP=10.0.0.0/24", &conf)
Expect(err).To(HaveOccurred())
})
})
Context("When loading known arguments", func() {
It("should succeed if argument is marshallable value type", func() {
conf := struct {
IP net.IP
CommonArgs
}{}
err := LoadArgs("IP=10.0.0.0", &conf)
Expect(err).NotTo(HaveOccurred())
Expect(conf.IP.String()).To(Equal("10.0.0.0"))
})
It("should succeed if argument is marshallable pointer type", func() {
conf := struct {
IP *net.IP
CommonArgs
}{}
err := LoadArgs("IP=10.0.0.0", &conf)
Expect(err).NotTo(HaveOccurred())
Expect(conf.IP.String()).To(Equal("10.0.0.0"))
})
It("should fail if argument is pointer of marshallable pointer type", func() {
conf := struct {
IP **net.IP
CommonArgs
}{}
err := LoadArgs("IP=10.0.0.0", &conf)
Expect(err).To(HaveOccurred())
})
})
})

View File

@ -0,0 +1,59 @@
// Copyright 2016 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package create
import (
"encoding/json"
"fmt"
"github.com/containernetworking/cni/pkg/types"
_ "github.com/containernetworking/cni/pkg/types/020"
_ "github.com/containernetworking/cni/pkg/types/040"
_ "github.com/containernetworking/cni/pkg/types/100"
convert "github.com/containernetworking/cni/pkg/types/internal"
)
// DecodeVersion returns the CNI version from CNI configuration or result JSON,
// or an error if the operation could not be performed.
func DecodeVersion(jsonBytes []byte) (string, error) {
var conf struct {
CNIVersion string `json:"cniVersion"`
}
err := json.Unmarshal(jsonBytes, &conf)
if err != nil {
return "", fmt.Errorf("decoding version from network config: %w", err)
}
if conf.CNIVersion == "" {
return "0.1.0", nil
}
return conf.CNIVersion, nil
}
// Create creates a CNI Result using the given JSON with the expected
// version, or an error if the creation could not be performed
func Create(version string, bytes []byte) (types.Result, error) {
return convert.Create(version, bytes)
}
// CreateFromBytes creates a CNI Result from the given JSON, automatically
// detecting the CNI spec version of the result. An error is returned if the
// operation could not be performed.
func CreateFromBytes(bytes []byte) (types.Result, error) {
version, err := DecodeVersion(bytes)
if err != nil {
return nil, err
}
return convert.Create(version, bytes)
}

View File

@ -0,0 +1,92 @@
// Copyright 2016 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package convert
import (
"fmt"
"github.com/containernetworking/cni/pkg/types"
)
// ConvertFn should convert from the given arbitrary Result type into a
// Result implementing CNI specification version passed in toVersion.
// The function is guaranteed to be passed a Result type matching the
// fromVersion it was registered with, and is guaranteed to be
// passed a toVersion matching one of the toVersions it was registered with.
type ConvertFn func(from types.Result, toVersion string) (types.Result, error)
type converter struct {
// fromVersion is the CNI Result spec version that convertFn accepts
fromVersion string
// toVersions is a list of versions that convertFn can convert to
toVersions []string
convertFn ConvertFn
}
var converters []*converter
func findConverter(fromVersion, toVersion string) *converter {
for _, c := range converters {
if c.fromVersion == fromVersion {
for _, v := range c.toVersions {
if v == toVersion {
return c
}
}
}
}
return nil
}
// Convert converts a CNI Result to the requested CNI specification version,
// or returns an error if the conversion could not be performed or failed
func Convert(from types.Result, toVersion string) (types.Result, error) {
if toVersion == "" {
toVersion = "0.1.0"
}
fromVersion := from.Version()
// Shortcut for same version
if fromVersion == toVersion {
return from, nil
}
// Otherwise find the right converter
c := findConverter(fromVersion, toVersion)
if c == nil {
return nil, fmt.Errorf("no converter for CNI result version %s to %s",
fromVersion, toVersion)
}
return c.convertFn(from, toVersion)
}
// RegisterConverter registers a CNI Result converter. SHOULD NOT BE CALLED
// EXCEPT FROM CNI ITSELF.
func RegisterConverter(fromVersion string, toVersions []string, convertFn ConvertFn) {
// Make sure there is no converter already registered for these
// from and to versions
for _, v := range toVersions {
if findConverter(fromVersion, v) != nil {
panic(fmt.Sprintf("converter already registered for %s to %s",
fromVersion, v))
}
}
converters = append(converters, &converter{
fromVersion: fromVersion,
toVersions: toVersions,
convertFn: convertFn,
})
}

View File

@ -0,0 +1,66 @@
// Copyright 2016 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package convert
import (
"fmt"
"github.com/containernetworking/cni/pkg/types"
)
type ResultFactoryFunc func([]byte) (types.Result, error)
type creator struct {
// CNI Result spec versions that createFn can create a Result for
versions []string
createFn ResultFactoryFunc
}
var creators []*creator
func findCreator(version string) *creator {
for _, c := range creators {
for _, v := range c.versions {
if v == version {
return c
}
}
}
return nil
}
// Create creates a CNI Result using the given JSON, or an error if the creation
// could not be performed
func Create(version string, bytes []byte) (types.Result, error) {
if c := findCreator(version); c != nil {
return c.createFn(bytes)
}
return nil, fmt.Errorf("unsupported CNI result version %q", version)
}
// RegisterCreator registers a CNI Result creator. SHOULD NOT BE CALLED
// EXCEPT FROM CNI ITSELF.
func RegisterCreator(versions []string, createFn ResultFactoryFunc) {
// Make sure there is no creator already registered for these versions
for _, v := range versions {
if findCreator(v) != nil {
panic(fmt.Sprintf("creator already registered for %s", v))
}
}
creators = append(creators, &creator{
versions: versions,
createFn: createFn,
})
}

View File

@ -17,6 +17,7 @@ package types
import (
"encoding/json"
"fmt"
"io"
"net"
"os"
)
@ -55,46 +56,97 @@ func (n *IPNet) UnmarshalJSON(data []byte) error {
return nil
}
// NetConf describes a network.
type NetConf struct {
Name string `json:"name,omitempty"`
// Use PluginConf instead of NetConf, the NetConf
// 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"`
Name string `json:"name,omitempty"`
Type string `json:"type,omitempty"`
Capabilities map[string]bool `json:"capabilities,omitempty"`
IPAM IPAM `json:"ipam,omitempty"`
DNS DNS `json:"dns,omitempty"`
RawPrevResult map[string]interface{} `json:"prevResult,omitempty"`
PrevResult Result `json:"-"`
// ValidAttachments is only supplied when executing a GC operation
ValidAttachments []GCAttachment `json:"cni.dev/valid-attachments,omitempty"`
}
// GCAttachment is the parameters to a GC call -- namely,
// the container ID and ifname pair that represents a
// still-valid attachment.
type GCAttachment struct {
ContainerID string `json:"containerID"`
IfName string `json:"ifname"`
}
// Note: DNS should be omit if DNS is empty but default Marshal function
// will output empty structure hence need to write a Marshal function
func (n *PluginConf) MarshalJSON() ([]byte, error) {
bytes, err := json.Marshal(*n)
if err != nil {
return nil, err
}
fixupObj := make(map[string]interface{})
if err := json.Unmarshal(bytes, &fixupObj); err != nil {
return nil, err
}
if n.DNS.IsEmpty() {
delete(fixupObj, "dns")
}
return json.Marshal(fixupObj)
}
type IPAM struct {
Type string `json:"type,omitempty"`
IPAM struct {
Type string `json:"type,omitempty"`
} `json:"ipam,omitempty"`
DNS DNS `json:"dns"`
}
// Result is what gets returned from the plugin (via stdout) to the caller
type Result struct {
IP4 *IPConfig `json:"ip4,omitempty"`
IP6 *IPConfig `json:"ip6,omitempty"`
DNS DNS `json:"dns,omitempty"`
// IsEmpty returns true if IPAM structure has no value, otherwise return false
func (i *IPAM) IsEmpty() bool {
return i.Type == ""
}
func (r *Result) Print() error {
return prettyPrint(r)
// NetConfList describes an ordered list of networks.
type NetConfList struct {
CNIVersion string `json:"cniVersion,omitempty"`
Name string `json:"name,omitempty"`
DisableCheck bool `json:"disableCheck,omitempty"`
DisableGC bool `json:"disableGC,omitempty"`
Plugins []*PluginConf `json:"plugins,omitempty"`
}
// String returns a formatted string in the form of "[IP4: $1,][ IP6: $2,] DNS: $3" where
// $1 represents the receiver's IPv4, $2 represents the receiver's IPv6 and $3 the
// receiver's DNS. If $1 or $2 are nil, they won't be present in the returned string.
func (r *Result) String() string {
var str string
if r.IP4 != nil {
str = fmt.Sprintf("IP4:%+v, ", *r.IP4)
// Result is an interface that provides the result of plugin execution
type Result interface {
// The highest CNI specification result version the result supports
// without having to convert
Version() string
// Returns the result converted into the requested CNI specification
// result version, or an error if conversion failed
GetAsVersion(version string) (Result, error)
// Prints the result in JSON format to stdout
Print() error
// Prints the result in JSON format to provided writer
PrintTo(writer io.Writer) error
}
func PrintResult(result Result, version string) error {
newResult, err := result.GetAsVersion(version)
if err != nil {
return err
}
if r.IP6 != nil {
str += fmt.Sprintf("IP6:%+v, ", *r.IP6)
}
return fmt.Sprintf("%sDNS:%+v", str, r.DNS)
}
// IPConfig contains values necessary to configure an interface
type IPConfig struct {
IP net.IPNet
Gateway net.IP
Routes []Route
return newResult.Print()
}
// DNS contains values interesting for DNS resolvers
@ -105,19 +157,115 @@ type DNS struct {
Options []string `json:"options,omitempty"`
}
type Route struct {
Dst net.IPNet
GW net.IP
// IsEmpty returns true if DNS structure has no value, otherwise return false
func (d *DNS) IsEmpty() bool {
if len(d.Nameservers) == 0 && d.Domain == "" && len(d.Search) == 0 && len(d.Options) == 0 {
return true
}
return false
}
func (d *DNS) Copy() *DNS {
if d == nil {
return nil
}
to := &DNS{Domain: d.Domain}
to.Nameservers = append(to.Nameservers, d.Nameservers...)
to.Search = append(to.Search, d.Search...)
to.Options = append(to.Options, d.Options...)
return to
}
type Route struct {
Dst net.IPNet
GW net.IP
MTU int
AdvMSS int
Priority int
Table *int
Scope *int
}
func (r *Route) String() string {
table := "<nil>"
if r.Table != nil {
table = fmt.Sprintf("%d", *r.Table)
}
scope := "<nil>"
if r.Scope != nil {
scope = fmt.Sprintf("%d", *r.Scope)
}
return fmt.Sprintf("{Dst:%+v GW:%v MTU:%d AdvMSS:%d Priority:%d Table:%s Scope:%s}", r.Dst, r.GW, r.MTU, r.AdvMSS, r.Priority, table, scope)
}
func (r *Route) Copy() *Route {
if r == nil {
return nil
}
route := &Route{
Dst: r.Dst,
GW: r.GW,
MTU: r.MTU,
AdvMSS: r.AdvMSS,
Priority: r.Priority,
Scope: r.Scope,
}
if r.Table != nil {
table := *r.Table
route.Table = &table
}
if r.Scope != nil {
scope := *r.Scope
route.Scope = &scope
}
return route
}
// Well known error codes
// see https://github.com/containernetworking/cni/blob/main/SPEC.md#error
const (
ErrUnknown uint = iota // 0
ErrIncompatibleCNIVersion // 1
ErrUnsupportedField // 2
ErrUnknownContainer // 3
ErrInvalidEnvironmentVariables // 4
ErrIOFailure // 5
ErrDecodingFailure // 6
ErrInvalidNetworkConfig // 7
ErrInvalidNetNS // 8
ErrTryAgainLater uint = 11
ErrPluginNotAvailable uint = 50
ErrLimitedConnectivity uint = 51
ErrInternal uint = 999
)
type Error struct {
Code uint `json:"code"`
Msg string `json:"msg"`
Details string `json:"details,omitempty"`
}
func NewError(code uint, msg, details string) *Error {
return &Error{
Code: code,
Msg: msg,
Details: details,
}
}
func (e *Error) Error() string {
return e.Msg
details := ""
if e.Details != "" {
details = fmt.Sprintf("; %v", e.Details)
}
return fmt.Sprintf("%v%v", e.Msg, details)
}
func (e *Error) Print() error {
@ -128,37 +276,14 @@ func (e *Error) Print() error {
// for our custom IPNet type
// JSON (un)marshallable types
type ipConfig struct {
IP IPNet `json:"ip"`
Gateway net.IP `json:"gateway,omitempty"`
Routes []Route `json:"routes,omitempty"`
}
type route struct {
Dst IPNet `json:"dst"`
GW net.IP `json:"gw,omitempty"`
}
func (c *IPConfig) MarshalJSON() ([]byte, error) {
ipc := ipConfig{
IP: IPNet(c.IP),
Gateway: c.Gateway,
Routes: c.Routes,
}
return json.Marshal(ipc)
}
func (c *IPConfig) UnmarshalJSON(data []byte) error {
ipc := ipConfig{}
if err := json.Unmarshal(data, &ipc); err != nil {
return err
}
c.IP = net.IPNet(ipc.IP)
c.Gateway = ipc.Gateway
c.Routes = ipc.Routes
return nil
Dst IPNet `json:"dst"`
GW net.IP `json:"gw,omitempty"`
MTU int `json:"mtu,omitempty"`
AdvMSS int `json:"advmss,omitempty"`
Priority int `json:"priority,omitempty"`
Table *int `json:"table,omitempty"`
Scope *int `json:"scope,omitempty"`
}
func (r *Route) UnmarshalJSON(data []byte) error {
@ -169,13 +294,24 @@ func (r *Route) UnmarshalJSON(data []byte) error {
r.Dst = net.IPNet(rt.Dst)
r.GW = rt.GW
r.MTU = rt.MTU
r.AdvMSS = rt.AdvMSS
r.Priority = rt.Priority
r.Table = rt.Table
r.Scope = rt.Scope
return nil
}
func (r *Route) MarshalJSON() ([]byte, error) {
func (r Route) MarshalJSON() ([]byte, error) {
rt := route{
Dst: IPNet(r.Dst),
GW: r.GW,
Dst: IPNet(r.Dst),
GW: r.GW,
MTU: r.MTU,
AdvMSS: r.AdvMSS,
Priority: r.Priority,
Table: r.Table,
Scope: r.Scope,
}
return json.Marshal(rt)

View File

@ -15,10 +15,10 @@
package types_test
import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"testing"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
func TestTypes(t *testing.T) {

201
pkg/types/types_test.go Normal file
View File

@ -0,0 +1,201 @@
// Copyright 2017 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package types_test
import (
"encoding/json"
"net"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/containernetworking/cni/pkg/types"
types040 "github.com/containernetworking/cni/pkg/types/040"
current "github.com/containernetworking/cni/pkg/types/100"
)
var _ = Describe("Types", func() {
Describe("ParseCIDR", func() {
DescribeTable("Parse and stringify",
func(input, expectedIP string, expectedMask int) {
ipn, err := types.ParseCIDR(input)
Expect(err).NotTo(HaveOccurred())
Expect(ipn.String()).To(Equal(input))
Expect(ipn.IP.String()).To(Equal(expectedIP))
ones, _ := ipn.Mask.Size()
Expect(ones).To(Equal(expectedMask))
},
Entry("ipv4", "1.2.3.4/24", "1.2.3.4", 24),
Entry("ipv6", "2001:db8::/32", "2001:db8::", 32),
)
It("returns an error when given invalid inputs", func() {
ipn, err := types.ParseCIDR("1.2.3/45")
Expect(ipn).To(BeNil())
Expect(err).To(MatchError("invalid CIDR address: 1.2.3/45"))
})
})
Describe("custom IPNet type", func() {
It("marshals and unmarshals to JSON as a string", func() {
ipn := types.IPNet{
IP: net.ParseIP("1.2.3.4"),
Mask: net.CIDRMask(24, 32),
}
jsonBytes, err := json.Marshal(ipn)
Expect(err).NotTo(HaveOccurred())
Expect(jsonBytes).To(MatchJSON(`"1.2.3.4/24"`))
var unmarshaled types.IPNet
Expect(json.Unmarshal(jsonBytes, &unmarshaled)).To(Succeed())
Expect(unmarshaled).To(Equal(ipn))
})
Context("when the json data is not syntactically valid", func() {
Specify("UnmarshalJSON returns an error", func() {
ipn := new(types.IPNet)
err := ipn.UnmarshalJSON([]byte("1"))
Expect(err).To(MatchError("json: cannot unmarshal number into Go value of type string"))
})
})
Context("when the json data is not semantically valid", func() {
Specify("UnmarshalJSON returns an error", func() {
ipn := new(types.IPNet)
err := ipn.UnmarshalJSON([]byte(`"1.2.3.4/99"`))
Expect(err).To(MatchError("invalid CIDR address: 1.2.3.4/99"))
})
})
})
Describe("custom Route type", func() {
var example types.Route
BeforeEach(func() {
example = types.Route{
Dst: net.IPNet{
IP: net.ParseIP("1.2.3.0"),
Mask: net.CIDRMask(24, 32),
},
GW: net.ParseIP("1.2.3.1"),
MTU: 1500,
AdvMSS: 1340,
Priority: 100,
Table: types040.Int(50),
Scope: types040.Int(253),
}
})
It("marshals and unmarshals to JSON", func() {
jsonBytes, err := json.Marshal(example)
Expect(err).NotTo(HaveOccurred())
Expect(jsonBytes).To(MatchJSON(`{ "dst": "1.2.3.0/24", "gw": "1.2.3.1", "mtu": 1500, "advmss": 1340, "priority": 100, "table": 50, "scope": 253 }`))
var unmarshaled types.Route
Expect(json.Unmarshal(jsonBytes, &unmarshaled)).To(Succeed())
Expect(unmarshaled).To(Equal(example))
})
Context("when the json data is not valid", func() {
Specify("UnmarshalJSON returns an error", func() {
route := new(types.Route)
err := route.UnmarshalJSON([]byte(`{ "dst": "1.2.3.0/24", "gw": "1.2.3.x" }`))
Expect(err).To(MatchError("invalid IP address: 1.2.3.x"))
})
})
It("formats as a string with a hex mask", func() {
Expect(example.String()).To(Equal(`{Dst:{IP:1.2.3.0 Mask:ffffff00} GW:1.2.3.1 MTU:1500 AdvMSS:1340 Priority:100 Table:50 Scope:253}`))
})
})
Describe("Error type", func() {
var example *types.Error
BeforeEach(func() {
example = &types.Error{
Code: 1234,
Msg: "some message",
Details: "some details",
}
})
Describe("Error() method (basic string)", func() {
It("returns a formatted string", func() {
Expect(example.Error()).To(Equal("some message; some details"))
})
Context("when details are not present", func() {
BeforeEach(func() {
example.Details = ""
})
It("returns only the message", func() {
Expect(example.Error()).To(Equal("some message"))
})
})
})
It("NewError method", func() {
err := types.NewError(1234, "some message", "some details")
Expect(err).To(Equal(example))
})
})
Describe("Result conversion", func() {
var result *current.Result
BeforeEach(func() {
ipv4, err := types.ParseCIDR("1.2.3.30/24")
Expect(err).NotTo(HaveOccurred())
Expect(ipv4).NotTo(BeNil())
ipv6, err := types.ParseCIDR("abcd:1234:ffff::cdde/64")
Expect(err).NotTo(HaveOccurred())
Expect(ipv6).NotTo(BeNil())
result = &current.Result{
CNIVersion: "1.0.0",
Interfaces: []*current.Interface{
{
Name: "eth0",
Mac: "00:11:22:33:44:55",
Sandbox: "/proc/3553/ns/net",
PciID: "8086:9a01",
SocketPath: "/path/to/vhost/fd",
},
},
IPs: []*current.IPConfig{
{
Interface: current.Int(0),
Address: *ipv4,
Gateway: net.ParseIP("1.2.3.1"),
},
{
Interface: current.Int(0),
Address: *ipv6,
Gateway: net.ParseIP("abcd:1234:ffff::1"),
},
},
DNS: types.DNS{
Nameservers: []string{"1.2.3.4", "1::cafe"},
Domain: "acompany.com",
Search: []string{"somedomain.com", "otherdomain.net"},
Options: []string{"foo", "bar"},
},
}
})
It("can create a CNIVersion '' (0.1.0) result", func() {
newResult, err := result.GetAsVersion("")
Expect(err).NotTo(HaveOccurred())
Expect(newResult.Version()).To(Equal("0.1.0"))
})
})
})

View File

@ -1,58 +0,0 @@
// Copyright 2016 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// build +linux
package sysctl
import (
"fmt"
"io/ioutil"
"path/filepath"
"strings"
)
// Sysctl provides a method to set/get values from /proc/sys - in linux systems
// new interface to set/get values of variables formerly handled by sysctl syscall
// If optional `params` have only one string value - this function will
// set this value into coresponding sysctl variable
func Sysctl(name string, params ...string) (string, error) {
if len(params) > 1 {
return "", fmt.Errorf("unexcepted additional parameters")
} else if len(params) == 1 {
return setSysctl(name, params[0])
}
return getSysctl(name)
}
func getSysctl(name string) (string, error) {
fullName := filepath.Join("/proc/sys", strings.Replace(name, ".", "/", -1))
fullName = filepath.Clean(fullName)
data, err := ioutil.ReadFile(fullName)
if err != nil {
return "", err
}
return string(data[:len(data)-1]), nil
}
func setSysctl(name, value string) (string, error) {
fullName := filepath.Join("/proc/sys", strings.Replace(name, ".", "/", -1))
fullName = filepath.Clean(fullName)
if err := ioutil.WriteFile(fullName, []byte(value), 0644); err != nil {
return "", err
}
return getSysctl(name)
}

Some files were not shown because too many files have changed in this diff Show More