diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5e1d44d84..9786115f3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -4,7 +4,7 @@ jobs: build: strategy: matrix: - go-version: [1.17.x, 1.18.x] + go-version: [1.19.x] #goarch: [386, amd64, arm, ppc64le, arm64] goarch: [amd64, arm64] os: [ubuntu-latest] #, macos-latest, windows-latest] diff --git a/.github/workflows/image-build.yml b/.github/workflows/image-build.yml index d34b0628f..8a3b1634c 100644 --- a/.github/workflows/image-build.yml +++ b/.github/workflows/image-build.yml @@ -44,6 +44,9 @@ jobs: - name: Check out code into the Go module directory uses: actions/checkout@v2 + - name: Set up QEMU + uses: docker/setup-qemu-action@v2 + - name: Set up Docker Buildx uses: docker/setup-buildx-action@v1 @@ -54,3 +57,4 @@ jobs: push: false tags: ghcr.io/${{ github.repository }}:latest-arm64 file: Dockerfile.arm64 + platforms: linux/arm64 diff --git a/.github/workflows/image-push-master.yml b/.github/workflows/image-push-master.yml index 49ec3d6cf..685ca29a6 100644 --- a/.github/workflows/image-push-master.yml +++ b/.github/workflows/image-push-master.yml @@ -1,5 +1,5 @@ name: Image push for master -on: +on: push: branches: - master @@ -59,3 +59,24 @@ jobs: push: true tags: ghcr.io/${{ github.repository }}:latest-ocp file: Dockerfile.openshift + + push-arm64: + name: Image build/arm64 + runs-on: ubuntu-latest + steps: + - name: Check out code into the Go module directory + uses: actions/checkout@v2 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v2 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + + - name: Build container image + uses: docker/build-push-action@v2 + with: + context: . + push: true + tags: ghcr.io/${{ github.repository }}:latest-arm64 + file: Dockerfile.arm64 diff --git a/.github/workflows/image-push-release.yml b/.github/workflows/image-push-release.yml index 5185fd4af..e3159033f 100644 --- a/.github/workflows/image-push-release.yml +++ b/.github/workflows/image-push-release.yml @@ -1,5 +1,5 @@ name: Image push release -on: +on: push: tags: - v* @@ -48,6 +48,9 @@ jobs: - name: Check out code into the Go module directory uses: actions/checkout@v2 + - name: Set up QEMU + uses: docker/setup-qemu-action@v2 + - name: Set up Docker Buildx uses: docker/setup-buildx-action@v1 @@ -75,3 +78,4 @@ jobs: tags: | ${{ steps.docker_meta.outputs.tags }}-arm64 file: Dockerfile + platforms: linux/arm64 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a132e5c16..deb9c429a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -4,8 +4,7 @@ jobs: test: strategy: matrix: - #go-version: [1.15.x, 1.16.x] - go-version: [1.17.x] + go-version: [1.19.x] os: [ubuntu-latest] runs-on: ${{ matrix.os }} steps: @@ -17,49 +16,55 @@ jobs: - name: Checkout code uses: actions/checkout@v2 + - name: Get all changed files excluding docs and README + id: changed-files + uses: tj-actions/changed-files@v35 + with: + files_ignore: | + **/*.md + doc/*.{json,png,svg} + - name: Run Revive Action by building from repository - uses: morphy2k/revive-action@v1.4.1 + uses: morphy2k/revive-action@v2.4.1 + if: steps.changed-files.outputs.any_changed == 'true' with: exclude: "./vendor/..." name: "Revive" - name: Install kubebuilder tools + if: steps.changed-files.outputs.any_changed == 'true' run: ./hack/install-kubebuilder-tools.sh - name: Generate code + if: steps.changed-files.outputs.any_changed == 'true' run: ./hack/generate-code.sh && hack/verify-codegen.sh - name: Run go fmt + if: steps.changed-files.outputs.any_changed == 'true' run: go fmt ./... #run: diff -u <(echo -n) <(gofmt -d -s .) - name: Run go vet + if: steps.changed-files.outputs.any_changed == 'true' run: go vet --tags=test ./... - name: Install static check + if: steps.changed-files.outputs.any_changed == 'true' run: go install honnef.co/go/tools/cmd/staticcheck@latest - name: Test + if: steps.changed-files.outputs.any_changed == 'true' run: sudo PATH=${PATH}:./bin ./hack/test-go.sh - name: Send coverage + if: steps.changed-files.outputs.any_changed == 'true' uses: shogo82148/actions-goveralls@v1 with: path-to-profile: coverage.out flag-name: Go-${{ matrix.go }} - parallel: true env: KUBEBUILDER_ASSETS: "$(pwd)/bin" - # notifies that all test jobs are finished. - finish: - needs: test - runs-on: ubuntu-latest - steps: - - uses: shogo82148/actions-goveralls@v1 - with: - parallel-finished: true - e2e-test: name: e2e test runs-on: ubuntu-latest @@ -69,21 +74,33 @@ jobs: - name: Set up Go version uses: actions/setup-go@v1 with: - go-version: 1.17 + go-version: 1.19.x - name: Checkout code into the Go module directory uses: actions/checkout@v2 + - name: Get all changed files excluding docs and README + id: changed-files + uses: tj-actions/changed-files@v35 + with: + files_ignore: | + **/*.md + doc/*.{json,png,svg} + - name: Install requirements + if: steps.changed-files.outputs.any_changed == 'true' run: sudo apt-get install nmap jq && ./hack/build-go.sh - name: Get tools, setup KinD cluster test environment + if: steps.changed-files.outputs.any_changed == 'true' run: source hack/e2e-get-test-tools.sh && ./hack/e2e-setup-kind-cluster.sh --number-of-compute $NUMBER_OF_COMPUTE_NODES - name: Clear test-cache + if: steps.changed-files.outputs.any_changed == 'true' run: go clean -testcache - name: Execute golang based E2E tests + if: steps.changed-files.outputs.any_changed == 'true' env: KUBECONFIG: /home/runner/.kube/config NUMBER_OF_THRASH_ITER: 20 diff --git a/Dockerfile b/Dockerfile index e5660020a..b44309955 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.17 +FROM golang:1.19 ADD . /usr/src/whereabouts RUN mkdir -p $GOPATH/src/github.com/k8snetworkplumbingwg/whereabouts WORKDIR $GOPATH/src/github.com/k8snetworkplumbingwg/whereabouts diff --git a/Dockerfile.arm64 b/Dockerfile.arm64 index e1713143a..0b1c7fd54 100644 --- a/Dockerfile.arm64 +++ b/Dockerfile.arm64 @@ -1,4 +1,4 @@ -FROM golang:1.17 +FROM golang:1.19 ADD . /usr/src/whereabouts ENV GOARCH "arm64" diff --git a/Makefile b/Makefile index ec5439f92..d74fbdc8e 100644 --- a/Makefile +++ b/Makefile @@ -2,6 +2,7 @@ IMAGE_NAME ?= whereabouts IMAGE_REGISTRY ?= ghcr.io/k8snetworkplumbingwg IMAGE_PULL_POLICY ?= Always IMAGE_TAG ?= latest +COMPUTE_NODES ?= 2 OCI_BIN ?= docker @@ -20,3 +21,6 @@ install-tools: test: build install-tools hack/test-go.sh + +kind: + hack/e2e-setup-kind-cluster.sh -n $(COMPUTE_NODES) diff --git a/README.md b/README.md index 8106710b3..f3c3b6ceb 100644 --- a/README.md +++ b/README.md @@ -242,6 +242,67 @@ Run the build command from the `./hack` directory: ./hack/build-go.sh ``` +## Running whereabouts CNI in a local kind cluster + +You can start a kind cluster to run local changes with: +``` +make kind +# or make kind COMPUTE_NODES= +``` + +You can then create a NetworkAttachmentDefinition with: +``` +cat <<'EOF' | kubectl apply -f - +apiVersion: "k8s.cni.cncf.io/v1" +kind: NetworkAttachmentDefinition +metadata: + name: whereabouts-conf +spec: + config: '{ + "cniVersion": "0.3.0", + "name": "whereaboutsexample", + "type": "macvlan", + "master": "eth0", + "mode": "bridge", + "ipam": { + "type": "whereabouts", + "range": "192.168.2.225/28" + } + }' +EOF +``` + +Create a deployment that uses the NetworkAttachmentDefinition, for example: +``` +cat <<'EOF' | kubectl apply -f - +apiVersion: apps/v1 +kind: Deployment +metadata: + name: netshoot-deployment + labels: + app: netshoot-deployment +spec: + replicas: 1 + selector: + matchLabels: + app: netshoot-pod + template: + metadata: + annotations: + k8s.v1.cni.cncf.io/networks: whereabouts-conf + labels: + app: netshoot-pod + spec: + containers: + - name: netshoot + image: nicolaka/netshoot + command: + - sleep + - "3600" + imagePullPolicy: IfNotPresent +EOF +``` + ## Acknowledgements Thanks big time to [Tomofumi Hayashi](https://github.com/s1061123), I utilized his [static CNI IPAM plugin](https://github.com/containernetworking/plugins/tree/master/plugins/ipam/static) as a basis for this project to give me a head start! diff --git a/cmd/controlloop/controlloop.go b/cmd/controlloop/controlloop.go index 0c599e5cd..15c1186b9 100644 --- a/cmd/controlloop/controlloop.go +++ b/cmd/controlloop/controlloop.go @@ -9,9 +9,6 @@ import ( gocron "github.com/go-co-op/gocron" corev1 "k8s.io/api/core/v1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/fields" - v1coreinformerfactory "k8s.io/client-go/informers" "k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes/scheme" typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1" @@ -132,15 +129,10 @@ func newPodController(stopChannel chan struct{}) (*controlloop.PodController, er const noResyncPeriod = 0 ipPoolInformerFactory := wbinformers.NewSharedInformerFactory(wbClientSet, noResyncPeriod) netAttachDefInformerFactory := nadinformers.NewSharedInformerFactory(nadK8sClientSet, noResyncPeriod) - podInformerFactory := v1coreinformerfactory.NewSharedInformerFactoryWithOptions( - k8sClientSet, noResyncPeriod, v1coreinformerfactory.WithTweakListOptions( - func(options *v1.ListOptions) { - const ( - filterKey = "spec.nodeName" - hostnameEnvVariable = "HOSTNAME" - ) - options.FieldSelector = fields.OneTermEqualSelector(filterKey, os.Getenv(hostnameEnvVariable)).String() - })) + podInformerFactory, err := controlloop.PodInformerFactory(k8sClientSet) + if err != nil { + return nil, err + } controller := controlloop.NewPodController( k8sClientSet, diff --git a/cmd/whereabouts_test.go b/cmd/whereabouts_test.go index af529cc52..7aefbff4f 100644 --- a/cmd/whereabouts_test.go +++ b/cmd/whereabouts_test.go @@ -5,7 +5,6 @@ import ( "errors" "fmt" "io/fs" - "io/ioutil" "net" "os" "strings" @@ -58,7 +57,7 @@ func AllocateAndReleaseAddressesTest(ipVersion string, ipamConf *whereaboutstype Expect(ipamConf.IPRanges).NotTo(BeEmpty()) wbClient := *kubernetes.NewKubernetesClient( fake.NewSimpleClientset( - ipPool(ipamConf.IPRanges[0].Range, podNamespace)), + ipPool(ipamConf.IPRanges[0].Range, podNamespace, ipamConf.NetworkName)), fakek8sclient.NewSimpleClientset(), 0) for i := 0; i < len(expectedAddresses); i++ { @@ -124,7 +123,7 @@ func AllocateAndReleaseAddressesTest(ipVersion string, ipamConf *whereaboutstype ipamConf, fakek8sclient.NewSimpleClientset(), fake.NewSimpleClientset( - ipPool(ipamConf.IPRanges[0].Range, podNamespace))) + ipPool(ipamConf.IPRanges[0].Range, podNamespace, ipamConf.NetworkName))) return cmdDel(args, client) }) Expect(err).NotTo(HaveOccurred()) @@ -145,7 +144,7 @@ var _ = Describe("Whereabouts operations", func() { BeforeEach(func() { var err error - tmpDir, err = ioutil.TempDir("/tmp", "whereabouts") + tmpDir, err = os.MkdirTemp("/tmp", "whereabouts") Expect(err).ToNot(HaveOccurred()) kubeConfigPath = fmt.Sprintf("%s/%s", tmpDir, whereaboutsConfigFile) Expect(os.WriteFile(kubeConfigPath, kubeconfig(), fs.ModePerm)).To(Succeed()) @@ -234,6 +233,30 @@ var _ = Describe("Whereabouts operations", func() { ) }) + It("allocates and releases an IPv6 range that ends with zeroes with a Kubernetes backend", func() { + + ipVersion := "6" + ipRange := "2001:db8:480:603d:0304:0403:000:0000-2001:db8:480:603d:0304:0403:0000:0004/64" + ipGateway := "2001:db8:480:603d::1" + expectedAddress := "2001:db8:480:603d:0304:0403:000:0000/64" + + AllocateAndReleaseAddressesTest( + ipVersion, + ipamConfig(podName, podNamespace, ipRange, ipGateway, kubeConfigPath), + []string{expectedAddress}, + ) + + ipRange = "2001:db8:5422:0005::-2001:db8:5422:0005:7fff:ffff:ffff:ffff/64" + ipGateway = "2001:db8:5422:0005::1" + expectedAddress = "2001:db8:5422:0005::/64" + + AllocateAndReleaseAddressesTest( + ipVersion, + ipamConfig(podName, podNamespace, ipRange, ipGateway, kubeConfigPath), + []string{expectedAddress}, + ) + }) + It("allocates IPv6 addresses with DNS-1123 conformant naming with a Kubernetes backend", func() { ipVersion := "6" @@ -292,7 +315,7 @@ var _ = Describe("Whereabouts operations", func() { ipamConf, fakek8sclient.NewSimpleClientset(), fake.NewSimpleClientset( - ipPool(ipamConf.IPRanges[0].Range, podNamespace))) + ipPool(ipamConf.IPRanges[0].Range, podNamespace, ipamConf.NetworkName))) // Allocate the IP r, raw, err := testutils.CmdAddWithArgs(args, func() error { @@ -364,7 +387,7 @@ var _ = Describe("Whereabouts operations", func() { ipamConf, fakek8sclient.NewSimpleClientset(), fake.NewSimpleClientset( - ipPool(ipamConf.IPRanges[0].Range, podNamespace))) + ipPool(ipamConf.IPRanges[0].Range, podNamespace, ipamConf.NetworkName))) // Allocate the IP r, raw, err := testutils.CmdAddWithArgs(args, func() error { @@ -433,7 +456,7 @@ var _ = Describe("Whereabouts operations", func() { ipamConf, fakek8sclient.NewSimpleClientset(), fake.NewSimpleClientset( - ipPool(ipamConf.IPRanges[0].Range, podNamespace))) + ipPool(ipamConf.IPRanges[0].Range, podNamespace, ipamConf.NetworkName))) // Allocate the IP r, raw, err := testutils.CmdAddWithArgs(args, func() error { @@ -513,7 +536,7 @@ var _ = Describe("Whereabouts operations", func() { ipamConf, fakek8sclient.NewSimpleClientset(), fake.NewSimpleClientset( - ipPool(ipamConf.IPRanges[0].Range, podNamespace))) + ipPool(ipamConf.IPRanges[0].Range, podNamespace, ipamConf.NetworkName))) // Allocate the IP r, raw, err := testutils.CmdAddWithArgs(args, func() error { @@ -602,7 +625,7 @@ var _ = Describe("Whereabouts operations", func() { ipamConf, fakek8sclient.NewSimpleClientset(), fake.NewSimpleClientset( - ipPool(ipamConf.IPRanges[0].Range, podNamespace))) + ipPool(ipamConf.IPRanges[0].Range, podNamespace, ipamConf.NetworkName))) // Allocate the IP r, raw, err := testutils.CmdAddWithArgs(args, func() error { @@ -666,8 +689,8 @@ var _ = Describe("Whereabouts operations", func() { ipamConf, fakek8sclient.NewSimpleClientset(), fake.NewSimpleClientset( - ipPool(ipamConf.IPRanges[0].Range, podNamespace), - ipPool(ipamConf.IPRanges[1].Range, podNamespace))) + ipPool(ipamConf.IPRanges[0].Range, podNamespace, ipamConf.NetworkName), + ipPool(ipamConf.IPRanges[1].Range, podNamespace, ipamConf.NetworkName))) // Allocate the IP r, raw, err := testutils.CmdAddWithArgs(args, func() error { @@ -731,8 +754,8 @@ var _ = Describe("Whereabouts operations", func() { ipamConf, fakek8sclient.NewSimpleClientset(), fake.NewSimpleClientset( - ipPool(ipamConf.IPRanges[0].Range, podNamespace), - ipPool(ipamConf.IPRanges[1].Range, podNamespace))) + ipPool(ipamConf.IPRanges[0].Range, podNamespace, ipamConf.NetworkName), + ipPool(ipamConf.IPRanges[1].Range, podNamespace, ipamConf.NetworkName))) // Allocate the IP r, raw, err := testutils.CmdAddWithArgs(args, func() error { @@ -794,7 +817,7 @@ var _ = Describe("Whereabouts operations", func() { ipamConf, fakek8sclient.NewSimpleClientset(), fake.NewSimpleClientset( - ipPool(ipamConf.IPRanges[0].Range, podNamespace))) + ipPool(ipamConf.IPRanges[0].Range, podNamespace, ipamConf.NetworkName))) // Allocate the IP r, raw, err := testutils.CmdAddWithArgs(args, func() error { @@ -860,7 +883,7 @@ var _ = Describe("Whereabouts operations", func() { ipamConf, fakek8sclient.NewSimpleClientset(), fake.NewSimpleClientset( - ipPool(ipamConf.IPRanges[0].Range, podNamespace))) + ipPool(ipamConf.IPRanges[0].Range, podNamespace, ipamConf.NetworkName))) // Allocate the IP r, raw, err := testutils.CmdAddWithArgs(args, func() error { @@ -916,7 +939,7 @@ var _ = Describe("Whereabouts operations", func() { Expect(ipamConf.IPRanges).NotTo(BeEmpty()) wbClient := *kubernetes.NewKubernetesClient( fake.NewSimpleClientset( - ipPool(ipamConf.IPRanges[0].Range, podNamespace)), + ipPool(ipamConf.IPRanges[0].Range, podNamespace, ipamConf.NetworkName)), fakek8sclient.NewSimpleClientset(), 0) @@ -1013,7 +1036,7 @@ var _ = Describe("Whereabouts operations", func() { Expect(ipamConf.IPRanges).NotTo(BeEmpty()) wbClient := *kubernetes.NewKubernetesClient( fake.NewSimpleClientset( - ipPool(ipamConf.IPRanges[0].Range, podNamespace)), + ipPool(ipamConf.IPRanges[0].Range, podNamespace, ipamConf.NetworkName)), fakek8sclient.NewSimpleClientset(), 0) @@ -1131,7 +1154,7 @@ var _ = Describe("Whereabouts operations", func() { Expect(ipamConf.IPRanges).NotTo(BeEmpty()) wbClient := *kubernetes.NewKubernetesClient( fake.NewSimpleClientset( - ipPool(ipamConf.IPRanges[0].Range, podNamespace)), + ipPool(ipamConf.IPRanges[0].Range, podNamespace, ipamConf.NetworkName)), fakek8sclient.NewSimpleClientset(), 0) @@ -1250,7 +1273,7 @@ var _ = Describe("Whereabouts operations", func() { Expect(ipamConf.IPRanges).NotTo(BeEmpty()) wbClient := *kubernetes.NewKubernetesClient( fake.NewSimpleClientset( - ipPool(ipamConf.IPRanges[0].Range, podNamespace)), + ipPool(ipamConf.IPRanges[0].Range, podNamespace, ipamConf.NetworkName)), fakek8sclient.NewSimpleClientset(), 0) @@ -1429,10 +1452,10 @@ users: `) } -func ipPool(ipRange string, namespace string, podReferences ...string) *v1alpha1.IPPool { +func ipPool(ipRange string, namespace string, networkName string, podReferences ...string) *v1alpha1.IPPool { return &v1alpha1.IPPool{ ObjectMeta: metav1.ObjectMeta{ - Name: kubernetes.NormalizeRange(ipRange), + Name: kubernetes.IPPoolName(kubernetes.PoolIdentifier{IpRange: ipRange, NetworkName: networkName}), Namespace: namespace, ResourceVersion: "1", }, diff --git a/doc/crds/daemonset-install.yaml b/doc/crds/daemonset-install.yaml index 624dde257..7fba9ec88 100644 --- a/doc/crds/daemonset-install.yaml +++ b/doc/crds/daemonset-install.yaml @@ -48,6 +48,11 @@ rules: verbs: - list - watch +- apiGroups: [""] + resources: + - nodes + verbs: + - get - apiGroups: ["k8s.cni.cncf.io"] resources: - network-attachment-definitions @@ -103,6 +108,11 @@ spec: /ip-control-loop -log-level debug image: ghcr.io/k8snetworkplumbingwg/whereabouts:latest-amd64 env: + - name: NODENAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: spec.nodeName - name: WHEREABOUTS_NAMESPACE valueFrom: fieldRef: diff --git a/doc/proposals/named-networks.md b/doc/proposals/named-networks.md new file mode 100644 index 000000000..d1f711898 --- /dev/null +++ b/doc/proposals/named-networks.md @@ -0,0 +1,233 @@ +# Support for named networks + +It was decided in March 2023 to implement scheme 3. This means that we added a new field to the `IPAMConfig` called `network_name` that is included in the `IPPool` name to distinguish it from other pools with the same CIDR. + +# Table of contents + +- [Introduction](#introduction) + - [Goal](#goal-of-this-proposal) +- [Design](#design) + - [Analysis of the proposed schemes](#analysis-of-the-proposed-schemes) + - [Changes in Modules](#changes-in-modules) +- [Summary](#summary) +- [Discussions and Decisions](#discussions-and-decisions) + +
+ +## Introduction + +When whereabouts assigns an IP to a Pod this fact is recorded in a CR of kind `IPPool` that has its name derived from the CIDR range in question. +This CR is stored in the namespace of whereabouts (`kube-system` by default). + +Should the user configure multiple overlapping ranges, it is possible to configure whereabouts to allow assigning duplicate IPs. + +However, since the storage of the assignments is done in a CR that is named like the CIDR range, it is not possible to configure *the same CIDR range* twice and have whereabouts assign from the ranges independently. + +This is, for example, useful in multi-tenant situations where more than one group is responsible for selecting CIDR ranges. + +### Goal of this Proposal + +- Allow configuring the same CIDR range multiple times (e.g. in separate multus-`NetworkAttachmentDefinition`s) + +
+ +## Design + +The network configuration already has a field `name`: + +```json +{ + "cniVersion": "0.3.0", + "name": "whereaboutsexample", + "type": "macvlan", + "master": "eth0", + "mode": "bridge", + "ipam": { + "type": "whereabouts", + "range": "192.168.2.225/28" + } +} +``` + +This is also parsed into the internal representation of the `IPAMConfig`. + +This proposal shows three schemes of implementing using that name to distinguish the assignment of IPs: + +1. Store the CR with the name of the network configuration instead of the canonicalized CIDR range +2. Store the CR with the name of the network configuration prepended (or appended) to the canonicalized CIDR range +3. Add a new field into the `IPAMConfig` to allow users to decide when whereabouts should use the name or the CIDR range for identifying the configuration + +Further, this proposal changes the namespace of the stored `IPPool` to be the same as the multus `NetworkAttachmentDefinition` if it exists, falling back to storing in the `kube-system` namespace. +Cross-namespace access will be allowed in cases where multus allows cross-namespace access to the `NetworkAttachmentDefinition` + +### Analysis of the proposed schemes + +#### Store the CR under the name of the network configuration + + + + + + + + + + +
ProsCons
+:green_circle: Clean design
+:green_circle: Ranges are easy to find during debugging
+:green_circle: No more IP-to-string canonicalization
+
+:red_circle: Not backwards compatible, existing installation would need to carefully migrate the existing `IPPool`s to not get duplicate IPs
+
+ +#### Store the CR under a name combined from the name of the network configuration and the CIDR range + + + + + + + + + + +
ProsCons
+:green_circle: Clean design
+:green_circle: Ranges are easy to find during debugging
+
+:red_circle: Not backwards compatible, existing installation would need to carefully migrate the existing `IPPool`s to not get duplicate IPs
+
+ +#### Add a new field to decide whether this is a named range or not + + + + + + + + + + +
ProsCons
+:green_circle: Backwards compatible, existing `IPPool`s are still where we left them
+:yellow_circle: Named ranges are easy to find during debugging, other ranges are unchanged
+
+:red_circle: "API" change
+
+ +### Changes in Modules + +#### Schemes 2 and 3 + +##### whereabouts/pkg/storage/kubernetes/ipam.go + +```diff +-func NormalizeRange(ipRange string) string { ++func NormalizeRange(ipRange string, networkName string) string { + // v6 filter + normalized := strings.ReplaceAll(ipRange, ":", "-") + // replace subnet cidr slash + normalized = strings.ReplaceAll(normalized, "/", "-") +- return normalized + ++ if ThisIsANamedRange { ++ return networkName + "-" + normalized ++ } else { ++ return normalized ++ } +} +``` + +This will ensure that every time whereabouts looks up the current assignments on a range, it queries not for `192.168.2.225-28` but for `whereaboutsexample-192.168.2.225-28`. +Should the network be configured to use the "old" names (scheme 3), the lookup is for the unchanged name `192.168.2.225-28`. + +#### Scheme 1 + +##### whereabouts/pkg/storage/kubernetes/ipam.go + +```diff +-func (i *KubernetesIPAM) GetIPPool(ctx context.Context, ipRange string) (storage.IPPool, error) { ++func (i *KubernetesIPAM) GetIPPool(ctx context.Context, ipRange string, networkName string, namespace string) (storage.IPPool, error) { +- normalized := NormalizeRange(ipRange) +- +- pool, err := i.getPool(ctx, normalized, ipRange) ++ // The ipRange is passed in so that the CR can be created if needed ++ pool, err := i.getPool(ctx, networkName, namespace, ipRange) + if err != nil { + return nil, err + } + + firstIP, _, err := pool.ParseCIDR() + if err != nil { + return nil, err + } + + return &KubernetesIPPool{i.client, i.containerID, firstIP, pool}, nil + } + +-func (i *KubernetesIPAM) getPool(ctx context.Context, name string, iprange string) (*whereaboutsv1alpha1.IPPool, error) { ++func (i *KubernetesIPAM) getPool(ctx context.Context, name string, namespace string, iprange string) (*whereaboutsv1alpha1.IPPool, error) { + pool := &whereaboutsv1alpha1.IPPool{ + ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: i.namespace}, + } +- if err := i.client.Get(ctx, types.NamespacedName{Name: name, Namespace: i.namespace}, pool); errors.IsNotFound(err) { ++ if err := i.client.Get(ctx, types.NamespacedName{Name: name, Namespace: namespace}, pool); errors.IsNotFound(err) { + // pool does not exist, create it + pool.ObjectMeta.Name = name + pool.Spec.Range = iprange + pool.Spec.Allocations = make(map[string]whereaboutsv1alpha1.IPAllocation) + if err := i.client.Create(ctx, pool); errors.IsAlreadyExists(err) { + // the pool was just created -- allow retry + return nil, &temporaryError{err} + } else if err != nil { + return nil, fmt.Errorf("k8s create error: %s", err) + } + // if the pool was created for the first time, trigger another retry of the allocation loop + // so all of the metadata / resourceVersions are populated as necessary by the `client.Get` call + return nil, &temporaryError{fmt.Errorf("k8s pool initialized")} + } else if err != nil { + return nil, fmt.Errorf("k8s get error: %s", err) + } + return pool, nil + } +``` + +### Summary + +whereabouts supports disabling the check for overlapping IP assignments, however it does not alllow actually configuring two identical ranges. + +This proposal (and the prototypical implementation in https://github.com/k8snetworkplumbingwg/whereabouts/pull/256) allows doing exactly that by introducing a new IPAM parameter `network_name`. + +### Discussions and Decisions + +See +- https://github.com/k8snetworkplumbingwg/whereabouts/pull/256 +- https://github.com/k8snetworkplumbingwg/whereabouts/issues/50#issuecomment-874040513 + +#### Migration toolkit + +Should the decision be made that scheme 1 or scheme 2 are implemented, we are facing a backwards-compatibility issue: whereabouts would not find existing `IPPool`s as they are stored under the CIDR-derived name. +We propose to supply a small program (ideally also as OCI image for use in kubernetes `Job`s) that migrates an existing cluster from "legacy whereabouts" to the new, named and namespaced scheme. + +This tool would do roughly the following: + +0. Grab the leader election lock to prevent any whereabouts to run during the operation +1. List all `IPPool`s in the `kube-system` namespace +2. List all `NetworkAttachmentDefinition`s in all namespaces +3. Match the `NetworkAttachmentDefinition`s to the `IPPool`s using the CIDR canonicalization rules +4. Delete the `IPPool`s and re-create it under the namespace of the `NetworkAttachmentDefinition` with the name derived from the CNI config +5. List any unmatched `IPPool`s and ask the user to rename/migrate them manually + +#### Guide for decision + +It is the authors (@toelke) opinion that scheme 3 is the easiest to implement and deploy. +It needs no re-configuration from current users of whereabouts. +It is, however, a kludge designed to allow exactly this backwards compatibility. +For that reason it will probably cause problems in the future. + +Implementing schemes 1 or 2 where either the name of the `NetworkAttachmentDefinition` or both the name and CIDR-Range are used for identifying `IPPool` is the greater development effort but gives a clearer desgned outcome. +It could be possible to feature gate this for existing users of whereabouts. + +Pending the communities decision we would like to go forward with implementing scheme 1. diff --git a/e2e/client/ippool.go b/e2e/client/ippool.go index 194069982..3a25f5d9c 100644 --- a/e2e/client/ippool.go +++ b/e2e/client/ippool.go @@ -13,11 +13,11 @@ import ( "k8s.io/apimachinery/pkg/util/wait" ) -func isIPPoolAllocationsEmpty(k8sIPAM *kubeClient.KubernetesIPAM, ipPoolName string) wait.ConditionFunc { +func isIPPoolAllocationsEmpty(k8sIPAM *kubeClient.KubernetesIPAM, ipPoolCIDR string) wait.ConditionFunc { return func() (bool, error) { - ipPool, err := k8sIPAM.GetIPPool(context.Background(), ipPoolName) + ipPool, err := k8sIPAM.GetIPPool(context.Background(), kubeClient.PoolIdentifier{IpRange: ipPoolCIDR, NetworkName: kubeClient.UnnamedNetwork}) noPoolError := fmt.Errorf("k8s pool initialized") - if errors.As(err, &noPoolError) { + if errors.Is(err, noPoolError) { return true, nil } else if err != nil { return false, err @@ -33,6 +33,6 @@ func isIPPoolAllocationsEmpty(k8sIPAM *kubeClient.KubernetesIPAM, ipPoolName str // WaitForZeroIPPoolAllocations polls up to timeout seconds for IP pool allocations to be gone from the Kubernetes cluster. // Returns an error if any IP pool allocations remain after time limit, or if GETing IP pools causes an error. -func WaitForZeroIPPoolAllocations(k8sIPAM *kubeClient.KubernetesIPAM, ipPoolName string, timeout time.Duration) error { - return wait.PollImmediate(time.Second, timeout, isIPPoolAllocationsEmpty(k8sIPAM, ipPoolName)) +func WaitForZeroIPPoolAllocations(k8sIPAM *kubeClient.KubernetesIPAM, ipPoolCIDR string, timeout time.Duration) error { + return wait.PollImmediate(time.Second, timeout, isIPPoolAllocationsEmpty(k8sIPAM, ipPoolCIDR)) } diff --git a/e2e/e2e_test.go b/e2e/e2e_test.go index 5934258c3..25e310963 100644 --- a/e2e/e2e_test.go +++ b/e2e/e2e_test.go @@ -47,7 +47,7 @@ var _ = Describe("Whereabouts functionality", func() { ipv4TestRange = "10.10.0.0/16" testNetworkName = "wa-nad" rsName = "whereabouts-scale-test" - ipPoolName = "10.10.0.0/16" + ipPoolCIDR = "10.10.0.0/16" ) var ( @@ -73,7 +73,7 @@ var _ = Describe("Whereabouts functionality", func() { clientInfo, err = wbtestclient.NewClientInfo(config) Expect(err).NotTo(HaveOccurred()) - netAttachDef = macvlanNetworkWithWhereaboutsIPAMNetwork(testNetworkName, testNamespace, ipv4TestRange, []string{}) + netAttachDef = macvlanNetworkWithWhereaboutsIPAMNetwork(testNetworkName, testNamespace, ipv4TestRange, []string{}, wbstorage.UnnamedNetwork, true) By("creating a NetworkAttachmentDefinition for whereabouts") _, err = clientInfo.AddNetAttachDef(netAttachDef) @@ -134,7 +134,7 @@ var _ = Describe("Whereabouts functionality", func() { testDualStackNetworkName, testNamespace, "", - testIPRangesDualStack) + testIPRangesDualStack, wbstorage.UnnamedNetwork, true) By("creating DualStack NetworkAttachmentDefinition for whereabouts") _, err = clientInfo.AddNetAttachDef(netAttachDefDualStack) @@ -176,7 +176,7 @@ var _ = Describe("Whereabouts functionality", func() { testDualStackNetworkName, testNamespace, ipv4TestRange, - testIPRangesDualStack) + testIPRangesDualStack, wbstorage.UnnamedNetwork, true) By("creating DualStack NetworkAttachmentDefinition for whereabouts") _, err = clientInfo.AddNetAttachDef(netAttachDefDualStack) @@ -245,7 +245,7 @@ var _ = Describe("Whereabouts functionality", func() { By("removing replicas and expecting 0 IP pool allocations") Expect( checkZeroIPPoolAllocationsAndReplicas( - clientInfo, k8sIPAM, rsName, testNamespace, ipPoolName, testNetworkName)).To(Succeed()) + clientInfo, k8sIPAM, rsName, testNamespace, ipPoolCIDR, testNetworkName)).To(Succeed()) By("deleting replicaset with whereabouts net-attach-def") Expect(clientInfo.DeleteReplicaSet(replicaSet)).To(Succeed()) @@ -256,7 +256,7 @@ var _ = Describe("Whereabouts functionality", func() { for i := 0; i < testConfig.NumberOfIterations; i++ { Expect( checkZeroIPPoolAllocationsAndReplicas( - clientInfo, k8sIPAM, rsName, testNamespace, ipPoolName, testNetworkName)).To(Succeed()) + clientInfo, k8sIPAM, rsName, testNamespace, ipPoolCIDR, testNetworkName)).To(Succeed()) allPods, err := clientInfo.Client.CoreV1().Pods(core.NamespaceAll).List(context.TODO(), metav1.ListOptions{}) Expect(err).NotTo(HaveOccurred()) @@ -282,7 +282,7 @@ var _ = Describe("Whereabouts functionality", func() { Expect(err).NotTo(HaveOccurred()) Expect(podList.Items).NotTo(BeEmpty()) - ipPool, err := k8sIPAM.GetIPPool(context.Background(), ipPoolName) + ipPool, err := k8sIPAM.GetIPPool(context.Background(), wbstorage.PoolIdentifier{IpRange: ipPoolCIDR, NetworkName: wbstorage.UnnamedNetwork}) Expect(err).NotTo(HaveOccurred()) Expect(poolconsistency.NewPoolConsistencyCheck(ipPool, podList.Items).MissingIPs()).To(BeEmpty()) Expect(poolconsistency.NewPoolConsistencyCheck(ipPool, podList.Items).StaleIPs()).To(BeEmpty()) @@ -327,14 +327,16 @@ var _ = Describe("Whereabouts functionality", func() { Expect( clientInfo.WbClient.WhereaboutsV1alpha1().IPPools(ipPoolNamespace).Get( context.TODO(), - wbstorage.NormalizeRange(ipv4TestRange), + wbstorage.IPPoolName(wbstorage.PoolIdentifier{IpRange: ipv4TestRange, NetworkName: wbstorage.UnnamedNetwork}), metav1.GetOptions{})).To( WithTransform(poolAllocations, BeEmpty()), "cannot have leaked IPAllocations in the system") }) It("IPPools feature allocations", func() { - ipPool, err := clientInfo.WbClient.WhereaboutsV1alpha1().IPPools(ipPoolNamespace).Get(context.TODO(), wbstorage.NormalizeRange(ipv4TestRange), metav1.GetOptions{}) + ipPool, err := clientInfo.WbClient.WhereaboutsV1alpha1().IPPools(ipPoolNamespace).Get(context.TODO(), + wbstorage.IPPoolName(wbstorage.PoolIdentifier{IpRange: ipv4TestRange, NetworkName: wbstorage.UnnamedNetwork}), + metav1.GetOptions{}) Expect(err).NotTo(HaveOccurred()) Expect(ipPool.Spec.Allocations).To(HaveLen(initialReplicaNumber)) }) @@ -346,7 +348,9 @@ var _ = Describe("Whereabouts functionality", func() { Eventually(func() (map[string]v1alpha1.IPAllocation, error) { ipPool, err := clientInfo.WbClient.WhereaboutsV1alpha1().IPPools(ipPoolNamespace).Get( - context.TODO(), wbstorage.NormalizeRange(ipv4TestRange), metav1.GetOptions{}) + context.TODO(), + wbstorage.IPPoolName(wbstorage.PoolIdentifier{IpRange: ipv4TestRange, NetworkName: wbstorage.UnnamedNetwork}), + metav1.GetOptions{}) if err != nil { return map[string]v1alpha1.IPAllocation{}, err } @@ -396,7 +400,7 @@ var _ = Describe("Whereabouts functionality", func() { BeforeEach(func() { var err error tinyNetwork, err = clientInfo.AddNetAttachDef( - macvlanNetworkWithWhereaboutsIPAMNetwork(networkName, namespace, rangeWithTwoIPs, []string{})) + macvlanNetworkWithWhereaboutsIPAMNetwork(networkName, namespace, rangeWithTwoIPs, []string{}, wbstorage.UnnamedNetwork, true)) Expect(err).NotTo(HaveOccurred()) _, err = clientInfo.ProvisionStatefulSet(statefulSetName, namespace, serviceName, replicaNumber, networkName) @@ -430,7 +434,7 @@ var _ = Describe("Whereabouts functionality", func() { BeforeEach(func() { ipPool, err := clientInfo.WbClient.WhereaboutsV1alpha1().IPPools(ipPoolNamespace).Get( context.TODO(), - wbstorage.NormalizeRange(rangeWithTwoIPs), + wbstorage.IPPoolName(wbstorage.PoolIdentifier{IpRange: rangeWithTwoIPs, NetworkName: wbstorage.UnnamedNetwork}), metav1.GetOptions{}) Expect(err).NotTo(HaveOccurred()) Expect(ipPool.Spec.Allocations).NotTo(BeEmpty()) @@ -467,7 +471,7 @@ var _ = Describe("Whereabouts functionality", func() { It("can recover from an exhausted IP pool", func() { ipPool, err := clientInfo.WbClient.WhereaboutsV1alpha1().IPPools(ipPoolNamespace).Get( context.TODO(), - wbstorage.NormalizeRange(rangeWithTwoIPs), + wbstorage.IPPoolName(wbstorage.PoolIdentifier{IpRange: rangeWithTwoIPs, NetworkName: wbstorage.UnnamedNetwork}), metav1.GetOptions{}) Expect(err).NotTo(HaveOccurred()) Expect(ipPool.Spec.Allocations).NotTo(BeEmpty()) @@ -477,6 +481,79 @@ var _ = Describe("Whereabouts functionality", func() { }) }) }) + + Context("Named ranges test", func() { + const ( + testNetwork2Name = "wa-nad-2" + ) + var ( + netAttachDef2 *nettypes.NetworkAttachmentDefinition + pod2 *core.Pod + ) + + BeforeEach(func() { + var ( + err error + ) + + netAttachDef2 = macvlanNetworkWithWhereaboutsIPAMNetwork(testNetwork2Name, testNamespace, ipv4TestRange, []string{}, testNetwork2Name, false) + + By("creating a second NetworkAttachmentDefinition for whereabouts") + _, err = clientInfo.AddNetAttachDef(netAttachDef2) + Expect(err).NotTo(HaveOccurred()) + }) + + AfterEach(func() { + Expect(clientInfo.DelNetAttachDef(netAttachDef2)).To(Succeed()) + }) + + BeforeEach(func() { + const ( + singlePodName = "whereabouts-basic-test" + singlePod2Name = "whereabouts-basic-test-2" + ) + var err error + + By("creating a pod with whereabouts net-attach-def") + pod, err = clientInfo.ProvisionPod( + singlePodName, + testNamespace, + podTierLabel(singlePodName), + entities.PodNetworkSelectionElements(testNetworkName), + ) + Expect(err).NotTo(HaveOccurred()) + + By("creating a second pod with the second whereabouts net-attach-def") + pod2, err = clientInfo.ProvisionPod( + singlePod2Name, + testNamespace, + podTierLabel(singlePodName), + entities.PodNetworkSelectionElements(testNetwork2Name), + ) + Expect(err).NotTo(HaveOccurred()) + }) + + AfterEach(func() { + By("deleting pod with whereabouts net-attach-def") + Expect(clientInfo.DeletePod(pod)).To(Succeed()) + By("deleting the second pod with whereabouts net-attach-def") + Expect(clientInfo.DeletePod(pod2)).To(Succeed()) + }) + + It("allocates the same IP to the Pods as they are in differenct address collision domains", func() { + By("checking pod IP is within whereabouts IPAM range") + secondaryIfaceIPs, err := retrievers.SecondaryIfaceIPValue(pod) + Expect(err).NotTo(HaveOccurred()) + Expect(secondaryIfaceIPs).NotTo(BeEmpty()) + + By("checking pod 2 IP is within whereabouts IPAM range") + secondaryIfaceIPs2, err := retrievers.SecondaryIfaceIPValue(pod2) + Expect(err).NotTo(HaveOccurred()) + Expect(secondaryIfaceIPs2).NotTo(BeEmpty()) + + Expect(secondaryIfaceIPs[0]).To(Equal(secondaryIfaceIPs2[0])) + }) + }) }) }) @@ -510,7 +587,7 @@ func podTierLabel(podTier string) map[string]string { } // Waits for all replicas to be fully removed from replicaset, and checks that there are 0 ip pool allocations -func checkZeroIPPoolAllocationsAndReplicas(clientInfo *wbtestclient.ClientInfo, k8sIPAM *wbstorage.KubernetesIPAM, rsName, namespace string, ipPoolName string, networkNames ...string) error { +func checkZeroIPPoolAllocationsAndReplicas(clientInfo *wbtestclient.ClientInfo, k8sIPAM *wbstorage.KubernetesIPAM, rsName, namespace string, ipPoolCIDR string, networkNames ...string) error { const ( emptyReplicaSet = 0 rsSteadyTimeout = 1200 * time.Second @@ -535,7 +612,7 @@ func checkZeroIPPoolAllocationsAndReplicas(clientInfo *wbtestclient.ClientInfo, return err } - if err = wbtestclient.WaitForZeroIPPoolAllocations(k8sIPAM, ipPoolName, zeroIPPoolTimeout); err != nil { + if err = wbtestclient.WaitForZeroIPPoolAllocations(k8sIPAM, ipPoolCIDR, zeroIPPoolTimeout); err != nil { return err } @@ -559,7 +636,7 @@ func generateNetAttachDefSpec(name, namespace, config string) *nettypes.NetworkA } } -func macvlanNetworkWithWhereaboutsIPAMNetwork(networkName string, namespaceName string, ipRange string, ipRanges []string) *nettypes.NetworkAttachmentDefinition { +func macvlanNetworkWithWhereaboutsIPAMNetwork(networkName string, namespaceName string, ipRange string, ipRanges []string, poolName string, enableOverlappingRanges bool) *nettypes.NetworkAttachmentDefinition { macvlanConfig := fmt.Sprintf(`{ "cniVersion": "0.3.0", "disableCheck": true, @@ -576,11 +653,13 @@ func macvlanNetworkWithWhereaboutsIPAMNetwork(networkName string, namespaceName "range": "%s", "ipRanges": %s, "log_level": "debug", - "log_file": "/tmp/wb" + "log_file": "/tmp/wb", + "network_name": "%s", + "enable_overlapping_ranges": %v } } ] - }`, ipRange, createIPRanges(ipRanges)) + }`, ipRange, createIPRanges(ipRanges), poolName, enableOverlappingRanges) return generateNetAttachDefSpec(networkName, namespaceName, macvlanConfig) } diff --git a/go.mod b/go.mod index 558fbba62..d3155cc9a 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/k8snetworkplumbingwg/whereabouts -go 1.17 +go 1.19 require ( github.com/blang/semver v3.5.1+incompatible @@ -51,11 +51,11 @@ require ( github.com/nxadm/tail v1.4.8 // indirect github.com/spf13/pflag v1.0.5 // indirect golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect - golang.org/x/net v0.1.0 // indirect + golang.org/x/net v0.7.0 // indirect golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 // indirect - golang.org/x/sys v0.1.0 // indirect - golang.org/x/term v0.1.0 // indirect - golang.org/x/text v0.4.0 // indirect + golang.org/x/sys v0.5.0 // indirect + golang.org/x/term v0.5.0 // indirect + golang.org/x/text v0.7.0 // indirect golang.org/x/time v0.0.0-20220411224347-583f2d630306 // indirect golang.org/x/tools v0.1.12 // indirect google.golang.org/appengine v1.6.7 // indirect diff --git a/go.sum b/go.sum index 59a1f590b..9e8d7ca65 100644 --- a/go.sum +++ b/go.sum @@ -355,7 +355,6 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -459,9 +458,8 @@ golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0= -golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -542,14 +540,12 @@ golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw= -golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -560,8 +556,8 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -791,7 +787,6 @@ k8s.io/gengo v0.0.0-20211129171323-c02415ce4185 h1:TT1WdmqqXareKxZ/oNXEUSwKlLiHz k8s.io/gengo v0.0.0-20211129171323-c02415ce4185/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= -k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= diff --git a/pkg/api/whereabouts.cni.cncf.io/v1alpha1/zz_generated.deepcopy.go b/pkg/api/whereabouts.cni.cncf.io/v1alpha1/zz_generated.deepcopy.go index 6fc63bd7e..18d1e4b1f 100644 --- a/pkg/api/whereabouts.cni.cncf.io/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/api/whereabouts.cni.cncf.io/v1alpha1/zz_generated.deepcopy.go @@ -1,3 +1,4 @@ +//go:build !ignore_autogenerated // +build !ignore_autogenerated // Code generated by controller-gen. DO NOT EDIT. diff --git a/pkg/client/clientset/versioned/clientset.go b/pkg/client/clientset/versioned/clientset.go index 2bcb94dd8..d62009227 100644 --- a/pkg/client/clientset/versioned/clientset.go +++ b/pkg/client/clientset/versioned/clientset.go @@ -1,5 +1,5 @@ /* -Copyright 2022 The Kubernetes Authors +Copyright 2023 The Kubernetes Authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/client/clientset/versioned/doc.go b/pkg/client/clientset/versioned/doc.go index 023e8cb0c..c96cc7638 100644 --- a/pkg/client/clientset/versioned/doc.go +++ b/pkg/client/clientset/versioned/doc.go @@ -1,5 +1,5 @@ /* -Copyright 2022 The Kubernetes Authors +Copyright 2023 The Kubernetes Authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/client/clientset/versioned/fake/clientset_generated.go b/pkg/client/clientset/versioned/fake/clientset_generated.go index bdd75b0a9..cc612df55 100644 --- a/pkg/client/clientset/versioned/fake/clientset_generated.go +++ b/pkg/client/clientset/versioned/fake/clientset_generated.go @@ -1,5 +1,5 @@ /* -Copyright 2022 The Kubernetes Authors +Copyright 2023 The Kubernetes Authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/client/clientset/versioned/fake/doc.go b/pkg/client/clientset/versioned/fake/doc.go index fc6be49ec..b3516b841 100644 --- a/pkg/client/clientset/versioned/fake/doc.go +++ b/pkg/client/clientset/versioned/fake/doc.go @@ -1,5 +1,5 @@ /* -Copyright 2022 The Kubernetes Authors +Copyright 2023 The Kubernetes Authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/client/clientset/versioned/fake/register.go b/pkg/client/clientset/versioned/fake/register.go index 91586fde3..ecda40017 100644 --- a/pkg/client/clientset/versioned/fake/register.go +++ b/pkg/client/clientset/versioned/fake/register.go @@ -1,5 +1,5 @@ /* -Copyright 2022 The Kubernetes Authors +Copyright 2023 The Kubernetes Authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -36,14 +36,14 @@ var localSchemeBuilder = runtime.SchemeBuilder{ // AddToScheme adds all types of this clientset into the given scheme. This allows composition // of clientsets, like in: // -// import ( -// "k8s.io/client-go/kubernetes" -// clientsetscheme "k8s.io/client-go/kubernetes/scheme" -// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" -// ) +// import ( +// "k8s.io/client-go/kubernetes" +// clientsetscheme "k8s.io/client-go/kubernetes/scheme" +// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" +// ) // -// kclientset, _ := kubernetes.NewForConfig(c) -// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) +// kclientset, _ := kubernetes.NewForConfig(c) +// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) // // After this, RawExtensions in Kubernetes types will serialize kube-aggregator types // correctly. diff --git a/pkg/client/clientset/versioned/scheme/doc.go b/pkg/client/clientset/versioned/scheme/doc.go index 5b786b60e..c2ef9f987 100644 --- a/pkg/client/clientset/versioned/scheme/doc.go +++ b/pkg/client/clientset/versioned/scheme/doc.go @@ -1,5 +1,5 @@ /* -Copyright 2022 The Kubernetes Authors +Copyright 2023 The Kubernetes Authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/client/clientset/versioned/scheme/register.go b/pkg/client/clientset/versioned/scheme/register.go index 660d0cc18..fced29932 100644 --- a/pkg/client/clientset/versioned/scheme/register.go +++ b/pkg/client/clientset/versioned/scheme/register.go @@ -1,5 +1,5 @@ /* -Copyright 2022 The Kubernetes Authors +Copyright 2023 The Kubernetes Authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -36,14 +36,14 @@ var localSchemeBuilder = runtime.SchemeBuilder{ // AddToScheme adds all types of this clientset into the given scheme. This allows composition // of clientsets, like in: // -// import ( -// "k8s.io/client-go/kubernetes" -// clientsetscheme "k8s.io/client-go/kubernetes/scheme" -// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" -// ) +// import ( +// "k8s.io/client-go/kubernetes" +// clientsetscheme "k8s.io/client-go/kubernetes/scheme" +// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" +// ) // -// kclientset, _ := kubernetes.NewForConfig(c) -// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) +// kclientset, _ := kubernetes.NewForConfig(c) +// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) // // After this, RawExtensions in Kubernetes types will serialize kube-aggregator types // correctly. diff --git a/pkg/client/clientset/versioned/typed/whereabouts.cni.cncf.io/v1alpha1/doc.go b/pkg/client/clientset/versioned/typed/whereabouts.cni.cncf.io/v1alpha1/doc.go index c3a41caa8..1368a893d 100644 --- a/pkg/client/clientset/versioned/typed/whereabouts.cni.cncf.io/v1alpha1/doc.go +++ b/pkg/client/clientset/versioned/typed/whereabouts.cni.cncf.io/v1alpha1/doc.go @@ -1,5 +1,5 @@ /* -Copyright 2022 The Kubernetes Authors +Copyright 2023 The Kubernetes Authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/client/clientset/versioned/typed/whereabouts.cni.cncf.io/v1alpha1/fake/doc.go b/pkg/client/clientset/versioned/typed/whereabouts.cni.cncf.io/v1alpha1/fake/doc.go index 83af49a9a..a854eb270 100644 --- a/pkg/client/clientset/versioned/typed/whereabouts.cni.cncf.io/v1alpha1/fake/doc.go +++ b/pkg/client/clientset/versioned/typed/whereabouts.cni.cncf.io/v1alpha1/fake/doc.go @@ -1,5 +1,5 @@ /* -Copyright 2022 The Kubernetes Authors +Copyright 2023 The Kubernetes Authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/client/clientset/versioned/typed/whereabouts.cni.cncf.io/v1alpha1/fake/fake_ippool.go b/pkg/client/clientset/versioned/typed/whereabouts.cni.cncf.io/v1alpha1/fake/fake_ippool.go index 4e514ee41..eb9ddf5e5 100644 --- a/pkg/client/clientset/versioned/typed/whereabouts.cni.cncf.io/v1alpha1/fake/fake_ippool.go +++ b/pkg/client/clientset/versioned/typed/whereabouts.cni.cncf.io/v1alpha1/fake/fake_ippool.go @@ -1,5 +1,5 @@ /* -Copyright 2022 The Kubernetes Authors +Copyright 2023 The Kubernetes Authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/client/clientset/versioned/typed/whereabouts.cni.cncf.io/v1alpha1/fake/fake_overlappingrangeipreservation.go b/pkg/client/clientset/versioned/typed/whereabouts.cni.cncf.io/v1alpha1/fake/fake_overlappingrangeipreservation.go index 58b896d53..003ffbdd0 100644 --- a/pkg/client/clientset/versioned/typed/whereabouts.cni.cncf.io/v1alpha1/fake/fake_overlappingrangeipreservation.go +++ b/pkg/client/clientset/versioned/typed/whereabouts.cni.cncf.io/v1alpha1/fake/fake_overlappingrangeipreservation.go @@ -1,5 +1,5 @@ /* -Copyright 2022 The Kubernetes Authors +Copyright 2023 The Kubernetes Authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/client/clientset/versioned/typed/whereabouts.cni.cncf.io/v1alpha1/fake/fake_whereabouts.cni.cncf.io_client.go b/pkg/client/clientset/versioned/typed/whereabouts.cni.cncf.io/v1alpha1/fake/fake_whereabouts.cni.cncf.io_client.go index ff4207719..41bf61eb5 100644 --- a/pkg/client/clientset/versioned/typed/whereabouts.cni.cncf.io/v1alpha1/fake/fake_whereabouts.cni.cncf.io_client.go +++ b/pkg/client/clientset/versioned/typed/whereabouts.cni.cncf.io/v1alpha1/fake/fake_whereabouts.cni.cncf.io_client.go @@ -1,5 +1,5 @@ /* -Copyright 2022 The Kubernetes Authors +Copyright 2023 The Kubernetes Authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/client/clientset/versioned/typed/whereabouts.cni.cncf.io/v1alpha1/generated_expansion.go b/pkg/client/clientset/versioned/typed/whereabouts.cni.cncf.io/v1alpha1/generated_expansion.go index 46d2d6266..5703bc3f0 100644 --- a/pkg/client/clientset/versioned/typed/whereabouts.cni.cncf.io/v1alpha1/generated_expansion.go +++ b/pkg/client/clientset/versioned/typed/whereabouts.cni.cncf.io/v1alpha1/generated_expansion.go @@ -1,5 +1,5 @@ /* -Copyright 2022 The Kubernetes Authors +Copyright 2023 The Kubernetes Authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/client/clientset/versioned/typed/whereabouts.cni.cncf.io/v1alpha1/ippool.go b/pkg/client/clientset/versioned/typed/whereabouts.cni.cncf.io/v1alpha1/ippool.go index 9385ee6e6..d19ed1bf9 100644 --- a/pkg/client/clientset/versioned/typed/whereabouts.cni.cncf.io/v1alpha1/ippool.go +++ b/pkg/client/clientset/versioned/typed/whereabouts.cni.cncf.io/v1alpha1/ippool.go @@ -1,5 +1,5 @@ /* -Copyright 2022 The Kubernetes Authors +Copyright 2023 The Kubernetes Authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/client/clientset/versioned/typed/whereabouts.cni.cncf.io/v1alpha1/overlappingrangeipreservation.go b/pkg/client/clientset/versioned/typed/whereabouts.cni.cncf.io/v1alpha1/overlappingrangeipreservation.go index c182206b8..3f3765dc1 100644 --- a/pkg/client/clientset/versioned/typed/whereabouts.cni.cncf.io/v1alpha1/overlappingrangeipreservation.go +++ b/pkg/client/clientset/versioned/typed/whereabouts.cni.cncf.io/v1alpha1/overlappingrangeipreservation.go @@ -1,5 +1,5 @@ /* -Copyright 2022 The Kubernetes Authors +Copyright 2023 The Kubernetes Authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/client/clientset/versioned/typed/whereabouts.cni.cncf.io/v1alpha1/whereabouts.cni.cncf.io_client.go b/pkg/client/clientset/versioned/typed/whereabouts.cni.cncf.io/v1alpha1/whereabouts.cni.cncf.io_client.go index e6ce36442..0ac06e0e5 100644 --- a/pkg/client/clientset/versioned/typed/whereabouts.cni.cncf.io/v1alpha1/whereabouts.cni.cncf.io_client.go +++ b/pkg/client/clientset/versioned/typed/whereabouts.cni.cncf.io/v1alpha1/whereabouts.cni.cncf.io_client.go @@ -1,5 +1,5 @@ /* -Copyright 2022 The Kubernetes Authors +Copyright 2023 The Kubernetes Authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/client/informers/externalversions/factory.go b/pkg/client/informers/externalversions/factory.go index 86e6e5122..b5ef4ccf0 100644 --- a/pkg/client/informers/externalversions/factory.go +++ b/pkg/client/informers/externalversions/factory.go @@ -1,5 +1,5 @@ /* -Copyright 2022 The Kubernetes Authors +Copyright 2023 The Kubernetes Authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/client/informers/externalversions/generic.go b/pkg/client/informers/externalversions/generic.go index 4eea52b5e..766b21407 100644 --- a/pkg/client/informers/externalversions/generic.go +++ b/pkg/client/informers/externalversions/generic.go @@ -1,5 +1,5 @@ /* -Copyright 2022 The Kubernetes Authors +Copyright 2023 The Kubernetes Authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/client/informers/externalversions/internalinterfaces/factory_interfaces.go b/pkg/client/informers/externalversions/internalinterfaces/factory_interfaces.go index cc34d1a2f..5d0b1958a 100644 --- a/pkg/client/informers/externalversions/internalinterfaces/factory_interfaces.go +++ b/pkg/client/informers/externalversions/internalinterfaces/factory_interfaces.go @@ -1,5 +1,5 @@ /* -Copyright 2022 The Kubernetes Authors +Copyright 2023 The Kubernetes Authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/client/informers/externalversions/whereabouts.cni.cncf.io/interface.go b/pkg/client/informers/externalversions/whereabouts.cni.cncf.io/interface.go index 63a10ecb9..f37a1fb13 100644 --- a/pkg/client/informers/externalversions/whereabouts.cni.cncf.io/interface.go +++ b/pkg/client/informers/externalversions/whereabouts.cni.cncf.io/interface.go @@ -1,5 +1,5 @@ /* -Copyright 2022 The Kubernetes Authors +Copyright 2023 The Kubernetes Authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/client/informers/externalversions/whereabouts.cni.cncf.io/v1alpha1/interface.go b/pkg/client/informers/externalversions/whereabouts.cni.cncf.io/v1alpha1/interface.go index 4e0b90191..90855a3b3 100644 --- a/pkg/client/informers/externalversions/whereabouts.cni.cncf.io/v1alpha1/interface.go +++ b/pkg/client/informers/externalversions/whereabouts.cni.cncf.io/v1alpha1/interface.go @@ -1,5 +1,5 @@ /* -Copyright 2022 The Kubernetes Authors +Copyright 2023 The Kubernetes Authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/client/informers/externalversions/whereabouts.cni.cncf.io/v1alpha1/ippool.go b/pkg/client/informers/externalversions/whereabouts.cni.cncf.io/v1alpha1/ippool.go index 096e2963a..aadbe8782 100644 --- a/pkg/client/informers/externalversions/whereabouts.cni.cncf.io/v1alpha1/ippool.go +++ b/pkg/client/informers/externalversions/whereabouts.cni.cncf.io/v1alpha1/ippool.go @@ -1,5 +1,5 @@ /* -Copyright 2022 The Kubernetes Authors +Copyright 2023 The Kubernetes Authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/client/informers/externalversions/whereabouts.cni.cncf.io/v1alpha1/overlappingrangeipreservation.go b/pkg/client/informers/externalversions/whereabouts.cni.cncf.io/v1alpha1/overlappingrangeipreservation.go index b21a9d67f..702ba6dd7 100644 --- a/pkg/client/informers/externalversions/whereabouts.cni.cncf.io/v1alpha1/overlappingrangeipreservation.go +++ b/pkg/client/informers/externalversions/whereabouts.cni.cncf.io/v1alpha1/overlappingrangeipreservation.go @@ -1,5 +1,5 @@ /* -Copyright 2022 The Kubernetes Authors +Copyright 2023 The Kubernetes Authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/client/listers/whereabouts.cni.cncf.io/v1alpha1/expansion_generated.go b/pkg/client/listers/whereabouts.cni.cncf.io/v1alpha1/expansion_generated.go index 3faa09403..976ec3eb1 100644 --- a/pkg/client/listers/whereabouts.cni.cncf.io/v1alpha1/expansion_generated.go +++ b/pkg/client/listers/whereabouts.cni.cncf.io/v1alpha1/expansion_generated.go @@ -1,5 +1,5 @@ /* -Copyright 2022 The Kubernetes Authors +Copyright 2023 The Kubernetes Authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/client/listers/whereabouts.cni.cncf.io/v1alpha1/ippool.go b/pkg/client/listers/whereabouts.cni.cncf.io/v1alpha1/ippool.go index 204e5f3e4..cd0ce5af2 100644 --- a/pkg/client/listers/whereabouts.cni.cncf.io/v1alpha1/ippool.go +++ b/pkg/client/listers/whereabouts.cni.cncf.io/v1alpha1/ippool.go @@ -1,5 +1,5 @@ /* -Copyright 2022 The Kubernetes Authors +Copyright 2023 The Kubernetes Authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/client/listers/whereabouts.cni.cncf.io/v1alpha1/overlappingrangeipreservation.go b/pkg/client/listers/whereabouts.cni.cncf.io/v1alpha1/overlappingrangeipreservation.go index 6ea630ccc..9e158f0d2 100644 --- a/pkg/client/listers/whereabouts.cni.cncf.io/v1alpha1/overlappingrangeipreservation.go +++ b/pkg/client/listers/whereabouts.cni.cncf.io/v1alpha1/overlappingrangeipreservation.go @@ -1,5 +1,5 @@ /* -Copyright 2022 The Kubernetes Authors +Copyright 2023 The Kubernetes Authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/config/config.go b/pkg/config/config.go index edceb8ffc..dd29ab32e 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -3,7 +3,7 @@ package config import ( "encoding/json" "fmt" - "io/ioutil" + "io" "net" "os" "strings" @@ -246,9 +246,9 @@ func GetFlatIPAM(isControlLoop bool, IPAM *types.IPAMConfig, extraConfigPaths .. defer jsonFile.Close() - jsonBytes, err := ioutil.ReadAll(jsonFile) + jsonBytes, err := io.ReadAll(jsonFile) if err != nil { - return flatipam, foundflatfile, fmt.Errorf("LoadIPAMConfig Flatfile (%s) - ioutil.ReadAll error: %s", confpath, err) + return flatipam, foundflatfile, fmt.Errorf("LoadIPAMConfig Flatfile (%s) - io.ReadAll error: %s", confpath, err) } if err := json.Unmarshal(jsonBytes, &flatipam.IPAM); err != nil { diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index aab2b06ba..54b8f6838 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -2,8 +2,8 @@ package config import ( "fmt" - "io/ioutil" "net" + "os" "testing" . "github.com/onsi/ginkgo" @@ -61,7 +61,7 @@ var _ = Describe("Allocation operations", func() { "gateway": "192.168.5.5" }` - err := ioutil.WriteFile("/tmp/whereabouts.conf", []byte(globalconf), 0755) + err := os.WriteFile("/tmp/whereabouts.conf", []byte(globalconf), 0755) Expect(err).NotTo(HaveOccurred()) conf := `{ @@ -107,7 +107,7 @@ var _ = Describe("Allocation operations", func() { "gateway": "192.168.5.5", "enable_overlapping_ranges": false }` - Expect(ioutil.WriteFile("/tmp/whereabouts.conf", []byte(globalConf), 0755)).To(Succeed()) + Expect(os.WriteFile("/tmp/whereabouts.conf", []byte(globalConf), 0755)).To(Succeed()) ipamconfig, _, err := LoadIPAMConfig([]byte(generateIPAMConfWithOverlappingRanges()), "") Expect(err).NotTo(HaveOccurred()) @@ -126,7 +126,7 @@ var _ = Describe("Allocation operations", func() { "gateway": "192.168.5.5", "enable_overlapping_ranges": true }` - Expect(ioutil.WriteFile("/tmp/whereabouts.conf", []byte(globalConf), 0755)).To(Succeed()) + Expect(os.WriteFile("/tmp/whereabouts.conf", []byte(globalConf), 0755)).To(Succeed()) ipamconfig, _, err := LoadIPAMConfig([]byte(generateIPAMConfWithoutOverlappingRanges()), "") Expect(err).NotTo(HaveOccurred()) diff --git a/pkg/controlloop/dummy_controller.go b/pkg/controlloop/dummy_controller.go index 228c93577..05e873352 100644 --- a/pkg/controlloop/dummy_controller.go +++ b/pkg/controlloop/dummy_controller.go @@ -5,11 +5,11 @@ package controlloop import ( "context" - kubeClient "github.com/k8snetworkplumbingwg/whereabouts/pkg/storage/kubernetes" "net" + kubeClient "github.com/k8snetworkplumbingwg/whereabouts/pkg/storage/kubernetes" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - v1coreinformerfactory "k8s.io/client-go/informers" k8sclient "k8s.io/client-go/kubernetes" "k8s.io/client-go/tools/cache" "k8s.io/client-go/tools/record" @@ -41,7 +41,10 @@ func newDummyPodController( const noResyncPeriod = 0 netAttachDefInformerFactory := nadinformers.NewSharedInformerFactory(nadClient, noResyncPeriod) wbInformerFactory := wbinformers.NewSharedInformerFactory(wbClient, noResyncPeriod) - podInformerFactory := v1coreinformerfactory.NewSharedInformerFactory(k8sClient, noResyncPeriod) + podInformerFactory, err := PodInformerFactory(k8sClient) + if err != nil { + return nil, err + } podController := newPodController( k8sClient, diff --git a/pkg/controlloop/entity_generators.go b/pkg/controlloop/entity_generators.go index 40223809a..efceb9e6f 100644 --- a/pkg/controlloop/entity_generators.go +++ b/pkg/controlloop/entity_generators.go @@ -50,13 +50,24 @@ func dummyNonWhereaboutsIPAMNetSpec(networkName string) string { }`, networkName) } -func podSpec(name string, namespace string, networks ...string) *v1.Pod { +func nodeSpec(name string) *v1.Node { + return &v1.Node{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + }, + } +} + +func podSpec(name string, namespace string, nodeName string, networks ...string) *v1.Pod { return &v1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: namespace, Annotations: podNetworkSelectionElements(networks...), }, + Spec: v1.PodSpec{ + NodeName: nodeName, + }, } } @@ -96,14 +107,14 @@ func podNetworkStatusAnnotations(namespace string, networkNames ...string) strin return string(serelizedNetStatus) } -func ipPool(ipRange string, namespace string, podReferences ...string) *v1alpha1.IPPool { +func ipPool(poolIdentifier kubernetes.PoolIdentifier, namespace string, podReferences ...string) *v1alpha1.IPPool { return &v1alpha1.IPPool{ ObjectMeta: metav1.ObjectMeta{ - Name: kubernetes.NormalizeRange(ipRange), + Name: kubernetes.IPPoolName(poolIdentifier), Namespace: namespace, }, Spec: v1alpha1.IPPoolSpec{ - Range: ipRange, + Range: poolIdentifier.IpRange, Allocations: allocations(podReferences...), }, } diff --git a/pkg/controlloop/pod.go b/pkg/controlloop/pod.go index f0abb45fe..51b7a9ed4 100644 --- a/pkg/controlloop/pod.go +++ b/pkg/controlloop/pod.go @@ -4,14 +4,17 @@ import ( "context" "encoding/json" "fmt" - "k8s.io/client-go/kubernetes" "net" "os" "strconv" "strings" "time" + "k8s.io/client-go/kubernetes" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/util/wait" v1coreinformerfactory "k8s.io/client-go/informers" v1corelisters "k8s.io/client-go/listers/core/v1" @@ -22,6 +25,7 @@ import ( nadv1 "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1" nadinformers "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/client/informers/externalversions" nadlister "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/client/listers/k8s.cni.cncf.io/v1" + "github.com/pkg/errors" "github.com/k8snetworkplumbingwg/whereabouts/pkg/allocate" whereaboutsv1alpha1 "github.com/k8snetworkplumbingwg/whereabouts/pkg/api/whereabouts.cni.cncf.io/v1alpha1" @@ -47,6 +51,12 @@ const ( addressGarbageCollectionFailed = "IPAddressGarbageCollectionFailed" ) +const ( + podControllerFilterKey = "spec.nodeName" + podControllerNodeNameEnvVariable = "NODENAME" + noResyncPeriod = 0 +) + type garbageCollector func(ctx context.Context, mode int, ipamConf types.IPAMConfig, client *wbclient.KubernetesIPAM) ([]net.IPNet, error) type PodController struct { @@ -73,6 +83,22 @@ func NewPodController(k8sCoreClient kubernetes.Interface, wbClient wbclientset.I return newPodController(k8sCoreClient, wbClient, k8sCoreInformerFactory, wbSharedInformerFactory, netAttachDefInformerFactory, broadcaster, recorder, wbclient.IPManagement) } +// PodInformerFactory is a wrapper around NewSharedInformerFactoryWithOptions. Before returning the informer, it will +// extract the node name from environment variable "NODENAME". It will then try to look up the node with the given name. +// On success, it will create an informer that filters all pods with spec.nodeName == . +func PodInformerFactory(k8sClientSet kubernetes.Interface) (v1coreinformerfactory.SharedInformerFactory, error) { + nodeName := os.Getenv(podControllerNodeNameEnvVariable) + logging.Debugf("Filtering pods with filter key '%s' and filter value '%s'", podControllerFilterKey, nodeName) + if _, err := k8sClientSet.CoreV1().Nodes().Get(context.TODO(), nodeName, metav1.GetOptions{}); err != nil { + return nil, errors.Wrap(err, fmt.Sprintf("Could not find node with node name '%s'.", nodeName)) + } + return v1coreinformerfactory.NewSharedInformerFactoryWithOptions( + k8sClientSet, noResyncPeriod, v1coreinformerfactory.WithTweakListOptions( + func(options *metav1.ListOptions) { + options.FieldSelector = fields.OneTermEqualSelector(podControllerFilterKey, nodeName).String() + })), nil +} + func newPodController(k8sCoreClient kubernetes.Interface, wbClient wbclientset.Interface, k8sCoreInformerFactory v1coreinformerfactory.SharedInformerFactory, wbSharedInformerFactory wbinformers.SharedInformerFactory, netAttachDefInformerFactory nadinformers.SharedInformerFactory, broadcaster record.EventBroadcaster, recorder record.EventRecorder, cleanupFunc garbageCollector) *PodController { k8sPodFilteredInformer := k8sCoreInformerFactory.Core().V1().Pods() ipPoolInformer := wbSharedInformerFactory.Whereabouts().V1alpha1().IPPools() @@ -183,7 +209,7 @@ func (pc *PodController) garbageCollectPodIPs(pod *v1.Pod) error { var pools []*whereaboutsv1alpha1.IPPool for _, rangeConfig := range ipamConfig.IPRanges { - pool, err := pc.ipPool(rangeConfig.Range) + pool, err := pc.ipPool(wbclient.PoolIdentifier{IpRange: rangeConfig.Range, NetworkName: ipamConfig.NetworkName}) if err != nil { return fmt.Errorf("failed to get the IPPool data: %+v", err) @@ -271,8 +297,8 @@ func (pc *PodController) ifaceNetAttachDef(ifaceStatus nadv1.NetworkStatus) (*na return nad, nil } -func (pc *PodController) ipPool(cidr string) (*whereaboutsv1alpha1.IPPool, error) { - pool, err := pc.ipPoolLister.IPPools(ipPoolsNamespace()).Get(wbclient.NormalizeRange(cidr)) +func (pc *PodController) ipPool(poolIdentifier wbclient.PoolIdentifier) (*whereaboutsv1alpha1.IPPool, error) { + pool, err := pc.ipPoolLister.IPPools(ipPoolsNamespace()).Get(wbclient.IPPoolName(poolIdentifier)) if err != nil { return nil, err } diff --git a/pkg/controlloop/pod_controller_test.go b/pkg/controlloop/pod_controller_test.go index de8d318ed..4d295b335 100644 --- a/pkg/controlloop/pod_controller_test.go +++ b/pkg/controlloop/pod_controller_test.go @@ -3,7 +3,6 @@ package controlloop import ( "context" "fmt" - "io/ioutil" "os" "path" "testing" @@ -13,6 +12,7 @@ import ( . "github.com/onsi/gomega" v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" k8sclient "k8s.io/client-go/kubernetes" @@ -26,6 +26,7 @@ import ( "github.com/k8snetworkplumbingwg/whereabouts/pkg/api/whereabouts.cni.cncf.io/v1alpha1" wbclient "github.com/k8snetworkplumbingwg/whereabouts/pkg/client/clientset/versioned" fakewbclient "github.com/k8snetworkplumbingwg/whereabouts/pkg/client/clientset/versioned/fake" + "github.com/k8snetworkplumbingwg/whereabouts/pkg/storage/kubernetes" ) func TestIPControlLoop(t *testing.T) { @@ -45,10 +46,10 @@ var _ = Describe("IPControlLoop", func() { const configFilePermissions = 0755 var err error - cniConfigDir, err = ioutil.TempDir("", "multus-config") + cniConfigDir, err = os.MkdirTemp("", "multus-config") Expect(err).ToNot(HaveOccurred()) Expect(os.MkdirAll(path.Join(cniConfigDir, path.Dir(whereaboutsConfigPath)), configFilePermissions)).To(Succeed()) - Expect(ioutil.WriteFile( + Expect(os.WriteFile( path.Join(cniConfigDir, whereaboutsConfigPath), []byte(dummyWhereaboutsConfig()), configFilePermissions)).To(Succeed()) }) @@ -57,20 +58,59 @@ var _ = Describe("IPControlLoop", func() { Expect(os.RemoveAll(cniConfigDir)).To(Succeed()) }) - Context("a running pod", func() { + Context("a running pod on a node", func() { const ( networkName = "meganet" podName = "tiny-winy-pod" + nodeName = "hypernode" ) var ( - k8sClient k8sclient.Interface - pod *v1.Pod + k8sClient k8sclient.Interface + pod *v1.Pod + node *v1.Node + dummyPodController *dummyPodController + podControllerError error ) BeforeEach(func() { - pod = podSpec(podName, namespace, networkName) - k8sClient = fakek8sclient.NewSimpleClientset(pod) + pod = podSpec(podName, namespace, nodeName, networkName) + node = nodeSpec(nodeName) + k8sClient = fakek8sclient.NewSimpleClientset(pod, node) + os.Setenv("NODENAME", nodeName) + }) + + When("NODENAME is set to an invalid value", func() { + var ( + wbClient wbclient.Interface + eventRecorder *record.FakeRecorder + netAttachDefClient nadclient.Interface + stopChannel chan struct{} + ) + + BeforeEach(func() { + os.Setenv("NODENAME", "invalid-node-name") + + stopChannel = make(chan struct{}) + wbClient = fakewbclient.NewSimpleClientset() + netAttachDefClient, podControllerError = newFakeNetAttachDefClient(namespace, netAttachDef(networkName, namespace, dummyNonWhereaboutsIPAMNetSpec(networkName))) + Expect(podControllerError).NotTo(HaveOccurred()) + + const maxEvents = 1 + eventRecorder = record.NewFakeRecorder(maxEvents) + }) + + It("should fail", func() { + _, podControllerError = newDummyPodController(k8sClient, wbClient, netAttachDefClient, stopChannel, cniConfigDir, eventRecorder) + Expect(errors.IsNotFound(podControllerError)).Should(BeTrue()) + }) + + AfterEach(func() { + if podControllerError != nil { + return + } + stopChannel <- struct{}{} + }) }) Context("IPPool featuring an allocation for the pod", func() { @@ -80,7 +120,7 @@ var _ = Describe("IPControlLoop", func() { ) BeforeEach(func() { - dummyNetworkPool = ipPool(dummyNetIPRange, ipPoolsNamespace(), podReference(pod)) + dummyNetworkPool = ipPool(kubernetes.PoolIdentifier{IpRange: dummyNetIPRange, NetworkName: kubernetes.UnnamedNetwork}, ipPoolsNamespace(), podReference(pod)) wbClient = fakewbclient.NewSimpleClientset(dummyNetworkPool) }) @@ -99,7 +139,10 @@ var _ = Describe("IPControlLoop", func() { const maxEvents = 10 stopChannel = make(chan struct{}) eventRecorder = record.NewFakeRecorder(maxEvents) - Expect(newDummyPodController(k8sClient, wbClient, netAttachDefClient, stopChannel, cniConfigDir, eventRecorder)).NotTo(BeNil()) + + dummyPodController, podControllerError = newDummyPodController(k8sClient, wbClient, netAttachDefClient, stopChannel, cniConfigDir, eventRecorder) + Expect(podControllerError).NotTo(HaveOccurred()) + Expect(dummyPodController).NotTo(BeNil()) // assure the pool features an allocated address ipPool, err := wbClient.WhereaboutsV1alpha1().IPPools(dummyNetworkPool.GetNamespace()).Get(context.TODO(), dummyNetworkPool.GetName(), metav1.GetOptions{}) @@ -108,6 +151,9 @@ var _ = Describe("IPControlLoop", func() { }) AfterEach(func() { + if podControllerError != nil { + return + } stopChannel <- struct{}{} }) @@ -146,7 +192,9 @@ var _ = Describe("IPControlLoop", func() { const maxEvents = 10 eventRecorder = record.NewFakeRecorder(maxEvents) - Expect(newDummyPodController(k8sClient, wbClient, netAttachDefClient, stopChannel, cniConfigDir, eventRecorder)).NotTo(BeNil()) + dummyPodController, podControllerError = newDummyPodController(k8sClient, wbClient, netAttachDefClient, stopChannel, cniConfigDir, eventRecorder) + Expect(podControllerError).NotTo(HaveOccurred()) + Expect(dummyPodController).NotTo(BeNil()) // assure the pool features an allocated address ipPool, err := wbClient.WhereaboutsV1alpha1().IPPools(dummyNetworkPool.GetNamespace()).Get(context.TODO(), dummyNetworkPool.GetName(), metav1.GetOptions{}) @@ -197,10 +245,16 @@ var _ = Describe("IPControlLoop", func() { const maxEvents = 1 eventRecorder = record.NewFakeRecorder(maxEvents) - Expect(newDummyPodController(k8sClient, wbClient, netAttachDefClient, stopChannel, cniConfigDir, eventRecorder)).NotTo(BeNil()) + + dummyPodController, podControllerError = newDummyPodController(k8sClient, wbClient, netAttachDefClient, stopChannel, cniConfigDir, eventRecorder) + Expect(podControllerError).NotTo(HaveOccurred()) + Expect(dummyPodController).NotTo(BeNil()) }) AfterEach(func() { + if podControllerError != nil { + return + } stopChannel <- struct{}{} }) diff --git a/pkg/reconciler/ip_test.go b/pkg/reconciler/ip_test.go index bc7cb5adf..f76fb6841 100644 --- a/pkg/reconciler/ip_test.go +++ b/pkg/reconciler/ip_test.go @@ -443,8 +443,8 @@ func generatePodAnnotations(ipNetworks ...ipInNetwork) map[string]string { networks = append(networks, ipNetworkInfo.networkName) } networkAnnotations := map[string]string{ - MultusNetworkAnnotation: strings.Join(networks, ","), - MultusNetworkStatusAnnotation: generatePodNetworkStatusAnnotation(ipNetworks...), + multusv1.NetworkAttachmentAnnot: strings.Join(networks, ","), + multusv1.NetworkStatusAnnot: generatePodNetworkStatusAnnotation(ipNetworks...), } return networkAnnotations } diff --git a/pkg/reconciler/wrappedPod.go b/pkg/reconciler/wrappedPod.go index 9ed265ba3..9f4f81610 100644 --- a/pkg/reconciler/wrappedPod.go +++ b/pkg/reconciler/wrappedPod.go @@ -10,13 +10,6 @@ import ( v1 "k8s.io/api/core/v1" ) -const ( - multusInterfaceNamePrefix = "net" - multusPrefixSize = len(multusInterfaceNamePrefix) - MultusNetworkAnnotation = "k8s.v1.cni.cncf.io/networks" - MultusNetworkStatusAnnotation = "k8s.v1.cni.cncf.io/networks-status" -) - type podWrapper struct { ips map[string]void phase v1.PodPhase @@ -90,7 +83,7 @@ func getFlatIPSet(pod v1.Pod) (map[string]void, error) { } func networkStatusFromPod(pod v1.Pod) string { - networkStatusAnnotationValue, isStatusAnnotationPresent := pod.Annotations[MultusNetworkStatusAnnotation] + networkStatusAnnotationValue, isStatusAnnotationPresent := pod.Annotations[k8snetworkplumbingwgv1.NetworkStatusAnnot] if !isStatusAnnotationPresent || len(networkStatusAnnotationValue) == 0 { return "[]" } diff --git a/pkg/reconciler/wrappedPod_test.go b/pkg/reconciler/wrappedPod_test.go index faae9ff19..aba34e686 100644 --- a/pkg/reconciler/wrappedPod_test.go +++ b/pkg/reconciler/wrappedPod_test.go @@ -50,7 +50,7 @@ var _ = Describe("Pod Wrapper operations", func() { annotationString = []byte("") } return map[string]string{ - MultusNetworkStatusAnnotation: string(annotationString), + k8snetworkplumbingwgv1.NetworkStatusAnnot: string(annotationString), } } @@ -60,7 +60,7 @@ var _ = Describe("Pod Wrapper operations", func() { annotationString = []byte("") } return map[string]string{ - MultusNetworkStatusAnnotation: string(annotationString), + k8snetworkplumbingwgv1.NetworkStatusAnnot: string(annotationString), } } @@ -117,7 +117,7 @@ var _ = Describe("Pod Wrapper operations", func() { It("return an empty list when the network annotations of a pod are invalid", func() { pod := v1.Pod{ ObjectMeta: metav1.ObjectMeta{ - Annotations: map[string]string{MultusNetworkStatusAnnotation: "this-wont-fly"}, + Annotations: map[string]string{k8snetworkplumbingwgv1.NetworkStatusAnnot: "this-wont-fly"}, }, } Expect(wrapPod(pod).ips).To(BeEmpty()) diff --git a/pkg/storage/kubernetes/ipam.go b/pkg/storage/kubernetes/ipam.go index 574b79c0f..da2a054ea 100644 --- a/pkg/storage/kubernetes/ipam.go +++ b/pkg/storage/kubernetes/ipam.go @@ -74,6 +74,13 @@ type KubernetesIPAM struct { namespace string } +const UnnamedNetwork string = "" + +type PoolIdentifier struct { + IpRange string + NetworkName string +} + func toIPReservationList(allocations map[string]whereaboutsv1alpha1.IPAllocation, firstip net.IP) []whereaboutstypes.IPReservation { reservelist := []whereaboutstypes.IPReservation{} for offset, a := range allocations { @@ -100,10 +107,10 @@ func toAllocationMap(reservelist []whereaboutstypes.IPReservation, firstip net.I } // GetIPPool returns a storage.IPPool for the given range -func (i *KubernetesIPAM) GetIPPool(ctx context.Context, ipRange string) (storage.IPPool, error) { - normalized := NormalizeRange(ipRange) +func (i *KubernetesIPAM) GetIPPool(ctx context.Context, poolIdentifier PoolIdentifier) (storage.IPPool, error) { + name := IPPoolName(poolIdentifier) - pool, err := i.getPool(ctx, normalized, ipRange) + pool, err := i.getPool(ctx, name, poolIdentifier.IpRange) if err != nil { return nil, err } @@ -116,9 +123,21 @@ func (i *KubernetesIPAM) GetIPPool(ctx context.Context, ipRange string) (storage return &KubernetesIPPool{i.client, i.containerID, firstIP, pool}, nil } -func NormalizeRange(ipRange string) string { +func IPPoolName(poolIdentifier PoolIdentifier) string { + if poolIdentifier.NetworkName == UnnamedNetwork { + return normalizeRange(poolIdentifier.IpRange) + } else { + return fmt.Sprintf("%s-%s", poolIdentifier.NetworkName, normalizeRange(poolIdentifier.IpRange)) + } +} + +func normalizeRange(ipRange string) string { // v6 filter + if ipRange[len(ipRange)-1] == ':' { + ipRange = ipRange + "0" + } normalized := strings.ReplaceAll(ipRange, ":", "-") + // replace subnet cidr slash normalized = strings.ReplaceAll(normalized, "/", "-") return normalized @@ -178,7 +197,12 @@ func (i *KubernetesIPAM) GetOverlappingRangeStore() (storage.OverlappingRangeSto func (c *KubernetesOverlappingRangeStore) IsAllocatedInOverlappingRange(ctx context.Context, ip net.IP) (bool, error) { // IPv6 doesn't make for valid CR names, so normalize it. - normalizedip := strings.ReplaceAll(fmt.Sprint(ip), ":", "-") + ipStr := fmt.Sprint(ip) + if ipStr[len(ipStr)-1] == ':' { + ipStr += "0" + logging.Debugf("modified: %s", ipStr) + } + normalizedip := strings.ReplaceAll(ipStr, ":", "-") logging.Debugf("OverlappingRangewide allocation check for IP: %v", normalizedip) @@ -199,7 +223,12 @@ func (c *KubernetesOverlappingRangeStore) IsAllocatedInOverlappingRange(ctx cont // UpdateOverlappingRangeAllocation updates clusterwide allocation for overlapping ranges. func (c *KubernetesOverlappingRangeStore) UpdateOverlappingRangeAllocation(ctx context.Context, mode int, ip net.IP, containerID string, podRef string) error { // Normalize the IP - normalizedip := strings.ReplaceAll(fmt.Sprint(ip), ":", "-") + ipStr := fmt.Sprint(ip) + if ipStr[len(ipStr)-1] == ':' { + ipStr += "0" + logging.Debugf("modified: %s", ipStr) + } + normalizedip := strings.ReplaceAll(ipStr, ":", "-") clusteripres := &whereaboutsv1alpha1.OverlappingRangeIPReservation{ ObjectMeta: metav1.ObjectMeta{Name: normalizedip, Namespace: c.namespace}, @@ -457,7 +486,7 @@ func IPManagementKubernetesUpdate(ctx context.Context, mode int, ipam *Kubernete return newips, err } - pool, err = ipam.GetIPPool(requestCtx, ipRange.Range) + pool, err = ipam.GetIPPool(requestCtx, PoolIdentifier{IpRange: ipRange.Range, NetworkName: ipamConf.NetworkName}) if err != nil { logging.Errorf("IPAM error reading pool allocations (attempt: %d): %v", j, err) if e, ok := err.(storage.Temporary); ok && e.Temporary() { diff --git a/pkg/types/types.go b/pkg/types/types.go index d7804e16f..a12ec43ed 100644 --- a/pkg/types/types.go +++ b/pkg/types/types.go @@ -70,6 +70,7 @@ type IPAMConfig struct { ConfigurationPath string `json:"configuration_path"` PodName string PodNamespace string + NetworkName string `json:"network_name,omitempty"` } func (ic *IPAMConfig) UnmarshalJSON(data []byte) error { @@ -105,6 +106,7 @@ func (ic *IPAMConfig) UnmarshalJSON(data []byte) error { ConfigurationPath string `json:"configuration_path"` PodName string PodNamespace string + NetworkName string `json:"network_name,omitempty"` } ipamConfigAlias := IPAMConfigAlias{ @@ -140,6 +142,7 @@ func (ic *IPAMConfig) UnmarshalJSON(data []byte) error { ConfigurationPath: ipamConfigAlias.ConfigurationPath, PodName: ipamConfigAlias.PodName, PodNamespace: ipamConfigAlias.PodNamespace, + NetworkName: ipamConfigAlias.NetworkName, } return nil } diff --git a/vendor/golang.org/x/net/html/parse.go b/vendor/golang.org/x/net/html/parse.go index 291c91908..46a89eda6 100644 --- a/vendor/golang.org/x/net/html/parse.go +++ b/vendor/golang.org/x/net/html/parse.go @@ -184,7 +184,7 @@ func (p *parser) clearStackToContext(s scope) { } } -// parseGenericRawTextElements implements the generic raw text element parsing +// parseGenericRawTextElement implements the generic raw text element parsing // algorithm defined in 12.2.6.2. // https://html.spec.whatwg.org/multipage/parsing.html#parsing-elements-that-contain-only-text // TODO: Since both RAWTEXT and RCDATA states are treated as tokenizer's part diff --git a/vendor/golang.org/x/net/html/token.go b/vendor/golang.org/x/net/html/token.go index be3c75414..50f7c6aac 100644 --- a/vendor/golang.org/x/net/html/token.go +++ b/vendor/golang.org/x/net/html/token.go @@ -598,6 +598,11 @@ scriptDataDoubleEscapeEnd: // readComment reads the next comment token starting with "") return } @@ -628,17 +632,50 @@ func (z *Tokenizer) readComment() { if dashCount >= 2 { c = z.readByte() if z.err != nil { - z.data.end = z.raw.end + z.data.end = z.calculateAbruptCommentDataEnd() return - } - if c == '>' { + } else if c == '>' { z.data.end = z.raw.end - len("--!>") return + } else if c == '-' { + dashCount = 1 + beginning = false + continue } } } dashCount = 0 + beginning = false + } +} + +func (z *Tokenizer) calculateAbruptCommentDataEnd() int { + raw := z.Raw() + const prefixLen = len("