diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 90e05c40d..0f95de250 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -3,9 +3,12 @@ # 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: "github-actions" # See documentation for possible values directory: "/" # Location of package manifests schedule: interval: "weekly" + cooldown: + default-days: 7 diff --git a/.github/linters/.checkov.yaml b/.github/linters/.checkov.yaml index cd392e19c..55e40987c 100644 --- a/.github/linters/.checkov.yaml +++ b/.github/linters/.checkov.yaml @@ -1,3 +1,4 @@ +--- skip-framework: - cloudformation skip-path: @@ -7,3 +8,5 @@ skip-path: - x/webhook - x/wire - init_wait + - "third_party/meshnet/tests/.*" + - "third_party/meshnet/manifests/.*" diff --git a/.github/linters/.codespellrc b/.github/linters/.codespellrc new file mode 100644 index 000000000..cf0bf4c2c --- /dev/null +++ b/.github/linters/.codespellrc @@ -0,0 +1,3 @@ +[codespell] +skip = *.pdf,*.png,*.jpg,*.gif,*.ico,go.sum +ignore-words-list = notin,NotIn diff --git a/.github/linters/.gitleaks.toml b/.github/linters/.gitleaks.toml index 9d0b308f0..f820c075c 100644 --- a/.github/linters/.gitleaks.toml +++ b/.github/linters/.gitleaks.toml @@ -209,6 +209,7 @@ title = "gitleaks config" paths = [ '''^\.?gitleaks.toml$''', '''topo/node/srl/generate_certificate_success$''', # exclude dummy test file with random cert + '''x/webhook/manifests/tls\.secret\.yaml$''', '''(.*?)super-linter.log$''', # exclude linter logs which might contain past errored runs with keys/certs '''(.*?)(png|jpg|gif|doc|docx|pdf|bin|xls|pyc|zip)$''', '''(go.mod|go.sum)$''' diff --git a/.github/linters/.golangci.yml b/.github/linters/.golangci.yml index 5e10a88a4..f0e73e113 100644 --- a/.github/linters/.golangci.yml +++ b/.github/linters/.golangci.yml @@ -1,4 +1,48 @@ +--- version: "2" - -issues: - new-from-rev: origin/main +linters: + enable: + - gocritic + - gosec + - revive + - unconvert + - unparam + - wastedassign + - whitespace + settings: + errcheck: + check-blank: true + gocritic: + disabled-checks: + - singleCaseSwitch + - appendAssign + revive: + severity: warning + exclusions: + generated: lax + presets: + - comments + - common-false-positives + - legacy + - std-error-handling + rules: + - linters: + - dupl + - goconst + - gosec + path: _test\.go + paths: + - third_party$ + - builtin$ + - examples$ +formatters: + enable: + - gofmt + - goimports + exclusions: + generated: lax + paths: + - third_party$ + - builtin$ + - examples$ + - \.pb\.go diff --git a/.github/linters/.markdown-lint.yml b/.github/linters/.markdown-lint.yml index 64f68b737..f9b628240 100644 --- a/.github/linters/.markdown-lint.yml +++ b/.github/linters/.markdown-lint.yml @@ -1,4 +1,6 @@ +--- default: true +extends: markdownlint/style/prettier MD001: false # header levels @@ -6,4 +8,4 @@ MD013: false # line length MD028: false # blank lines between indents -MD033: false # inline HTML \ No newline at end of file +MD033: false # inline HTML diff --git a/.github/linters/.protolintrc.yml b/.github/linters/.protolintrc.yml index 7bb2bd1ea..057e733b6 100644 --- a/.github/linters/.protolintrc.yml +++ b/.github/linters/.protolintrc.yml @@ -1,3 +1,4 @@ +--- lint: rules: remove: # TODO: fix proto files and remove these. diff --git a/.github/linters/trivy.yaml b/.github/linters/trivy.yaml index fc175b68b..3aae11706 100644 --- a/.github/linters/trivy.yaml +++ b/.github/linters/trivy.yaml @@ -1,3 +1,4 @@ +--- scan: skip-dirs: - manifests diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index dcb1de91a..fddd790ef 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -28,3 +28,23 @@ jobs: permissions: contents: read packages: read + + meshnet: + name: Meshnet Go Tests + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - name: Check out code + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + with: + persist-credentials: false + - name: Set up Go + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 + with: + go-version: "1.26" + - name: Install dependencies + run: sudo apt-get install -y libpcap-dev + - name: Run Go tests + working-directory: ./third_party/meshnet + run: make test diff --git a/.github/workflows/meshnet-e2e.yml b/.github/workflows/meshnet-e2e.yml new file mode 100644 index 000000000..a1d6d7446 --- /dev/null +++ b/.github/workflows/meshnet-e2e.yml @@ -0,0 +1,49 @@ +--- +name: Meshnet E2E + +on: + push: + branches: [main] + pull_request: + branches: [main] + +permissions: {} + +jobs: + e2e: + name: E2E (${{ matrix.link }}) + runs-on: ubuntu-latest + permissions: + contents: read + strategy: + fail-fast: false + matrix: + link: [vxlan, grpc] + steps: + - name: Check out code + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + with: + persist-credentials: false + - name: Set up Go + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 + with: + go-version: "1.26" + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0 + - name: Build e2e test env + working-directory: ./third_party/meshnet + run: make up + - name: Build docker image + working-directory: ./third_party/meshnet + run: make docker + - name: Install meshnet (${{ matrix.link }}) + working-directory: ./third_party/meshnet + run: | + if [ "${{ matrix.link }}" = "grpc" ]; then + make grpc=1 install + else + make install + fi + - name: Run tests + working-directory: ./third_party/meshnet + run: make e2e diff --git a/README.md b/README.md index 9cd435b50..8819cb67b 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,6 @@ [![Go Report Card](https://goreportcard.com/badge/github.com/openconfig/kne)](https://goreportcard.com/report/github.com/openconfig/kne) [![GoDoc](https://godoc.org/istio.io/istio?status.svg)](https://pkg.go.dev/github.com/openconfig/kne) [![License: BSD](https://img.shields.io/badge/license-Apache%202-blue)](https://opensource.org/licenses/Apache-2.0) -[![GitHub Super-Linter](https://github.com/openconfig/kne/workflows/Lint%20Code%20Base/badge.svg)](https://github.com/marketplace/actions/super-linter) [![Coverage Status](https://coveralls.io/repos/github/openconfig/kne/badge.svg?branch=main)](https://coveralls.io/github/openconfig/kne?branch=main) This is not an officially supported Google product. @@ -12,42 +11,44 @@ This is not an officially supported Google product. ## Goal For network emulation, there are many approaches using VM's for emulation of a -hardware router. Arista, Cisco, Juniper, Drivenets, and Nokia have multiple implementations -of their network operating system and various generations of hardware emulation. -These systems are very good for most validation of vendor control plane -implementations and data plane for limited certifications. The idea of this -project is to provide a standard "interface" so that vendors can produce a -standard container implementation which can be used to build complex topologies. - -* Have standard lifecycle management infrastructure for allowing multiple vendor - device emulations to be present in a single "topology" -* Allow for control plane access via standard k8s networking -* Provide a common networking interface for the forwarding plane between network - pods. - * Data plane wires between pods - * Control plane wires between topology manager -* Define service implementation for allowing interaction with the topology +hardware router. Arista, Cisco, Juniper, Drivenets, and Nokia have multiple +implementations of their network operating system and various generations of +hardware emulation. These systems are very good for most validation of vendor +control plane implementations and data plane for limited certifications. The +idea of this project is to provide a standard "interface" so that vendors can +produce a standard container implementation which can be used to build complex +topologies. + +- Have standard lifecycle management infrastructure for allowing multiple + vendor device emulations to be present in a single "topology" +- Allow for control plane access via standard k8s networking +- Provide a common networking interface for the forwarding plane between + network pods. + - Data plane wires between pods + - Control plane wires between topology manager +- Define service implementation for allowing interaction with the topology manager service. - * Topology manager is the public API for allowing external users to manipulate - the link state in the topology. - * The topology manager will run as a service in k8s environment. - * It will provide a gRPC interface for tests to interact with - * It will listen to CRDs published via the network device pods for discovery -* Data plane connections for connectivity between pods must be a public + - Topology manager is the public API for allowing external users to + manipulate the link state in the topology. + - The topology manager will run as a service in k8s environment. + - It will provide a gRPC interface for tests to interact with + - It will listen to CRDs published via the network device pods for + discovery +- Data plane connections for connectivity between pods must be a public transport mechanism - * This can't be implemented as just exposing "x eth devices on the pod" - because Linux doesn't understand the associated control messages which are - needed to make this work like a wire. - * Transceiver state, optical characteristics, wire state, packet filtering / - shaping / drops - * LACP or other port aggregation protocols or APS cannot be simulated + - This can't be implemented as just exposing "x eth devices on the pod" + because Linux doesn't understand the associated control messages which + are needed to make this work like a wire. + - Transceiver state, optical characteristics, wire state, packet filtering + / shaping / drops + - LACP or other port aggregation protocols or APS cannot be simulated correctly - * The topology manager will start a topology agent on each host for the pod to - directly interact with. - * The topology agent will provide the connectivity between nodes -* Define how pods boot an initial configuration - * Ideally, this method would allow for dynamic -* Define how pods express services for use in-cluster as well as external + - The topology manager will start a topology agent on each host for the + pod to directly interact with. + - The topology agent will provide the connectivity between nodes +- Define how pods boot an initial configuration + - Ideally, this method would allow for dynamic +- Define how pods express services for use in-cluster as well as external services ## Use Cases @@ -88,23 +89,23 @@ Kubernetes Network Emulation (KNE). ### Usage Metrics Reporting -The KNE CLI optionally collects anonymous usage metrics. **This is turned OFF -by default.** We use the metrics to gauge the health and performance of various -KNE operations (i.e. cluster deployment, topology creation) on an **opt-in** -basis. There is a global flag `--report_usage` that when provided shares -anonymous details about certain KNE CLI commands. Collected data can be seen in -the [event proto definition](proto/event.proto). **Usage metrics are NOT shared -by default.** Additionally the PubSub project and topic the events are published -to are configurable. If you want to track your own private metrics about your -KNE usage then that is supported by providing a Cloud PubSub project/topic of -your choosing. Full details about how/when usage events are published can be -found in the codebase [here](metrics/metrics.go). We appreciate usage metric -reporting as it helps us develop a better KNE experience for all of our users. -Whether that be detecting an abnormally high number of cluster deployment -failures due to an upgrade to an underlying dependency introduced by a new -commit, or detecting a bug from a scenario where the failure rate for topologies -over *n* links is far greater than *n-1* links. Usage metric reporting is -helpful tool for the KNE developers. +The KNE CLI optionally collects anonymous usage metrics. **This is turned OFF by +default.** We use the metrics to gauge the health and performance of various KNE +operations (i.e. cluster deployment, topology creation) on an **opt-in** basis. +There is a global flag `--report_usage` that when provided shares anonymous +details about certain KNE CLI commands. Collected data can be seen in the +[event proto definition](proto/event.proto). **Usage metrics are NOT shared by +default.** Additionally the PubSub project and topic the events are published to +are configurable. If you want to track your own private metrics about your KNE +usage then that is supported by providing a Cloud PubSub project/topic of your +choosing. Full details about how/when usage events are published can be found in +the [codebase](metrics/metrics.go). We appreciate usage metric reporting as it +helps us develop a better KNE experience for all of our users. Whether that be +detecting an abnormally high number of cluster deployment failures due to an +upgrade to an underlying dependency introduced by a new commit, or detecting a +bug from a scenario where the failure rate for topologies over _n_ links is far +greater than _n-1_ links. Usage metric reporting is helpful tool for the KNE +developers. ## Thanks diff --git a/api/metallb/clientset/v1beta1/client.go b/api/metallb/clientset/v1beta1/client.go index 0c88fbb6c..1287c69bb 100644 --- a/api/metallb/clientset/v1beta1/client.go +++ b/api/metallb/clientset/v1beta1/client.go @@ -92,10 +92,14 @@ var ( ) func init() { - metallbv1.AddToScheme(Scheme) + if err := metallbv1.AddToScheme(Scheme); err != nil { + panic(err) + } metav1.AddToGroupVersion(Scheme, groupVersion) - metav1.AddMetaToScheme(Scheme) + if err := metav1.AddMetaToScheme(Scheme); err != nil { + panic(err) + } } func GV() *schema.GroupVersion { @@ -105,7 +109,7 @@ func GV() *schema.GroupVersion { // NewForConfig returns a new Clientset based on c. func NewForConfig(c *rest.Config) (*Clientset, error) { config := *c - config.ContentConfig.GroupVersion = &groupVersion + config.GroupVersion = &groupVersion config.APIPath = "/apis" config.NegotiatedSerializer = scheme.Codecs.WithoutConversion() config.UserAgent = rest.DefaultKubernetesUserAgent() diff --git a/cloudbuild/postsubmit.yaml b/cloudbuild/postsubmit.yaml index 956bc85f5..189de3c01 100644 --- a/cloudbuild/postsubmit.yaml +++ b/cloudbuild/postsubmit.yaml @@ -1,35 +1,46 @@ +--- steps: - id: packer_init_external name: "us-west1-docker.pkg.dev/gep-kne/packer/packer:1.9.1" - args: ["init", "cloudbuild/external.pkr.hcl"] - waitFor: ["-"] # run the builds concurrently - + args: + - "init" + - "cloudbuild/external.pkr.hcl" + waitFor: + - "-" # run the builds concurrently + - id: packer_build_external name: "us-west1-docker.pkg.dev/gep-kne/packer/packer:1.9.1" - args: ["build", "cloudbuild/external.pkr.hcl"] - env: [ - "PKR_VAR_build_id=$BUILD_ID", - "PKR_VAR_short_sha=$SHORT_SHA", - "PKR_VAR_branch_name=$BRANCH_NAME", - "PKR_VAR_zone=${_ZONE}", - ] - waitFor: [packer_init_external] - + args: + - "build" + - "cloudbuild/external.pkr.hcl" + env: + - "PKR_VAR_build_id=$BUILD_ID" + - "PKR_VAR_short_sha=$SHORT_SHA" + - "PKR_VAR_branch_name=$BRANCH_NAME" + - "PKR_VAR_zone=${_ZONE}" + waitFor: + - packer_init_external + - id: packer_init_internal name: "us-west1-docker.pkg.dev/gep-kne/packer/packer:1.9.1" - args: ["init", "cloudbuild/internal.pkr.hcl"] - waitFor: ["-"] # run the builds concurrently + args: + - "init" + - "cloudbuild/internal.pkr.hcl" + waitFor: + - "-" # run the builds concurrently - id: packer_build_internal name: "us-west1-docker.pkg.dev/gep-kne/packer/packer:1.9.1" - args: ["build", "cloudbuild/internal.pkr.hcl"] - env: [ - "PKR_VAR_build_id=$BUILD_ID", - "PKR_VAR_short_sha=$SHORT_SHA", - "PKR_VAR_branch_name=$BRANCH_NAME", - "PKR_VAR_zone=${_ZONE}", - ] - waitFor: [packer_init_internal] + args: + - "build" + - "cloudbuild/internal.pkr.hcl" + env: + - "PKR_VAR_build_id=$BUILD_ID" + - "PKR_VAR_short_sha=$SHORT_SHA" + - "PKR_VAR_branch_name=$BRANCH_NAME" + - "PKR_VAR_zone=${_ZONE}" + waitFor: + - packer_init_internal timeout: 5400s diff --git a/cloudbuild/presubmit.yaml b/cloudbuild/presubmit.yaml index 2d659273b..13adf9b4c 100644 --- a/cloudbuild/presubmit.yaml +++ b/cloudbuild/presubmit.yaml @@ -1,3 +1,4 @@ +--- steps: - id: kne_test name: us-west1-docker.pkg.dev/$PROJECT_ID/utilities/remote-builder @@ -26,4 +27,4 @@ timeout: 2700s options: pool: - name: 'projects/kne-external/locations/us-central1/workerPools/kne-cloudbuild-pool' + name: "projects/kne-external/locations/us-central1/workerPools/kne-cloudbuild-pool" diff --git a/cloudbuild/vendors/deployment.yaml b/cloudbuild/vendors/deployment.yaml index fdd38c018..0103a30a5 100644 --- a/cloudbuild/vendors/deployment.yaml +++ b/cloudbuild/vendors/deployment.yaml @@ -1,6 +1,7 @@ # kind-bridge.yaml cluster config file sets up a kind cluster where default PTP CNI plugin # is swapped with the Bridge CNI plugin. # Bridge CNI plugin is required by some Network OSes to operate. +--- cluster: kind: Kind spec: diff --git a/cmd/deploy/deploy.go b/cmd/deploy/deploy.go index f55ad53fe..389205697 100644 --- a/cmd/deploy/deploy.go +++ b/cmd/deploy/deploy.go @@ -84,10 +84,10 @@ func newDeployment(cfgPath string, testing bool) (*deploy.Deployment, error) { return nil, err } if cfg.Cluster == nil { - return nil, fmt.Errorf("Cluster not specified") + return nil, fmt.Errorf("cluster not specified") } if cfg.Ingress == nil { - return nil, fmt.Errorf("Ingress not specified") + return nil, fmt.Errorf("ingress not specified") } if cfg.CNI == nil { return nil, fmt.Errorf("CNI not specified") diff --git a/cmd/deploy/testdata/kind-deployment.yaml b/cmd/deploy/testdata/kind-deployment.yaml index b7faf3241..f40a3a6af 100644 --- a/cmd/deploy/testdata/kind-deployment.yaml +++ b/cmd/deploy/testdata/kind-deployment.yaml @@ -1,3 +1,4 @@ +--- cluster: kind: Kind spec: diff --git a/cmd/root.go b/cmd/root.go index af6a54f9e..884e4bc1a 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -54,7 +54,9 @@ environment.`, return fmt.Errorf("error reading config: %w", err) } } - viper.BindPFlags(cmd.Flags()) + if err := viper.BindPFlags(cmd.Flags()); err != nil { + return err + } viper.SetDefault("report_usage", false) return nil } diff --git a/cmd/topology/topology_test.go b/cmd/topology/topology_test.go index 7aa1a1924..bc3f3bb7a 100644 --- a/cmd/topology/topology_test.go +++ b/cmd/topology/topology_test.go @@ -150,7 +150,7 @@ func TestReset(t *testing.T) { } fConfigRelative, closer := writeTopology(t, tWithConfigRelative) defer closer() - tWithConfigDNE := &tpb.Topology{ + tWithConfigMissing := &tpb.Topology{ Nodes: []*tpb.Node{{ Name: "resettable1", Vendor: tpb.Vendor(1001), @@ -164,7 +164,7 @@ func TestReset(t *testing.T) { Vendor: tpb.Vendor(1001), Config: &tpb.Config{ ConfigData: &tpb.Config_File{ - File: "dne", + File: "missing", }, }, }, { @@ -172,7 +172,7 @@ func TestReset(t *testing.T) { Vendor: tpb.Vendor(1002), }}, } - fConfigDNE, closer := writeTopology(t, tWithConfigDNE) + fConfigMissing, closer := writeTopology(t, tWithConfigMissing) defer closer() node.Vendor(tpb.Vendor(1001), NewR) node.Vendor(tpb.Vendor(1002), NewNR) @@ -206,15 +206,15 @@ func TestReset(t *testing.T) { desc: "valid topology push with relative file location", args: []string{"reset", fConfigRelative.Name(), "--skip", "--push"}, }, { - desc: "valid topology push with config DNE", - args: []string{"reset", fConfigDNE.Name(), "--skip", "--push"}, + desc: "valid topology push with config missing", + args: []string{"reset", fConfigMissing.Name(), "--skip", "--push"}, wantErr: "no such file or directory", }, { - desc: "valid topology push with config DNE single device", - args: []string{"reset", fConfigDNE.Name(), "--skip", "--push", "resettable1"}, + desc: "valid topology push with config missing single device", + args: []string{"reset", fConfigMissing.Name(), "--skip", "--push", "resettable1"}, }, { - desc: "valid topology push with config DNE single device invalid", - args: []string{"reset", fConfigDNE.Name(), "--skip", "--push", "dne"}, + desc: "valid topology push with config missing single device invalid", + args: []string{"reset", fConfigMissing.Name(), "--skip", "--push", "missing"}, wantErr: "not found", }} @@ -234,8 +234,7 @@ func TestReset(t *testing.T) { }() rCmd.PersistentFlags().String("kubecfg", "", "") rCmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error { - viper.BindPFlags(cmd.Flags()) - return nil + return viper.BindPFlags(cmd.Flags()) } buf := bytes.NewBuffer([]byte{}) rCmd.SetOut(buf) @@ -370,7 +369,7 @@ func TestGenerateRing(t *testing.T) { wantErr: "links must be positive", }, { desc: "file not found", - args: []string{"generate", "ring", "dne.textproto", "2", "8"}, + args: []string{"generate", "ring", "missing.textproto", "2", "8"}, wantErr: "no such file", }, { desc: "empty topology", @@ -571,10 +570,10 @@ func TestPush(t *testing.T) { defer os.Remove(confFile.Name()) tWithConfig := &tpb.Topology{ Nodes: []*tpb.Node{{ - Name: "configable", + Name: "configurable", Vendor: tpb.Vendor(1003), }, { - Name: "notconfigable", + Name: "notconfigurable", Vendor: tpb.Vendor(1004), }}, } @@ -594,22 +593,22 @@ func TestPush(t *testing.T) { }, { desc: "missing args", wantErr: "invalid args", - args: []string{"push", fConfig.Name(), "configable"}, + args: []string{"push", fConfig.Name(), "configurable"}, }, { desc: "no file", - args: []string{"push", fConfig.Name(), "configable", "filedne"}, + args: []string{"push", fConfig.Name(), "configurable", "filemissing"}, wantErr: "no such file", }, { desc: "valid file invalid device", args: []string{"push", fConfig.Name(), "foo", confFile.Name()}, wantErr: `node "foo" not found`, }, { - desc: "valid file notconfigable device", - args: []string{"push", fConfig.Name(), "notconfigable", confFile.Name()}, + desc: "valid file notconfigurable device", + args: []string{"push", fConfig.Name(), "notconfigurable", confFile.Name()}, wantErr: "does not implement ConfigPusher", }, { desc: "valid file", - args: []string{"push", fConfig.Name(), "configable", confFile.Name()}, + args: []string{"push", fConfig.Name(), "configurable", confFile.Name()}, }} rCmd := New() diff --git a/deploy/deploy.go b/deploy/deploy.go index 47ae5cc1a..1523e519e 100644 --- a/deploy/deploy.go +++ b/deploy/deploy.go @@ -110,7 +110,10 @@ type Deployment struct { } func (d *Deployment) String() string { - b, _ := json.MarshalIndent(d, "", "\t") + b, err := json.MarshalIndent(d, "", "\t") + if err != nil { + return fmt.Sprintf("Deployment: %+v (marshal error: %v)", *d, err) + } return string(b) } @@ -219,7 +222,7 @@ func (d *Deployment) Deploy(ctx context.Context, kubecfg string) (rerr error) { ctx, cancel := context.WithCancel(ctx) - // Watch the containter status of the pods so we can fail if a container fails to start running. + // Watch the container status of the pods so we can fail if a container fails to start running. if w, err := pods.NewWatcher(ctx, kClient, cancel); err != nil { log.Warningf("Failed to start pod watcher: %v", err) } else { diff --git a/deploy/kne/external-multinode-cdnos.yaml b/deploy/kne/external-multinode-cdnos.yaml index 256496eb5..40884be34 100644 --- a/deploy/kne/external-multinode-cdnos.yaml +++ b/deploy/kne/external-multinode-cdnos.yaml @@ -1,6 +1,7 @@ # external-multinode.yaml cluster config file sets up ingress, cni, and controllers in an existing k8 cluster. # This spec instructs Metallb to use a docker network named multinode. # The "external" cluster lifecycle is not managed by the KNE deployment. +--- cluster: kind: External spec: diff --git a/deploy/kne/external-multinode.yaml b/deploy/kne/external-multinode.yaml index 56665504c..2f63acc0a 100644 --- a/deploy/kne/external-multinode.yaml +++ b/deploy/kne/external-multinode.yaml @@ -1,6 +1,7 @@ # external-multinode.yaml cluster config file sets up ingress, cni, and controllers in an existing k8 cluster. # This spec instructs Metallb to use a docker network named multinode. # The "external" cluster lifecycle is not managed by the KNE deployment. +--- cluster: kind: External spec: diff --git a/deploy/kne/external.yaml b/deploy/kne/external.yaml index 4d808e182..b8dc3dcb5 100644 --- a/deploy/kne/external.yaml +++ b/deploy/kne/external.yaml @@ -1,5 +1,6 @@ # external.yaml cluster config file sets up ingress, cni, and controllers in an existing k8 cluster. # The "external" cluster lifecycle is not managed by the KNE deployment. +--- cluster: kind: External ingress: diff --git a/deploy/kne/kind-bridge-cdnos.yaml b/deploy/kne/kind-bridge-cdnos.yaml index c07f19e32..7cefa0fc0 100644 --- a/deploy/kne/kind-bridge-cdnos.yaml +++ b/deploy/kne/kind-bridge-cdnos.yaml @@ -1,6 +1,7 @@ # kind-bridge.yaml cluster config file sets up a kind cluster where default PTP CNI plugin # is swapped with the Bridge CNI plugin. # Bridge CNI plugin is required by some Network OSes to operate. +--- cluster: kind: Kind spec: @@ -36,4 +37,4 @@ controllers: operator: ../../manifests/controllers/lemming/manifest.yaml - kind: Cdnos spec: - operator: ../../manifests/controllers/cdnos/manifest.yaml \ No newline at end of file + operator: ../../manifests/controllers/cdnos/manifest.yaml diff --git a/deploy/kne/kind-bridge.yaml b/deploy/kne/kind-bridge.yaml index afe6d381b..54a3fe36b 100644 --- a/deploy/kne/kind-bridge.yaml +++ b/deploy/kne/kind-bridge.yaml @@ -1,6 +1,7 @@ # kind-bridge.yaml cluster config file sets up a kind cluster where default PTP CNI plugin # is swapped with the Bridge CNI plugin. # Bridge CNI plugin is required by some Network OSes to operate. +--- cluster: kind: Kind spec: diff --git a/deploy/kne/kubeadm.yaml b/deploy/kne/kubeadm.yaml index a2bd07a0d..827ef7a67 100644 --- a/deploy/kne/kubeadm.yaml +++ b/deploy/kne/kubeadm.yaml @@ -1,6 +1,7 @@ # kubeadm.yaml cluster config file sets up ingress, cni, and controllers in a new k8 cluster # created using kubeadm. The kubeadm cluster starts as a single node cluster but can be joined # from other hosts to create a multinode cluster. +--- cluster: kind: Kubeadm spec: diff --git a/deploy/ubuntu/serviceaccount.yaml b/deploy/ubuntu/serviceaccount.yaml index a52b76bb9..1e5b74588 100644 --- a/deploy/ubuntu/serviceaccount.yaml +++ b/deploy/ubuntu/serviceaccount.yaml @@ -1,11 +1,12 @@ +--- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: podrunner subjects: -- kind: ServiceAccount - name: podrunner - namespace: default + - kind: ServiceAccount + name: podrunner + namespace: default roleRef: kind: ClusterRole name: podrunner @@ -19,19 +20,19 @@ metadata: labels: k8s-app: foo rules: -- apiGroups: [""] - resources: - - pods - - services - - logs - verbs: - - create - - update - - patch - - delete - - get - - watch - - list + - apiGroups: [""] + resources: + - pods + - services + - logs + verbs: + - create + - update + - patch + - delete + - get + - watch + - list --- apiVersion: v1 kind: ServiceAccount diff --git a/deploy/ubuntu/ubuntu.yaml b/deploy/ubuntu/ubuntu.yaml index 916a57fb4..f59d8c549 100644 --- a/deploy/ubuntu/ubuntu.yaml +++ b/deploy/ubuntu/ubuntu.yaml @@ -1,3 +1,4 @@ +--- apiVersion: v1 kind: Pod metadata: @@ -7,40 +8,40 @@ metadata: namespace: default spec: containers: - - args: - - sleep - - "90000" - image: hfam/ubuntu:latest - imagePullPolicy: IfNotPresent - name: foo - resources: {} - stdin: true - stdinOnce: true - terminationMessagePath: /dev/termination-log - terminationMessagePolicy: File - tty: true - volumeMounts: - - mountPath: /var/run/secrets/kubernetes.io/serviceaccount - name: kube-api-access-m6mv6 - readOnly: true + - args: + - sleep + - "90000" + image: hfam/ubuntu:latest + imagePullPolicy: IfNotPresent + name: foo + resources: {} + stdin: true + stdinOnce: true + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + tty: true + volumeMounts: + - mountPath: /var/run/secrets/kubernetes.io/serviceaccount + name: kube-api-access-m6mv6 + readOnly: true restartPolicy: Never serviceAccountName: podrunner volumes: - - name: kube-api-access-m6mv6 - projected: - defaultMode: 420 - sources: - - serviceAccountToken: - expirationSeconds: 3607 - path: token - - configMap: - items: - - key: ca.crt - path: ca.crt - name: kube-root-ca.crt - - downwardAPI: - items: - - fieldRef: - apiVersion: v1 - fieldPath: metadata.namespace - path: namespace + - name: kube-api-access-m6mv6 + projected: + defaultMode: 420 + sources: + - serviceAccountToken: + expirationSeconds: 3607 + path: token + - configMap: + items: + - key: ca.crt + path: ca.crt + name: kube-root-ca.crt + - downwardAPI: + items: + - fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + path: namespace diff --git a/docs/README.md b/docs/README.md index 030e23f92..c18d273ea 100644 --- a/docs/README.md +++ b/docs/README.md @@ -11,12 +11,12 @@ of containers running various device OSes. This document is meant to serve as a How-To guide for various KNE usage. The guide is broken up into multiple sections spanning multiple documents. -* [Setup](setup.md): A guide to first time setup for KNE. -* [Create a topology](create_topology.md): A guide to deploying a KNE cluster +- [Setup](setup.md): A guide to first time setup for KNE. +- [Create a topology](create_topology.md): A guide to deploying a KNE cluster and creating a topology. -* [Interact with a topology](interact_topology.md): A guide to interacting with +- [Interact with a topology](interact_topology.md): A guide to interacting with a KNE topology after creation. -* [Troubleshooting](troubleshoot.md): A troubleshooting guide if anything goes +- [Troubleshooting](troubleshoot.md): A troubleshooting guide if anything goes wrong along the way. They are recommended to be done in order. @@ -26,15 +26,15 @@ They are recommended to be done in order. [KNE with a Multi Node Cluster](multinode.md) KNE can easily be scaled to run large topologies utilizing its Kubernetes -backbone. This guide describes how to set up a k8s multi worker node cluster -and get a 150 node KNE topology up and running. +backbone. This guide describes how to set up a k8s multi worker node cluster and +get a 150 node KNE topology up and running. ## Vendor Image Requirements [Vendor Image Requirements](vendor.md) -KNE uses vendor supplied images. This document describes the expectations -for those images. +KNE uses vendor supplied images. This document describes the expectations for +those images. ## Kubernetes Reference @@ -45,7 +45,17 @@ concepts and how they are used in KNE by running through an example topology creation. ## Support for AlpineVS in KNE -[AlpineVS](https://github.com/sonic-net/sonic-alpine/blob/master/README.md) (AVS) is a SONiC Virtual Switch with dataplane deployed as a k8s Pod within KNE. It provides switch capabilities in a simulated environment with following key features: -* **Dual-Container Architecture:** Encloses a SwitchStack container (running SONiC VM) and an ASIC Simulation container through vendor node definition for [alpine](../topo/node/alpine/alpine.go). -* **Multiple Dataplanes:** Integrates with Lucius (default gRPC-based SAI implementation) as well as vendor ASIC simulations. -* **Natively in KNE:** Runs natively in KNE with simple [2-switch topologies](https://github.com/sonic-net/sonic-alpine/blob/master/src/deploy/kne/twodut-alpine-vs.pb.txt) and scaled topologies for automated testing of the SONiC stack. + +[AlpineVS](https://github.com/sonic-net/sonic-alpine/blob/master/README.md) +(AVS) is a SONiC Virtual Switch with dataplane deployed as a k8s Pod within KNE. +It provides switch capabilities in a simulated environment with following key +features: + +- **Dual-Container Architecture:** Encloses a SwitchStack container (running + SONiC VM) and an ASIC Simulation container through vendor node definition for + [alpine](https://github.com/openconfig/kne/blob/main/topo/node/alpine/alpine.go). +- **Multiple Dataplanes:** Integrates with Lucius (default gRPC-based SAI + implementation) as well as vendor ASIC simulations. +- **Natively in KNE:** Runs natively in KNE with simple + [2-switch topologies](https://github.com/sonic-net/sonic-alpine/blob/master/src/deploy/kne/twodut-alpine-vs.pb.txt) + and scaled topologies for automated testing of the SONiC stack. diff --git a/docs/create_topology.md b/docs/create_topology.md index 3f0ce0181..50994386f 100644 --- a/docs/create_topology.md +++ b/docs/create_topology.md @@ -31,12 +31,12 @@ Global Flags: -v, --verbosity string log level (default "info") ``` -A deployment yaml file specifies 4 things (*optional in italics*): +A deployment yaml file specifies 4 things (_optional in italics_): 1. A cluster spec 2. An ingress spec 3. A CNI spec -4. *A list of controller specs* +4. _A list of controller specs_ Expand the below section for a full description of all fields in the deployment yaml. @@ -48,106 +48,106 @@ yaml. > NOTE: ~~Strikethrough~~ fields are DEPRECATED and should not be used. -Field | Type | Description -------------- | ---------------- | --------------------------------------------- -`cluster` | ClusterSpec | Spec for the cluster. -`ingress` | IngressSpec | Spec for the ingress. -`cni` | CNISpec | Spec for the CNI. -`controllers` | []ControllerSpec | List of specs for the additional controllers. +| Field | Type | Description | +| ------------- | ---------------- | --------------------------------------------- | +| `cluster` | ClusterSpec | Spec for the cluster. | +| `ingress` | IngressSpec | Spec for the ingress. | +| `cni` | CNISpec | Spec for the CNI. | +| `controllers` | []ControllerSpec | List of specs for the additional controllers. | #### Cluster -Field | Type | Description ------- | --------- | --------------------------------------------------- -`kind` | string | Name of the cluster type. The options currently are `Kind` or `External`. -`spec` | yaml.Node | Fields that set the options for the cluster type. +| Field | Type | Description | +| ------ | --------- | ------------------------------------------------------------------------- | +| `kind` | string | Name of the cluster type. The options currently are `Kind` or `External`. | +| `spec` | yaml.Node | Fields that set the options for the cluster type. | ##### Kind -Field | Type | Description --------------------------- | ----------------- | -------------------------- -`name` | string | Cluster name, overrides `KIND_CLUSTER_NAME`, config (default `kind`). -`recycle` | bool | Reuse an existing cluster of the same name if it exists. -`version` | string | Desired version of the `kubectl` client. -`image` | string | Node docker image to use for booting the cluster. -`retain` | bool | Retain nodes for debugging when cluster creation fails. -`wait` | time.Duration | Wait for control plane node to be ready (default 0s). -`kubecfg` | string | Sets kubeconfig path instead of `$KUBECONFIG` or `$HOME/.kube/config`. -`googleArtifactRegistries` | []string | List of Google Artifact Registries to setup credentials for in the cluster. Example value for registry would be `us-west1-docker.pkg.dev`. Credentials used are associated with the configured `gcloud` user on the host. -`containerImages` | map[string]string | Map of source images to target images for containers to load in the cluster. Empty values cause the source image to be loaded into the cluster without being renamed. -`config` | string | Path to a kind config file. -`additionalManifests` | []string | List of paths to manifests to be applied using `kubectl` directly after cluster creation. +| Field | Type | Description | +| -------------------------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `name` | string | Cluster name, overrides `KIND_CLUSTER_NAME`, config (default `kind`). | +| `recycle` | bool | Reuse an existing cluster of the same name if it exists. | +| `version` | string | Desired version of the `kubectl` client. | +| `image` | string | Node docker image to use for booting the cluster. | +| `retain` | bool | Retain nodes for debugging when cluster creation fails. | +| `wait` | time.Duration | Wait for control plane node to be ready (default 0s). | +| `kubecfg` | string | Sets kubeconfig path instead of `$KUBECONFIG` or `$HOME/.kube/config`. | +| `googleArtifactRegistries` | []string | List of Google Artifact Registries to setup credentials for in the cluster. Example value for registry would be `us-west1-docker.pkg.dev`. Credentials used are associated with the configured `gcloud` user on the host. | +| `containerImages` | map[string]string | Map of source images to target images for containers to load in the cluster. Empty values cause the source image to be loaded into the cluster without being renamed. | +| `config` | string | Path to a kind config file. | +| `additionalManifests` | []string | List of paths to manifests to be applied using `kubectl` directly after cluster creation. | ##### External -Field | Type | Description ---------- | ------ | ------------------------------------------------------- -`network` | string | Name of the docker network to create a pool of external IP addresses for ingress to assign to services. +| Field | Type | Description | +| --------- | ------ | ------------------------------------------------------------------------------------------------------- | +| `network` | string | Name of the docker network to create a pool of external IP addresses for ingress to assign to services. | #### Ingress -Field | Type | Description ------- | --------- | ------------------------------------------------------ -`kind` | string | Name of the ingress type. The only option currently is `MetalLB`. -`spec` | yaml.Node | Fields that set the options for the ingress type. +| Field | Type | Description | +| ------ | --------- | ----------------------------------------------------------------- | +| `kind` | string | Name of the ingress type. The only option currently is `MetalLB`. | +| `spec` | yaml.Node | Fields that set the options for the ingress type. | ##### MetalLB -Field | Type | Description ---------------- | ---------- | ----------- -`ip_count` | int | Number of IP addresses to include in the available pool. -`manifest` | string | Path of the manifest yaml file to create MetalLB in the cluster. The validated manifest for use with KNE can be found [here](https://github.com/openconfig/kne/tree/main/manifests/metallb/manifest.yaml). -~~`manifests`~~ | ~~string~~ | ~~Path of the directory holding the manifests to create MetalLB in the cluster. The directory is expected to contain a file with the name `metallb-native.yaml`.~~ +| Field | Type | Description | +| --------------- | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `ip_count` | int | Number of IP addresses to include in the available pool. | +| `manifest` | string | Path of the manifest yaml file to create MetalLB in the cluster. The validated manifest for use with KNE can be found in the [MetalLB manifest](https://github.com/openconfig/kne/tree/main/manifests/metallb/manifest.yaml). | +| ~~`manifests`~~ | ~~string~~ | ~~Path of the directory holding the manifests to create MetalLB in the cluster. The directory is expected to contain a file with the name `metallb-native.yaml`.~~ | #### CNI -Field | Type | Description ------- | --------- | -------------------------------------------------- -`kind` | string | Name of the CNI type. The only option currently is `Meshnet`. -`spec` | yaml.Node | Fields that set the options for the CNI type. +| Field | Type | Description | +| ------ | --------- | ------------------------------------------------------------- | +| `kind` | string | Name of the CNI type. The only option currently is `Meshnet`. | +| `spec` | yaml.Node | Fields that set the options for the CNI type. | ##### Meshnet -Field | Type | Description ---------------- | ---------- | ----------- -`manifest` | string | Path of the manifest yaml file to create Meshnet in the cluster. The validated manifest for use with KNE can be found [here](https://github.com/openconfig/kne/tree/main/manifests/meshnet/grpc/manifest.yaml). -~~`manifests`~~ | ~~string~~ | ~~Path of the directory holding the manifests to create Meshnet in the cluster. The directory is expected to contain a file with the name `manifest.yaml`.~~ +| Field | Type | Description | +| --------------- | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `manifest` | string | Path of the manifest yaml file to create Meshnet in the cluster. The validated manifest for use with KNE can be found in the [Meshnet manifest](https://github.com/openconfig/kne/tree/main/manifests/meshnet/grpc/manifest.yaml). | +| ~~`manifests`~~ | ~~string~~ | ~~Path of the directory holding the manifests to create Meshnet in the cluster. The directory is expected to contain a file with the name `manifest.yaml`.~~ | #### Controllers -Field | Type | Description ------- | --------- | ---------------------------------------------------- -`kind` | string | Name of the controller type. The current options currently are `IxiaTG`, `SRLinux`, `CEOSLab`, and `Lemming`. -`spec` | yaml.Node | Fields that set the options for the controller type. +| Field | Type | Description | +| ------ | --------- | ------------------------------------------------------------------------------------------------------------- | +| `kind` | string | Name of the controller type. The current options currently are `IxiaTG`, `SRLinux`, `CEOSLab`, and `Lemming`. | +| `spec` | yaml.Node | Fields that set the options for the controller type. | ##### IxiaTG -Field | Type | Description ---------------- | ---------- | ----------- -`operator` | string | Path of the yaml file to create an IxiaTG operator in the cluster. The validated operator for use with KNE can be found [here](https://github.com/openconfig/kne/tree/main/manifests/keysight/ixiatg-operator.yaml). -`configMap` | string | Path of the yaml file to create an IxiaTG config map in the cluster. The validated config map for use with KNE can be found [here](https://github.com/openconfig/kne/tree/main/manifests/keysight/ixiatg-configmap.yaml). -~~`manifests`~~ | ~~string~~ | ~~Path of the directory holding the manifests to create an IxiaTG operator in the cluster. The directory is expected to contain a file with the name `ixiatg-operator.yaml`. Optionally the directory can contain a file with the name `ixiatg-configmap.yaml` to apply a config map of the desired container images used by the controller.~~ +| Field | Type | Description | +| --------------- | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `operator` | string | Path of the yaml file to create an IxiaTG operator in the cluster. The validated operator for use with KNE can be found in the [IxiaTG operator manifest](https://github.com/openconfig/kne/tree/main/manifests/keysight/ixiatg-operator.yaml). | +| `configMap` | string | Path of the yaml file to create an IxiaTG config map in the cluster. The validated config map for use with KNE can be found in the [IxiaTG config map manifest](https://github.com/openconfig/kne/tree/main/manifests/keysight/ixiatg-configmap.yaml). | +| ~~`manifests`~~ | ~~string~~ | ~~Path of the directory holding the manifests to create an IxiaTG operator in the cluster. The directory is expected to contain a file with the name `ixiatg-operator.yaml`. Optionally the directory can contain a file with the name `ixiatg-configmap.yaml` to apply a config map of the desired container images used by the controller.~~ | ##### SRLinux -Field | Type | Description ---------------- | ---------- | ----------- -`operator` | string | Path of the yaml file to create an SRLinux operator in the cluster. The validated operator for use with KNE can be found [here](https://github.com/openconfig/kne/tree/main/manifests/controllers/srlinux/manifest.yaml). -~~`manifests`~~ | ~~string~~ | ~~Path of the directory holding the manifests to create an SRLinux operator in the cluster. The directory is expected to contain a file with the name `manifest.yaml`.~~ +| Field | Type | Description | +| --------------- | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `operator` | string | Path of the yaml file to create an SRLinux operator in the cluster. The validated operator for use with KNE can be found in the [SRLinux operator manifest](https://github.com/openconfig/kne/tree/main/manifests/controllers/srlinux/manifest.yaml). | +| ~~`manifests`~~ | ~~string~~ | ~~Path of the directory holding the manifests to create an SRLinux operator in the cluster. The directory is expected to contain a file with the name `manifest.yaml`.~~ | ##### CEOSLab -Field | Type | Description ---------------- | ---------- | ----------- -`operator` | string | Path of the yaml file to create a CEOSLab operator in the cluster. The validated operator for use with KNE can be found [here](https://github.com/openconfig/kne/tree/main/manifests/controllers/ceoslab/manifest.yaml). -~~`manifests`~~ | ~~string~~ | ~~Path of the directory holding the manifests to create a CEOSLab operator in the cluster. The directory is expected to contain a file with the name `manifest.yaml`.~~ +| Field | Type | Description | +| --------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `operator` | string | Path of the yaml file to create a CEOSLab operator in the cluster. The validated operator for use with KNE can be found in the [cEOS operator manifest](https://github.com/openconfig/kne/tree/main/manifests/controllers/ceoslab/manifest.yaml). | +| ~~`manifests`~~ | ~~string~~ | ~~Path of the directory holding the manifests to create a CEOSLab operator in the cluster. The directory is expected to contain a file with the name `manifest.yaml`.~~ | ##### Lemming -Field | Type | Description ---------------- | ---------- | ----------- -`operator` | string | Path of the yaml file to create a Lemming operator in the cluster. The validated operator for use with KNE can be found [here](https://github.com/openconfig/kne/tree/main/manifests/controllers/lemming/manifest.yaml). -~~`manifests`~~ | ~~string~~ | ~~Path of the directory holding the manifests to create a Lemming operator in the cluster. The directory is expected to contain a file with the name `manifest.yaml`.~~ +| Field | Type | Description | +| --------------- | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `operator` | string | Path of the yaml file to create a Lemming operator in the cluster. The validated operator for use with KNE can be found in the [Lemming operator manifest](https://github.com/openconfig/kne/tree/main/manifests/controllers/lemming/manifest.yaml). | +| ~~`manifests`~~ | ~~string~~ | ~~Path of the directory holding the manifests to create a Lemming operator in the cluster. The directory is expected to contain a file with the name `manifest.yaml`.~~ | @@ -256,21 +256,21 @@ To load an image into a `kind` cluster there is a 3 step process: 1. Pull the desired image: - ```bash - docker pull src_image:src_tag - ``` + ```bash + docker pull src_image:src_tag + ``` 2. Tag the image with the desired in-cluster name: - ```bash - docker tag src_image:src_tag dst_image:dst_tag - ``` + ```bash + docker tag src_image:src_tag dst_image:dst_tag + ``` 3. Load the image into the `kind` cluster: - ```bash - kind load docker-image dst_image:dst_tag --name=kne - ``` + ```bash + kind load docker-image dst_image:dst_tag --name=kne + ``` Now the `dst_image:dst_tag` image will be present for use in the `kind` cluster. @@ -315,8 +315,8 @@ node definitions interfaces, services, and initial configs can be specified. An example topology containing 4 DUT nodes (Arista, Cisco, Nokia, and Juniper) and 1 ATE node (Keysight) can be found under the examples directory at [examples/multivendor/multivendor.pb.txt](https://github.com/openconfig/kne/blob/main/examples/multivendor/multivendor.pb.txt). -The initial vendor router configs referenced in the topology are found -[here](https://github.com/openconfig/kne/tree/main/examples/multivendor) +The initial vendor router configs referenced in the topology are found in the +[multivendor example directory](https://github.com/openconfig/kne/tree/main/examples/multivendor) See the [push config](interact_topology.md#push_config) section for details about pushing config after initial creation. diff --git a/docs/interact_topology.md b/docs/interact_topology.md index 90509c241..3c390bb93 100644 --- a/docs/interact_topology.md +++ b/docs/interact_topology.md @@ -66,32 +66,32 @@ $ ssh admin@192.168.11.50 1. Get the IP range used by KNE services: - ```bash - $ kubectl get services -n multivendor - NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE - service-gnmi-otg-controller LoadBalancer 10.96.179.48 192.168.11.55 50051:30901/TCP 4m9s - service-grpc-otg-controller LoadBalancer 10.96.33.245 192.168.11.56 40051:30449/TCP 4m9s - service-https-otg-controller LoadBalancer 10.96.215.225 192.168.11.54 443:32556/TCP 4m9s - service-otg-port-eth1 LoadBalancer 10.96.82.37 192.168.11.58 5555:30886/TCP,50071:30286/TCP 4m9s - service-otg-port-eth2 LoadBalancer 10.96.204.154 192.168.11.59 5555:31326/TCP,50071:31860/TCP 4m9s - service-otg-port-eth3 LoadBalancer 10.96.136.253 192.168.11.60 5555:30181/TCP,50071:31619/TCP 4m9s - service-otg-port-eth4 LoadBalancer 10.96.205.227 192.168.11.57 5555:32636/TCP,50071:31247/TCP 4m9s - service-r1 LoadBalancer 10.96.130.198 192.168.11.50 443:32101/TCP,22:32304/TCP,6030:32011/TCP 4m12s - service-r2 LoadBalancer 10.96.107.2 192.168.11.51 443:31942/TCP,22:30785/TCP,57400:30921/TCP 4m11s - service-r3 LoadBalancer 10.96.80.18 192.168.11.52 22:32410/TCP 4m11s - service-r4 LoadBalancer 10.96.138.204 192.168.11.53 22:31932/TCP,50051:32666/TCP 4m10s - ``` - - In this case the IP range would be `192.168.11.*`. + ```bash + $ kubectl get services -n multivendor + NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE + service-gnmi-otg-controller LoadBalancer 10.96.179.48 192.168.11.55 50051:30901/TCP 4m9s + service-grpc-otg-controller LoadBalancer 10.96.33.245 192.168.11.56 40051:30449/TCP 4m9s + service-https-otg-controller LoadBalancer 10.96.215.225 192.168.11.54 443:32556/TCP 4m9s + service-otg-port-eth1 LoadBalancer 10.96.82.37 192.168.11.58 5555:30886/TCP,50071:30286/TCP 4m9s + service-otg-port-eth2 LoadBalancer 10.96.204.154 192.168.11.59 5555:31326/TCP,50071:31860/TCP 4m9s + service-otg-port-eth3 LoadBalancer 10.96.136.253 192.168.11.60 5555:30181/TCP,50071:31619/TCP 4m9s + service-otg-port-eth4 LoadBalancer 10.96.205.227 192.168.11.57 5555:32636/TCP,50071:31247/TCP 4m9s + service-r1 LoadBalancer 10.96.130.198 192.168.11.50 443:32101/TCP,22:32304/TCP,6030:32011/TCP 4m12s + service-r2 LoadBalancer 10.96.107.2 192.168.11.51 443:31942/TCP,22:30785/TCP,57400:30921/TCP 4m11s + service-r3 LoadBalancer 10.96.80.18 192.168.11.52 22:32410/TCP 4m11s + service-r4 LoadBalancer 10.96.138.204 192.168.11.53 22:31932/TCP,50051:32666/TCP 4m10s + ``` + + In this case the IP range would be `192.168.11.*`. 1. Edit your SSH config found at `~/.ssh/config` to include: - ```bash - Host 192.168.11.* - UserKnownHostsFile /dev/null - StrictHostKeyChecking no - ProxyCommand none - ``` + ```bash + Host 192.168.11.* + UserKnownHostsFile /dev/null + StrictHostKeyChecking no + ProxyCommand none + ``` @@ -252,7 +252,7 @@ See the external cptx with services -### Using OpenConfig g* services +### Using OpenConfig g\* services #### Using the CLI diff --git a/docs/kubernetes_reference.md b/docs/kubernetes_reference.md index db36dc85d..d318883a5 100644 --- a/docs/kubernetes_reference.md +++ b/docs/kubernetes_reference.md @@ -74,12 +74,12 @@ cluster creation, but regardless of which is chosen a k8s cluster will be created ready for topology creation. Currently, the most used cluster tool in KNE is **kind**. This tool actually hosts a single node k8s cluster inside of a docker container. The details here are not important for the purpose of this -reference, but if you see the term *kind* then know it may be referring to a +reference, but if you see the term _kind_ then know it may be referring to a tool for creating a k8s cluster. You can also bring your own cluster for use with KNE, this is convenient for users with custom k8s setups. -NOTE: *kind* is also a field in kubeyaml used to specify resource type, you may -see this inside of k8s manifests. However when we refer to *kind*, it's likely +NOTE: _kind_ is also a field in kubeyaml used to specify resource type, you may +see this inside of k8s manifests. However when we refer to _kind_, it's likely the cluster tool. After the cluster is created, several k8s deployments are created to initialize diff --git a/docs/multinode.md b/docs/multinode.md index 7e56ac3f7..dfbf04e98 100644 --- a/docs/multinode.md +++ b/docs/multinode.md @@ -3,8 +3,7 @@ ## Background A k8s cluster is made up of 1 or more nodes. Each node can hold up to 110 pods. -See the official large cluster considerations -[here](https://kubernetes.io/docs/setup/best-practices/cluster-large/). An +See the [official large cluster considerations](https://kubernetes.io/docs/setup/best-practices/cluster-large/). An emulated DUT in KNE brings up 1 pod. An emulated ATE in KNE brings up 1 pod per port. Together with the controller pods and other dependency pods, this in turn restricts a KNE user using kind (a single node cluster) to less than ~100 DUTs + diff --git a/docs/setup.md b/docs/setup.md index 32ad2ad05..ccbe9bc6f 100644 --- a/docs/setup.md +++ b/docs/setup.md @@ -7,11 +7,11 @@ to work with your Linux distribution. The following dependencies and required to use KNE: -* Golang -* Docker -* Kubectl -* Kind -* Make +- Golang +- Docker +- Kubectl +- Kind +- Make ## Install Golang diff --git a/docs/vendor.md b/docs/vendor.md index 093a8fe3b..7fe07f2d3 100644 --- a/docs/vendor.md +++ b/docs/vendor.md @@ -1,18 +1,19 @@ # Vendor Image Requirements A Vendor Image is a docker container that can be used with KNE to emulate a -vendor's devices. A Vendor Image might also be a fully virtual device with -no physical version, such as openconfig/lemming. +vendor's devices. A Vendor Image might also be a fully virtual device with no +physical version, such as openconfig/lemming. Without vendor images KNE is just an empty virtual machine rack that does -nothing. Vendor supplied images are what the user of KNE sees and is interested -in. This document describes the requirements and expectations of vendor images +nothing. Vendor supplied images are what the user of KNE sees and is interested +in. This document describes the requirements and expectations of vendor images and the associated code that is included in the KNE repository A vendor image requires a corresponding node implementation in topo/node/vendor -and should have working examples in examples/vendor. A single node -implementation may support multiple vendor images (e.g., cisco-xrd and cisco-8000e). A maintainer is the person or organization that maintains the vendor -specific node implementation and examples. +and should have working examples in examples/vendor. A single node +implementation may support multiple vendor images (e.g., cisco-xrd and +cisco-8000e). A maintainer is the person or organization that maintains the +vendor specific node implementation and examples. In this document a vendor is considered the person or organization that makes image containers available for use by others. @@ -20,15 +21,15 @@ image containers available for use by others. ## KNE Uses KNE was built to enable testing the functionality of networks without physical -hardware. Due to the obvious limitations of emulation, KNE is not designed to -test bandwidth and latency of connections. KNE is designed to enable testing of -the control protocols and interaction between devices. There are several +hardware. Due to the obvious limitations of emulation, KNE is not designed to +test bandwidth and latency of connections. KNE is designed to enable testing of +the control protocols and interaction between devices. There are several different types of testing. ### Testing new Topologies -KNE is used to test changes in network topology. Changes in network topology -can impact various protocols use in the network (e.g. BGP). +KNE is used to test changes in network topology. Changes in network topology can +impact various protocols use in the network (e.g. BGP). ### Testing Changes in Protocol or Configuration @@ -36,7 +37,7 @@ KNE is used to test protocol changes or other configuration changes. ### Testing Device Functionality -KNE is used to test changes to a device's Network Operating System (NOS). This +KNE is used to test changes to a device's Network Operating System (NOS). This is a crucial step in validating a devices usability for a particular purpose when a new NOS is released. @@ -45,57 +46,57 @@ when a new NOS is released. A network device in KNE can be viewed as two main components, the control plane and the data plane (the ASIC). -KNE is used to test the control plane of the NOS. This requires the control -software in the virtual device behave the same as in the hardware. It is -expected that the control software used in an image is the same as the -software used on the physical device and that it is configured and reacts in the -same way as the hardware. +KNE is used to test the control plane of the NOS. This requires the control +software in the virtual device behave the same as in the hardware. It is +expected that the control software used in an image is the same as the software +used on the physical device and that it is configured and reacts in the same way +as the hardware. -KNE is not designed to test the data plane or ASIC. The emulated data plane -must support routing and packet forwarding. ASIC specific commands and features -do not need to be supported as long as the data plane provides basic -functionality. +KNE is not designed to test the data plane or ASIC. The emulated data plane must +support routing and packet forwarding. ASIC specific commands and features do +not need to be supported as long as the data plane provides basic functionality. All of these use cases require that the vendor images to behave functionally as -if it were the hardware. The image is expected to be built from the same source -code base as the NOS used in the hardware. Faithful emulation of the ASIC is -not a requirement. The emulated ASIC (data plane) must correctly handle routing +if it were the hardware. The image is expected to be built from the same source +codebase as the NOS used in the hardware. Faithful emulation of the ASIC is not +a requirement. The emulated ASIC (data plane) must correctly handle routing changes and packet forwarding. ### Deviations The vendor should supply a document that describes what series of devices the -image emulates as well as known limits and deviations. These include +image emulates as well as known limits and deviations. These include -* Protocols not supported -* Protocols that deviate from the hardware (and how) -* OpenConfig paths only supported by hardware -* OpenConfig paths that report different results compared to the hardware. -* Known limitations of the emulated device -* Supported port configurations (e.g, number of ports, line cards, etc). +- Protocols not supported +- Protocols that deviate from the hardware (and how) +- OpenConfig paths only supported by hardware +- OpenConfig paths that report different results compared to the hardware. +- Known limitations of the emulated device +- Supported port configurations (e.g, number of ports, line cards, etc). -The listed OpenConfig paths need not be leaf nodes. Wildcards may be used in -the path where applicable. +The listed OpenConfig paths need not be leaf nodes. Wildcards may be used in the +path where applicable. ## Testing -Vendor images must be tested prior to publication. A standard set of tests is -found at . At a minimum, a KNE node using that image should -start and not cause the KNE emulation to hang. It should work in both a single -Kubernetes Worker Node environment as well as a multi-worker node environment. +Vendor images must be tested prior to publication. A standard set of tests is +found at `under development`. At a minimum, a KNE node using that image should +start and not cause the KNE emulation to stop responding. It should work in both +a single Kubernetes Worker Node environment as well as a multi-worker node +environment. It is expected that released images undergo repeated testing to identify non-deterministic errors. ## Support -Vendors are responsible for support of their images. The maintainer (typically +Vendors are responsible for support of their images. The maintainer (typically the person or organization that provides the associated container images) is responsible for the support of the vendor image specific node implementation in [kne/topo/node](https://github.com/openconfig/kne/tree/main/topo/node), the vendor specific examples in [kne/examples](https://github.com/openconfig/kne/tree/main/examples), as well as -other vendor software reqiured by the node implementation (e.g., controller or -operator). The maintainer should be responsive to community contributions. In +other vendor software required by the node implementation (e.g., controller or +operator). The maintainer should be responsive to community contributions. In the event the maintainer of a particular node implementation is unresponsive a new maintainer may take over that implementation. diff --git a/events/events.go b/events/events.go index 15ee0cec2..62218a338 100644 --- a/events/events.go +++ b/events/events.go @@ -102,9 +102,9 @@ func WatchEventStatus(ctx context.Context, client kubernetes.Interface, namespac // EventToStatus returns a pointer to a new EventStatus for an event. func EventToStatus(event *corev1.Event) *EventStatus { s := EventStatus{ - Name: event.ObjectMeta.Name, - Namespace: event.ObjectMeta.Namespace, - UID: event.ObjectMeta.UID, + Name: event.Name, + Namespace: event.Namespace, + UID: event.UID, } event.DeepCopyInto(&s.Event) event = &s.Event diff --git a/events/status.go b/events/status.go index 2df348290..1c9a3168a 100644 --- a/events/status.go +++ b/events/status.go @@ -63,7 +63,7 @@ func newWatcher(ctx context.Context, cancel func(), ch chan *EventStatus, stop f return w } -// SetProgress determins if progress output should be displayed while watching. +// SetProgress determines if progress output should be displayed while watching. func (w *Watcher) SetProgress(value bool) { w.mu.Lock() w.progress = value @@ -126,7 +126,7 @@ func (w *Watcher) isEventNormal(s *EventStatus) bool { for _, m := range errorMsgs { // Error out if message contains predefined message if strings.Contains(message, m) { - w.errCh <- fmt.Errorf("Event failed due to %s . Message: %s", s.Event.Reason, message) + w.errCh <- fmt.Errorf("event failed due to %s. message: %s", s.Event.Reason, message) w.cancel() return false } diff --git a/events/status_test.go b/events/status_test.go index 9dbdce136..bb70c741f 100644 --- a/events/status_test.go +++ b/events/status_test.go @@ -266,7 +266,7 @@ func TestIsEventNormal(t *testing.T) { want: ` 01:23:45 NS: ns1 Event name: event2 Type: Warning Message: 0/1 nodes are available: 1 Insufficient cpu. preemption: 0/1 nodes are available: 1 No preemption victims found for incoming pod.. `[1:], - errch: "Event failed due to . Message: 0/1 nodes are available: 1 Insufficient cpu. preemption: 0/1 nodes are available: 1 No preemption victims found for incoming pod..", + errch: "event failed due to . message: 0/1 nodes are available: 1 Insufficient cpu. preemption: 0/1 nodes are available: 1 No preemption victims found for incoming pod..", canceled: true, }, { @@ -275,7 +275,7 @@ func TestIsEventNormal(t *testing.T) { want: ` 01:23:45 NS: ns1 Event name: event3 Type: Warning Message: 0/1 nodes are available: 1 Insufficient memory. preemption: 0/1 nodes are available: 1 No preemption victims found for incoming pod.. `[1:], - errch: "Event failed due to . Message: 0/1 nodes are available: 1 Insufficient memory. preemption: 0/1 nodes are available: 1 No preemption victims found for incoming pod..", + errch: "event failed due to . message: 0/1 nodes are available: 1 Insufficient memory. preemption: 0/1 nodes are available: 1 No preemption victims found for incoming pod..", canceled: true, }, } { diff --git a/examples/cisco/8000e/README.md b/examples/cisco/8000e/README.md index 7ea53bd90..656232b22 100644 --- a/examples/cisco/8000e/README.md +++ b/examples/cisco/8000e/README.md @@ -2,19 +2,30 @@ **Note:** The following instruction is validated on Ubuntu 20.04.1. -## Check perquisites and create a kne topology using topology [8000e-ixia.pb.txt](8000e-ixia.pb.txt) - -- Ensure you have a healthy kind cluster. Please refer [setup](../../../docs/setup.md) and [topology](../../../docs/create_topology.md) documents for the detailed instructions. -- Verify if nested virtualization is configured correctly by checking presence of /dev/kvm (`ls /dev/kvm`). -- Verify if Open vSwitch is installed by running `ovs-vswitchd --version` and install if it is missing by running `sudo apt-get install openvswitch-switch-dpdk`. -- Set pid_max <= 1048575 using `echo "kernel.pid_max=1048575" >> /etc/sysctl.conf` or `sysctl kernel.pid_max=1048575`. +## Check prerequisites and create a kne topology using topology [8000e-ixia.pb.txt](8000e-ixia.pb.txt) + +- Ensure you have a healthy kind cluster. Please refer + [setup](../../../docs/setup.md) and + [topology](../../../docs/create_topology.md) documents for the detailed + instructions. +- Verify if nested virtualization is configured correctly by checking presence + of /dev/kvm (`ls /dev/kvm`). +- Verify if Open vSwitch is installed by running `ovs-vswitchd --version` and + install if it is missing by running `sudo apt-get install +openvswitch-switch-dpdk`. +- Set pid_max <= 1048575 using `echo "kernel.pid_max=1048575" >> +/etc/sysctl.conf` or `sysctl kernel.pid_max=1048575`. - Create a KNE topology using `kne create path/to/8000e-ixia.pb.txt` ## Make sure the topology is healthy -- Make sure nodes are up and running by running command `kubectl get pods -A`. The output of the command should contain namespace `cisco-ixia` and 6 nodes with status `Running`. 4 otg ports (`otg-port-*`), one otg controller (`otg-controller`), and one cisco 8000e (`8000e`) are expected to be shown if the topology is created successfully. - -``` bash +- Make sure nodes are up and running by running command `kubectl get pods -A`. + The output of the command should contain namespace `cisco-ixia` and 6 nodes + with status `Running`. 4 otg ports (`otg-port-*`), one otg controller + (`otg-controller`), and one cisco 8000e (`8000e`) are expected to be shown + if the topology is created successfully. + +```bash kubectl get pods -A NAMESPACE NAME READY STATUS RESTARTS AGE cisco-ixia 8000e 1/1 Running 0 4m28s @@ -23,13 +34,17 @@ cisco-ixia otg-port-eth1 2/2 Run cisco-ixia otg-port-eth2 2/2 Running 0 4m28s cisco-ixia otg-port-eth3 2/2 Running 0 4m28s cisco-ixia otg-port-eth4 2/2 Running 0 4m27s --- omitted -- - +-- omitted -- + ``` -- Make sure external ip are mapped correctly by running command `kubectl get services -n cisco-ixia`. It is expected an external ip is assigned to each of the six nodes mentioned above. Also, the port mapping of the gnmi/gnoi/gribi/p4rt/ssh services for 8000e should match the port mapping in the topology file. - -``` bash +- Make sure external ip are mapped correctly by running command `kubectl get +services -n cisco-ixia`. It is expected an external ip is assigned to each + of the six nodes mentioned above. Also, the port mapping of the + gnmi/gnoi/gribi/p4rt/ssh services for 8000e should match the port mapping in + the topology file. + +```bash kubectl get services -n cisco-ixia NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service-8000e LoadBalancer 10.96.195.26 172.18.0.50 22:31633/TCP,9339:31543/TCP,9340:30751/TCP,9337:30331/TCP,9559:31439/TCP 7m45s @@ -40,23 +55,25 @@ service-otg-port-eth1 LoadBalancer 10.96.70.21 172.18.0.54 5555 service-otg-port-eth2 LoadBalancer 10.96.166.2 172.18.0.55 5555:31205/TCP,50071:32376/TCP 7m45s service-otg-port-eth3 LoadBalancer 10.96.108.38 172.18.0.56 5555:32396/TCP,50071:30361/TCP 7m45s service-otg-port-eth4 LoadBalancer 10.96.24.228 172.18.0.57 5555:31664/TCP,50071:30416/TCP 7m44s - ``` +``` ## Check 8000e status -- Based on the above output, you may use `ssh cisco@172.18.0.50` with user/pass (`cisco/cisco123`) to access cisco e8000 instance (`e8000`). +- Based on the above output, you may use `ssh cisco@172.18.0.50` with + user/pass (`cisco/cisco123`) to access cisco e8000 instance (`e8000`). -``` bash -ssh cisco@172.18.0.50 -Password: +```bash +ssh cisco@172.18.0.50 +Password: Last login: Thu Feb 23 05:03:48 2023 from 10.244.0.1 -RP/0/RP0/CPU0:ios# +RP/0/RP0/CPU0:ios# ``` -- You can also use `kubectl exec -it -n cisco-ixia 8000e -- telnet 0 60000` to get console access to the device. - -``` bash +- You can also use `kubectl exec -it -n cisco-ixia 8000e -- telnet 0 60000` to + get console access to the device. + +```bash kubectl exec -it -n cisco-ixia 8000e -- telnet 0 60000 Defaulted container "vxr" out of: vxr, init-vxr (init) Trying 0.0.0.0... @@ -64,23 +81,27 @@ Connected to 0. Escape character is '^]'. RP/0/RP0/CPU0:ios# - ``` +``` -**Note:** Depending on the model, it may takes around 6 minutes for the 8000e to be fully up. You may check `startup.log` and `startup.err` using the following steps if the telnet fails: +**Note:** Depending on the model, it may takes around 6 minutes for the 8000e to +be fully up. You may check `startup.log` and `startup.err` using the following +steps if the telnet fails: -``` bash -$ kubectl exec -it -n cisco-ixia 8000e -- bash +```bash +$ kubectl exec -it -n cisco-ixia 8000e -- bash Defaulted container "8000e" out of: 8000e, init-8000e (init) root@8000e:/# cd /nobackup/ root@8000e:/nobackup# ls -ltr -rw-r--r-- 1 root root 0 Feb 23 14:14 startup.err -rw-r--r-- 1 root root 4894 Feb 23 14:18 startup.log -root@8000e:/nobackup# +root@8000e:/nobackup# ``` -To check if the grpc is configured, you may use `show running-config grpc` after login to the router. By default grpc for 8000e is configured using tls without authentication (`insecure: false & skip_verify: true`). +To check if the grpc is configured, you may use `show running-config grpc` after +login to the router. By default grpc for 8000e is configured using tls without +authentication (`insecure: false & skip_verify: true`). -``` bash +```bash RP/0/RP0/CPU0:ios#show running-config grpc Thu Feb 23 06:04:04.562 UTC grpc @@ -97,7 +118,7 @@ RP/0/RP0/CPU0:ios# ## Test GNMI using external service ip and outside port -``` bash +```bash gnmic -a 172.18.0.50:9339 -u cisco -p cisco123 capabilities --skip-verify gNMI version: 0.8.0 supported encodings: @@ -130,12 +151,12 @@ supported models: ] } ] - + ``` ## Test gNOI service using external service ip and outside port -``` bash +```bash gnoic -a 172.18.0.50:9337 --skip-verify -u cisco -p cisco123 system ping --destination 44.44.44.44 100 bytes from 44.44.44.44: icmp_seq=1 ttl=255 time=3ns 100 bytes from 44.44.44.44: icmp_seq=2 ttl=255 time=1ns @@ -149,11 +170,11 @@ round-trip min/avg/max/stddev = 1.000/1.000/3.000/1.000 ms ## Test gRIBI service using external service ip and outside port -``` bash -gribic -a 172.18.0.50:9340 -u cisco -p cisco --skip-verify flush --ns DEFAULT -INFO[0000] got 1 results +```bash +gribic -a 172.18.0.50:9340 -u cisco -p cisco --skip-verify flush --ns DEFAULT +INFO[0000] got 1 results INFO[0000] "172.18.0.50:9340": timestamp: 1677161921484040943 -result: OK -$ +result: OK +$ ``` diff --git a/examples/gobgp/r1.yaml b/examples/gobgp/r1.yaml index 721e34299..8f77a5316 100644 --- a/examples/gobgp/r1.yaml +++ b/examples/gobgp/r1.yaml @@ -1,8 +1,9 @@ +--- global: - config: - as: 65001 - router-id: 10.1.0.1 + config: + as: 65001 + router-id: 10.1.0.1 neighbors: - - config: - neighbor-address: 10.0.0.2 - peer-as: 65002 \ No newline at end of file + - config: + neighbor-address: 10.0.0.2 + peer-as: 65002 diff --git a/examples/gobgp/r2.yaml b/examples/gobgp/r2.yaml index b770e9fa4..a35bc06a2 100644 --- a/examples/gobgp/r2.yaml +++ b/examples/gobgp/r2.yaml @@ -1,8 +1,9 @@ +--- global: - config: - as: 65002 - router-id: 10.1.0.2 + config: + as: 65002 + router-id: 10.1.0.2 neighbors: - - config: - neighbor-address: 10.0.0.1 - peer-as: 65001 \ No newline at end of file + - config: + neighbor-address: 10.0.0.1 + peer-as: 65001 diff --git a/examples/juniper/cptx-ixia/README.md b/examples/juniper/cptx-ixia/README.md index df981f7c5..f88719bed 100644 --- a/examples/juniper/cptx-ixia/README.md +++ b/examples/juniper/cptx-ixia/README.md @@ -167,35 +167,39 @@ entry: { - cPTX can be configured in a channelized or non-channelized mode. - cPTX will be started in channelized mode if any of the interfaces in the interface mapping of KNE config are channelized. - cPTX ethernet interfaces to software wire interface mapping (channelized). Follow the `juniper.config` for more info. Here is an example. - ```bash - et-0/0/0:0 (eth4) - et-0/0/0:1 (eth5) - -- snip -- - et-0/0/1:0 (eth12) - et-0/0/1:1 (eth13) - -- snip -- - et-0/0/2:0 (eth20) - -- snip -- - et-0/0/3:0 (eth28) - et-0/0/4:0 (eth36) - et-0/0/5:0 (unused) - et-0/0/6:0 (eth40) - -- snip -- - et-0/0/7:0 (unused) - -- snip -- - et-0/0/11:0 (eth68) - ``` + + ```bash + et-0/0/0:0 (eth4) + et-0/0/0:1 (eth5) + -- snip -- + et-0/0/1:0 (eth12) + et-0/0/1:1 (eth13) + -- snip -- + et-0/0/2:0 (eth20) + -- snip -- + et-0/0/3:0 (eth28) + et-0/0/4:0 (eth36) + et-0/0/5:0 (unused) + et-0/0/6:0 (eth40) + -- snip -- + et-0/0/7:0 (unused) + -- snip -- + et-0/0/11:0 (eth68) + ``` + - cPTX ethernet interfaces to software wire interface mapping (non-channelized). Here is an example. - ```bash - et-0/0/0 (eth4) - et-0/0/1 (eth5) - et-0/0/2 (eth6) - -- snip -- - et-0/0/5 (unused) - et-0/0/6 (eth10) - et-0/0/7 (unused) - et-0/0/8 (eth12) - -- snip -- - et-0/0/11 (eth15) - ``` + + ```bash + et-0/0/0 (eth4) + et-0/0/1 (eth5) + et-0/0/2 (eth6) + -- snip -- + et-0/0/5 (unused) + et-0/0/6 (eth10) + et-0/0/7 (unused) + et-0/0/8 (eth12) + -- snip -- + et-0/0/11 (eth15) + ``` + - Pass gRPC client option `-skip-verify` as only self-signed TLS certificates are configured as of today. diff --git a/exec/fake/fake_test.go b/exec/fake/fake_test.go index 351518788..3b60c125b 100644 --- a/exec/fake/fake_test.go +++ b/exec/fake/fake_test.go @@ -284,11 +284,11 @@ func TestFailed(t *testing.T) { } } for _, u := range cmds.unexpected { - var ue Response + var unexpectedResp Response if len(tt.unexpected) > 0 { - ue = tt.unexpected[0] + unexpectedResp = tt.unexpected[0] } - t.Logf("Compare %v and %v", u, ue) + t.Logf("Compare %v and %v", u, unexpectedResp) if len(tt.unexpected) > 0 && tt.unexpected[0].String() == u.String() { tt.unexpected = tt.unexpected[1:] continue @@ -385,7 +385,9 @@ unexpected executions: c := cmds.Command(cmd.Cmd, cmd.Args...) c.SetStdout(&stdout) c.SetStderr(&stderr) - _ = c.Run() + if err := c.Run(); err != nil { + t.Errorf("c.Run() returned unexpected error: %v", err) + } } err := cmds.Done() if err.Error() != tt.done { diff --git a/exec/run/run_test.go b/exec/run/run_test.go index b57a6bc92..71c784571 100644 --- a/exec/run/run_test.go +++ b/exec/run/run_test.go @@ -111,11 +111,11 @@ func TestRunCommand(t *testing.T) { if string(got) != tt.want { t.Errorf("runCommand() got output %v, want %v", string(got), tt.want) } - if string(infos.Bytes()) != tt.wantInfos { - t.Errorf("runCommand() got info logs %v, want %v", string(infos.Bytes()), tt.wantInfos) + if infos.String() != tt.wantInfos { + t.Errorf("runCommand() got info logs %v, want %v", infos.String(), tt.wantInfos) } - if string(warnings.Bytes()) != tt.wantWarnings { - t.Errorf("runCommand() got warning logs %v, want %v", string(warnings.Bytes()), tt.wantWarnings) + if warnings.String() != tt.wantWarnings { + t.Errorf("runCommand() got warning logs %v, want %v", warnings.String(), tt.wantWarnings) } }) } diff --git a/flags/flags.go b/flags/flags.go index 765b9e354..6e58d3307 100644 --- a/flags/flags.go +++ b/flags/flags.go @@ -35,7 +35,9 @@ func Import(defmap map[string]string) { flag.Set("stderrthreshold", "INFO") //nolint:errcheck for k, v := range defmap { if f := flag.Lookup(k); f != nil { - f.Value.Set(v) + if err := f.Value.Set(v); err != nil { + klog.Warningf("Failed to set default value %q for flag %q: %v", v, k, err) + } f.DefValue = v } } diff --git a/load/deploy.go b/load/deploy.go index 684e363db..aaf427001 100644 --- a/load/deploy.go +++ b/load/deploy.go @@ -13,7 +13,7 @@ import ( var yamlNodeType = reflect.TypeOf(yaml.Node{}) -// open is overriden in tests. +// open is overridden in tests. var open = os.Open // A Spec represents a structure that yaml can be decoded into. The type is the @@ -43,7 +43,7 @@ func Register(kind string, spec *Spec) { // A Config represents a KNE deployment configuration. type Config struct { Path string // Path of the configuration file - Dir string // Absolute path of the diretory Path is in + Dir string // Absolute path of the directory Path is in Config interface{} // The configuration structure Deployment interface{} // Filled by Config.Decode @@ -179,7 +179,7 @@ func (c *Config) decode(v reflect.Value, path []string, tag reflect.StructTag) ( } case "spec": if sf.Type != yamlNodeType { - return fmt.Errorf("%s is not of type %v\n", strings.Join(append(path, sf.Name), "."), yamlNodeType) + return fmt.Errorf("%s is not of type %v", strings.Join(append(path, sf.Name), "."), yamlNodeType) } node := sv.Interface().(yaml.Node) spec = &node @@ -193,9 +193,9 @@ func (c *Config) decode(v reflect.Value, path []string, tag reflect.StructTag) ( switch { case kind == "" && spec == nil: case kind == "": - return fmt.Errorf("spec field without kind: %s\n", strings.Join(path, ".")) + return fmt.Errorf("spec field without kind: %s", strings.Join(path, ".")) case spec == nil: - return fmt.Errorf("kind field without spec: %s\n", strings.Join(path, ".")) + return fmt.Errorf("kind field without spec: %s", strings.Join(path, ".")) default: // kind and spec have been supplied. diff --git a/load/testdata/deploy/kne/kind-bridge.yaml b/load/testdata/deploy/kne/kind-bridge.yaml index 0832c0c2f..00bcbf8b9 100644 --- a/load/testdata/deploy/kne/kind-bridge.yaml +++ b/load/testdata/deploy/kne/kind-bridge.yaml @@ -1,6 +1,7 @@ # kind-bridge.yaml cluster config file sets up a kind cluster where default PTP CNI plugin # is swapped with the Bridge CNI plugin. # Bridge CNI plugin is required by some Network OSes to operate. +--- cluster: kind: Kind spec: diff --git a/load/testdata/kind/kind-no-cni.yaml b/load/testdata/kind/kind-no-cni.yaml index 1494699dc..aa3bb8295 100644 --- a/load/testdata/kind/kind-no-cni.yaml +++ b/load/testdata/kind/kind-no-cni.yaml @@ -1,3 +1,4 @@ +--- kind: Cluster apiVersion: kind.x-k8s.io/v1alpha4 networking: diff --git a/load/testdata/manifests/controllers/ceoslab/manifest.yaml b/load/testdata/manifests/controllers/ceoslab/manifest.yaml index da7def69c..b21d6793f 100644 --- a/load/testdata/manifests/controllers/ceoslab/manifest.yaml +++ b/load/testdata/manifests/controllers/ceoslab/manifest.yaml @@ -1,3 +1,4 @@ +--- apiVersion: v1 kind: Namespace metadata: @@ -21,193 +22,193 @@ spec: singular: ceoslabdevice scope: Namespaced versions: - - name: v1alpha1 - schema: - openAPIV3Schema: - description: CEosLabDevice is the Schema for the ceoslabdevices API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: CEosLabDeviceSpec defines the desired state of CEosLabDevice - properties: - args: - description: Additional arguments to pass to /sbin/init. Those necessary to boot properly are already present. - items: - type: string - type: array - certconfig: - description: X.509 certificate configuration. - properties: - selfsignedcerts: - description: Configuration for self-signed certificates. - items: - properties: - certname: - description: Certificate name on the node. - type: string - commonname: - description: Common name to set in the cert. - type: string - keyname: - description: Key name on the node. - type: string - keysize: - description: RSA keysize to use for key generation. - format: int32 - type: integer - type: object - type: array - type: object - envvars: - additionalProperties: - type: string - description: Additional environment variables. Those necessary to boot properly are already present. - type: object - image: - description: 'Image name. Default: ceos:latest' - type: string - initcontainerimage: - description: 'Init container image name. Default: networkop/init-wait:latest' - type: string - intfmapping: - additionalProperties: - type: string - description: Explicit interface mapping between kernel devices and interface names. If this is defined, any unmapped devices are ignored. - type: object - numinterfaces: - description: 'Number of data interfaces to create. An additional interface (eth0) is created for pod connectivity. Default: 0 interfaces' - format: int32 - type: integer - resourcerequirements: - additionalProperties: - type: string - description: 'Resource requests to configure on the pod. Default: none' - type: object - services: - additionalProperties: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: CEosLabDevice is the Schema for the ceoslabdevices API + properties: + apiVersion: + description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources" + type: string + kind: + description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" + type: string + metadata: + type: object + spec: + description: CEosLabDeviceSpec defines the desired state of CEosLabDevice + properties: + args: + description: Additional arguments to pass to /sbin/init. Those necessary to boot properly are already present. + items: + type: string + type: array + certconfig: + description: X.509 certificate configuration. properties: - tcpports: - description: TCP ports to forward to the pod. + selfsignedcerts: + description: Configuration for self-signed certificates. items: properties: - in: - description: Port inside the container. - format: int32 - type: integer - out: - description: Port outside the container. Defaults to the same as in. + certname: + description: Certificate name on the node. + type: string + commonname: + description: Common name to set in the cert. + type: string + keyname: + description: Key name on the node. + type: string + keysize: + description: RSA keysize to use for key generation. format: int32 type: integer type: object type: array type: object - description: 'Port mappings for container services. Default: none' - type: object - sleep: - description: 'Time (in seconds) to wait before starting the device. Default: 0 seconds' - format: int32 - type: integer - toggleoverrides: - additionalProperties: - type: boolean - description: EOS feature toggle overrides - type: object - waitforagents: - description: EOS agents to for the startup probe to block on - items: + envvars: + additionalProperties: + type: string + description: Additional environment variables. Those necessary to boot properly are already present. + type: object + image: + description: "Image name. Default: ceos:latest" type: string - type: array - type: object - status: - description: CEosLabDeviceStatus defines the observed state of CEosLabDevice - properties: - configmapconfig: - description: ConfigMap state as configured in configmaps - properties: - intfmappingstatus: - additionalProperties: - type: string + initcontainerimage: + description: "Init container image name. Default: networkop/init-wait:latest" + type: string + intfmapping: + additionalProperties: + type: string + description: Explicit interface mapping between kernel devices and interface names. If this is defined, any unmapped devices are ignored. + type: object + numinterfaces: + description: "Number of data interfaces to create. An additional interface (eth0) is created for pod connectivity. Default: 0 interfaces" + format: int32 + type: integer + resourcerequirements: + additionalProperties: + type: string + description: "Resource requests to configure on the pod. Default: none" + type: object + services: + additionalProperties: + properties: + tcpports: + description: TCP ports to forward to the pod. + items: + properties: + in: + description: Port inside the container. + format: int32 + type: integer + out: + description: Port outside the container. Defaults to the same as in. + format: int32 + type: integer + type: object + type: array type: object - rceosstale: + description: "Port mappings for container services. Default: none" + type: object + sleep: + description: "Time (in seconds) to wait before starting the device. Default: 0 seconds" + format: int32 + type: integer + toggleoverrides: + additionalProperties: type: boolean - selfsignedcertstatus: - additionalProperties: - properties: - certname: - description: Certificate name on the node. - type: string - commonname: - description: Common name to set in the cert. - type: string - keyname: - description: Key name on the node. - type: string - keysize: - description: RSA keysize to use for key generation. - format: int32 - type: integer - type: object - type: object - startupconfigresourceversion: + description: EOS feature toggle overrides + type: object + waitforagents: + description: EOS agents to for the startup probe to block on + items: type: string - toggleoverridesstatus: - additionalProperties: + type: array + type: object + status: + description: CEosLabDeviceStatus defines the observed state of CEosLabDevice + properties: + configmapconfig: + description: ConfigMap state as configured in configmaps + properties: + intfmappingstatus: + additionalProperties: + type: string + type: object + rceosstale: type: boolean - type: object - type: object - podconfigmapconfig: - description: ConfigMap state as present in the pod. If these diverge, we need to restart the pod to update. Even if an in-place update is possible these are needed at boot time. - properties: - intfmappingstatus: - additionalProperties: + selfsignedcertstatus: + additionalProperties: + properties: + certname: + description: Certificate name on the node. + type: string + commonname: + description: Common name to set in the cert. + type: string + keyname: + description: Key name on the node. + type: string + keysize: + description: RSA keysize to use for key generation. + format: int32 + type: integer + type: object + type: object + startupconfigresourceversion: type: string - type: object - rceosstale: - type: boolean - selfsignedcertstatus: - additionalProperties: - properties: - certname: - description: Certificate name on the node. - type: string - commonname: - description: Common name to set in the cert. - type: string - keyname: - description: Key name on the node. - type: string - keysize: - description: RSA keysize to use for key generation. - format: int32 - type: integer + toggleoverridesstatus: + additionalProperties: + type: boolean type: object - type: object - startupconfigresourceversion: - type: string - toggleoverridesstatus: - additionalProperties: + type: object + podconfigmapconfig: + description: ConfigMap state as present in the pod. If these diverge, we need to restart the pod to update. Even if an in-place update is possible these are needed at boot time. + properties: + intfmappingstatus: + additionalProperties: + type: string + type: object + rceosstale: type: boolean - type: object - type: object - reason: - description: Reason for potential failure - type: string - status: - description: Device status - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} + selfsignedcertstatus: + additionalProperties: + properties: + certname: + description: Certificate name on the node. + type: string + commonname: + description: Common name to set in the cert. + type: string + keyname: + description: Key name on the node. + type: string + keysize: + description: RSA keysize to use for key generation. + format: int32 + type: integer + type: object + type: object + startupconfigresourceversion: + type: string + toggleoverridesstatus: + additionalProperties: + type: boolean + type: object + type: object + reason: + description: Reason for potential failure + type: string + status: + description: Device status + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} status: acceptedNames: kind: "" @@ -227,37 +228,37 @@ metadata: name: arista-ceoslab-operator-leader-election-role namespace: arista-ceoslab-operator-system rules: -- apiGroups: - - "" - resources: - - configmaps - verbs: - - get - - list - - watch - - create - - update - - patch - - delete -- apiGroups: - - coordination.k8s.io - resources: - - leases - verbs: - - get - - list - - watch - - create - - update - - patch - - delete -- apiGroups: - - "" - resources: - - events - verbs: - - create - - patch + - apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch + - create + - update + - patch + - delete + - apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - get + - list + - watch + - create + - update + - patch + - delete + - apiGroups: + - "" + resources: + - events + verbs: + - create + - patch --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -265,108 +266,108 @@ metadata: creationTimestamp: null name: arista-ceoslab-operator-manager-role rules: -- apiGroups: - - ceoslab.arista.com - resources: - - ceoslabdevices - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - ceoslab.arista.com - resources: - - ceoslabdevices/finalizers - verbs: - - update -- apiGroups: - - ceoslab.arista.com - resources: - - ceoslabdevices/status - verbs: - - get - - patch - - update -- apiGroups: - - "" - resources: - - configmaps - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - pods - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - secrets - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - services - verbs: - - create - - delete - - get - - list - - patch - - update - - watch + - apiGroups: + - ceoslab.arista.com + resources: + - ceoslabdevices + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - ceoslab.arista.com + resources: + - ceoslabdevices/finalizers + verbs: + - update + - apiGroups: + - ceoslab.arista.com + resources: + - ceoslabdevices/status + verbs: + - get + - patch + - update + - apiGroups: + - "" + resources: + - configmaps + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - "" + resources: + - pods + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - "" + resources: + - secrets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - "" + resources: + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: arista-ceoslab-operator-metrics-reader rules: -- nonResourceURLs: - - /metrics - verbs: - - get + - nonResourceURLs: + - /metrics + verbs: + - get --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: arista-ceoslab-operator-proxy-role rules: -- apiGroups: - - authentication.k8s.io - resources: - - tokenreviews - verbs: - - create -- apiGroups: - - authorization.k8s.io - resources: - - subjectaccessreviews - verbs: - - create + - apiGroups: + - authentication.k8s.io + resources: + - tokenreviews + verbs: + - create + - apiGroups: + - authorization.k8s.io + resources: + - subjectaccessreviews + verbs: + - create --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding @@ -378,9 +379,9 @@ roleRef: kind: Role name: arista-ceoslab-operator-leader-election-role subjects: -- kind: ServiceAccount - name: arista-ceoslab-operator-controller-manager - namespace: arista-ceoslab-operator-system + - kind: ServiceAccount + name: arista-ceoslab-operator-controller-manager + namespace: arista-ceoslab-operator-system --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding @@ -391,9 +392,9 @@ roleRef: kind: ClusterRole name: arista-ceoslab-operator-manager-role subjects: -- kind: ServiceAccount - name: arista-ceoslab-operator-controller-manager - namespace: arista-ceoslab-operator-system + - kind: ServiceAccount + name: arista-ceoslab-operator-controller-manager + namespace: arista-ceoslab-operator-system --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding @@ -404,9 +405,9 @@ roleRef: kind: ClusterRole name: arista-ceoslab-operator-proxy-role subjects: -- kind: ServiceAccount - name: arista-ceoslab-operator-controller-manager - namespace: arista-ceoslab-operator-system + - kind: ServiceAccount + name: arista-ceoslab-operator-controller-manager + namespace: arista-ceoslab-operator-system --- apiVersion: v1 data: @@ -436,10 +437,10 @@ metadata: namespace: arista-ceoslab-operator-system spec: ports: - - name: https - port: 8443 - protocol: TCP - targetPort: https + - name: https + port: 8443 + protocol: TCP + targetPort: https selector: control-plane: controller-manager --- @@ -463,53 +464,53 @@ spec: control-plane: controller-manager spec: containers: - - args: - - --secure-listen-address=0.0.0.0:8443 - - --upstream=http://127.0.0.1:8080/ - - --logtostderr=true - - --v=0 - image: registry.k8s.io/kubebuilder/kube-rbac-proxy:v0.11.0 - name: kube-rbac-proxy - ports: - - containerPort: 8443 - name: https - protocol: TCP - resources: - limits: - cpu: 500m - memory: 128Mi - requests: - cpu: 5m - memory: 64Mi - - args: - - --health-probe-bind-address=:8081 - - --metrics-bind-address=127.0.0.1:8080 - - --leader-elect - command: - - /manager - image: ghcr.io/aristanetworks/arista-ceoslab-operator:v2.0.1 - livenessProbe: - httpGet: - path: /healthz - port: 8081 - initialDelaySeconds: 15 - periodSeconds: 20 - name: manager - readinessProbe: - httpGet: - path: /readyz - port: 8081 - initialDelaySeconds: 5 - periodSeconds: 10 - resources: - limits: - cpu: 500m - memory: 128Mi - requests: - cpu: 10m - memory: 64Mi - securityContext: - allowPrivilegeEscalation: false + - args: + - --secure-listen-address=0.0.0.0:8443 + - --upstream=http://127.0.0.1:8080/ + - --logtostderr=true + - --v=0 + image: registry.k8s.io/kubebuilder/kube-rbac-proxy:v0.11.0 + name: kube-rbac-proxy + ports: + - containerPort: 8443 + name: https + protocol: TCP + resources: + limits: + cpu: 500m + memory: 128Mi + requests: + cpu: 5m + memory: 64Mi + - args: + - --health-probe-bind-address=:8081 + - --metrics-bind-address=127.0.0.1:8080 + - --leader-elect + command: + - /manager + image: ghcr.io/aristanetworks/arista-ceoslab-operator:v2.0.1 + livenessProbe: + httpGet: + path: /healthz + port: 8081 + initialDelaySeconds: 15 + periodSeconds: 20 + name: manager + readinessProbe: + httpGet: + path: /readyz + port: 8081 + initialDelaySeconds: 5 + periodSeconds: 10 + resources: + limits: + cpu: 500m + memory: 128Mi + requests: + cpu: 10m + memory: 64Mi + securityContext: + allowPrivilegeEscalation: false securityContext: runAsNonRoot: true serviceAccountName: arista-ceoslab-operator-controller-manager diff --git a/load/testdata/manifests/controllers/lemming/manifest.yaml b/load/testdata/manifests/controllers/lemming/manifest.yaml index 273dc2d43..70931f9d3 100644 --- a/load/testdata/manifests/controllers/lemming/manifest.yaml +++ b/load/testdata/manifests/controllers/lemming/manifest.yaml @@ -1,3 +1,4 @@ +--- apiVersion: v1 kind: Namespace metadata: @@ -21,245 +22,268 @@ spec: singular: lemming scope: Namespaced versions: - - name: v1alpha1 - schema: - openAPIV3Schema: - description: Lemming is the Schema for the lemmings API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: LemmingSpec defines the desired state of Lemming. - properties: - args: - description: Args are the args to pass to the command. - items: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: Lemming is the Schema for the lemmings API + properties: + apiVersion: + description: + "APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources" + type: string + kind: + description: + "Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" + type: string + metadata: + type: object + spec: + description: LemmingSpec defines the desired state of Lemming. + properties: + args: + description: Args are the args to pass to the command. + items: + type: string + type: array + command: + description: Command is the name of the executable to run. type: string - type: array - command: - description: Command is the name of the executable to run. - type: string - configFile: - description: ConfigFile is the default configuration file name for - the pod. - type: string - configPath: - description: ConfigPath is the mount point for configuration inside - the pod. - type: string - env: - description: Env are the environment variables to set for the container. - items: - description: EnvVar represents an environment variable present in - a Container. - properties: - name: - description: Name of the environment variable. Must be a C_IDENTIFIER. - type: string - value: - description: 'Variable references $(VAR_NAME) are expanded using - the previously defined environment variables in the container - and any service environment variables. If a variable cannot - be resolved, the reference in the input string will be unchanged. - Double $$ are reduced to a single $, which allows for escaping - the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will produce the - string literal "$(VAR_NAME)". Escaped references will never - be expanded, regardless of whether the variable exists or - not. Defaults to "".' - type: string - valueFrom: - description: Source for the environment variable's value. Cannot - be used if value is not empty. - properties: - configMapKeyRef: - description: Selects a key of a ConfigMap. - properties: - key: - description: The key to select. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - optional: - description: Specify whether the ConfigMap or its key - must be defined - type: boolean - required: - - key - type: object - fieldRef: - description: 'Selects a field of the pod: supports metadata.name, - metadata.namespace, `metadata.labels['''']`, `metadata.annotations['''']`, - spec.nodeName, spec.serviceAccountName, status.hostIP, - status.podIP, status.podIPs.' - properties: - apiVersion: - description: Version of the schema the FieldPath is - written in terms of, defaults to "v1". - type: string - fieldPath: - description: Path of the field to select in the specified - API version. - type: string - required: - - fieldPath - type: object - resourceFieldRef: - description: 'Selects a resource of the container: only - resources limits and requests (limits.cpu, limits.memory, - limits.ephemeral-storage, requests.cpu, requests.memory - and requests.ephemeral-storage) are currently supported.' - properties: - containerName: - description: 'Container name: required for volumes, - optional for env vars' - type: string - divisor: - anyOf: - - type: integer - - type: string - description: Specifies the output format of the exposed - resources, defaults to "1" - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - description: 'Required: resource to select' - type: string - required: - - resource - type: object - secretKeyRef: - description: Selects a key of a secret in the pod's namespace - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - optional: - description: Specify whether the Secret or its key must - be defined - type: boolean - required: - - key - type: object - type: object - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - image: - description: Image is the container image to run. - type: string - initImage: - description: InitImage is the docker image to use as an init container - for the pod. - type: string - initSleep: - description: InitSleep is the time sleep in the init container - type: integer - interfaceCount: - description: InterfaceCount is number of interfaces to be attached - to the pod. - type: integer - ports: - additionalProperties: - description: ServicePort describes an external L4 port on the device. - properties: - innerPort: - description: InnerPort is port on the container to expose. - format: int32 - type: integer - outerPort: - description: OuterPort is port on the container to expose. - format: int32 - type: integer - required: - - innerPort - - outerPort - type: object - description: Ports are ports to create on the service. - type: object - resources: - description: Resources are the K8s resources to allocate to lemming - container. - properties: - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount of compute resources - allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount of compute - resources required. If Requests is omitted for a container, - it defaults to Limits if that is explicitly specified, otherwise - to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - type: object - tls: - description: TLS is the configuration the key/certs to use for management. - properties: - selfSigned: - description: SelfSigned generates a new self signed certificate. + configFile: + description: + ConfigFile is the default configuration file name for + the pod. + type: string + configPath: + description: + ConfigPath is the mount point for configuration inside + the pod. + type: string + env: + description: Env are the environment variables to set for the container. + items: + description: + EnvVar represents an environment variable present in + a Container. properties: - commonName: - description: / Common name to set in the cert. + name: + description: Name of the environment variable. Must be a C_IDENTIFIER. + type: string + value: + description: + 'Variable references $(VAR_NAME) are expanded using + the previously defined environment variables in the container + and any service environment variables. If a variable cannot + be resolved, the reference in the input string will be unchanged. + Double $$ are reduced to a single $, which allows for escaping + the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will produce the + string literal "$(VAR_NAME)". Escaped references will never + be expanded, regardless of whether the variable exists or + not. Defaults to "".' type: string - keySize: - description: RSA keysize to use for key generation. + valueFrom: + description: + Source for the environment variable's value. Cannot + be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: + "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?" + type: string + optional: + description: + Specify whether the ConfigMap or its key + must be defined + type: boolean + required: + - key + type: object + fieldRef: + description: + "Selects a field of the pod: supports metadata.name, + metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, + spec.nodeName, spec.serviceAccountName, status.hostIP, + status.podIP, status.podIPs." + properties: + apiVersion: + description: + Version of the schema the FieldPath is + written in terms of, defaults to "v1". + type: string + fieldPath: + description: + Path of the field to select in the specified + API version. + type: string + required: + - fieldPath + type: object + resourceFieldRef: + description: + "Selects a resource of the container: only + resources limits and requests (limits.cpu, limits.memory, + limits.ephemeral-storage, requests.cpu, requests.memory + and requests.ephemeral-storage) are currently supported." + properties: + containerName: + description: + "Container name: required for volumes, + optional for env vars" + type: string + divisor: + anyOf: + - type: integer + - type: string + description: + Specifies the output format of the exposed + resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: "Required: resource to select" + type: string + required: + - resource + type: object + secretKeyRef: + description: Selects a key of a secret in the pod's namespace + properties: + key: + description: + The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: + "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?" + type: string + optional: + description: + Specify whether the Secret or its key must + be defined + type: boolean + required: + - key + type: object + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + image: + description: Image is the container image to run. + type: string + initImage: + description: + InitImage is the docker image to use as an init container + for the pod. + type: string + initSleep: + description: InitSleep is the time sleep in the init container + type: integer + interfaceCount: + description: + InterfaceCount is number of interfaces to be attached + to the pod. + type: integer + ports: + additionalProperties: + description: ServicePort describes an external L4 port on the device. + properties: + innerPort: + description: InnerPort is port on the container to expose. + format: int32 + type: integer + outerPort: + description: OuterPort is port on the container to expose. + format: int32 type: integer required: - - commonName - - keySize + - innerPort + - outerPort type: object - type: object - type: object - status: - description: LemmingStatus defines the observed state of Lemming - properties: - message: - description: Message describes why the lemming is in the current phase. - type: string - phase: - description: Phase is the overall status of the Lemming. - type: string - required: - - message - - phase - type: object - type: object - served: true - storage: true - subresources: - status: {} + description: Ports are ports to create on the service. + type: object + resources: + description: + Resources are the K8s resources to allocate to lemming + container. + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: + "Limits describes the maximum amount of compute resources + allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/" + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: + "Requests describes the minimum amount of compute + resources required. If Requests is omitted for a container, + it defaults to Limits if that is explicitly specified, otherwise + to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/" + type: object + type: object + tls: + description: TLS is the configuration the key/certs to use for management. + properties: + selfSigned: + description: SelfSigned generates a new self signed certificate. + properties: + commonName: + description: / Common name to set in the cert. + type: string + keySize: + description: RSA keysize to use for key generation. + type: integer + required: + - commonName + - keySize + type: object + type: object + type: object + status: + description: LemmingStatus defines the observed state of Lemming + properties: + message: + description: Message describes why the lemming is in the current phase. + type: string + phase: + description: Phase is the overall status of the Lemming. + type: string + required: + - message + - phase + type: object + type: object + served: true + storage: true + subresources: + status: {} --- apiVersion: v1 kind: ServiceAccount @@ -273,37 +297,37 @@ metadata: name: lemming-leader-election-role namespace: lemming-operator rules: -- apiGroups: - - "" - resources: - - configmaps - verbs: - - get - - list - - watch - - create - - update - - patch - - delete -- apiGroups: - - coordination.k8s.io - resources: - - leases - verbs: - - get - - list - - watch - - create - - update - - patch - - delete -- apiGroups: - - "" - resources: - - events - verbs: - - create - - patch + - apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch + - create + - update + - patch + - delete + - apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - get + - list + - watch + - create + - update + - patch + - delete + - apiGroups: + - "" + resources: + - events + verbs: + - create + - patch --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -311,74 +335,74 @@ metadata: creationTimestamp: null name: lemming-manager-role rules: -- apiGroups: - - "" - resources: - - pods - - secrets - - services - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - lemming.openconfig.net - resources: - - lemmings - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - lemming.openconfig.net - resources: - - lemmings/finalizers - verbs: - - update -- apiGroups: - - lemming.openconfig.net - resources: - - lemmings/status - verbs: - - get - - patch - - update + - apiGroups: + - "" + resources: + - pods + - secrets + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - lemming.openconfig.net + resources: + - lemmings + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - lemming.openconfig.net + resources: + - lemmings/finalizers + verbs: + - update + - apiGroups: + - lemming.openconfig.net + resources: + - lemmings/status + verbs: + - get + - patch + - update --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: lemming-metrics-reader rules: -- nonResourceURLs: - - /metrics - verbs: - - get + - nonResourceURLs: + - /metrics + verbs: + - get --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: lemming-proxy-role rules: -- apiGroups: - - authentication.k8s.io - resources: - - tokenreviews - verbs: - - create -- apiGroups: - - authorization.k8s.io - resources: - - subjectaccessreviews - verbs: - - create + - apiGroups: + - authentication.k8s.io + resources: + - tokenreviews + verbs: + - create + - apiGroups: + - authorization.k8s.io + resources: + - subjectaccessreviews + verbs: + - create --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding @@ -390,9 +414,9 @@ roleRef: kind: Role name: lemming-leader-election-role subjects: -- kind: ServiceAccount - name: lemming-controller-manager - namespace: lemming-operator + - kind: ServiceAccount + name: lemming-controller-manager + namespace: lemming-operator --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding @@ -403,9 +427,9 @@ roleRef: kind: ClusterRole name: lemming-manager-role subjects: -- kind: ServiceAccount - name: lemming-controller-manager - namespace: lemming-operator + - kind: ServiceAccount + name: lemming-controller-manager + namespace: lemming-operator --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding @@ -416,9 +440,9 @@ roleRef: kind: ClusterRole name: lemming-proxy-role subjects: -- kind: ServiceAccount - name: lemming-controller-manager - namespace: lemming-operator + - kind: ServiceAccount + name: lemming-controller-manager + namespace: lemming-operator --- apiVersion: v1 data: @@ -458,10 +482,10 @@ metadata: namespace: lemming-operator spec: ports: - - name: https - port: 8443 - protocol: TCP - targetPort: https + - name: https + port: 8443 + protocol: TCP + targetPort: https selector: control-plane: controller-manager --- @@ -485,61 +509,61 @@ spec: control-plane: controller-manager spec: containers: - - args: - - --secure-listen-address=0.0.0.0:8443 - - --upstream=http://127.0.0.1:8080/ - - --logtostderr=true - - --v=0 - image: registry.k8s.io/kubebuilder/kube-rbac-proxy:v0.12.0 - name: kube-rbac-proxy - ports: - - containerPort: 8443 - name: https - protocol: TCP - resources: - limits: - cpu: 500m - memory: 128Mi - requests: - cpu: 5m - memory: 64Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - - args: - - --health-probe-bind-address=:8081 - - --metrics-bind-address=127.0.0.1:8080 - - --leader-elect - command: - - /manager - image: us-west1-docker.pkg.dev/openconfig-lemming/release/operator:v0.2.3 - livenessProbe: - httpGet: - path: /healthz - port: 8081 - initialDelaySeconds: 15 - periodSeconds: 20 - name: manager - readinessProbe: - httpGet: - path: /readyz - port: 8081 - initialDelaySeconds: 5 - periodSeconds: 10 - resources: - limits: - cpu: 500m - memory: 128Mi - requests: - cpu: 10m - memory: 64Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL + - args: + - --secure-listen-address=0.0.0.0:8443 + - --upstream=http://127.0.0.1:8080/ + - --logtostderr=true + - --v=0 + image: registry.k8s.io/kubebuilder/kube-rbac-proxy:v0.12.0 + name: kube-rbac-proxy + ports: + - containerPort: 8443 + name: https + protocol: TCP + resources: + limits: + cpu: 500m + memory: 128Mi + requests: + cpu: 5m + memory: 64Mi + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + - args: + - --health-probe-bind-address=:8081 + - --metrics-bind-address=127.0.0.1:8080 + - --leader-elect + command: + - /manager + image: us-west1-docker.pkg.dev/openconfig-lemming/release/operator:v0.2.3 + livenessProbe: + httpGet: + path: /healthz + port: 8081 + initialDelaySeconds: 15 + periodSeconds: 20 + name: manager + readinessProbe: + httpGet: + path: /readyz + port: 8081 + initialDelaySeconds: 5 + periodSeconds: 10 + resources: + limits: + cpu: 500m + memory: 128Mi + requests: + cpu: 10m + memory: 64Mi + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL securityContext: runAsNonRoot: true serviceAccountName: lemming-controller-manager diff --git a/load/testdata/manifests/controllers/srlinux/manifest.yaml b/load/testdata/manifests/controllers/srlinux/manifest.yaml index da3d2c48a..6ba42e3f3 100644 --- a/load/testdata/manifests/controllers/srlinux/manifest.yaml +++ b/load/testdata/manifests/controllers/srlinux/manifest.yaml @@ -1,3 +1,4 @@ +--- apiVersion: v1 kind: Namespace metadata: @@ -21,119 +22,127 @@ spec: singular: srlinux scope: Namespaced versions: - - additionalPrinterColumns: - - jsonPath: .status.image - name: Image - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha1 - schema: - openAPIV3Schema: - description: Srlinux is the Schema for the srlinuxes API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - license_key: - description: license key from license secret that contains a license file - for this Srlinux - type: string - metadata: - type: object - spec: - description: SrlinuxSpec defines the desired state of Srlinux. - properties: - config: - description: NodeConfig represents srlinux node configuration parameters. - properties: - args: - items: - type: string - type: array - cert: - description: CertificateCfg represents srlinux certificate configuration - parameters. - properties: - cert_name: - description: Certificate name on the node. - type: string - common_name: - description: Common name to set in the cert. + - additionalPrinterColumns: + - jsonPath: .status.image + name: Image + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: Srlinux is the Schema for the srlinuxes API + properties: + apiVersion: + description: + "APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources" + type: string + kind: + description: + "Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" + type: string + license_key: + description: + license key from license secret that contains a license file + for this Srlinux + type: string + metadata: + type: object + spec: + description: SrlinuxSpec defines the desired state of Srlinux. + properties: + config: + description: NodeConfig represents srlinux node configuration parameters. + properties: + args: + items: type: string - key_name: - description: Key name on the node. + type: array + cert: + description: + CertificateCfg represents srlinux certificate configuration + parameters. + properties: + cert_name: + description: Certificate name on the node. + type: string + common_name: + description: Common name to set in the cert. + type: string + key_name: + description: Key name on the node. + type: string + key_size: + description: RSA keysize to use for key generation. + format: int32 + type: integer + type: object + command: + items: type: string - key_size: - description: RSA keysize to use for key generation. - format: int32 - type: integer - type: object - command: - items: + type: array + config_data_present: + description: + When set to true by kne, srlinux controller will + attempt to mount the file with startup config to the pod + type: boolean + config_file: + description: + Startup configuration file name for the pod. Set + in the kne topo and created by kne as a config map type: string - type: array - config_data_present: - description: When set to true by kne, srlinux controller will - attempt to mount the file with startup config to the pod - type: boolean - config_file: - description: Startup configuration file name for the pod. Set - in the kne topo and created by kne as a config map - type: string - config_path: - description: Mount point for configuration inside the pod. Should - point to a dir that contains ConfigFile - type: string - entry_command: - description: Specific entry point command for accessing the pod. - type: string - env: - additionalProperties: + config_path: + description: + Mount point for configuration inside the pod. Should + point to a dir that contains ConfigFile + type: string + entry_command: + description: Specific entry point command for accessing the pod. + type: string + env: + additionalProperties: + type: string + description: Map of environment variables to pass into the pod. + type: object + image: type: string - description: Map of environment variables to pass into the pod. - type: object - image: + sleep: + format: int32 + type: integer + type: object + constraints: + additionalProperties: type: string - sleep: - format: int32 - type: integer - type: object - constraints: - additionalProperties: + type: object + model: + description: Model encodes SR Linux variant (ixr-d3, ixr-6e, etc) type: string - type: object - model: - description: Model encodes SR Linux variant (ixr-d3, ixr-6e, etc) - type: string - num-interfaces: - type: integer - version: - description: Version may be set in kne topology as a mean to explicitly - provide version information in case it is not encoded in the image - tag - type: string - type: object - status: - description: SrlinuxStatus defines the observed state of Srlinux. - properties: - image: - description: Image used to run srlinux pod - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} + num-interfaces: + type: integer + version: + description: + Version may be set in kne topology as a mean to explicitly + provide version information in case it is not encoded in the image + tag + type: string + type: object + status: + description: SrlinuxStatus defines the observed state of Srlinux. + properties: + image: + description: Image used to run srlinux pod + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} status: acceptedNames: kind: "" @@ -153,37 +162,37 @@ metadata: name: srlinux-controller-leader-election-role namespace: srlinux-controller rules: -- apiGroups: - - "" - resources: - - configmaps - verbs: - - get - - list - - watch - - create - - update - - patch - - delete -- apiGroups: - - coordination.k8s.io - resources: - - leases - verbs: - - get - - list - - watch - - create - - update - - patch - - delete -- apiGroups: - - "" - resources: - - events - verbs: - - create - - patch + - apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch + - create + - update + - patch + - delete + - apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - get + - list + - watch + - create + - update + - patch + - delete + - apiGroups: + - "" + resources: + - events + verbs: + - create + - patch --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -191,96 +200,96 @@ metadata: creationTimestamp: null name: srlinux-controller-manager-role rules: -- apiGroups: - - "" - resources: - - configmaps - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - pods - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - secrets - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - kne.srlinux.dev - resources: - - srlinuxes - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - kne.srlinux.dev - resources: - - srlinuxes/finalizers - verbs: - - update -- apiGroups: - - kne.srlinux.dev - resources: - - srlinuxes/status - verbs: - - get - - patch - - update + - apiGroups: + - "" + resources: + - configmaps + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - "" + resources: + - pods + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - "" + resources: + - secrets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - kne.srlinux.dev + resources: + - srlinuxes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - kne.srlinux.dev + resources: + - srlinuxes/finalizers + verbs: + - update + - apiGroups: + - kne.srlinux.dev + resources: + - srlinuxes/status + verbs: + - get + - patch + - update --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: srlinux-controller-metrics-reader rules: -- nonResourceURLs: - - /metrics - verbs: - - get + - nonResourceURLs: + - /metrics + verbs: + - get --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: srlinux-controller-proxy-role rules: -- apiGroups: - - authentication.k8s.io - resources: - - tokenreviews - verbs: - - create -- apiGroups: - - authorization.k8s.io - resources: - - subjectaccessreviews - verbs: - - create + - apiGroups: + - authentication.k8s.io + resources: + - tokenreviews + verbs: + - create + - apiGroups: + - authorization.k8s.io + resources: + - subjectaccessreviews + verbs: + - create --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding @@ -292,9 +301,9 @@ roleRef: kind: Role name: srlinux-controller-leader-election-role subjects: -- kind: ServiceAccount - name: srlinux-controller-controller-manager - namespace: srlinux-controller + - kind: ServiceAccount + name: srlinux-controller-controller-manager + namespace: srlinux-controller --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding @@ -305,9 +314,9 @@ roleRef: kind: ClusterRole name: srlinux-controller-manager-role subjects: -- kind: ServiceAccount - name: srlinux-controller-controller-manager - namespace: srlinux-controller + - kind: ServiceAccount + name: srlinux-controller-controller-manager + namespace: srlinux-controller --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding @@ -318,9 +327,9 @@ roleRef: kind: ClusterRole name: srlinux-controller-proxy-role subjects: -- kind: ServiceAccount - name: srlinux-controller-controller-manager - namespace: srlinux-controller + - kind: ServiceAccount + name: srlinux-controller-controller-manager + namespace: srlinux-controller --- apiVersion: v1 data: @@ -354,9 +363,9 @@ metadata: namespace: srlinux-controller spec: ports: - - name: https - port: 8443 - targetPort: https + - name: https + port: 8443 + targetPort: https selector: control-plane: controller-manager --- @@ -378,45 +387,45 @@ spec: control-plane: controller-manager spec: containers: - - args: - - --secure-listen-address=0.0.0.0:8443 - - --upstream=http://127.0.0.1:8080/ - - --logtostderr=true - - --v=10 - image: registry.k8s.io/kubebuilder/kube-rbac-proxy:v0.8.0 - name: kube-rbac-proxy - ports: - - containerPort: 8443 - name: https - - args: - - --health-probe-bind-address=:8081 - - --metrics-bind-address=127.0.0.1:8080 - - --leader-elect - command: - - /manager - image: ghcr.io/srl-labs/srl-controller:0.4.6 - livenessProbe: - httpGet: - path: /healthz - port: 8081 - initialDelaySeconds: 15 - periodSeconds: 20 - name: manager - readinessProbe: - httpGet: - path: /readyz - port: 8081 - initialDelaySeconds: 5 - periodSeconds: 10 - resources: - limits: - cpu: 100m - memory: 30Mi - requests: - cpu: 100m - memory: 20Mi - securityContext: - allowPrivilegeEscalation: false + - args: + - --secure-listen-address=0.0.0.0:8443 + - --upstream=http://127.0.0.1:8080/ + - --logtostderr=true + - --v=10 + image: registry.k8s.io/kubebuilder/kube-rbac-proxy:v0.8.0 + name: kube-rbac-proxy + ports: + - containerPort: 8443 + name: https + - args: + - --health-probe-bind-address=:8081 + - --metrics-bind-address=127.0.0.1:8080 + - --leader-elect + command: + - /manager + image: ghcr.io/srl-labs/srl-controller:0.4.6 + livenessProbe: + httpGet: + path: /healthz + port: 8081 + initialDelaySeconds: 15 + periodSeconds: 20 + name: manager + readinessProbe: + httpGet: + path: /readyz + port: 8081 + initialDelaySeconds: 5 + periodSeconds: 10 + resources: + limits: + cpu: 100m + memory: 30Mi + requests: + cpu: 100m + memory: 20Mi + securityContext: + allowPrivilegeEscalation: false securityContext: runAsNonRoot: true serviceAccountName: srlinux-controller-controller-manager diff --git a/load/testdata/manifests/keysight/ixiatg-configmap.yaml b/load/testdata/manifests/keysight/ixiatg-configmap.yaml index d66becc56..48b03c804 100644 --- a/load/testdata/manifests/keysight/ixiatg-configmap.yaml +++ b/load/testdata/manifests/keysight/ixiatg-configmap.yaml @@ -1,3 +1,4 @@ +--- apiVersion: v1 kind: ConfigMap metadata: diff --git a/load/testdata/manifests/keysight/ixiatg-operator.yaml b/load/testdata/manifests/keysight/ixiatg-operator.yaml index 004e58503..94537d4e6 100644 --- a/load/testdata/manifests/keysight/ixiatg-operator.yaml +++ b/load/testdata/manifests/keysight/ixiatg-operator.yaml @@ -1,3 +1,4 @@ +--- apiVersion: v1 kind: Namespace metadata: @@ -21,104 +22,104 @@ spec: singular: ixiatg scope: Namespaced versions: - - name: v1beta1 - schema: - openAPIV3Schema: - description: IxiaTG is the Schema for the ixiatg API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: IxiaTGSpec defines the desired state of IxiaTG - properties: - api_endpoint_map: - additionalProperties: - description: IxiaTGSvcPort defines the endpoint services for configuration and stats for the OTG node - properties: - in: - format: int32 - type: integer - out: - format: int32 - type: integer - required: - - in + - name: v1beta1 + schema: + openAPIV3Schema: + description: IxiaTG is the Schema for the ixiatg API + properties: + apiVersion: + description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources" + type: string + kind: + description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" + type: string + metadata: + type: object + spec: + description: IxiaTGSpec defines the desired state of IxiaTG + properties: + api_endpoint_map: + additionalProperties: + description: IxiaTGSvcPort defines the endpoint services for configuration and stats for the OTG node + properties: + in: + format: int32 + type: integer + out: + format: int32 + type: integer + required: + - in + type: object + description: ApiEndPoint as define in OTG config type: object - description: ApiEndPoint as define in OTG config - type: object - desired_state: - description: Desired state by network emulation (KNE) - type: string - init_container: - description: Init container image of the node - properties: - image: - type: string - sleep: - format: int32 - type: integer - type: object - interfaces: - description: Interfaces with DUT - items: - description: IxiaTGSvcPort defines the endpoint ports for network traffic for the OTG node + desired_state: + description: Desired state by network emulation (KNE) + type: string + init_container: + description: Init container image of the node properties: - group: - type: string - name: + image: type: string - required: - - name + sleep: + format: int32 + type: integer type: object - type: array - release: - description: Version of the node - type: string - type: object - status: - description: IxiaTGStatus defines the observed state of IxiaTG - properties: - api_endpoint: - description: List of OTG service names - properties: - pod_name: - type: string - service_names: - items: - type: string - type: array - type: object - interfaces: - description: List of OTG port and pod mapping - items: - description: IxiaTGIntfStatus defines the mapping between endpoint ports and encasing pods + interfaces: + description: Interfaces with DUT + items: + description: IxiaTGSvcPort defines the endpoint ports for network traffic for the OTG node + properties: + group: + type: string + name: + type: string + required: + - name + type: object + type: array + release: + description: Version of the node + type: string + type: object + status: + description: IxiaTGStatus defines the observed state of IxiaTG + properties: + api_endpoint: + description: List of OTG service names properties: - interface: - type: string - name: - type: string pod_name: type: string + service_names: + items: + type: string + type: array type: object - type: array - reason: - description: Reason in case of failure - type: string - state: - description: Observed state - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} + interfaces: + description: List of OTG port and pod mapping + items: + description: IxiaTGIntfStatus defines the mapping between endpoint ports and encasing pods + properties: + interface: + type: string + name: + type: string + pod_name: + type: string + type: object + type: array + reason: + description: Reason in case of failure + type: string + state: + description: Observed state + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} status: acceptedNames: kind: "" @@ -138,37 +139,37 @@ metadata: name: ixiatg-op-leader-election-role namespace: ixiatg-op-system rules: -- apiGroups: - - "" - resources: - - configmaps - verbs: - - get - - list - - watch - - create - - update - - patch - - delete -- apiGroups: - - coordination.k8s.io - resources: - - leases - verbs: - - get - - list - - watch - - create - - update - - patch - - delete -- apiGroups: - - "" - resources: - - events - verbs: - - create - - patch + - apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch + - create + - update + - patch + - delete + - apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - get + - list + - watch + - create + - update + - patch + - delete + - apiGroups: + - "" + resources: + - events + verbs: + - create + - patch --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -176,108 +177,108 @@ metadata: creationTimestamp: null name: ixiatg-op-manager-role rules: -- apiGroups: - - "" - resources: - - configmaps - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - pods - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - secrets - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - services - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - network.keysight.com - resources: - - ixiatgs - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - network.keysight.com - resources: - - ixiatgs/finalizers - verbs: - - update -- apiGroups: - - network.keysight.com - resources: - - ixiatgs/status - verbs: - - get - - patch - - update + - apiGroups: + - "" + resources: + - configmaps + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - "" + resources: + - pods + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - "" + resources: + - secrets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - "" + resources: + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - network.keysight.com + resources: + - ixiatgs + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - network.keysight.com + resources: + - ixiatgs/finalizers + verbs: + - update + - apiGroups: + - network.keysight.com + resources: + - ixiatgs/status + verbs: + - get + - patch + - update --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: ixiatg-op-metrics-reader rules: -- nonResourceURLs: - - /metrics - verbs: - - get + - nonResourceURLs: + - /metrics + verbs: + - get --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: ixiatg-op-proxy-role rules: -- apiGroups: - - authentication.k8s.io - resources: - - tokenreviews - verbs: - - create -- apiGroups: - - authorization.k8s.io - resources: - - subjectaccessreviews - verbs: - - create + - apiGroups: + - authentication.k8s.io + resources: + - tokenreviews + verbs: + - create + - apiGroups: + - authorization.k8s.io + resources: + - subjectaccessreviews + verbs: + - create --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding @@ -289,9 +290,9 @@ roleRef: kind: Role name: ixiatg-op-leader-election-role subjects: -- kind: ServiceAccount - name: ixiatg-op-controller-manager - namespace: ixiatg-op-system + - kind: ServiceAccount + name: ixiatg-op-controller-manager + namespace: ixiatg-op-system --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding @@ -302,9 +303,9 @@ roleRef: kind: ClusterRole name: ixiatg-op-manager-role subjects: -- kind: ServiceAccount - name: ixiatg-op-controller-manager - namespace: ixiatg-op-system + - kind: ServiceAccount + name: ixiatg-op-controller-manager + namespace: ixiatg-op-system --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding @@ -315,9 +316,9 @@ roleRef: kind: ClusterRole name: ixiatg-op-proxy-role subjects: -- kind: ServiceAccount - name: ixiatg-op-controller-manager - namespace: ixiatg-op-system + - kind: ServiceAccount + name: ixiatg-op-controller-manager + namespace: ixiatg-op-system --- apiVersion: v1 data: @@ -347,9 +348,9 @@ metadata: namespace: ixiatg-op-system spec: ports: - - name: https - port: 8443 - targetPort: https + - name: https + port: 8443 + targetPort: https selector: control-plane: controller-manager --- @@ -371,47 +372,47 @@ spec: control-plane: controller-manager spec: containers: - - args: - - --secure-listen-address=0.0.0.0:8443 - - --upstream=http://127.0.0.1:8080/ - - --logtostderr=true - - --v=10 - image: registry.k8s.io/kubebuilder/kube-rbac-proxy:v0.8.0 - name: kube-rbac-proxy - ports: - - containerPort: 8443 - name: https - - args: - - --health-probe-bind-address=:8081 - - --metrics-bind-address=127.0.0.1:8080 - - --leader-elect - command: - - /manager - image: ghcr.io/open-traffic-generator/keng-operator:0.3.13 - livenessProbe: - httpGet: - path: /healthz - port: 8081 - initialDelaySeconds: 15 - periodSeconds: 20 - name: manager - readinessProbe: - httpGet: - path: /readyz - port: 8081 - initialDelaySeconds: 5 - periodSeconds: 10 - resources: - limits: - cpu: 100m - memory: 200Mi - requests: - cpu: 100m - memory: 20Mi - securityContext: - allowPrivilegeEscalation: false + - args: + - --secure-listen-address=0.0.0.0:8443 + - --upstream=http://127.0.0.1:8080/ + - --logtostderr=true + - --v=10 + image: registry.k8s.io/kubebuilder/kube-rbac-proxy:v0.8.0 + name: kube-rbac-proxy + ports: + - containerPort: 8443 + name: https + - args: + - --health-probe-bind-address=:8081 + - --metrics-bind-address=127.0.0.1:8080 + - --leader-elect + command: + - /manager + image: ghcr.io/open-traffic-generator/keng-operator:0.3.13 + livenessProbe: + httpGet: + path: /healthz + port: 8081 + initialDelaySeconds: 15 + periodSeconds: 20 + name: manager + readinessProbe: + httpGet: + path: /readyz + port: 8081 + initialDelaySeconds: 5 + periodSeconds: 10 + resources: + limits: + cpu: 100m + memory: 200Mi + requests: + cpu: 100m + memory: 20Mi + securityContext: + allowPrivilegeEscalation: false imagePullSecrets: - - name: ixia-pull-secret + - name: ixia-pull-secret securityContext: runAsNonRoot: true serviceAccountName: ixiatg-op-controller-manager diff --git a/load/testdata/manifests/kind/kind-bridge.yaml b/load/testdata/manifests/kind/kind-bridge.yaml index 83d9c92f9..2b9b7246a 100644 --- a/load/testdata/manifests/kind/kind-bridge.yaml +++ b/load/testdata/manifests/kind/kind-bridge.yaml @@ -13,7 +13,7 @@ rules: - watch - patch - apiGroups: - - "" + - "" resources: - configmaps verbs: @@ -28,9 +28,9 @@ roleRef: kind: ClusterRole name: kindnet subjects: -- kind: ServiceAccount - name: kindnet - namespace: kube-system + - kind: ServiceAccount + name: kindnet + namespace: kube-system --- apiVersion: v1 kind: ServiceAccount @@ -60,66 +60,71 @@ spec: spec: hostNetwork: true tolerations: - - operator: Exists - effect: NoSchedule + - operator: Exists + effect: NoSchedule serviceAccountName: kindnet initContainers: - - name: install-cni-bin - image: ghcr.io/aojea/kindnetd:v1.1.0 - command: ['sh', '-c', 'cd /opt/cni/bin; for i in * ; do cat $i > /cni/$i ; chmod +x /cni/$i ; done'] - volumeMounts: - - name: cni-bin - mountPath: /cni + - name: install-cni-bin + image: ghcr.io/aojea/kindnetd:v1.1.0 + command: + [ + "sh", + "-c", + "cd /opt/cni/bin; for i in * ; do cat $i > /cni/$i ; chmod +x /cni/$i ; done", + ] + volumeMounts: + - name: cni-bin + mountPath: /cni containers: - - name: kindnet-cni - image: ghcr.io/aojea/kindnetd:v1.1.0 - env: - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: POD_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: CNI_BRIDGE - value: "true" - - name: DISABLE_CNI_BRIDGE_OFFLOAD - value: "true" - volumeMounts: + - name: kindnet-cni + image: ghcr.io/aojea/kindnetd:v1.1.0 + env: + - name: HOST_IP + valueFrom: + fieldRef: + fieldPath: status.hostIP + - name: POD_IP + valueFrom: + fieldRef: + fieldPath: status.podIP + - name: CNI_BRIDGE + value: "true" + - name: DISABLE_CNI_BRIDGE_OFFLOAD + value: "true" + volumeMounts: + - name: cni-cfg + mountPath: /etc/cni/net.d + - name: xtables-lock + mountPath: /run/xtables.lock + readOnly: false + - name: lib-modules + mountPath: /lib/modules + readOnly: true + resources: + requests: + cpu: "100m" + memory: "50Mi" + limits: + cpu: "100m" + memory: "50Mi" + securityContext: + privileged: false + capabilities: + add: ["NET_RAW", "NET_ADMIN"] + volumes: + - name: cni-bin + hostPath: + path: /opt/cni/bin + type: DirectoryOrCreate - name: cni-cfg - mountPath: /etc/cni/net.d + hostPath: + path: /etc/cni/net.d + type: DirectoryOrCreate - name: xtables-lock - mountPath: /run/xtables.lock - readOnly: false + hostPath: + path: /run/xtables.lock + type: FileOrCreate - name: lib-modules - mountPath: /lib/modules - readOnly: true - resources: - requests: - cpu: "100m" - memory: "50Mi" - limits: - cpu: "100m" - memory: "50Mi" - securityContext: - privileged: false - capabilities: - add: ["NET_RAW", "NET_ADMIN"] - volumes: - - name: cni-bin - hostPath: - path: /opt/cni/bin - type: DirectoryOrCreate - - name: cni-cfg - hostPath: - path: /etc/cni/net.d - type: DirectoryOrCreate - - name: xtables-lock - hostPath: - path: /run/xtables.lock - type: FileOrCreate - - name: lib-modules - hostPath: - path: /lib/modules + hostPath: + path: /lib/modules --- diff --git a/load/testdata/manifests/meshnet/grpc/manifest.yaml b/load/testdata/manifests/meshnet/grpc/manifest.yaml index c3a3e3f33..b2b75db48 100644 --- a/load/testdata/manifests/meshnet/grpc/manifest.yaml +++ b/load/testdata/manifests/meshnet/grpc/manifest.yaml @@ -1,3 +1,4 @@ +--- apiVersion: v1 kind: Namespace metadata: @@ -17,66 +18,66 @@ spec: kind: Topology plural: topologies shortNames: - - topo + - topo singular: topology scope: Namespaced versions: - - name: v1beta1 - schema: - openAPIV3Schema: - properties: - spec: - properties: - links: - items: - description: A complete definition of a p2p link - properties: - local_intf: - description: Local interface name - type: string - local_ip: - description: (Optional) Local IP address - type: string - peer_intf: - description: Peer interface name - type: string - peer_ip: - description: (Optional) Peer IP address - type: string - peer_pod: - description: Name of the peer pod - type: string - uid: - description: Unique identified of a p2p link - type: integer - required: - - uid - - peer_pod - - local_intf - - peer_intf - type: object - type: array - type: object - status: - properties: - net_ns: - description: Network namespace of the POD - type: string - skipped: - description: List of pods that are skipped by local pod - items: - description: peer pod name + - name: v1beta1 + schema: + openAPIV3Schema: + properties: + spec: + properties: + links: + items: + description: A complete definition of a p2p link + properties: + local_intf: + description: Local interface name + type: string + local_ip: + description: (Optional) Local IP address + type: string + peer_intf: + description: Peer interface name + type: string + peer_ip: + description: (Optional) Peer IP address + type: string + peer_pod: + description: Name of the peer pod + type: string + uid: + description: Unique identified of a p2p link + type: integer + required: + - uid + - peer_pod + - local_intf + - peer_intf + type: object + type: array + type: object + status: + properties: + net_ns: + description: Network namespace of the POD + type: string + skipped: + description: List of pods that are skipped by local pod + items: + description: peer pod name + type: string + type: array + src_ip: + description: Source IP of the POD type: string - type: array - src_ip: - description: Source IP of the POD - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} + type: object + type: object + served: true + storage: true + subresources: + status: {} status: acceptedNames: kind: "" @@ -99,18 +100,18 @@ metadata: app: meshnet name: meshnet-clusterrole rules: -- apiGroups: - - networkop.co.uk - resources: - - topologies - verbs: - - '*' -- apiGroups: - - networkop.co.uk - resources: - - topologies/status - verbs: - - '*' + - apiGroups: + - networkop.co.uk + resources: + - topologies + verbs: + - "*" + - apiGroups: + - networkop.co.uk + resources: + - topologies/status + verbs: + - "*" --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding @@ -123,9 +124,9 @@ roleRef: kind: ClusterRole name: meshnet-clusterrole subjects: -- kind: ServiceAccount - name: meshnet - namespace: meshnet + - kind: ServiceAccount + name: meshnet + namespace: meshnet --- apiVersion: apps/v1 kind: DaemonSet @@ -147,32 +148,32 @@ spec: name: meshnet spec: containers: - - env: - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: INTER_NODE_LINK_TYPE - value: GRPC - image: us-west1-docker.pkg.dev/kne-external/kne/networkop/meshnet:v0.3.1 - imagePullPolicy: IfNotPresent - name: meshnet - resources: - limits: - memory: 1000Mi - requests: - cpu: 100m - memory: 1000Mi - securityContext: - privileged: true - volumeMounts: - - mountPath: /etc/cni/net.d - name: cni-cfg - - mountPath: /opt/cni/bin - name: cni-bin - - mountPath: /var/run/netns - mountPropagation: Bidirectional - name: var-run-netns + - env: + - name: HOST_IP + valueFrom: + fieldRef: + fieldPath: status.hostIP + - name: INTER_NODE_LINK_TYPE + value: GRPC + image: us-west1-docker.pkg.dev/kne-external/kne/networkop/meshnet:v0.3.1 + imagePullPolicy: IfNotPresent + name: meshnet + resources: + limits: + memory: 1000Mi + requests: + cpu: 100m + memory: 1000Mi + securityContext: + privileged: true + volumeMounts: + - mountPath: /etc/cni/net.d + name: cni-cfg + - mountPath: /opt/cni/bin + name: cni-bin + - mountPath: /var/run/netns + mountPropagation: Bidirectional + name: var-run-netns hostIPC: true hostNetwork: true hostPID: true @@ -181,15 +182,15 @@ spec: serviceAccountName: meshnet terminationGracePeriodSeconds: 30 tolerations: - - effect: NoSchedule - operator: Exists + - effect: NoSchedule + operator: Exists volumes: - - hostPath: - path: /opt/cni/bin - name: cni-bin - - hostPath: - path: /etc/cni/net.d - name: cni-cfg - - hostPath: - path: /var/run/netns - name: var-run-netns + - hostPath: + path: /opt/cni/bin + name: cni-bin + - hostPath: + path: /etc/cni/net.d + name: cni-cfg + - hostPath: + path: /var/run/netns + name: var-run-netns diff --git a/load/testdata/manifests/meshnet/vxlan/manifest.yaml b/load/testdata/manifests/meshnet/vxlan/manifest.yaml index 47ae67f1c..5c05eb71c 100644 --- a/load/testdata/manifests/meshnet/vxlan/manifest.yaml +++ b/load/testdata/manifests/meshnet/vxlan/manifest.yaml @@ -1,3 +1,4 @@ +--- apiVersion: v1 kind: Namespace metadata: @@ -17,66 +18,66 @@ spec: kind: Topology plural: topologies shortNames: - - topo + - topo singular: topology scope: Namespaced versions: - - name: v1beta1 - schema: - openAPIV3Schema: - properties: - spec: - properties: - links: - items: - description: A complete definition of a p2p link - properties: - local_intf: - description: Local interface name - type: string - local_ip: - description: (Optional) Local IP address - type: string - peer_intf: - description: Peer interface name - type: string - peer_ip: - description: (Optional) Peer IP address - type: string - peer_pod: - description: Name of the peer pod - type: string - uid: - description: Unique identified of a p2p link - type: integer - required: - - uid - - peer_pod - - local_intf - - peer_intf - type: object - type: array - type: object - status: - properties: - net_ns: - description: Network namespace of the POD - type: string - skipped: - description: List of pods that are skipped by local pod - items: - description: peer pod name + - name: v1beta1 + schema: + openAPIV3Schema: + properties: + spec: + properties: + links: + items: + description: A complete definition of a p2p link + properties: + local_intf: + description: Local interface name + type: string + local_ip: + description: (Optional) Local IP address + type: string + peer_intf: + description: Peer interface name + type: string + peer_ip: + description: (Optional) Peer IP address + type: string + peer_pod: + description: Name of the peer pod + type: string + uid: + description: Unique identified of a p2p link + type: integer + required: + - uid + - peer_pod + - local_intf + - peer_intf + type: object + type: array + type: object + status: + properties: + net_ns: + description: Network namespace of the POD + type: string + skipped: + description: List of pods that are skipped by local pod + items: + description: peer pod name + type: string + type: array + src_ip: + description: Source IP of the POD type: string - type: array - src_ip: - description: Source IP of the POD - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} + type: object + type: object + served: true + storage: true + subresources: + status: {} status: acceptedNames: kind: "" @@ -99,18 +100,18 @@ metadata: app: meshnet name: meshnet-clusterrole rules: -- apiGroups: - - networkop.co.uk - resources: - - topologies - verbs: - - '*' -- apiGroups: - - networkop.co.uk - resources: - - topologies/status - verbs: - - '*' + - apiGroups: + - networkop.co.uk + resources: + - topologies + verbs: + - "*" + - apiGroups: + - networkop.co.uk + resources: + - topologies/status + verbs: + - "*" --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding @@ -123,9 +124,9 @@ roleRef: kind: ClusterRole name: meshnet-clusterrole subjects: -- kind: ServiceAccount - name: meshnet - namespace: meshnet + - kind: ServiceAccount + name: meshnet + namespace: meshnet --- apiVersion: apps/v1 kind: DaemonSet @@ -147,32 +148,32 @@ spec: name: meshnet spec: containers: - - env: - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: INTER_NODE_LINK_TYPE - value: VXLAN - image: us-west1-docker.pkg.dev/kne-external/kne/networkop/meshnet:v0.3.1 - imagePullPolicy: IfNotPresent - name: meshnet - resources: - limits: - memory: 200Mi - requests: - cpu: 100m - memory: 200Mi - securityContext: - privileged: true - volumeMounts: - - mountPath: /etc/cni/net.d - name: cni-cfg - - mountPath: /opt/cni/bin - name: cni-bin - - mountPath: /var/run/netns - mountPropagation: Bidirectional - name: var-run-netns + - env: + - name: HOST_IP + valueFrom: + fieldRef: + fieldPath: status.hostIP + - name: INTER_NODE_LINK_TYPE + value: VXLAN + image: us-west1-docker.pkg.dev/kne-external/kne/networkop/meshnet:v0.3.1 + imagePullPolicy: IfNotPresent + name: meshnet + resources: + limits: + memory: 200Mi + requests: + cpu: 100m + memory: 200Mi + securityContext: + privileged: true + volumeMounts: + - mountPath: /etc/cni/net.d + name: cni-cfg + - mountPath: /opt/cni/bin + name: cni-bin + - mountPath: /var/run/netns + mountPropagation: Bidirectional + name: var-run-netns hostIPC: true hostNetwork: true hostPID: true @@ -181,15 +182,15 @@ spec: serviceAccountName: meshnet terminationGracePeriodSeconds: 30 tolerations: - - effect: NoSchedule - operator: Exists + - effect: NoSchedule + operator: Exists volumes: - - hostPath: - path: /opt/cni/bin - name: cni-bin - - hostPath: - path: /etc/cni/net.d - name: cni-cfg - - hostPath: - path: /var/run/netns - name: var-run-netns + - hostPath: + path: /opt/cni/bin + name: cni-bin + - hostPath: + path: /etc/cni/net.d + name: cni-cfg + - hostPath: + path: /var/run/netns + name: var-run-netns diff --git a/load/testdata/manifests/metallb/manifest.yaml b/load/testdata/manifests/metallb/manifest.yaml index 0f41d0c11..138a9526d 100644 --- a/load/testdata/manifests/metallb/manifest.yaml +++ b/load/testdata/manifests/metallb/manifest.yaml @@ -1,3 +1,4 @@ +--- apiVersion: v1 kind: Namespace metadata: @@ -24,8 +25,8 @@ spec: namespace: metallb-system path: /convert conversionReviewVersions: - - v1alpha1 - - v1beta1 + - v1alpha1 + - v1beta1 group: metallb.io names: kind: AddressPool @@ -34,184 +35,205 @@ spec: singular: addresspool scope: Namespaced versions: - - deprecated: true - deprecationWarning: metallb.io v1alpha1 AddressPool is deprecated - name: v1alpha1 - schema: - openAPIV3Schema: - description: AddressPool is the Schema for the addresspools API. - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: AddressPoolSpec defines the desired state of AddressPool. - properties: - addresses: - description: A list of IP address ranges over which MetalLB has authority. - You can list multiple ranges in a single pool, they will all share - the same settings. Each range can be either a CIDR prefix, or an - explicit start-end range of IPs. - items: + - deprecated: true + deprecationWarning: metallb.io v1alpha1 AddressPool is deprecated + name: v1alpha1 + schema: + openAPIV3Schema: + description: AddressPool is the Schema for the addresspools API. + properties: + apiVersion: + description: + "APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources" + type: string + kind: + description: + "Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" + type: string + metadata: + type: object + spec: + description: AddressPoolSpec defines the desired state of AddressPool. + properties: + addresses: + description: + A list of IP address ranges over which MetalLB has authority. + You can list multiple ranges in a single pool, they will all share + the same settings. Each range can be either a CIDR prefix, or an + explicit start-end range of IPs. + items: + type: string + type: array + autoAssign: + default: true + description: + AutoAssign flag used to prevent MetallB from automatic + allocation for a pool. + type: boolean + bgpAdvertisements: + description: + When an IP is allocated from this pool, how should it + be translated into BGP announcements? + items: + properties: + aggregationLength: + default: 32 + description: + The aggregation-length advertisement option lets + you “roll up” the /32s into a larger prefix. + format: int32 + minimum: 1 + type: integer + aggregationLengthV6: + default: 128 + description: + Optional, defaults to 128 (i.e. no aggregation) + if not specified. + format: int32 + type: integer + communities: + description: BGP communities + items: + type: string + type: array + localPref: + description: + BGP LOCAL_PREF attribute which is used by BGP best + path algorithm, Path with higher localpref is preferred over + one with lower localpref. + format: int32 + type: integer + type: object + type: array + protocol: + description: + Protocol can be used to select how the announcement is + done. + enum: + - layer2 + - bgp type: string - type: array - autoAssign: - default: true - description: AutoAssign flag used to prevent MetallB from automatic - allocation for a pool. - type: boolean - bgpAdvertisements: - description: When an IP is allocated from this pool, how should it - be translated into BGP announcements? - items: - properties: - aggregationLength: - default: 32 - description: The aggregation-length advertisement option lets - you “roll up” the /32s into a larger prefix. - format: int32 - minimum: 1 - type: integer - aggregationLengthV6: - default: 128 - description: Optional, defaults to 128 (i.e. no aggregation) - if not specified. - format: int32 - type: integer - communities: - description: BGP communities - items: - type: string - type: array - localPref: - description: BGP LOCAL_PREF attribute which is used by BGP best - path algorithm, Path with higher localpref is preferred over - one with lower localpref. - format: int32 - type: integer - type: object - type: array - protocol: - description: Protocol can be used to select how the announcement is - done. - enum: - - layer2 - - bgp - type: string - required: - - addresses - - protocol - type: object - status: - description: AddressPoolStatus defines the observed state of AddressPool. - type: object - required: - - spec - type: object - served: true - storage: false - subresources: - status: {} - - deprecated: true - deprecationWarning: metallb.io v1beta1 AddressPool is deprecated, consider using - IPAddressPool - name: v1beta1 - schema: - openAPIV3Schema: - description: AddressPool represents a pool of IP addresses that can be allocated - to LoadBalancer services. AddressPool is deprecated and being replaced by - IPAddressPool. - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: AddressPoolSpec defines the desired state of AddressPool. - properties: - addresses: - description: A list of IP address ranges over which MetalLB has authority. - You can list multiple ranges in a single pool, they will all share - the same settings. Each range can be either a CIDR prefix, or an - explicit start-end range of IPs. - items: + required: + - addresses + - protocol + type: object + status: + description: AddressPoolStatus defines the observed state of AddressPool. + type: object + required: + - spec + type: object + served: true + storage: false + subresources: + status: {} + - deprecated: true + deprecationWarning: + metallb.io v1beta1 AddressPool is deprecated, consider using + IPAddressPool + name: v1beta1 + schema: + openAPIV3Schema: + description: + AddressPool represents a pool of IP addresses that can be allocated + to LoadBalancer services. AddressPool is deprecated and being replaced by + IPAddressPool. + properties: + apiVersion: + description: + "APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources" + type: string + kind: + description: + "Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" + type: string + metadata: + type: object + spec: + description: AddressPoolSpec defines the desired state of AddressPool. + properties: + addresses: + description: + A list of IP address ranges over which MetalLB has authority. + You can list multiple ranges in a single pool, they will all share + the same settings. Each range can be either a CIDR prefix, or an + explicit start-end range of IPs. + items: + type: string + type: array + autoAssign: + default: true + description: + AutoAssign flag used to prevent MetallB from automatic + allocation for a pool. + type: boolean + bgpAdvertisements: + description: + Drives how an IP allocated from this pool should translated + into BGP announcements. + items: + properties: + aggregationLength: + default: 32 + description: + The aggregation-length advertisement option lets + you “roll up” the /32s into a larger prefix. + format: int32 + minimum: 1 + type: integer + aggregationLengthV6: + default: 128 + description: + Optional, defaults to 128 (i.e. no aggregation) + if not specified. + format: int32 + type: integer + communities: + description: + BGP communities to be associated with the given + advertisement. + items: + type: string + type: array + localPref: + description: + BGP LOCAL_PREF attribute which is used by BGP best + path algorithm, Path with higher localpref is preferred over + one with lower localpref. + format: int32 + type: integer + type: object + type: array + protocol: + description: + Protocol can be used to select how the announcement is + done. + enum: + - layer2 + - bgp type: string - type: array - autoAssign: - default: true - description: AutoAssign flag used to prevent MetallB from automatic - allocation for a pool. - type: boolean - bgpAdvertisements: - description: Drives how an IP allocated from this pool should translated - into BGP announcements. - items: - properties: - aggregationLength: - default: 32 - description: The aggregation-length advertisement option lets - you “roll up” the /32s into a larger prefix. - format: int32 - minimum: 1 - type: integer - aggregationLengthV6: - default: 128 - description: Optional, defaults to 128 (i.e. no aggregation) - if not specified. - format: int32 - type: integer - communities: - description: BGP communities to be associated with the given - advertisement. - items: - type: string - type: array - localPref: - description: BGP LOCAL_PREF attribute which is used by BGP best - path algorithm, Path with higher localpref is preferred over - one with lower localpref. - format: int32 - type: integer - type: object - type: array - protocol: - description: Protocol can be used to select how the announcement is - done. - enum: - - layer2 - - bgp - type: string - required: - - addresses - - protocol - type: object - status: - description: AddressPoolStatus defines the observed state of AddressPool. - type: object - required: - - spec - type: object - served: true - storage: true - subresources: - status: {} + required: + - addresses + - protocol + type: object + status: + description: AddressPoolStatus defines the observed state of AddressPool. + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} status: acceptedNames: kind: "" @@ -235,83 +257,93 @@ spec: singular: bfdprofile scope: Namespaced versions: - - name: v1beta1 - schema: - openAPIV3Schema: - description: BFDProfile represents the settings of the bfd session that can - be optionally associated with a BGP session. - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: BFDProfileSpec defines the desired state of BFDProfile. - properties: - detectMultiplier: - description: Configures the detection multiplier to determine packet - loss. The remote transmission interval will be multiplied by this - value to determine the connection loss detection timer. - format: int32 - maximum: 255 - minimum: 2 - type: integer - echoInterval: - description: Configures the minimal echo receive transmission interval - that this system is capable of handling in milliseconds. Defaults - to 50ms - format: int32 - maximum: 60000 - minimum: 10 - type: integer - echoMode: - description: Enables or disables the echo transmission mode. This - mode is disabled by default, and not supported on multi hops setups. - type: boolean - minimumTtl: - description: 'For multi hop sessions only: configure the minimum expected - TTL for an incoming BFD control packet.' - format: int32 - maximum: 254 - minimum: 1 - type: integer - passiveMode: - description: 'Mark session as passive: a passive session will not - attempt to start the connection and will wait for control packets - from peer before it begins replying.' - type: boolean - receiveInterval: - description: The minimum interval that this system is capable of receiving - control packets in milliseconds. Defaults to 300ms. - format: int32 - maximum: 60000 - minimum: 10 - type: integer - transmitInterval: - description: The minimum transmission interval (less jitter) that - this system wants to use to send BFD control packets in milliseconds. - Defaults to 300ms - format: int32 - maximum: 60000 - minimum: 10 - type: integer - type: object - status: - description: BFDProfileStatus defines the observed state of BFDProfile. - type: object - type: object - served: true - storage: true - subresources: - status: {} + - name: v1beta1 + schema: + openAPIV3Schema: + description: + BFDProfile represents the settings of the bfd session that can + be optionally associated with a BGP session. + properties: + apiVersion: + description: + "APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources" + type: string + kind: + description: + "Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" + type: string + metadata: + type: object + spec: + description: BFDProfileSpec defines the desired state of BFDProfile. + properties: + detectMultiplier: + description: + Configures the detection multiplier to determine packet + loss. The remote transmission interval will be multiplied by this + value to determine the connection loss detection timer. + format: int32 + maximum: 255 + minimum: 2 + type: integer + echoInterval: + description: + Configures the minimal echo receive transmission interval + that this system is capable of handling in milliseconds. Defaults + to 50ms + format: int32 + maximum: 60000 + minimum: 10 + type: integer + echoMode: + description: + Enables or disables the echo transmission mode. This + mode is disabled by default, and not supported on multi hops setups. + type: boolean + minimumTtl: + description: + "For multi hop sessions only: configure the minimum expected + TTL for an incoming BFD control packet." + format: int32 + maximum: 254 + minimum: 1 + type: integer + passiveMode: + description: + "Mark session as passive: a passive session will not + attempt to start the connection and will wait for control packets + from peer before it begins replying." + type: boolean + receiveInterval: + description: + The minimum interval that this system is capable of receiving + control packets in milliseconds. Defaults to 300ms. + format: int32 + maximum: 60000 + minimum: 10 + type: integer + transmitInterval: + description: + The minimum transmission interval (less jitter) that + this system wants to use to send BFD control packets in milliseconds. + Defaults to 300ms + format: int32 + maximum: 60000 + minimum: 10 + type: integer + type: object + status: + description: BFDProfileStatus defines the observed state of BFDProfile. + type: object + type: object + served: true + storage: true + subresources: + status: {} status: acceptedNames: kind: "" @@ -335,181 +367,206 @@ spec: singular: bgpadvertisement scope: Namespaced versions: - - name: v1beta1 - schema: - openAPIV3Schema: - description: BGPAdvertisement allows to advertise the IPs coming from the - selected IPAddressPools via BGP, setting the parameters of the BGP Advertisement. - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: BGPAdvertisementSpec defines the desired state of BGPAdvertisement. - properties: - aggregationLength: - default: 32 - description: The aggregation-length advertisement option lets you - “roll up” the /32s into a larger prefix. Defaults to 32. Works for - IPv4 addresses. - format: int32 - minimum: 1 - type: integer - aggregationLengthV6: - default: 128 - description: The aggregation-length advertisement option lets you - “roll up” the /128s into a larger prefix. Defaults to 128. Works - for IPv6 addresses. - format: int32 - type: integer - communities: - description: The BGP communities to be associated with the announcement. - Each item can be a community of the form 1234:1234 or the name of - an alias defined in the Community CRD. - items: - type: string - type: array - ipAddressPoolSelectors: - description: A selector for the IPAddressPools which would get advertised - via this advertisement. If no IPAddressPool is selected by this - or by the list, the advertisement is applied to all the IPAddressPools. - items: - description: A label selector is a label query over a set of resources. - The result of matchLabels and matchExpressions are ANDed. An empty - label selector matches all objects. A null label selector matches - no objects. - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. - The requirements are ANDed. - items: - description: A label selector requirement is a selector that - contains values, a key, and an operator that relates the - key and values. - properties: - key: - description: key is the label key that the selector applies - to. - type: string - operator: - description: operator represents a key's relationship - to a set of values. Valid operators are In, NotIn, Exists - and DoesNotExist. - type: string - values: - description: values is an array of string values. If the - operator is In or NotIn, the values array must be non-empty. - If the operator is Exists or DoesNotExist, the values - array must be empty. This array is replaced during a - strategic merge patch. - items: + - name: v1beta1 + schema: + openAPIV3Schema: + description: + BGPAdvertisement allows to advertise the IPs coming from the + selected IPAddressPools via BGP, setting the parameters of the BGP Advertisement. + properties: + apiVersion: + description: + "APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources" + type: string + kind: + description: + "Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" + type: string + metadata: + type: object + spec: + description: BGPAdvertisementSpec defines the desired state of BGPAdvertisement. + properties: + aggregationLength: + default: 32 + description: + The aggregation-length advertisement option lets you + “roll up” the /32s into a larger prefix. Defaults to 32. Works for + IPv4 addresses. + format: int32 + minimum: 1 + type: integer + aggregationLengthV6: + default: 128 + description: + The aggregation-length advertisement option lets you + “roll up” the /128s into a larger prefix. Defaults to 128. Works + for IPv6 addresses. + format: int32 + type: integer + communities: + description: + The BGP communities to be associated with the announcement. + Each item can be a community of the form 1234:1234 or the name of + an alias defined in the Community CRD. + items: + type: string + type: array + ipAddressPoolSelectors: + description: + A selector for the IPAddressPools which would get advertised + via this advertisement. If no IPAddressPool is selected by this + or by the list, the advertisement is applied to all the IPAddressPools. + items: + description: + A label selector is a label query over a set of resources. + The result of matchLabels and matchExpressions are ANDed. An empty + label selector matches all objects. A null label selector matches + no objects. + properties: + matchExpressions: + description: + matchExpressions is a list of label selector requirements. + The requirements are ANDed. + items: + description: + A label selector requirement is a selector that + contains values, a key, and an operator that relates the + key and values. + properties: + key: + description: + key is the label key that the selector applies + to. + type: string + operator: + description: + operator represents a key's relationship + to a set of values. Valid operators are In, NotIn, Exists + and DoesNotExist. type: string - type: array - required: - - key - - operator + values: + description: + values is an array of string values. If the + operator is In or NotIn, the values array must be non-empty. + If the operator is Exists or DoesNotExist, the values + array must be empty. This array is replaced during a + strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: + matchLabels is a map of {key,value} pairs. A single + {key,value} in the matchLabels map is equivalent to an element + of matchExpressions, whose key field is "key", the operator + is "In", and the values array contains only "value". The requirements + are ANDed. type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} pairs. A single - {key,value} in the matchLabels map is equivalent to an element - of matchExpressions, whose key field is "key", the operator - is "In", and the values array contains only "value". The requirements - are ANDed. - type: object - type: object - type: array - ipAddressPools: - description: The list of IPAddressPools to advertise via this advertisement, - selected by name. - items: - type: string - type: array - localPref: - description: The BGP LOCAL_PREF attribute which is used by BGP best - path algorithm, Path with higher localpref is preferred over one - with lower localpref. - format: int32 - type: integer - nodeSelectors: - description: NodeSelectors allows to limit the nodes to announce as - next hops for the LoadBalancer IP. When empty, all the nodes having are - announced as next hops. - items: - description: A label selector is a label query over a set of resources. - The result of matchLabels and matchExpressions are ANDed. An empty - label selector matches all objects. A null label selector matches - no objects. - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. - The requirements are ANDed. - items: - description: A label selector requirement is a selector that - contains values, a key, and an operator that relates the - key and values. - properties: - key: - description: key is the label key that the selector applies - to. - type: string - operator: - description: operator represents a key's relationship - to a set of values. Valid operators are In, NotIn, Exists - and DoesNotExist. - type: string - values: - description: values is an array of string values. If the - operator is In or NotIn, the values array must be non-empty. - If the operator is Exists or DoesNotExist, the values - array must be empty. This array is replaced during a - strategic merge patch. - items: + type: object + type: array + ipAddressPools: + description: + The list of IPAddressPools to advertise via this advertisement, + selected by name. + items: + type: string + type: array + localPref: + description: + The BGP LOCAL_PREF attribute which is used by BGP best + path algorithm, Path with higher localpref is preferred over one + with lower localpref. + format: int32 + type: integer + nodeSelectors: + description: + NodeSelectors allows to limit the nodes to announce as + next hops for the LoadBalancer IP. When empty, all the nodes having are + announced as next hops. + items: + description: + A label selector is a label query over a set of resources. + The result of matchLabels and matchExpressions are ANDed. An empty + label selector matches all objects. A null label selector matches + no objects. + properties: + matchExpressions: + description: + matchExpressions is a list of label selector requirements. + The requirements are ANDed. + items: + description: + A label selector requirement is a selector that + contains values, a key, and an operator that relates the + key and values. + properties: + key: + description: + key is the label key that the selector applies + to. + type: string + operator: + description: + operator represents a key's relationship + to a set of values. Valid operators are In, NotIn, Exists + and DoesNotExist. type: string - type: array - required: - - key - - operator + values: + description: + values is an array of string values. If the + operator is In or NotIn, the values array must be non-empty. + If the operator is Exists or DoesNotExist, the values + array must be empty. This array is replaced during a + strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: + matchLabels is a map of {key,value} pairs. A single + {key,value} in the matchLabels map is equivalent to an element + of matchExpressions, whose key field is "key", the operator + is "In", and the values array contains only "value". The requirements + are ANDed. type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} pairs. A single - {key,value} in the matchLabels map is equivalent to an element - of matchExpressions, whose key field is "key", the operator - is "In", and the values array contains only "value". The requirements - are ANDed. - type: object - type: object - type: array - peers: - description: Peers limits the bgppeer to advertise the ips of the - selected pools to. When empty, the loadbalancer IP is announced - to all the BGPPeers configured. - items: - type: string - type: array - type: object - status: - description: BGPAdvertisementStatus defines the observed state of BGPAdvertisement. - type: object - type: object - served: true - storage: true - subresources: - status: {} + type: object + type: array + peers: + description: + Peers limits the bgppeer to advertise the ips of the + selected pools to. When empty, the loadbalancer IP is announced + to all the BGPPeers configured. + items: + type: string + type: array + type: object + status: + description: BGPAdvertisementStatus defines the observed state of BGPAdvertisement. + type: object + type: object + served: true + storage: true + subresources: + status: {} status: acceptedNames: kind: "" @@ -534,8 +591,8 @@ spec: namespace: metallb-system path: /convert conversionReviewVersions: - - v1beta1 - - v1beta2 + - v1beta1 + - v1beta2 group: metallb.io names: kind: BGPPeer @@ -544,254 +601,274 @@ spec: singular: bgppeer scope: Namespaced versions: - - name: v1beta1 - schema: - openAPIV3Schema: - description: BGPPeer is the Schema for the peers API. - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: BGPPeerSpec defines the desired state of Peer. - properties: - bfdProfile: - type: string - ebgpMultiHop: - description: EBGP peer is multi-hops away - type: boolean - holdTime: - description: Requested BGP hold time, per RFC4271. - type: string - keepaliveTime: - description: Requested BGP keepalive time, per RFC4271. - type: string - myASN: - description: AS number to use for the local end of the session. - format: int32 - maximum: 4294967295 - minimum: 0 - type: integer - nodeSelectors: - description: Only connect to this peer on nodes that match one of - these selectors. - items: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: + - name: v1beta1 + schema: + openAPIV3Schema: + description: BGPPeer is the Schema for the peers API. + properties: + apiVersion: + description: + "APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources" + type: string + kind: + description: + "Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" + type: string + metadata: + type: object + spec: + description: BGPPeerSpec defines the desired state of Peer. + properties: + bfdProfile: + type: string + ebgpMultiHop: + description: EBGP peer is multi-hops away + type: boolean + holdTime: + description: Requested BGP hold time, per RFC4271. + type: string + keepaliveTime: + description: Requested BGP keepalive time, per RFC4271. + type: string + myASN: + description: AS number to use for the local end of the session. + format: int32 + maximum: 4294967295 + minimum: 0 + type: integer + nodeSelectors: + description: + Only connect to this peer on nodes that match one of + these selectors. + items: + properties: + matchExpressions: + items: + properties: + key: type: string - minItems: 1 - type: array - required: - - key - - operator - - values + operator: + type: string + values: + items: + type: string + minItems: 1 + type: array + required: + - key + - operator + - values + type: object + type: array + matchLabels: + additionalProperties: + type: string type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - type: array - password: - description: Authentication password for routers enforcing TCP MD5 - authenticated sessions - type: string - peerASN: - description: AS number to expect from the remote end of the session. - format: int32 - maximum: 4294967295 - minimum: 0 - type: integer - peerAddress: - description: Address to dial when establishing the session. - type: string - peerPort: - description: Port to dial when establishing the session. - maximum: 16384 - minimum: 0 - type: integer - routerID: - description: BGP router ID to advertise to the peer - type: string - sourceAddress: - description: Source address to use when establishing the session. - type: string - required: - - myASN - - peerASN - - peerAddress - type: object - status: - description: BGPPeerStatus defines the observed state of Peer. - type: object - type: object - served: true - storage: false - subresources: - status: {} - - name: v1beta2 - schema: - openAPIV3Schema: - description: BGPPeer is the Schema for the peers API. - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: BGPPeerSpec defines the desired state of Peer. - properties: - bfdProfile: - description: The name of the BFD Profile to be used for the BFD session - associated to the BGP session. If not set, the BFD session won't - be set up. - type: string - ebgpMultiHop: - description: To set if the BGPPeer is multi-hops away. Needed for - FRR mode only. - type: boolean - holdTime: - description: Requested BGP hold time, per RFC4271. - type: string - keepaliveTime: - description: Requested BGP keepalive time, per RFC4271. - type: string - myASN: - description: AS number to use for the local end of the session. - format: int32 - maximum: 4294967295 - minimum: 0 - type: integer - nodeSelectors: - description: Only connect to this peer on nodes that match one of - these selectors. - items: - description: A label selector is a label query over a set of resources. - The result of matchLabels and matchExpressions are ANDed. An empty - label selector matches all objects. A null label selector matches - no objects. - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. - The requirements are ANDed. - items: - description: A label selector requirement is a selector that - contains values, a key, and an operator that relates the - key and values. - properties: - key: - description: key is the label key that the selector applies - to. - type: string - operator: - description: operator represents a key's relationship - to a set of values. Valid operators are In, NotIn, Exists - and DoesNotExist. - type: string - values: - description: values is an array of string values. If the - operator is In or NotIn, the values array must be non-empty. - If the operator is Exists or DoesNotExist, the values - array must be empty. This array is replaced during a - strategic merge patch. - items: + type: object + type: array + password: + description: + Authentication password for routers enforcing TCP MD5 + authenticated sessions + type: string + peerASN: + description: AS number to expect from the remote end of the session. + format: int32 + maximum: 4294967295 + minimum: 0 + type: integer + peerAddress: + description: Address to dial when establishing the session. + type: string + peerPort: + description: Port to dial when establishing the session. + maximum: 16384 + minimum: 0 + type: integer + routerID: + description: BGP router ID to advertise to the peer + type: string + sourceAddress: + description: Source address to use when establishing the session. + type: string + required: + - myASN + - peerASN + - peerAddress + type: object + status: + description: BGPPeerStatus defines the observed state of Peer. + type: object + type: object + served: true + storage: false + subresources: + status: {} + - name: v1beta2 + schema: + openAPIV3Schema: + description: BGPPeer is the Schema for the peers API. + properties: + apiVersion: + description: + "APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources" + type: string + kind: + description: + "Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" + type: string + metadata: + type: object + spec: + description: BGPPeerSpec defines the desired state of Peer. + properties: + bfdProfile: + description: + The name of the BFD Profile to be used for the BFD session + associated to the BGP session. If not set, the BFD session won't + be set up. + type: string + ebgpMultiHop: + description: + To set if the BGPPeer is multi-hops away. Needed for + FRR mode only. + type: boolean + holdTime: + description: Requested BGP hold time, per RFC4271. + type: string + keepaliveTime: + description: Requested BGP keepalive time, per RFC4271. + type: string + myASN: + description: AS number to use for the local end of the session. + format: int32 + maximum: 4294967295 + minimum: 0 + type: integer + nodeSelectors: + description: + Only connect to this peer on nodes that match one of + these selectors. + items: + description: + A label selector is a label query over a set of resources. + The result of matchLabels and matchExpressions are ANDed. An empty + label selector matches all objects. A null label selector matches + no objects. + properties: + matchExpressions: + description: + matchExpressions is a list of label selector requirements. + The requirements are ANDed. + items: + description: + A label selector requirement is a selector that + contains values, a key, and an operator that relates the + key and values. + properties: + key: + description: + key is the label key that the selector applies + to. type: string - type: array - required: - - key - - operator + operator: + description: + operator represents a key's relationship + to a set of values. Valid operators are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: + values is an array of string values. If the + operator is In or NotIn, the values array must be non-empty. + If the operator is Exists or DoesNotExist, the values + array must be empty. This array is replaced during a + strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: + matchLabels is a map of {key,value} pairs. A single + {key,value} in the matchLabels map is equivalent to an element + of matchExpressions, whose key field is "key", the operator + is "In", and the values array contains only "value". The requirements + are ANDed. type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} pairs. A single - {key,value} in the matchLabels map is equivalent to an element - of matchExpressions, whose key field is "key", the operator - is "In", and the values array contains only "value". The requirements - are ANDed. - type: object + type: object + type: array + password: + description: + Authentication password for routers enforcing TCP MD5 + authenticated sessions + type: string + passwordSecret: + description: + passwordSecret is name of the authentication secret for + BGP Peer. the secret must be of type "kubernetes.io/basic-auth", + and created in the same namespace as the MetalLB deployment. The + password is stored in the secret as the key "password". + properties: + name: + description: + name is unique within a namespace to reference a + secret resource. + type: string + namespace: + description: + namespace defines the space within which the secret + name must be unique. + type: string type: object - type: array - password: - description: Authentication password for routers enforcing TCP MD5 - authenticated sessions - type: string - passwordSecret: - description: passwordSecret is name of the authentication secret for - BGP Peer. the secret must be of type "kubernetes.io/basic-auth", - and created in the same namespace as the MetalLB deployment. The - password is stored in the secret as the key "password". - properties: - name: - description: name is unique within a namespace to reference a - secret resource. - type: string - namespace: - description: namespace defines the space within which the secret - name must be unique. - type: string - type: object - peerASN: - description: AS number to expect from the remote end of the session. - format: int32 - maximum: 4294967295 - minimum: 0 - type: integer - peerAddress: - description: Address to dial when establishing the session. - type: string - peerPort: - default: 179 - description: Port to dial when establishing the session. - maximum: 16384 - minimum: 0 - type: integer - routerID: - description: BGP router ID to advertise to the peer - type: string - sourceAddress: - description: Source address to use when establishing the session. - type: string - required: - - myASN - - peerASN - - peerAddress - type: object - status: - description: BGPPeerStatus defines the observed state of Peer. - type: object - type: object - served: true - storage: true - subresources: - status: {} + peerASN: + description: AS number to expect from the remote end of the session. + format: int32 + maximum: 4294967295 + minimum: 0 + type: integer + peerAddress: + description: Address to dial when establishing the session. + type: string + peerPort: + default: 179 + description: Port to dial when establishing the session. + maximum: 16384 + minimum: 0 + type: integer + routerID: + description: BGP router ID to advertise to the peer + type: string + sourceAddress: + description: Source address to use when establishing the session. + type: string + required: + - myASN + - peerASN + - peerAddress + type: object + status: + description: BGPPeerStatus defines the observed state of Peer. + type: object + type: object + served: true + storage: true + subresources: + status: {} status: acceptedNames: kind: "" @@ -815,48 +892,52 @@ spec: singular: community scope: Namespaced versions: - - name: v1beta1 - schema: - openAPIV3Schema: - description: Community is a collection of aliases for communities. Users can - define named aliases to be used in the BGPPeer CRD. - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: CommunitySpec defines the desired state of Community. - properties: - communities: - items: - properties: - name: - description: The name of the alias for the community. - type: string - value: - description: The BGP community value corresponding to the given - name. - type: string - type: object - type: array - type: object - status: - description: CommunityStatus defines the observed state of Community. - type: object - type: object - served: true - storage: true - subresources: - status: {} + - name: v1beta1 + schema: + openAPIV3Schema: + description: + Community is a collection of aliases for communities. Users can + define named aliases to be used in the BGPPeer CRD. + properties: + apiVersion: + description: + "APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources" + type: string + kind: + description: + "Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" + type: string + metadata: + type: object + spec: + description: CommunitySpec defines the desired state of Community. + properties: + communities: + items: + properties: + name: + description: The name of the alias for the community. + type: string + value: + description: + The BGP community value corresponding to the given + name. + type: string + type: object + type: array + type: object + status: + description: CommunityStatus defines the observed state of Community. + type: object + type: object + served: true + storage: true + subresources: + status: {} status: acceptedNames: kind: "" @@ -880,58 +961,64 @@ spec: singular: ipaddresspool scope: Namespaced versions: - - name: v1beta1 - schema: - openAPIV3Schema: - description: IPAddressPool represents a pool of IP addresses that can be allocated - to LoadBalancer services. - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: IPAddressPoolSpec defines the desired state of IPAddressPool. - properties: - addresses: - description: A list of IP address ranges over which MetalLB has authority. - You can list multiple ranges in a single pool, they will all share - the same settings. Each range can be either a CIDR prefix, or an - explicit start-end range of IPs. - items: - type: string - type: array - autoAssign: - default: true - description: AutoAssign flag used to prevent MetallB from automatic - allocation for a pool. - type: boolean - avoidBuggyIPs: - default: false - description: AvoidBuggyIPs prevents addresses ending with .0 and .255 - to be used by a pool. - type: boolean - required: - - addresses - type: object - status: - description: IPAddressPoolStatus defines the observed state of IPAddressPool. - type: object - required: - - spec - type: object - served: true - storage: true - subresources: - status: {} + - name: v1beta1 + schema: + openAPIV3Schema: + description: + IPAddressPool represents a pool of IP addresses that can be allocated + to LoadBalancer services. + properties: + apiVersion: + description: + "APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources" + type: string + kind: + description: + "Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" + type: string + metadata: + type: object + spec: + description: IPAddressPoolSpec defines the desired state of IPAddressPool. + properties: + addresses: + description: + A list of IP address ranges over which MetalLB has authority. + You can list multiple ranges in a single pool, they will all share + the same settings. Each range can be either a CIDR prefix, or an + explicit start-end range of IPs. + items: + type: string + type: array + autoAssign: + default: true + description: + AutoAssign flag used to prevent MetallB from automatic + allocation for a pool. + type: boolean + avoidBuggyIPs: + default: false + description: + AvoidBuggyIPs prevents addresses ending with .0 and .255 + to be used by a pool. + type: boolean + required: + - addresses + type: object + status: + description: IPAddressPoolStatus defines the observed state of IPAddressPool. + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} status: acceptedNames: kind: "" @@ -955,146 +1042,166 @@ spec: singular: l2advertisement scope: Namespaced versions: - - name: v1beta1 - schema: - openAPIV3Schema: - description: L2Advertisement allows to advertise the LoadBalancer IPs provided - by the selected pools via L2. - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: L2AdvertisementSpec defines the desired state of L2Advertisement. - properties: - ipAddressPoolSelectors: - description: A selector for the IPAddressPools which would get advertised - via this advertisement. If no IPAddressPool is selected by this - or by the list, the advertisement is applied to all the IPAddressPools. - items: - description: A label selector is a label query over a set of resources. - The result of matchLabels and matchExpressions are ANDed. An empty - label selector matches all objects. A null label selector matches - no objects. - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. - The requirements are ANDed. - items: - description: A label selector requirement is a selector that - contains values, a key, and an operator that relates the - key and values. - properties: - key: - description: key is the label key that the selector applies - to. - type: string - operator: - description: operator represents a key's relationship - to a set of values. Valid operators are In, NotIn, Exists - and DoesNotExist. - type: string - values: - description: values is an array of string values. If the - operator is In or NotIn, the values array must be non-empty. - If the operator is Exists or DoesNotExist, the values - array must be empty. This array is replaced during a - strategic merge patch. - items: + - name: v1beta1 + schema: + openAPIV3Schema: + description: + L2Advertisement allows to advertise the LoadBalancer IPs provided + by the selected pools via L2. + properties: + apiVersion: + description: + "APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources" + type: string + kind: + description: + "Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" + type: string + metadata: + type: object + spec: + description: L2AdvertisementSpec defines the desired state of L2Advertisement. + properties: + ipAddressPoolSelectors: + description: + A selector for the IPAddressPools which would get advertised + via this advertisement. If no IPAddressPool is selected by this + or by the list, the advertisement is applied to all the IPAddressPools. + items: + description: + A label selector is a label query over a set of resources. + The result of matchLabels and matchExpressions are ANDed. An empty + label selector matches all objects. A null label selector matches + no objects. + properties: + matchExpressions: + description: + matchExpressions is a list of label selector requirements. + The requirements are ANDed. + items: + description: + A label selector requirement is a selector that + contains values, a key, and an operator that relates the + key and values. + properties: + key: + description: + key is the label key that the selector applies + to. type: string - type: array - required: - - key - - operator + operator: + description: + operator represents a key's relationship + to a set of values. Valid operators are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: + values is an array of string values. If the + operator is In or NotIn, the values array must be non-empty. + If the operator is Exists or DoesNotExist, the values + array must be empty. This array is replaced during a + strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: + matchLabels is a map of {key,value} pairs. A single + {key,value} in the matchLabels map is equivalent to an element + of matchExpressions, whose key field is "key", the operator + is "In", and the values array contains only "value". The requirements + are ANDed. type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} pairs. A single - {key,value} in the matchLabels map is equivalent to an element - of matchExpressions, whose key field is "key", the operator - is "In", and the values array contains only "value". The requirements - are ANDed. - type: object - type: object - type: array - ipAddressPools: - description: The list of IPAddressPools to advertise via this advertisement, - selected by name. - items: - type: string - type: array - nodeSelectors: - description: NodeSelectors allows to limit the nodes to announce as - next hops for the LoadBalancer IP. When empty, all the nodes having are - announced as next hops. - items: - description: A label selector is a label query over a set of resources. - The result of matchLabels and matchExpressions are ANDed. An empty - label selector matches all objects. A null label selector matches - no objects. - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. - The requirements are ANDed. - items: - description: A label selector requirement is a selector that - contains values, a key, and an operator that relates the - key and values. - properties: - key: - description: key is the label key that the selector applies - to. - type: string - operator: - description: operator represents a key's relationship - to a set of values. Valid operators are In, NotIn, Exists - and DoesNotExist. - type: string - values: - description: values is an array of string values. If the - operator is In or NotIn, the values array must be non-empty. - If the operator is Exists or DoesNotExist, the values - array must be empty. This array is replaced during a - strategic merge patch. - items: + type: object + type: array + ipAddressPools: + description: + The list of IPAddressPools to advertise via this advertisement, + selected by name. + items: + type: string + type: array + nodeSelectors: + description: + NodeSelectors allows to limit the nodes to announce as + next hops for the LoadBalancer IP. When empty, all the nodes having are + announced as next hops. + items: + description: + A label selector is a label query over a set of resources. + The result of matchLabels and matchExpressions are ANDed. An empty + label selector matches all objects. A null label selector matches + no objects. + properties: + matchExpressions: + description: + matchExpressions is a list of label selector requirements. + The requirements are ANDed. + items: + description: + A label selector requirement is a selector that + contains values, a key, and an operator that relates the + key and values. + properties: + key: + description: + key is the label key that the selector applies + to. type: string - type: array - required: - - key - - operator + operator: + description: + operator represents a key's relationship + to a set of values. Valid operators are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: + values is an array of string values. If the + operator is In or NotIn, the values array must be non-empty. + If the operator is Exists or DoesNotExist, the values + array must be empty. This array is replaced during a + strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: + matchLabels is a map of {key,value} pairs. A single + {key,value} in the matchLabels map is equivalent to an element + of matchExpressions, whose key field is "key", the operator + is "In", and the values array contains only "value". The requirements + are ANDed. type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} pairs. A single - {key,value} in the matchLabels map is equivalent to an element - of matchExpressions, whose key field is "key", the operator - is "In", and the values array contains only "value". The requirements - are ANDed. - type: object - type: object - type: array - type: object - status: - description: L2AdvertisementStatus defines the observed state of L2Advertisement. - type: object - type: object - served: true - storage: true - subresources: - status: {} + type: object + type: array + type: object + status: + description: L2AdvertisementStatus defines the observed state of L2Advertisement. + type: object + type: object + served: true + storage: true + subresources: + status: {} status: acceptedNames: kind: "" @@ -1126,89 +1233,89 @@ metadata: name: controller namespace: metallb-system rules: -- apiGroups: - - "" - resources: - - secrets - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resourceNames: - - memberlist - resources: - - secrets - verbs: - - list -- apiGroups: - - apps - resourceNames: - - controller - resources: - - deployments - verbs: - - get -- apiGroups: - - metallb.io - resources: - - bgppeers - verbs: - - get - - list -- apiGroups: - - metallb.io - resources: - - addresspools - verbs: - - get - - list - - watch -- apiGroups: - - metallb.io - resources: - - bfdprofiles - verbs: - - get - - list - - watch -- apiGroups: - - metallb.io - resources: - - ipaddresspools - verbs: - - get - - list - - watch -- apiGroups: - - metallb.io - resources: - - bgpadvertisements - verbs: - - get - - list - - watch -- apiGroups: - - metallb.io - resources: - - l2advertisements - verbs: - - get - - list - - watch -- apiGroups: - - metallb.io - resources: - - communities - verbs: - - get - - list - - watch + - apiGroups: + - "" + resources: + - secrets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - "" + resourceNames: + - memberlist + resources: + - secrets + verbs: + - list + - apiGroups: + - apps + resourceNames: + - controller + resources: + - deployments + verbs: + - get + - apiGroups: + - metallb.io + resources: + - bgppeers + verbs: + - get + - list + - apiGroups: + - metallb.io + resources: + - addresspools + verbs: + - get + - list + - watch + - apiGroups: + - metallb.io + resources: + - bfdprofiles + verbs: + - get + - list + - watch + - apiGroups: + - metallb.io + resources: + - ipaddresspools + verbs: + - get + - list + - watch + - apiGroups: + - metallb.io + resources: + - bgpadvertisements + verbs: + - get + - list + - watch + - apiGroups: + - metallb.io + resources: + - l2advertisements + verbs: + - get + - list + - watch + - apiGroups: + - metallb.io + resources: + - communities + verbs: + - get + - list + - watch --- apiVersion: rbac.authorization.k8s.io/v1 kind: Role @@ -1218,76 +1325,76 @@ metadata: name: pod-lister namespace: metallb-system rules: -- apiGroups: - - "" - resources: - - pods - verbs: - - list -- apiGroups: - - "" - resources: - - secrets - verbs: - - get - - list - - watch -- apiGroups: - - metallb.io - resources: - - addresspools - verbs: - - get - - list - - watch -- apiGroups: - - metallb.io - resources: - - bfdprofiles - verbs: - - get - - list - - watch -- apiGroups: - - metallb.io - resources: - - bgppeers - verbs: - - get - - list - - watch -- apiGroups: - - metallb.io - resources: - - l2advertisements - verbs: - - get - - list - - watch -- apiGroups: - - metallb.io - resources: - - bgpadvertisements - verbs: - - get - - list - - watch -- apiGroups: - - metallb.io - resources: - - ipaddresspools - verbs: - - get - - list - - watch -- apiGroups: - - metallb.io - resources: - - communities - verbs: - - get - - list - - watch + - apiGroups: + - "" + resources: + - pods + verbs: + - list + - apiGroups: + - "" + resources: + - secrets + verbs: + - get + - list + - watch + - apiGroups: + - metallb.io + resources: + - addresspools + verbs: + - get + - list + - watch + - apiGroups: + - metallb.io + resources: + - bfdprofiles + verbs: + - get + - list + - watch + - apiGroups: + - metallb.io + resources: + - bgppeers + verbs: + - get + - list + - watch + - apiGroups: + - metallb.io + resources: + - l2advertisements + verbs: + - get + - list + - watch + - apiGroups: + - metallb.io + resources: + - bgpadvertisements + verbs: + - get + - list + - watch + - apiGroups: + - metallb.io + resources: + - ipaddresspools + verbs: + - get + - list + - watch + - apiGroups: + - metallb.io + resources: + - communities + verbs: + - get + - list + - watch --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -1296,60 +1403,60 @@ metadata: app: metallb name: metallb-system:controller rules: -- apiGroups: - - "" - resources: - - services - verbs: - - get - - list - - watch -- apiGroups: - - "" - resources: - - services/status - verbs: - - update -- apiGroups: - - "" - resources: - - events - verbs: - - create - - patch -- apiGroups: - - policy - resourceNames: - - controller - resources: - - podsecuritypolicies - verbs: - - use -- apiGroups: - - admissionregistration.k8s.io - resources: - - validatingwebhookconfigurations - - mutatingwebhookconfigurations - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - apiextensions.k8s.io - resources: - - customresourcedefinitions - verbs: - - create - - delete - - get - - list - - patch - - update - - watch + - apiGroups: + - "" + resources: + - services + verbs: + - get + - list + - watch + - apiGroups: + - "" + resources: + - services/status + verbs: + - update + - apiGroups: + - "" + resources: + - events + verbs: + - create + - patch + - apiGroups: + - policy + resourceNames: + - controller + resources: + - podsecuritypolicies + verbs: + - use + - apiGroups: + - admissionregistration.k8s.io + resources: + - validatingwebhookconfigurations + - mutatingwebhookconfigurations + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - apiextensions.k8s.io + resources: + - customresourcedefinitions + verbs: + - create + - delete + - get + - list + - patch + - update + - watch --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -1358,39 +1465,39 @@ metadata: app: metallb name: metallb-system:speaker rules: -- apiGroups: - - "" - resources: - - services - - endpoints - - nodes - verbs: - - get - - list - - watch -- apiGroups: - - discovery.k8s.io - resources: - - endpointslices - verbs: - - get - - list - - watch -- apiGroups: - - "" - resources: - - events - verbs: - - create - - patch -- apiGroups: - - policy - resourceNames: - - speaker - resources: - - podsecuritypolicies - verbs: - - use + - apiGroups: + - "" + resources: + - services + - endpoints + - nodes + verbs: + - get + - list + - watch + - apiGroups: + - discovery.k8s.io + resources: + - endpointslices + verbs: + - get + - list + - watch + - apiGroups: + - "" + resources: + - events + verbs: + - create + - patch + - apiGroups: + - policy + resourceNames: + - speaker + resources: + - podsecuritypolicies + verbs: + - use --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding @@ -1404,9 +1511,9 @@ roleRef: kind: Role name: controller subjects: -- kind: ServiceAccount - name: controller - namespace: metallb-system + - kind: ServiceAccount + name: controller + namespace: metallb-system --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding @@ -1420,9 +1527,9 @@ roleRef: kind: Role name: pod-lister subjects: -- kind: ServiceAccount - name: speaker - namespace: metallb-system + - kind: ServiceAccount + name: speaker + namespace: metallb-system --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding @@ -1435,9 +1542,9 @@ roleRef: kind: ClusterRole name: metallb-system:controller subjects: -- kind: ServiceAccount - name: controller - namespace: metallb-system + - kind: ServiceAccount + name: controller + namespace: metallb-system --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding @@ -1450,9 +1557,9 @@ roleRef: kind: ClusterRole name: metallb-system:speaker subjects: -- kind: ServiceAccount - name: speaker - namespace: metallb-system + - kind: ServiceAccount + name: speaker + namespace: metallb-system --- apiVersion: v1 kind: Secret @@ -1467,8 +1574,8 @@ metadata: namespace: metallb-system spec: ports: - - port: 443 - targetPort: 9443 + - port: 443 + targetPort: 9443 selector: component: controller --- @@ -1496,50 +1603,50 @@ spec: component: controller spec: containers: - - args: - - --port=7472 - - --log-level=info - env: - - name: METALLB_ML_SECRET_NAME - value: memberlist - - name: METALLB_DEPLOYMENT - value: controller - image: us-west1-docker.pkg.dev/kne-external/kne/metallb/controller:v0.13.5 - livenessProbe: - failureThreshold: 3 - httpGet: - path: /metrics - port: monitoring - initialDelaySeconds: 10 - periodSeconds: 10 - successThreshold: 1 - timeoutSeconds: 1 - name: controller - ports: - - containerPort: 7472 - name: monitoring - - containerPort: 9443 - name: webhook-server - protocol: TCP - readinessProbe: - failureThreshold: 3 - httpGet: - path: /metrics - port: monitoring - initialDelaySeconds: 10 - periodSeconds: 10 - successThreshold: 1 - timeoutSeconds: 1 - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - all - readOnlyRootFilesystem: true - volumeMounts: - - mountPath: /tmp/k8s-webhook-server/serving-certs - name: cert - readOnly: true + - args: + - --port=7472 + - --log-level=info + env: + - name: METALLB_ML_SECRET_NAME + value: memberlist + - name: METALLB_DEPLOYMENT + value: controller + image: us-west1-docker.pkg.dev/kne-external/kne/metallb/controller:v0.13.5 + livenessProbe: + failureThreshold: 3 + httpGet: + path: /metrics + port: monitoring + initialDelaySeconds: 10 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + name: controller + ports: + - containerPort: 7472 + name: monitoring + - containerPort: 9443 + name: webhook-server + protocol: TCP + readinessProbe: + failureThreshold: 3 + httpGet: + path: /metrics + port: monitoring + initialDelaySeconds: 10 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - all + readOnlyRootFilesystem: true + volumeMounts: + - mountPath: /tmp/k8s-webhook-server/serving-certs + name: cert + readOnly: true nodeSelector: kubernetes.io/os: linux securityContext: @@ -1549,10 +1656,10 @@ spec: serviceAccountName: controller terminationGracePeriodSeconds: 0 volumes: - - name: cert - secret: - defaultMode: 420 - secretName: webhook-server-cert + - name: cert + secret: + defaultMode: 420 + secretName: webhook-server-cert --- apiVersion: apps/v1 kind: DaemonSet @@ -1577,77 +1684,77 @@ spec: component: speaker spec: containers: - - args: - - --port=7472 - - --log-level=info - env: - - name: METALLB_NODE_NAME - valueFrom: - fieldRef: - fieldPath: spec.nodeName - - name: METALLB_HOST - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: METALLB_ML_BIND_ADDR - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: METALLB_ML_LABELS - value: app=metallb,component=speaker - - name: METALLB_ML_SECRET_KEY - valueFrom: - secretKeyRef: - key: secretkey - name: memberlist - image: us-west1-docker.pkg.dev/kne-external/kne/metallb/speaker:v0.13.5 - livenessProbe: - failureThreshold: 3 - httpGet: - path: /metrics - port: monitoring - initialDelaySeconds: 10 - periodSeconds: 10 - successThreshold: 1 - timeoutSeconds: 1 - name: speaker - ports: - - containerPort: 7472 - name: monitoring - - containerPort: 7946 - name: memberlist-tcp - - containerPort: 7946 - name: memberlist-udp - protocol: UDP - readinessProbe: - failureThreshold: 3 - httpGet: - path: /metrics - port: monitoring - initialDelaySeconds: 10 - periodSeconds: 10 - successThreshold: 1 - timeoutSeconds: 1 - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_RAW - drop: - - ALL - readOnlyRootFilesystem: true + - args: + - --port=7472 + - --log-level=info + env: + - name: METALLB_NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + - name: METALLB_HOST + valueFrom: + fieldRef: + fieldPath: status.hostIP + - name: METALLB_ML_BIND_ADDR + valueFrom: + fieldRef: + fieldPath: status.podIP + - name: METALLB_ML_LABELS + value: app=metallb,component=speaker + - name: METALLB_ML_SECRET_KEY + valueFrom: + secretKeyRef: + key: secretkey + name: memberlist + image: us-west1-docker.pkg.dev/kne-external/kne/metallb/speaker:v0.13.5 + livenessProbe: + failureThreshold: 3 + httpGet: + path: /metrics + port: monitoring + initialDelaySeconds: 10 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + name: speaker + ports: + - containerPort: 7472 + name: monitoring + - containerPort: 7946 + name: memberlist-tcp + - containerPort: 7946 + name: memberlist-udp + protocol: UDP + readinessProbe: + failureThreshold: 3 + httpGet: + path: /metrics + port: monitoring + initialDelaySeconds: 10 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + securityContext: + allowPrivilegeEscalation: false + capabilities: + add: + - NET_RAW + drop: + - ALL + readOnlyRootFilesystem: true hostNetwork: true nodeSelector: kubernetes.io/os: linux serviceAccountName: speaker terminationGracePeriodSeconds: 2 tolerations: - - effect: NoSchedule - key: node-role.kubernetes.io/master - operator: Exists - - effect: NoSchedule - key: node-role.kubernetes.io/control-plane - operator: Exists + - effect: NoSchedule + key: node-role.kubernetes.io/master + operator: Exists + - effect: NoSchedule + key: node-role.kubernetes.io/control-plane + operator: Exists --- apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingWebhookConfiguration @@ -1655,143 +1762,143 @@ metadata: creationTimestamp: null name: metallb-webhook-configuration webhooks: -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: webhook-service - namespace: metallb-system - path: /validate-metallb-io-v1beta2-bgppeer - failurePolicy: Fail - name: bgppeersvalidationwebhook.metallb.io - rules: - - apiGroups: - - metallb.io - apiVersions: - - v1beta2 - operations: - - CREATE - - UPDATE - resources: - - bgppeers - sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: webhook-service - namespace: metallb-system - path: /validate-metallb-io-v1beta1-addresspool - failurePolicy: Fail - name: addresspoolvalidationwebhook.metallb.io - rules: - - apiGroups: - - metallb.io - apiVersions: - - v1beta1 - operations: - - CREATE - - UPDATE - resources: - - addresspools - sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: webhook-service - namespace: metallb-system - path: /validate-metallb-io-v1beta1-bfdprofile - failurePolicy: Fail - name: bfdprofilevalidationwebhook.metallb.io - rules: - - apiGroups: - - metallb.io - apiVersions: - - v1beta1 - operations: - - CREATE - - DELETE - resources: - - bfdprofiles - sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: webhook-service - namespace: metallb-system - path: /validate-metallb-io-v1beta1-bgpadvertisement - failurePolicy: Fail - name: bgpadvertisementvalidationwebhook.metallb.io - rules: - - apiGroups: - - metallb.io - apiVersions: - - v1beta1 - operations: - - CREATE - - UPDATE - resources: - - bgpadvertisements - sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: webhook-service - namespace: metallb-system - path: /validate-metallb-io-v1beta1-community - failurePolicy: Fail - name: communityvalidationwebhook.metallb.io - rules: - - apiGroups: - - metallb.io - apiVersions: - - v1beta1 - operations: - - CREATE - - UPDATE - resources: - - communities - sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: webhook-service - namespace: metallb-system - path: /validate-metallb-io-v1beta1-ipaddresspool - failurePolicy: Fail - name: ipaddresspoolvalidationwebhook.metallb.io - rules: - - apiGroups: - - metallb.io - apiVersions: - - v1beta1 - operations: - - CREATE - - UPDATE - resources: - - ipaddresspools - sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: webhook-service - namespace: metallb-system - path: /validate-metallb-io-v1beta1-l2advertisement - failurePolicy: Fail - name: l2advertisementvalidationwebhook.metallb.io - rules: - - apiGroups: - - metallb.io - apiVersions: - - v1beta1 - operations: - - CREATE - - UPDATE - resources: - - l2advertisements - sideEffects: None + - admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: metallb-system + path: /validate-metallb-io-v1beta2-bgppeer + failurePolicy: Fail + name: bgppeersvalidationwebhook.metallb.io + rules: + - apiGroups: + - metallb.io + apiVersions: + - v1beta2 + operations: + - CREATE + - UPDATE + resources: + - bgppeers + sideEffects: None + - admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: metallb-system + path: /validate-metallb-io-v1beta1-addresspool + failurePolicy: Fail + name: addresspoolvalidationwebhook.metallb.io + rules: + - apiGroups: + - metallb.io + apiVersions: + - v1beta1 + operations: + - CREATE + - UPDATE + resources: + - addresspools + sideEffects: None + - admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: metallb-system + path: /validate-metallb-io-v1beta1-bfdprofile + failurePolicy: Fail + name: bfdprofilevalidationwebhook.metallb.io + rules: + - apiGroups: + - metallb.io + apiVersions: + - v1beta1 + operations: + - CREATE + - DELETE + resources: + - bfdprofiles + sideEffects: None + - admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: metallb-system + path: /validate-metallb-io-v1beta1-bgpadvertisement + failurePolicy: Fail + name: bgpadvertisementvalidationwebhook.metallb.io + rules: + - apiGroups: + - metallb.io + apiVersions: + - v1beta1 + operations: + - CREATE + - UPDATE + resources: + - bgpadvertisements + sideEffects: None + - admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: metallb-system + path: /validate-metallb-io-v1beta1-community + failurePolicy: Fail + name: communityvalidationwebhook.metallb.io + rules: + - apiGroups: + - metallb.io + apiVersions: + - v1beta1 + operations: + - CREATE + - UPDATE + resources: + - communities + sideEffects: None + - admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: metallb-system + path: /validate-metallb-io-v1beta1-ipaddresspool + failurePolicy: Fail + name: ipaddresspoolvalidationwebhook.metallb.io + rules: + - apiGroups: + - metallb.io + apiVersions: + - v1beta1 + operations: + - CREATE + - UPDATE + resources: + - ipaddresspools + sideEffects: None + - admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: metallb-system + path: /validate-metallb-io-v1beta1-l2advertisement + failurePolicy: Fail + name: l2advertisementvalidationwebhook.metallb.io + rules: + - apiGroups: + - metallb.io + apiVersions: + - v1beta1 + operations: + - CREATE + - UPDATE + resources: + - l2advertisements + sideEffects: None diff --git a/logshim/logshim_test.go b/logshim/logshim_test.go index d63735c2a..114254235 100644 --- a/logshim/logshim_test.go +++ b/logshim/logshim_test.go @@ -36,10 +36,12 @@ Prefix:Line 3 want3 := want2 + `Prefix:partial line 2 ` // Writing with a partial line. - s.Write([]byte(`Line 1 + if _, err := s.Write([]byte(`Line 1 Line 2 Line 3 -partial line 1`)) +partial line 1`)); err != nil { + t.Fatalf("Write failed: %v", err) + } if got := b.String(); got != want1 { t.Errorf("First: Got %q, want %q", got, want1) } @@ -54,7 +56,9 @@ partial line 1`)) } // Write another partial line and close the shim - s.Write([]byte(`partial line 2`)) + if _, err := s.Write([]byte(`partial line 2`)); err != nil { + t.Fatalf("Write failed: %v", err) + } s.Close() if got := b.String(); got != want3 { t.Errorf("Second: Got %q, want %q", got, want3) diff --git a/manifests/base/kustomization.yaml b/manifests/base/kustomization.yaml index 10db6cfa8..aef5c5f36 100644 --- a/manifests/base/kustomization.yaml +++ b/manifests/base/kustomization.yaml @@ -1,10 +1,10 @@ +--- apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization namespace: neo4j commonLabels: app: neo4j resources: -- namespace.yaml -- serviceaccount.yaml -- neo4j.yaml - + - namespace.yaml + - serviceaccount.yaml + - neo4j.yaml diff --git a/manifests/base/namespace.yaml b/manifests/base/namespace.yaml index 087d8b0d3..f78e7bb80 100644 --- a/manifests/base/namespace.yaml +++ b/manifests/base/namespace.yaml @@ -1,4 +1,5 @@ +--- apiVersion: v1 kind: Namespace metadata: - name: neo4j \ No newline at end of file + name: neo4j diff --git a/manifests/base/neo4j.yaml b/manifests/base/neo4j.yaml index c5aef9dc3..9bf61586f 100644 --- a/manifests/base/neo4j.yaml +++ b/manifests/base/neo4j.yaml @@ -132,48 +132,48 @@ spec: app: neo4j spec: containers: - - name: neo4j - resources: - requests: - memory: "512Mi" - cpu: "250m" - limits: - memory: "1024Mi" - cpu: "500m" - image: neo4j:latest - ports: - - containerPort: 7474 - - containerPort: 7687 - env: - - name: SIMPLE_SERVICE_VERSION - value: "0.9" - - name: NEO4J_AUTH - value: neo4j/test - - name: NEO4J_dbms_connector_https_advertised__address - value: "localhost:7473" - - name: NEO4J_dbms_connector_http_advertised__address - value: "localhost:7474" - - name: NEO4J_dbms_connector_bolt_advertised__address - value: "localhost:7687" - volumeMounts: + - name: neo4j + resources: + requests: + memory: "512Mi" + cpu: "250m" + limits: + memory: "1024Mi" + cpu: "500m" + image: neo4j:latest + ports: + - containerPort: 7474 + - containerPort: 7687 + env: + - name: SIMPLE_SERVICE_VERSION + value: "0.9" + - name: NEO4J_AUTH + value: neo4j/test + - name: NEO4J_dbms_connector_https_advertised__address + value: "localhost:7473" + - name: NEO4J_dbms_connector_http_advertised__address + value: "localhost:7474" + - name: NEO4J_dbms_connector_bolt_advertised__address + value: "localhost:7687" + volumeMounts: + - name: neo4j-data + mountPath: /data + - name: neo4j-logs + mountPath: /logs + - name: neo4j-plugins + mountPath: /plugins + - name: neo4j-import + mountPath: /var/lib/neo4j/import + volumes: - name: neo4j-data - mountPath: /data + persistentVolumeClaim: + claimName: neo4j-data - name: neo4j-logs - mountPath: /logs + persistentVolumeClaim: + claimName: neo4j-logs - name: neo4j-plugins - mountPath: /plugins + persistentVolumeClaim: + claimName: neo4j-plugins - name: neo4j-import - mountPath: /var/lib/neo4j/import - volumes: - - name: neo4j-data - persistentVolumeClaim: - claimName: neo4j-data - - name: neo4j-logs - persistentVolumeClaim: - claimName: neo4j-logs - - name: neo4j-plugins - persistentVolumeClaim: - claimName: neo4j-plugins - - name: neo4j-import - persistentVolumeClaim: - claimName: neo4j-import \ No newline at end of file + persistentVolumeClaim: + claimName: neo4j-import diff --git a/manifests/base/serviceaccount.yaml b/manifests/base/serviceaccount.yaml index aac1834e3..9ec267ed7 100644 --- a/manifests/base/serviceaccount.yaml +++ b/manifests/base/serviceaccount.yaml @@ -1,3 +1,4 @@ +--- apiVersion: v1 kind: ServiceAccount metadata: diff --git a/manifests/controllers/cdnos/manifest.yaml b/manifests/controllers/cdnos/manifest.yaml index 0815bf137..2c9113ecf 100644 --- a/manifests/controllers/cdnos/manifest.yaml +++ b/manifests/controllers/cdnos/manifest.yaml @@ -1,3 +1,4 @@ +--- apiVersion: v1 kind: Namespace metadata: @@ -26,271 +27,296 @@ spec: singular: cdnos scope: Namespaced versions: - - name: v1 - schema: - openAPIV3Schema: - description: Cdnos is the Schema for the cdnoss API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: CdnosSpec defines the desired state of Cdnos - properties: - args: - description: Args are the args to pass to the command. - items: + - name: v1 + schema: + openAPIV3Schema: + description: Cdnos is the Schema for the cdnoss API + properties: + apiVersion: + description: + "APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources" + type: string + kind: + description: + "Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" + type: string + metadata: + type: object + spec: + description: CdnosSpec defines the desired state of Cdnos + properties: + args: + description: Args are the args to pass to the command. + items: + type: string + type: array + command: + description: Command is the name of the executable to run. + type: string + configFile: + description: + ConfigFile is the default configuration file name for + the pod. + type: string + configPath: + description: + ConfigPath is the mount point for configuration inside + the pod. + type: string + env: + description: Env are the environment variables to set for the container. + items: + description: + EnvVar represents an environment variable present in + a Container. + properties: + name: + description: Name of the environment variable. Must be a C_IDENTIFIER. + type: string + value: + description: + 'Variable references $(VAR_NAME) are expanded using + the previously defined environment variables in the container + and any service environment variables. If a variable cannot + be resolved, the reference in the input string will be unchanged. + Double $$ are reduced to a single $, which allows for escaping + the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will produce the + string literal "$(VAR_NAME)". Escaped references will never + be expanded, regardless of whether the variable exists or + not. Defaults to "".' + type: string + valueFrom: + description: + Source for the environment variable's value. Cannot + be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: + "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?" + type: string + optional: + description: + Specify whether the ConfigMap or its key + must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: + "Selects a field of the pod: supports metadata.name, + metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, + spec.nodeName, spec.serviceAccountName, status.hostIP, + status.podIP, status.podIPs." + properties: + apiVersion: + description: + Version of the schema the FieldPath is + written in terms of, defaults to "v1". + type: string + fieldPath: + description: + Path of the field to select in the specified + API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: + "Selects a resource of the container: only + resources limits and requests (limits.cpu, limits.memory, + limits.ephemeral-storage, requests.cpu, requests.memory + and requests.ephemeral-storage) are currently supported." + properties: + containerName: + description: + "Container name: required for volumes, + optional for env vars" + type: string + divisor: + anyOf: + - type: integer + - type: string + description: + Specifies the output format of the exposed + resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: "Required: resource to select" + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's namespace + properties: + key: + description: + The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: + "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?" + type: string + optional: + description: + Specify whether the Secret or its key must + be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + image: + description: Image to use for the CDNOS container + type: string + initImage: + description: + InitImage is the docker image to use as an init container + for the pod. type: string - type: array - command: - description: Command is the name of the executable to run. - type: string - configFile: - description: ConfigFile is the default configuration file name for - the pod. - type: string - configPath: - description: ConfigPath is the mount point for configuration inside - the pod. - type: string - env: - description: Env are the environment variables to set for the container. - items: - description: EnvVar represents an environment variable present in - a Container. + initSleep: + description: InitSleep is the time sleep in the init container + type: integer + interfaceCount: + description: + InterfaceCount is number of interfaces to be attached + to the pod. + type: integer + ports: + additionalProperties: + description: ServicePort describes an external L4 port on the device. + properties: + innerPort: + description: InnerPort is port on the container to expose. + format: int32 + type: integer + outerPort: + description: OuterPort is port on the container to expose. + format: int32 + type: integer + required: + - innerPort + - outerPort + type: object + description: Ports are ports to create on the service. + type: object + resources: + description: + Resources are the K8s resources to allocate to cdnos + container. properties: - name: - description: Name of the environment variable. Must be a C_IDENTIFIER. - type: string - value: - description: 'Variable references $(VAR_NAME) are expanded using - the previously defined environment variables in the container - and any service environment variables. If a variable cannot - be resolved, the reference in the input string will be unchanged. - Double $$ are reduced to a single $, which allows for escaping - the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will produce the - string literal "$(VAR_NAME)". Escaped references will never - be expanded, regardless of whether the variable exists or - not. Defaults to "".' - type: string - valueFrom: - description: Source for the environment variable's value. Cannot - be used if value is not empty. - properties: - configMapKeyRef: - description: Selects a key of a ConfigMap. - properties: - key: - description: The key to select. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - optional: - description: Specify whether the ConfigMap or its key - must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - fieldRef: - description: 'Selects a field of the pod: supports metadata.name, - metadata.namespace, `metadata.labels['''']`, `metadata.annotations['''']`, - spec.nodeName, spec.serviceAccountName, status.hostIP, - status.podIP, status.podIPs.' - properties: - apiVersion: - description: Version of the schema the FieldPath is - written in terms of, defaults to "v1". - type: string - fieldPath: - description: Path of the field to select in the specified - API version. - type: string - required: - - fieldPath - type: object - x-kubernetes-map-type: atomic - resourceFieldRef: - description: 'Selects a resource of the container: only - resources limits and requests (limits.cpu, limits.memory, - limits.ephemeral-storage, requests.cpu, requests.memory - and requests.ephemeral-storage) are currently supported.' - properties: - containerName: - description: 'Container name: required for volumes, - optional for env vars' - type: string - divisor: - anyOf: - - type: integer - - type: string - description: Specifies the output format of the exposed - resources, defaults to "1" - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - description: 'Required: resource to select' - type: string - required: - - resource - type: object - x-kubernetes-map-type: atomic - secretKeyRef: - description: Selects a key of a secret in the pod's namespace - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - optional: - description: Specify whether the Secret or its key must - be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic + claims: + description: + "Claims lists the names of resources, defined in + spec.resourceClaims, that are used by this container. \n This + is an alpha field and requires enabling the DynamicResourceAllocation + feature gate. \n This field is immutable. It can only be set + for containers." + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: + Name must match the name of one entry in pod.spec.resourceClaims + of the Pod where this field is used. It makes that resource + available inside a container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: + "Limits describes the maximum amount of compute resources + allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/" + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: + "Requests describes the minimum amount of compute + resources required. If Requests is omitted for a container, + it defaults to Limits if that is explicitly specified, otherwise + to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/" type: object - required: - - name type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - image: - description: Image to use for the CDNOS container - type: string - initImage: - description: InitImage is the docker image to use as an init container - for the pod. - type: string - initSleep: - description: InitSleep is the time sleep in the init container - type: integer - interfaceCount: - description: InterfaceCount is number of interfaces to be attached - to the pod. - type: integer - ports: - additionalProperties: - description: ServicePort describes an external L4 port on the device. + tls: + description: TLS is the configuration the key/certs to use for management. properties: - innerPort: - description: InnerPort is port on the container to expose. - format: int32 - type: integer - outerPort: - description: OuterPort is port on the container to expose. - format: int32 - type: integer - required: - - innerPort - - outerPort - type: object - description: Ports are ports to create on the service. - type: object - resources: - description: Resources are the K8s resources to allocate to cdnos - container. - properties: - claims: - description: "Claims lists the names of resources, defined in - spec.resourceClaims, that are used by this container. \n This - is an alpha field and requires enabling the DynamicResourceAllocation - feature gate. \n This field is immutable. It can only be set - for containers." - items: - description: ResourceClaim references one entry in PodSpec.ResourceClaims. + selfSigned: + description: SelfSigned generates a new self signed certificate. properties: - name: - description: Name must match the name of one entry in pod.spec.resourceClaims - of the Pod where this field is used. It makes that resource - available inside a container. + commonName: + description: / Common name to set in the cert. type: string + keySize: + description: RSA keysize to use for key generation. + type: integer required: - - name + - commonName + - keySize type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount of compute resources - allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount of compute - resources required. If Requests is omitted for a container, - it defaults to Limits if that is explicitly specified, otherwise - to an implementation-defined value. Requests cannot exceed Limits. - More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - type: object - tls: - description: TLS is the configuration the key/certs to use for management. - properties: - selfSigned: - description: SelfSigned generates a new self signed certificate. - properties: - commonName: - description: / Common name to set in the cert. - type: string - keySize: - description: RSA keysize to use for key generation. - type: integer - required: - - commonName - - keySize - type: object - type: object - type: object - status: - description: CdnosStatus defines the observed state of Cdnos - properties: - message: - description: Message describes why the Cdnos is in the current phase. - type: string - phase: - description: Phase is the overall status of the Cdnos. - type: string - required: - - message - - phase - type: object - type: object - served: true - storage: true - subresources: - status: {} + type: object + type: object + status: + description: CdnosStatus defines the observed state of Cdnos + properties: + message: + description: Message describes why the Cdnos is in the current phase. + type: string + phase: + description: Phase is the overall status of the Cdnos. + type: string + required: + - message + - phase + type: object + type: object + served: true + storage: true + subresources: + status: {} --- apiVersion: v1 kind: ServiceAccount @@ -318,83 +344,83 @@ metadata: name: cdnos-controller-leader-election-role namespace: cdnos-controller-system rules: -- apiGroups: - - "" - resources: - - configmaps - verbs: - - get - - list - - watch - - create - - update - - patch - - delete -- apiGroups: - - coordination.k8s.io - resources: - - leases - verbs: - - get - - list - - watch - - create - - update - - patch - - delete -- apiGroups: - - "" - resources: - - events - verbs: - - create - - patch + - apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch + - create + - update + - patch + - delete + - apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - get + - list + - watch + - create + - update + - patch + - delete + - apiGroups: + - "" + resources: + - events + verbs: + - create + - patch --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: cdnos-controller-manager-role rules: -- apiGroups: - - cdnos.dev.drivenets.net - resources: - - cdnoss - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - cdnos.dev.drivenets.net - resources: - - cdnoss/finalizers - verbs: - - update -- apiGroups: - - cdnos.dev.drivenets.net - resources: - - cdnoss/status - verbs: - - get - - patch - - update -- apiGroups: - - "" - resources: - - pods - - secrets - - services - verbs: - - create - - delete - - get - - list - - patch - - update - - watch + - apiGroups: + - cdnos.dev.drivenets.net + resources: + - cdnoss + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - cdnos.dev.drivenets.net + resources: + - cdnoss/finalizers + verbs: + - update + - apiGroups: + - cdnos.dev.drivenets.net + resources: + - cdnoss/status + verbs: + - get + - patch + - update + - apiGroups: + - "" + resources: + - pods + - secrets + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -408,10 +434,10 @@ metadata: app.kubernetes.io/part-of: cdnos-controller name: cdnos-controller-metrics-reader rules: -- nonResourceURLs: - - /metrics - verbs: - - get + - nonResourceURLs: + - /metrics + verbs: + - get --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -425,18 +451,18 @@ metadata: app.kubernetes.io/part-of: cdnos-controller name: cdnos-controller-proxy-role rules: -- apiGroups: - - authentication.k8s.io - resources: - - tokenreviews - verbs: - - create -- apiGroups: - - authorization.k8s.io - resources: - - subjectaccessreviews - verbs: - - create + - apiGroups: + - authentication.k8s.io + resources: + - tokenreviews + verbs: + - create + - apiGroups: + - authorization.k8s.io + resources: + - subjectaccessreviews + verbs: + - create --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding @@ -455,9 +481,9 @@ roleRef: kind: Role name: cdnos-controller-leader-election-role subjects: -- kind: ServiceAccount - name: cdnos-controller-controller-manager - namespace: cdnos-controller-system + - kind: ServiceAccount + name: cdnos-controller-controller-manager + namespace: cdnos-controller-system --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding @@ -475,9 +501,9 @@ roleRef: kind: ClusterRole name: cdnos-controller-manager-role subjects: -- kind: ServiceAccount - name: cdnos-controller-controller-manager - namespace: cdnos-controller-system + - kind: ServiceAccount + name: cdnos-controller-controller-manager + namespace: cdnos-controller-system --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding @@ -495,9 +521,9 @@ roleRef: kind: ClusterRole name: cdnos-controller-proxy-role subjects: -- kind: ServiceAccount - name: cdnos-controller-controller-manager - namespace: cdnos-controller-system + - kind: ServiceAccount + name: cdnos-controller-controller-manager + namespace: cdnos-controller-system --- apiVersion: v1 kind: Service @@ -514,10 +540,10 @@ metadata: namespace: cdnos-controller-system spec: ports: - - name: https - port: 8443 - protocol: TCP - targetPort: https + - name: https + port: 8443 + protocol: TCP + targetPort: https selector: control-plane: controller-manager --- @@ -547,61 +573,61 @@ spec: control-plane: controller-manager spec: containers: - - args: - - --secure-listen-address=0.0.0.0:8443 - - --upstream=http://127.0.0.1:8080/ - - --logtostderr=true - - --v=0 - image: registry.k8s.io/kubebuilder/kube-rbac-proxy:v0.15.0 - name: kube-rbac-proxy - ports: - - containerPort: 8443 - name: https - protocol: TCP - resources: - limits: - cpu: 500m - memory: 128Mi - requests: - cpu: 5m - memory: 64Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - - args: - - --health-probe-bind-address=:8081 - - --metrics-bind-address=127.0.0.1:8080 - - --leader-elect - command: - - /manager - image: public.ecr.aws/dn/cdnos-controller:1.7.5 - livenessProbe: - httpGet: - path: /healthz - port: 8081 - initialDelaySeconds: 15 - periodSeconds: 20 - name: manager - readinessProbe: - httpGet: - path: /readyz - port: 8081 - initialDelaySeconds: 5 - periodSeconds: 10 - resources: - limits: - cpu: 500m - memory: 128Mi - requests: - cpu: 10m - memory: 64Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL + - args: + - --secure-listen-address=0.0.0.0:8443 + - --upstream=http://127.0.0.1:8080/ + - --logtostderr=true + - --v=0 + image: registry.k8s.io/kubebuilder/kube-rbac-proxy:v0.15.0 + name: kube-rbac-proxy + ports: + - containerPort: 8443 + name: https + protocol: TCP + resources: + limits: + cpu: 500m + memory: 128Mi + requests: + cpu: 5m + memory: 64Mi + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + - args: + - --health-probe-bind-address=:8081 + - --metrics-bind-address=127.0.0.1:8080 + - --leader-elect + command: + - /manager + image: public.ecr.aws/dn/cdnos-controller:1.7.5 + livenessProbe: + httpGet: + path: /healthz + port: 8081 + initialDelaySeconds: 15 + periodSeconds: 20 + name: manager + readinessProbe: + httpGet: + path: /readyz + port: 8081 + initialDelaySeconds: 5 + periodSeconds: 10 + resources: + limits: + cpu: 500m + memory: 128Mi + requests: + cpu: 10m + memory: 64Mi + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL securityContext: runAsNonRoot: true serviceAccountName: cdnos-controller-controller-manager diff --git a/manifests/controllers/ceoslab/manifest.yaml b/manifests/controllers/ceoslab/manifest.yaml index f9d2be424..2f085db4c 100644 --- a/manifests/controllers/ceoslab/manifest.yaml +++ b/manifests/controllers/ceoslab/manifest.yaml @@ -1,3 +1,4 @@ +--- apiVersion: v1 kind: Namespace metadata: @@ -21,193 +22,193 @@ spec: singular: ceoslabdevice scope: Namespaced versions: - - name: v1alpha1 - schema: - openAPIV3Schema: - description: CEosLabDevice is the Schema for the ceoslabdevices API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: CEosLabDeviceSpec defines the desired state of CEosLabDevice - properties: - args: - description: Additional arguments to pass to /sbin/init. Those necessary to boot properly are already present. - items: - type: string - type: array - certconfig: - description: X.509 certificate configuration. - properties: - selfsignedcerts: - description: Configuration for self-signed certificates. - items: - properties: - certname: - description: Certificate name on the node. - type: string - commonname: - description: Common name to set in the cert. - type: string - keyname: - description: Key name on the node. - type: string - keysize: - description: RSA keysize to use for key generation. - format: int32 - type: integer - type: object - type: array - type: object - envvars: - additionalProperties: - type: string - description: Additional environment variables. Those necessary to boot properly are already present. - type: object - image: - description: 'Image name. Default: ceos:latest' - type: string - initcontainerimage: - description: 'Init container image name. Default: networkop/init-wait:latest' - type: string - intfmapping: - additionalProperties: - type: string - description: Explicit interface mapping between kernel devices and interface names. If this is defined, any unmapped devices are ignored. - type: object - numinterfaces: - description: 'Number of data interfaces to create. An additional interface (eth0) is created for pod connectivity. Default: 0 interfaces' - format: int32 - type: integer - resourcerequirements: - additionalProperties: - type: string - description: 'Resource requests to configure on the pod. Default: none' - type: object - services: - additionalProperties: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: CEosLabDevice is the Schema for the ceoslabdevices API + properties: + apiVersion: + description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources" + type: string + kind: + description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" + type: string + metadata: + type: object + spec: + description: CEosLabDeviceSpec defines the desired state of CEosLabDevice + properties: + args: + description: Additional arguments to pass to /sbin/init. Those necessary to boot properly are already present. + items: + type: string + type: array + certconfig: + description: X.509 certificate configuration. properties: - tcpports: - description: TCP ports to forward to the pod. + selfsignedcerts: + description: Configuration for self-signed certificates. items: properties: - in: - description: Port inside the container. - format: int32 - type: integer - out: - description: Port outside the container. Defaults to the same as in. + certname: + description: Certificate name on the node. + type: string + commonname: + description: Common name to set in the cert. + type: string + keyname: + description: Key name on the node. + type: string + keysize: + description: RSA keysize to use for key generation. format: int32 type: integer type: object type: array type: object - description: 'Port mappings for container services. Default: none' - type: object - sleep: - description: 'Time (in seconds) to wait before starting the device. Default: 0 seconds' - format: int32 - type: integer - toggleoverrides: - additionalProperties: - type: boolean - description: EOS feature toggle overrides - type: object - waitforagents: - description: EOS agents to for the startup probe to block on - items: + envvars: + additionalProperties: + type: string + description: Additional environment variables. Those necessary to boot properly are already present. + type: object + image: + description: "Image name. Default: ceos:latest" type: string - type: array - type: object - status: - description: CEosLabDeviceStatus defines the observed state of CEosLabDevice - properties: - configmapconfig: - description: ConfigMap state as configured in configmaps - properties: - intfmappingstatus: - additionalProperties: - type: string + initcontainerimage: + description: "Init container image name. Default: networkop/init-wait:latest" + type: string + intfmapping: + additionalProperties: + type: string + description: Explicit interface mapping between kernel devices and interface names. If this is defined, any unmapped devices are ignored. + type: object + numinterfaces: + description: "Number of data interfaces to create. An additional interface (eth0) is created for pod connectivity. Default: 0 interfaces" + format: int32 + type: integer + resourcerequirements: + additionalProperties: + type: string + description: "Resource requests to configure on the pod. Default: none" + type: object + services: + additionalProperties: + properties: + tcpports: + description: TCP ports to forward to the pod. + items: + properties: + in: + description: Port inside the container. + format: int32 + type: integer + out: + description: Port outside the container. Defaults to the same as in. + format: int32 + type: integer + type: object + type: array type: object - rceosstale: + description: "Port mappings for container services. Default: none" + type: object + sleep: + description: "Time (in seconds) to wait before starting the device. Default: 0 seconds" + format: int32 + type: integer + toggleoverrides: + additionalProperties: type: boolean - selfsignedcertstatus: - additionalProperties: - properties: - certname: - description: Certificate name on the node. - type: string - commonname: - description: Common name to set in the cert. - type: string - keyname: - description: Key name on the node. - type: string - keysize: - description: RSA keysize to use for key generation. - format: int32 - type: integer - type: object - type: object - startupconfigresourceversion: + description: EOS feature toggle overrides + type: object + waitforagents: + description: EOS agents to for the startup probe to block on + items: type: string - toggleoverridesstatus: - additionalProperties: + type: array + type: object + status: + description: CEosLabDeviceStatus defines the observed state of CEosLabDevice + properties: + configmapconfig: + description: ConfigMap state as configured in configmaps + properties: + intfmappingstatus: + additionalProperties: + type: string + type: object + rceosstale: type: boolean - type: object - type: object - podconfigmapconfig: - description: ConfigMap state as present in the pod. If these diverge, we need to restart the pod to update. Even if an in-place update is possible these are needed at boot time. - properties: - intfmappingstatus: - additionalProperties: + selfsignedcertstatus: + additionalProperties: + properties: + certname: + description: Certificate name on the node. + type: string + commonname: + description: Common name to set in the cert. + type: string + keyname: + description: Key name on the node. + type: string + keysize: + description: RSA keysize to use for key generation. + format: int32 + type: integer + type: object + type: object + startupconfigresourceversion: type: string - type: object - rceosstale: - type: boolean - selfsignedcertstatus: - additionalProperties: - properties: - certname: - description: Certificate name on the node. - type: string - commonname: - description: Common name to set in the cert. - type: string - keyname: - description: Key name on the node. - type: string - keysize: - description: RSA keysize to use for key generation. - format: int32 - type: integer + toggleoverridesstatus: + additionalProperties: + type: boolean type: object - type: object - startupconfigresourceversion: - type: string - toggleoverridesstatus: - additionalProperties: + type: object + podconfigmapconfig: + description: ConfigMap state as present in the pod. If these diverge, we need to restart the pod to update. Even if an in-place update is possible these are needed at boot time. + properties: + intfmappingstatus: + additionalProperties: + type: string + type: object + rceosstale: type: boolean - type: object - type: object - reason: - description: Reason for potential failure - type: string - status: - description: Device status - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} + selfsignedcertstatus: + additionalProperties: + properties: + certname: + description: Certificate name on the node. + type: string + commonname: + description: Common name to set in the cert. + type: string + keyname: + description: Key name on the node. + type: string + keysize: + description: RSA keysize to use for key generation. + format: int32 + type: integer + type: object + type: object + startupconfigresourceversion: + type: string + toggleoverridesstatus: + additionalProperties: + type: boolean + type: object + type: object + reason: + description: Reason for potential failure + type: string + status: + description: Device status + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} status: acceptedNames: kind: "" @@ -227,37 +228,37 @@ metadata: name: arista-ceoslab-operator-leader-election-role namespace: arista-ceoslab-operator-system rules: -- apiGroups: - - "" - resources: - - configmaps - verbs: - - get - - list - - watch - - create - - update - - patch - - delete -- apiGroups: - - coordination.k8s.io - resources: - - leases - verbs: - - get - - list - - watch - - create - - update - - patch - - delete -- apiGroups: - - "" - resources: - - events - verbs: - - create - - patch + - apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch + - create + - update + - patch + - delete + - apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - get + - list + - watch + - create + - update + - patch + - delete + - apiGroups: + - "" + resources: + - events + verbs: + - create + - patch --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -265,108 +266,108 @@ metadata: creationTimestamp: null name: arista-ceoslab-operator-manager-role rules: -- apiGroups: - - ceoslab.arista.com - resources: - - ceoslabdevices - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - ceoslab.arista.com - resources: - - ceoslabdevices/finalizers - verbs: - - update -- apiGroups: - - ceoslab.arista.com - resources: - - ceoslabdevices/status - verbs: - - get - - patch - - update -- apiGroups: - - "" - resources: - - configmaps - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - pods - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - secrets - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - services - verbs: - - create - - delete - - get - - list - - patch - - update - - watch + - apiGroups: + - ceoslab.arista.com + resources: + - ceoslabdevices + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - ceoslab.arista.com + resources: + - ceoslabdevices/finalizers + verbs: + - update + - apiGroups: + - ceoslab.arista.com + resources: + - ceoslabdevices/status + verbs: + - get + - patch + - update + - apiGroups: + - "" + resources: + - configmaps + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - "" + resources: + - pods + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - "" + resources: + - secrets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - "" + resources: + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: arista-ceoslab-operator-metrics-reader rules: -- nonResourceURLs: - - /metrics - verbs: - - get + - nonResourceURLs: + - /metrics + verbs: + - get --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: arista-ceoslab-operator-proxy-role rules: -- apiGroups: - - authentication.k8s.io - resources: - - tokenreviews - verbs: - - create -- apiGroups: - - authorization.k8s.io - resources: - - subjectaccessreviews - verbs: - - create + - apiGroups: + - authentication.k8s.io + resources: + - tokenreviews + verbs: + - create + - apiGroups: + - authorization.k8s.io + resources: + - subjectaccessreviews + verbs: + - create --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding @@ -378,9 +379,9 @@ roleRef: kind: Role name: arista-ceoslab-operator-leader-election-role subjects: -- kind: ServiceAccount - name: arista-ceoslab-operator-controller-manager - namespace: arista-ceoslab-operator-system + - kind: ServiceAccount + name: arista-ceoslab-operator-controller-manager + namespace: arista-ceoslab-operator-system --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding @@ -391,9 +392,9 @@ roleRef: kind: ClusterRole name: arista-ceoslab-operator-manager-role subjects: -- kind: ServiceAccount - name: arista-ceoslab-operator-controller-manager - namespace: arista-ceoslab-operator-system + - kind: ServiceAccount + name: arista-ceoslab-operator-controller-manager + namespace: arista-ceoslab-operator-system --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding @@ -404,9 +405,9 @@ roleRef: kind: ClusterRole name: arista-ceoslab-operator-proxy-role subjects: -- kind: ServiceAccount - name: arista-ceoslab-operator-controller-manager - namespace: arista-ceoslab-operator-system + - kind: ServiceAccount + name: arista-ceoslab-operator-controller-manager + namespace: arista-ceoslab-operator-system --- apiVersion: v1 data: @@ -436,10 +437,10 @@ metadata: namespace: arista-ceoslab-operator-system spec: ports: - - name: https - port: 8443 - protocol: TCP - targetPort: https + - name: https + port: 8443 + protocol: TCP + targetPort: https selector: control-plane: controller-manager --- @@ -463,53 +464,53 @@ spec: control-plane: controller-manager spec: containers: - - args: - - --secure-listen-address=0.0.0.0:8443 - - --upstream=http://127.0.0.1:8080/ - - --logtostderr=true - - --v=0 - image: registry.k8s.io/kubebuilder/kube-rbac-proxy:v0.11.0 - name: kube-rbac-proxy - ports: - - containerPort: 8443 - name: https - protocol: TCP - resources: - limits: - cpu: 500m - memory: 128Mi - requests: - cpu: 5m - memory: 64Mi - - args: - - --health-probe-bind-address=:8081 - - --metrics-bind-address=127.0.0.1:8080 - - --leader-elect - command: - - /manager - image: ghcr.io/aristanetworks/arista-ceoslab-operator:v2.1.2 - livenessProbe: - httpGet: - path: /healthz - port: 8081 - initialDelaySeconds: 15 - periodSeconds: 20 - name: manager - readinessProbe: - httpGet: - path: /readyz - port: 8081 - initialDelaySeconds: 5 - periodSeconds: 10 - resources: - limits: - cpu: 500m - memory: 128Mi - requests: - cpu: 10m - memory: 64Mi - securityContext: - allowPrivilegeEscalation: false + - args: + - --secure-listen-address=0.0.0.0:8443 + - --upstream=http://127.0.0.1:8080/ + - --logtostderr=true + - --v=0 + image: registry.k8s.io/kubebuilder/kube-rbac-proxy:v0.11.0 + name: kube-rbac-proxy + ports: + - containerPort: 8443 + name: https + protocol: TCP + resources: + limits: + cpu: 500m + memory: 128Mi + requests: + cpu: 5m + memory: 64Mi + - args: + - --health-probe-bind-address=:8081 + - --metrics-bind-address=127.0.0.1:8080 + - --leader-elect + command: + - /manager + image: ghcr.io/aristanetworks/arista-ceoslab-operator:v2.1.2 + livenessProbe: + httpGet: + path: /healthz + port: 8081 + initialDelaySeconds: 15 + periodSeconds: 20 + name: manager + readinessProbe: + httpGet: + path: /readyz + port: 8081 + initialDelaySeconds: 5 + periodSeconds: 10 + resources: + limits: + cpu: 500m + memory: 128Mi + requests: + cpu: 10m + memory: 64Mi + securityContext: + allowPrivilegeEscalation: false securityContext: runAsNonRoot: true serviceAccountName: arista-ceoslab-operator-controller-manager diff --git a/manifests/controllers/lemming/manifest.yaml b/manifests/controllers/lemming/manifest.yaml index 3f3b60158..eb5ad8118 100644 --- a/manifests/controllers/lemming/manifest.yaml +++ b/manifests/controllers/lemming/manifest.yaml @@ -1,3 +1,4 @@ +--- apiVersion: v1 kind: Namespace metadata: @@ -21,265 +22,290 @@ spec: singular: lemming scope: Namespaced versions: - - name: v1alpha1 - schema: - openAPIV3Schema: - description: Lemming is the Schema for the lemmings API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: LemmingSpec defines the desired state of Lemming. - properties: - args: - description: Args are the args to pass to the command. - items: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: Lemming is the Schema for the lemmings API + properties: + apiVersion: + description: + "APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources" + type: string + kind: + description: + "Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" + type: string + metadata: + type: object + spec: + description: LemmingSpec defines the desired state of Lemming. + properties: + args: + description: Args are the args to pass to the command. + items: + type: string + type: array + command: + description: Command is the name of the executable to run. + type: string + configFile: + description: + ConfigFile is the default configuration file name for + the pod. + type: string + configPath: + description: + ConfigPath is the mount point for configuration inside + the pod. + type: string + env: + description: Env are the environment variables to set for the container. + items: + description: + EnvVar represents an environment variable present in + a Container. + properties: + name: + description: Name of the environment variable. Must be a C_IDENTIFIER. + type: string + value: + description: + 'Variable references $(VAR_NAME) are expanded using + the previously defined environment variables in the container + and any service environment variables. If a variable cannot + be resolved, the reference in the input string will be unchanged. + Double $$ are reduced to a single $, which allows for escaping + the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will produce the + string literal "$(VAR_NAME)". Escaped references will never + be expanded, regardless of whether the variable exists or + not. Defaults to "".' + type: string + valueFrom: + description: + Source for the environment variable's value. Cannot + be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: + "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?" + type: string + optional: + description: + Specify whether the ConfigMap or its key + must be defined + type: boolean + required: + - key + type: object + fieldRef: + description: + "Selects a field of the pod: supports metadata.name, + metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, + spec.nodeName, spec.serviceAccountName, status.hostIP, + status.podIP, status.podIPs." + properties: + apiVersion: + description: + Version of the schema the FieldPath is + written in terms of, defaults to "v1". + type: string + fieldPath: + description: + Path of the field to select in the specified + API version. + type: string + required: + - fieldPath + type: object + resourceFieldRef: + description: + "Selects a resource of the container: only + resources limits and requests (limits.cpu, limits.memory, + limits.ephemeral-storage, requests.cpu, requests.memory + and requests.ephemeral-storage) are currently supported." + properties: + containerName: + description: + "Container name: required for volumes, + optional for env vars" + type: string + divisor: + anyOf: + - type: integer + - type: string + description: + Specifies the output format of the exposed + resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: "Required: resource to select" + type: string + required: + - resource + type: object + secretKeyRef: + description: Selects a key of a secret in the pod's namespace + properties: + key: + description: + The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: + "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?" + type: string + optional: + description: + Specify whether the Secret or its key must + be defined + type: boolean + required: + - key + type: object + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + image: + description: Image is the container image to run. + type: string + initImage: + description: + InitImage is the docker image to use as an init container + for the pod. type: string - type: array - command: - description: Command is the name of the executable to run. - type: string - configFile: - description: ConfigFile is the default configuration file name for - the pod. - type: string - configPath: - description: ConfigPath is the mount point for configuration inside - the pod. - type: string - env: - description: Env are the environment variables to set for the container. - items: - description: EnvVar represents an environment variable present in - a Container. + initSleep: + description: InitSleep is the time sleep in the init container + type: integer + interfaceCount: + description: + InterfaceCount is number of interfaces to be attached + to the pod. + type: integer + ports: + additionalProperties: + description: ServicePort describes an external L4 port on the device. + properties: + innerPort: + description: InnerPort is port on the container to expose. + format: int32 + type: integer + outerPort: + description: OuterPort is port on the container to expose. + format: int32 + type: integer + required: + - innerPort + - outerPort + type: object + description: Ports are ports to create on the service. + type: object + resources: + description: + Resources are the K8s resources to allocate to lemming + container. properties: - name: - description: Name of the environment variable. Must be a C_IDENTIFIER. - type: string - value: - description: 'Variable references $(VAR_NAME) are expanded using - the previously defined environment variables in the container - and any service environment variables. If a variable cannot - be resolved, the reference in the input string will be unchanged. - Double $$ are reduced to a single $, which allows for escaping - the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will produce the - string literal "$(VAR_NAME)". Escaped references will never - be expanded, regardless of whether the variable exists or - not. Defaults to "".' - type: string - valueFrom: - description: Source for the environment variable's value. Cannot - be used if value is not empty. - properties: - configMapKeyRef: - description: Selects a key of a ConfigMap. - properties: - key: - description: The key to select. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - optional: - description: Specify whether the ConfigMap or its key - must be defined - type: boolean - required: - - key - type: object - fieldRef: - description: 'Selects a field of the pod: supports metadata.name, - metadata.namespace, `metadata.labels['''']`, `metadata.annotations['''']`, - spec.nodeName, spec.serviceAccountName, status.hostIP, - status.podIP, status.podIPs.' - properties: - apiVersion: - description: Version of the schema the FieldPath is - written in terms of, defaults to "v1". - type: string - fieldPath: - description: Path of the field to select in the specified - API version. - type: string - required: - - fieldPath - type: object - resourceFieldRef: - description: 'Selects a resource of the container: only - resources limits and requests (limits.cpu, limits.memory, - limits.ephemeral-storage, requests.cpu, requests.memory - and requests.ephemeral-storage) are currently supported.' - properties: - containerName: - description: 'Container name: required for volumes, - optional for env vars' - type: string - divisor: - anyOf: - - type: integer - - type: string - description: Specifies the output format of the exposed - resources, defaults to "1" - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - description: 'Required: resource to select' - type: string - required: - - resource - type: object - secretKeyRef: - description: Selects a key of a secret in the pod's namespace - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - optional: - description: Specify whether the Secret or its key must - be defined - type: boolean - required: - - key - type: object + claims: + description: + "Claims lists the names of resources, defined in + spec.resourceClaims, that are used by this container. \n This + is an alpha field and requires enabling the DynamicResourceAllocation + feature gate. \n This field is immutable." + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: + Name must match the name of one entry in pod.spec.resourceClaims + of the Pod where this field is used. It makes that resource + available inside a container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: + "Limits describes the maximum amount of compute resources + allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/" + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: + "Requests describes the minimum amount of compute + resources required. If Requests is omitted for a container, + it defaults to Limits if that is explicitly specified, otherwise + to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/" type: object - required: - - name type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - image: - description: Image is the container image to run. - type: string - initImage: - description: InitImage is the docker image to use as an init container - for the pod. - type: string - initSleep: - description: InitSleep is the time sleep in the init container - type: integer - interfaceCount: - description: InterfaceCount is number of interfaces to be attached - to the pod. - type: integer - ports: - additionalProperties: - description: ServicePort describes an external L4 port on the device. + tls: + description: TLS is the configuration the key/certs to use for management. properties: - innerPort: - description: InnerPort is port on the container to expose. - format: int32 - type: integer - outerPort: - description: OuterPort is port on the container to expose. - format: int32 - type: integer - required: - - innerPort - - outerPort - type: object - description: Ports are ports to create on the service. - type: object - resources: - description: Resources are the K8s resources to allocate to lemming - container. - properties: - claims: - description: "Claims lists the names of resources, defined in - spec.resourceClaims, that are used by this container. \n This - is an alpha field and requires enabling the DynamicResourceAllocation - feature gate. \n This field is immutable." - items: - description: ResourceClaim references one entry in PodSpec.ResourceClaims. + selfSigned: + description: SelfSigned generates a new self signed certificate. properties: - name: - description: Name must match the name of one entry in pod.spec.resourceClaims - of the Pod where this field is used. It makes that resource - available inside a container. + commonName: + description: / Common name to set in the cert. type: string + keySize: + description: RSA keysize to use for key generation. + type: integer required: - - name + - commonName + - keySize type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount of compute resources - allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount of compute - resources required. If Requests is omitted for a container, - it defaults to Limits if that is explicitly specified, otherwise - to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - type: object - tls: - description: TLS is the configuration the key/certs to use for management. - properties: - selfSigned: - description: SelfSigned generates a new self signed certificate. - properties: - commonName: - description: / Common name to set in the cert. - type: string - keySize: - description: RSA keysize to use for key generation. - type: integer - required: - - commonName - - keySize - type: object - type: object - type: object - status: - description: LemmingStatus defines the observed state of Lemming - properties: - message: - description: Message describes why the lemming is in the current phase. - type: string - phase: - description: Phase is the overall status of the Lemming. - type: string - required: - - message - - phase - type: object - type: object - served: true - storage: true - subresources: - status: {} + type: object + type: object + status: + description: LemmingStatus defines the observed state of Lemming + properties: + message: + description: Message describes why the lemming is in the current phase. + type: string + phase: + description: Phase is the overall status of the Lemming. + type: string + required: + - message + - phase + type: object + type: object + served: true + storage: true + subresources: + status: {} --- apiVersion: v1 kind: ServiceAccount @@ -293,37 +319,37 @@ metadata: name: lemming-leader-election-role namespace: lemming-operator rules: -- apiGroups: - - "" - resources: - - configmaps - verbs: - - get - - list - - watch - - create - - update - - patch - - delete -- apiGroups: - - coordination.k8s.io - resources: - - leases - verbs: - - get - - list - - watch - - create - - update - - patch - - delete -- apiGroups: - - "" - resources: - - events - verbs: - - create - - patch + - apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch + - create + - update + - patch + - delete + - apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - get + - list + - watch + - create + - update + - patch + - delete + - apiGroups: + - "" + resources: + - events + verbs: + - create + - patch --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -331,74 +357,74 @@ metadata: creationTimestamp: null name: lemming-manager-role rules: -- apiGroups: - - "" - resources: - - pods - - secrets - - services - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - lemming.openconfig.net - resources: - - lemmings - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - lemming.openconfig.net - resources: - - lemmings/finalizers - verbs: - - update -- apiGroups: - - lemming.openconfig.net - resources: - - lemmings/status - verbs: - - get - - patch - - update + - apiGroups: + - "" + resources: + - pods + - secrets + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - lemming.openconfig.net + resources: + - lemmings + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - lemming.openconfig.net + resources: + - lemmings/finalizers + verbs: + - update + - apiGroups: + - lemming.openconfig.net + resources: + - lemmings/status + verbs: + - get + - patch + - update --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: lemming-metrics-reader rules: -- nonResourceURLs: - - /metrics - verbs: - - get + - nonResourceURLs: + - /metrics + verbs: + - get --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: lemming-proxy-role rules: -- apiGroups: - - authentication.k8s.io - resources: - - tokenreviews - verbs: - - create -- apiGroups: - - authorization.k8s.io - resources: - - subjectaccessreviews - verbs: - - create + - apiGroups: + - authentication.k8s.io + resources: + - tokenreviews + verbs: + - create + - apiGroups: + - authorization.k8s.io + resources: + - subjectaccessreviews + verbs: + - create --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding @@ -410,9 +436,9 @@ roleRef: kind: Role name: lemming-leader-election-role subjects: -- kind: ServiceAccount - name: lemming-controller-manager - namespace: lemming-operator + - kind: ServiceAccount + name: lemming-controller-manager + namespace: lemming-operator --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding @@ -423,9 +449,9 @@ roleRef: kind: ClusterRole name: lemming-manager-role subjects: -- kind: ServiceAccount - name: lemming-controller-manager - namespace: lemming-operator + - kind: ServiceAccount + name: lemming-controller-manager + namespace: lemming-operator --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding @@ -436,9 +462,9 @@ roleRef: kind: ClusterRole name: lemming-proxy-role subjects: -- kind: ServiceAccount - name: lemming-controller-manager - namespace: lemming-operator + - kind: ServiceAccount + name: lemming-controller-manager + namespace: lemming-operator --- apiVersion: v1 data: @@ -478,10 +504,10 @@ metadata: namespace: lemming-operator spec: ports: - - name: https - port: 8443 - protocol: TCP - targetPort: https + - name: https + port: 8443 + protocol: TCP + targetPort: https selector: control-plane: controller-manager --- @@ -505,61 +531,61 @@ spec: control-plane: controller-manager spec: containers: - - args: - - --secure-listen-address=0.0.0.0:8443 - - --upstream=http://127.0.0.1:8080/ - - --logtostderr=true - - --v=0 - image: registry.k8s.io/kubebuilder/kube-rbac-proxy:v0.12.0 - name: kube-rbac-proxy - ports: - - containerPort: 8443 - name: https - protocol: TCP - resources: - limits: - cpu: 500m - memory: 128Mi - requests: - cpu: 5m - memory: 64Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - - args: - - --health-probe-bind-address=:8081 - - --metrics-bind-address=127.0.0.1:8080 - - --leader-elect - command: - - /manager - image: us-west1-docker.pkg.dev/openconfig-lemming/release/operator:v0.2.4 - livenessProbe: - httpGet: - path: /healthz - port: 8081 - initialDelaySeconds: 15 - periodSeconds: 20 - name: manager - readinessProbe: - httpGet: - path: /readyz - port: 8081 - initialDelaySeconds: 5 - periodSeconds: 10 - resources: - limits: - cpu: 500m - memory: 128Mi - requests: - cpu: 10m - memory: 64Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL + - args: + - --secure-listen-address=0.0.0.0:8443 + - --upstream=http://127.0.0.1:8080/ + - --logtostderr=true + - --v=0 + image: registry.k8s.io/kubebuilder/kube-rbac-proxy:v0.12.0 + name: kube-rbac-proxy + ports: + - containerPort: 8443 + name: https + protocol: TCP + resources: + limits: + cpu: 500m + memory: 128Mi + requests: + cpu: 5m + memory: 64Mi + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + - args: + - --health-probe-bind-address=:8081 + - --metrics-bind-address=127.0.0.1:8080 + - --leader-elect + command: + - /manager + image: us-west1-docker.pkg.dev/openconfig-lemming/release/operator:v0.2.4 + livenessProbe: + httpGet: + path: /healthz + port: 8081 + initialDelaySeconds: 15 + periodSeconds: 20 + name: manager + readinessProbe: + httpGet: + path: /readyz + port: 8081 + initialDelaySeconds: 5 + periodSeconds: 10 + resources: + limits: + cpu: 500m + memory: 128Mi + requests: + cpu: 10m + memory: 64Mi + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL securityContext: runAsNonRoot: true serviceAccountName: lemming-controller-manager diff --git a/manifests/controllers/srlinux/manifest.yaml b/manifests/controllers/srlinux/manifest.yaml index ba211a4b2..6936bb2f2 100644 --- a/manifests/controllers/srlinux/manifest.yaml +++ b/manifests/controllers/srlinux/manifest.yaml @@ -1,3 +1,4 @@ +--- apiVersion: v1 kind: Namespace metadata: @@ -26,159 +27,166 @@ spec: singular: srlinux scope: Namespaced versions: - - additionalPrinterColumns: - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - - jsonPath: .status.image - name: Image - type: string - - jsonPath: .status.status - name: Status - type: string - - jsonPath: .status.ready - name: Ready - type: boolean - - jsonPath: .status.startup-config.phase - name: Config - type: string - name: v1 - schema: - openAPIV3Schema: - description: Srlinux is the Schema for the srlinuxes API. - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - license_key: - description: license key from license secret that contains a license file - for this Srlinux - type: string - metadata: - type: object - spec: - description: SrlinuxSpec defines the desired state of Srlinux. - properties: - config: - description: NodeConfig represents srlinux node configuration parameters. - properties: - args: - description: Command args to pass into the pod. - items: - type: string - type: array - cert: - description: CertificateCfg represents srlinux certificate configuration - parameters. - properties: - cert_name: - description: Certificate name on the node. + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + - jsonPath: .status.image + name: Image + type: string + - jsonPath: .status.status + name: Status + type: string + - jsonPath: .status.ready + name: Ready + type: boolean + - jsonPath: .status.startup-config.phase + name: Config + type: string + name: v1 + schema: + openAPIV3Schema: + description: Srlinux is the Schema for the srlinuxes API. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + license_key: + description: + license key from license secret that contains a license file + for this Srlinux + type: string + metadata: + type: object + spec: + description: SrlinuxSpec defines the desired state of Srlinux. + properties: + config: + description: NodeConfig represents srlinux node configuration parameters. + properties: + args: + description: Command args to pass into the pod. + items: type: string - common_name: - description: Common name to set in the cert. + type: array + cert: + description: + CertificateCfg represents srlinux certificate configuration + parameters. + properties: + cert_name: + description: Certificate name on the node. + type: string + common_name: + description: Common name to set in the cert. + type: string + key_name: + description: Key name on the node. + type: string + key_size: + description: RSA keysize to use for key generation. + format: int32 + type: integer + type: object + command: + description: Command to pass into pod. + items: type: string - key_name: - description: Key name on the node. + type: array + config_data_present: + description: + When set to true by kne, srlinux controller will + attempt to mount the file with startup config to the pod + type: boolean + config_file: + description: + Startup configuration file name for the pod. Set + in the kne topo and created by kne as a config map + type: string + config_path: + description: + Mount point for configuration inside the pod. Should + point to a dir that contains ConfigFile + type: string + entry_command: + description: Specific entry point command for accessing the pod. + type: string + env: + additionalProperties: type: string - key_size: - description: RSA keysize to use for key generation. - format: int32 - type: integer - type: object - command: - description: Command to pass into pod. - items: + description: Map of environment variables to pass into the pod. + type: object + image: + description: Container image to use with for the SR Linux container. type: string - type: array - config_data_present: - description: When set to true by kne, srlinux controller will - attempt to mount the file with startup config to the pod - type: boolean - config_file: - description: Startup configuration file name for the pod. Set - in the kne topo and created by kne as a config map - type: string - config_path: - description: Mount point for configuration inside the pod. Should - point to a dir that contains ConfigFile - type: string - entry_command: - description: Specific entry point command for accessing the pod. - type: string - env: - additionalProperties: + init-image: + description: + Init container image to use with for the SR Linux + container. type: string - description: Map of environment variables to pass into the pod. - type: object - image: - description: Container image to use with for the SR Linux container. + sleep: + description: Sleep time before starting the pod. + format: int32 + type: integer + type: object + constraints: + additionalProperties: type: string - init-image: - description: Init container image to use with for the SR Linux - container. - type: string - sleep: - description: Sleep time before starting the pod. - format: int32 - type: integer - type: object - constraints: - additionalProperties: + type: object + model: + description: Model encodes SR Linux variant (ixr-d3, ixr-6e, etc) type: string - type: object - model: - description: Model encodes SR Linux variant (ixr-d3, ixr-6e, etc) - type: string - num-interfaces: - type: integer - version: - description: |- - Version may be set in kne topology as a mean to explicitly provide version information - in case it is not encoded in the image tag - type: string - type: object - status: - description: SrlinuxStatus defines the observed state of Srlinux. - properties: - image: - description: Image used to run srlinux pod - type: string - ready: - description: |- - Ready is true if the srlinux NOS is ready to receive config. - This is when management server is running and initial commit is processed. - type: boolean - startup-config: - description: StartupConfig contains the status of the startup-config. - properties: - phase: - description: 'Phase is the phase startup-config is in. Can be - one of: "pending", "loaded", "not-provided", "failed".' - type: string - type: object - status: - description: |- - Status is the status of the srlinux custom resource. - Can be one of: "created", "running", "error". - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} + num-interfaces: + type: integer + version: + description: |- + Version may be set in kne topology as a mean to explicitly provide version information + in case it is not encoded in the image tag + type: string + type: object + status: + description: SrlinuxStatus defines the observed state of Srlinux. + properties: + image: + description: Image used to run srlinux pod + type: string + ready: + description: |- + Ready is true if the srlinux NOS is ready to receive config. + This is when management server is running and initial commit is processed. + type: boolean + startup-config: + description: StartupConfig contains the status of the startup-config. + properties: + phase: + description: + 'Phase is the phase startup-config is in. Can be + one of: "pending", "loaded", "not-provided", "failed".' + type: string + type: object + status: + description: |- + Status is the status of the srlinux custom resource. + Can be one of: "created", "running", "error". + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} --- apiVersion: v1 kind: ServiceAccount @@ -206,83 +214,83 @@ metadata: name: srlinux-controller-leader-election-role namespace: srlinux-controller rules: -- apiGroups: - - "" - resources: - - configmaps - verbs: - - get - - list - - watch - - create - - update - - patch - - delete -- apiGroups: - - coordination.k8s.io - resources: - - leases - verbs: - - get - - list - - watch - - create - - update - - patch - - delete -- apiGroups: - - "" - resources: - - events - verbs: - - create - - patch + - apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch + - create + - update + - patch + - delete + - apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - get + - list + - watch + - create + - update + - patch + - delete + - apiGroups: + - "" + resources: + - events + verbs: + - create + - patch --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: srlinux-controller-manager-role rules: -- apiGroups: - - "" - resources: - - configmaps - - pods - - secrets - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - kne.srlinux.dev - resources: - - srlinuxes - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - kne.srlinux.dev - resources: - - srlinuxes/finalizers - verbs: - - update -- apiGroups: - - kne.srlinux.dev - resources: - - srlinuxes/status - verbs: - - get - - patch - - update + - apiGroups: + - "" + resources: + - configmaps + - pods + - secrets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - kne.srlinux.dev + resources: + - srlinuxes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - kne.srlinux.dev + resources: + - srlinuxes/finalizers + verbs: + - update + - apiGroups: + - kne.srlinux.dev + resources: + - srlinuxes/status + verbs: + - get + - patch + - update --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -296,10 +304,10 @@ metadata: app.kubernetes.io/part-of: srlinux-controller name: srlinux-controller-metrics-reader rules: -- nonResourceURLs: - - /metrics - verbs: - - get + - nonResourceURLs: + - /metrics + verbs: + - get --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -313,18 +321,18 @@ metadata: app.kubernetes.io/part-of: srlinux-controller name: srlinux-controller-proxy-role rules: -- apiGroups: - - authentication.k8s.io - resources: - - tokenreviews - verbs: - - create -- apiGroups: - - authorization.k8s.io - resources: - - subjectaccessreviews - verbs: - - create + - apiGroups: + - authentication.k8s.io + resources: + - tokenreviews + verbs: + - create + - apiGroups: + - authorization.k8s.io + resources: + - subjectaccessreviews + verbs: + - create --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding @@ -343,9 +351,9 @@ roleRef: kind: Role name: srlinux-controller-leader-election-role subjects: -- kind: ServiceAccount - name: srlinux-controller-controller-manager - namespace: srlinux-controller + - kind: ServiceAccount + name: srlinux-controller-controller-manager + namespace: srlinux-controller --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding @@ -363,9 +371,9 @@ roleRef: kind: ClusterRole name: srlinux-controller-manager-role subjects: -- kind: ServiceAccount - name: srlinux-controller-controller-manager - namespace: srlinux-controller + - kind: ServiceAccount + name: srlinux-controller-controller-manager + namespace: srlinux-controller --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding @@ -383,9 +391,9 @@ roleRef: kind: ClusterRole name: srlinux-controller-proxy-role subjects: -- kind: ServiceAccount - name: srlinux-controller-controller-manager - namespace: srlinux-controller + - kind: ServiceAccount + name: srlinux-controller-controller-manager + namespace: srlinux-controller --- apiVersion: v1 kind: Service @@ -402,10 +410,10 @@ metadata: namespace: srlinux-controller spec: ports: - - name: https - port: 8443 - protocol: TCP - targetPort: https + - name: https + port: 8443 + protocol: TCP + targetPort: https selector: control-plane: controller-manager --- @@ -438,74 +446,74 @@ spec: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - - matchExpressions: - - key: kubernetes.io/arch - operator: In - values: - - amd64 - - arm64 - - ppc64le - - s390x - - key: kubernetes.io/os - operator: In - values: - - linux + - matchExpressions: + - key: kubernetes.io/arch + operator: In + values: + - amd64 + - arm64 + - ppc64le + - s390x + - key: kubernetes.io/os + operator: In + values: + - linux containers: - - args: - - --secure-listen-address=0.0.0.0:8443 - - --upstream=http://127.0.0.1:8080/ - - --logtostderr=true - - --v=0 - image: registry.k8s.io/kubebuilder/kube-rbac-proxy:v0.13.1 - name: kube-rbac-proxy - ports: - - containerPort: 8443 - name: https - protocol: TCP - resources: - limits: - cpu: 500m - memory: 128Mi - requests: - cpu: 5m - memory: 64Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - - args: - - --health-probe-bind-address=:8081 - - --metrics-bind-address=127.0.0.1:8080 - - --leader-elect - command: - - /manager - image: ghcr.io/srl-labs/srl-controller:v0.7.1 - livenessProbe: - httpGet: - path: /healthz - port: 8081 - initialDelaySeconds: 15 - periodSeconds: 20 - name: manager - readinessProbe: - httpGet: - path: /readyz - port: 8081 - initialDelaySeconds: 5 - periodSeconds: 10 - resources: - limits: - cpu: 500m - memory: 128Mi - requests: - cpu: 10m - memory: 64Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL + - args: + - --secure-listen-address=0.0.0.0:8443 + - --upstream=http://127.0.0.1:8080/ + - --logtostderr=true + - --v=0 + image: registry.k8s.io/kubebuilder/kube-rbac-proxy:v0.13.1 + name: kube-rbac-proxy + ports: + - containerPort: 8443 + name: https + protocol: TCP + resources: + limits: + cpu: 500m + memory: 128Mi + requests: + cpu: 5m + memory: 64Mi + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + - args: + - --health-probe-bind-address=:8081 + - --metrics-bind-address=127.0.0.1:8080 + - --leader-elect + command: + - /manager + image: ghcr.io/srl-labs/srl-controller:v0.7.1 + livenessProbe: + httpGet: + path: /healthz + port: 8081 + initialDelaySeconds: 15 + periodSeconds: 20 + name: manager + readinessProbe: + httpGet: + path: /readyz + port: 8081 + initialDelaySeconds: 5 + periodSeconds: 10 + resources: + limits: + cpu: 500m + memory: 128Mi + requests: + cpu: 10m + memory: 64Mi + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL securityContext: runAsNonRoot: true serviceAccountName: srlinux-controller-controller-manager diff --git a/manifests/flannel/manifest.yaml b/manifests/flannel/manifest.yaml index 9f7b2eb53..e846108ab 100644 --- a/manifests/flannel/manifest.yaml +++ b/manifests/flannel/manifest.yaml @@ -14,33 +14,33 @@ metadata: k8s-app: flannel name: flannel rules: -- apiGroups: - - "" - resources: - - pods - verbs: - - get -- apiGroups: - - "" - resources: - - nodes - verbs: - - get - - list - - watch -- apiGroups: - - "" - resources: - - nodes/status - verbs: - - patch -- apiGroups: - - networking.k8s.io - resources: - - clustercidrs - verbs: - - list - - watch + - apiGroups: + - "" + resources: + - pods + verbs: + - get + - apiGroups: + - "" + resources: + - nodes + verbs: + - get + - list + - watch + - apiGroups: + - "" + resources: + - nodes/status + verbs: + - patch + - apiGroups: + - networking.k8s.io + resources: + - clustercidrs + verbs: + - list + - watch --- kind: ClusterRoleBinding apiVersion: rbac.authorization.k8s.io/v1 @@ -53,9 +53,9 @@ roleRef: kind: ClusterRole name: flannel subjects: -- kind: ServiceAccount - name: flannel - namespace: kube-flannel + - kind: ServiceAccount + name: flannel + namespace: kube-flannel --- apiVersion: v1 kind: ServiceAccount @@ -126,90 +126,90 @@ spec: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - - matchExpressions: - - key: kubernetes.io/os - operator: In - values: - - linux + - matchExpressions: + - key: kubernetes.io/os + operator: In + values: + - linux hostNetwork: true priorityClassName: system-node-critical tolerations: - - operator: Exists - effect: NoSchedule + - operator: Exists + effect: NoSchedule serviceAccountName: flannel initContainers: - - name: install-cni-plugin - image: docker.io/flannel/flannel-cni-plugin:v1.4.0-flannel1 - command: - - cp - args: - - -f - - /flannel - - /opt/cni/bin/flannel - volumeMounts: - - name: cni-plugin - mountPath: /opt/cni/bin - - name: install-cni - image: docker.io/flannel/flannel:v0.24.3 - command: - - cp - args: - - -f - - /etc/kube-flannel/cni-conf.json - - /etc/cni/net.d/10-flannel.conflist - volumeMounts: - - name: cni - mountPath: /etc/cni/net.d - - name: flannel-cfg - mountPath: /etc/kube-flannel/ + - name: install-cni-plugin + image: docker.io/flannel/flannel-cni-plugin:v1.4.0-flannel1 + command: + - cp + args: + - -f + - /flannel + - /opt/cni/bin/flannel + volumeMounts: + - name: cni-plugin + mountPath: /opt/cni/bin + - name: install-cni + image: docker.io/flannel/flannel:v0.24.3 + command: + - cp + args: + - -f + - /etc/kube-flannel/cni-conf.json + - /etc/cni/net.d/10-flannel.conflist + volumeMounts: + - name: cni + mountPath: /etc/cni/net.d + - name: flannel-cfg + mountPath: /etc/kube-flannel/ containers: - - name: kube-flannel - image: docker.io/flannel/flannel:v0.24.3 - command: - - /opt/bin/flanneld - args: - - --ip-masq - - --kube-subnet-mgr - resources: - requests: - cpu: "100m" - memory: "50Mi" - securityContext: - privileged: false - capabilities: - add: ["NET_ADMIN", "NET_RAW"] - env: - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: EVENT_QUEUE_DEPTH - value: "5000" - volumeMounts: + - name: kube-flannel + image: docker.io/flannel/flannel:v0.24.3 + command: + - /opt/bin/flanneld + args: + - --ip-masq + - --kube-subnet-mgr + resources: + requests: + cpu: "100m" + memory: "50Mi" + securityContext: + privileged: false + capabilities: + add: ["NET_ADMIN", "NET_RAW"] + env: + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: EVENT_QUEUE_DEPTH + value: "5000" + volumeMounts: + - name: run + mountPath: /run/flannel + - name: flannel-cfg + mountPath: /etc/kube-flannel/ + - name: xtables-lock + mountPath: /run/xtables.lock + volumes: - name: run - mountPath: /run/flannel + hostPath: + path: /run/flannel + - name: cni-plugin + hostPath: + path: /opt/cni/bin + - name: cni + hostPath: + path: /etc/cni/net.d - name: flannel-cfg - mountPath: /etc/kube-flannel/ + configMap: + name: kube-flannel-cfg - name: xtables-lock - mountPath: /run/xtables.lock - volumes: - - name: run - hostPath: - path: /run/flannel - - name: cni-plugin - hostPath: - path: /opt/cni/bin - - name: cni - hostPath: - path: /etc/cni/net.d - - name: flannel-cfg - configMap: - name: kube-flannel-cfg - - name: xtables-lock - hostPath: - path: /run/xtables.lock - type: FileOrCreate + hostPath: + path: /run/xtables.lock + type: FileOrCreate diff --git a/manifests/keysight/ixiatg-configmap.yaml b/manifests/keysight/ixiatg-configmap.yaml index 2028969b0..f3fe166b2 100644 --- a/manifests/keysight/ixiatg-configmap.yaml +++ b/manifests/keysight/ixiatg-configmap.yaml @@ -1,3 +1,4 @@ +--- apiVersion: v1 kind: ConfigMap metadata: diff --git a/manifests/keysight/ixiatg-operator.yaml b/manifests/keysight/ixiatg-operator.yaml index 7905959fe..4d24e137c 100644 --- a/manifests/keysight/ixiatg-operator.yaml +++ b/manifests/keysight/ixiatg-operator.yaml @@ -1,3 +1,4 @@ +--- apiVersion: v1 kind: Namespace metadata: @@ -21,104 +22,104 @@ spec: singular: ixiatg scope: Namespaced versions: - - name: v1beta1 - schema: - openAPIV3Schema: - description: IxiaTG is the Schema for the ixiatg API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: IxiaTGSpec defines the desired state of IxiaTG - properties: - api_endpoint_map: - additionalProperties: - description: IxiaTGSvcPort defines the endpoint services for configuration and stats for the OTG node - properties: - in: - format: int32 - type: integer - out: - format: int32 - type: integer - required: - - in + - name: v1beta1 + schema: + openAPIV3Schema: + description: IxiaTG is the Schema for the ixiatg API + properties: + apiVersion: + description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources" + type: string + kind: + description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" + type: string + metadata: + type: object + spec: + description: IxiaTGSpec defines the desired state of IxiaTG + properties: + api_endpoint_map: + additionalProperties: + description: IxiaTGSvcPort defines the endpoint services for configuration and stats for the OTG node + properties: + in: + format: int32 + type: integer + out: + format: int32 + type: integer + required: + - in + type: object + description: ApiEndPoint as define in OTG config type: object - description: ApiEndPoint as define in OTG config - type: object - desired_state: - description: Desired state by network emulation (KNE) - type: string - init_container: - description: Init container image of the node - properties: - image: - type: string - sleep: - format: int32 - type: integer - type: object - interfaces: - description: Interfaces with DUT - items: - description: IxiaTGSvcPort defines the endpoint ports for network traffic for the OTG node + desired_state: + description: Desired state by network emulation (KNE) + type: string + init_container: + description: Init container image of the node properties: - group: - type: string - name: + image: type: string - required: - - name + sleep: + format: int32 + type: integer type: object - type: array - release: - description: Version of the node - type: string - type: object - status: - description: IxiaTGStatus defines the observed state of IxiaTG - properties: - api_endpoint: - description: List of OTG service names - properties: - pod_name: - type: string - service_names: - items: - type: string - type: array - type: object - interfaces: - description: List of OTG port and pod mapping - items: - description: IxiaTGIntfStatus defines the mapping between endpoint ports and encasing pods + interfaces: + description: Interfaces with DUT + items: + description: IxiaTGSvcPort defines the endpoint ports for network traffic for the OTG node + properties: + group: + type: string + name: + type: string + required: + - name + type: object + type: array + release: + description: Version of the node + type: string + type: object + status: + description: IxiaTGStatus defines the observed state of IxiaTG + properties: + api_endpoint: + description: List of OTG service names properties: - interface: - type: string - name: - type: string pod_name: type: string + service_names: + items: + type: string + type: array type: object - type: array - reason: - description: Reason in case of failure - type: string - state: - description: Observed state - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} + interfaces: + description: List of OTG port and pod mapping + items: + description: IxiaTGIntfStatus defines the mapping between endpoint ports and encasing pods + properties: + interface: + type: string + name: + type: string + pod_name: + type: string + type: object + type: array + reason: + description: Reason in case of failure + type: string + state: + description: Observed state + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} status: acceptedNames: kind: "" @@ -138,37 +139,37 @@ metadata: name: ixiatg-op-leader-election-role namespace: ixiatg-op-system rules: -- apiGroups: - - "" - resources: - - configmaps - verbs: - - get - - list - - watch - - create - - update - - patch - - delete -- apiGroups: - - coordination.k8s.io - resources: - - leases - verbs: - - get - - list - - watch - - create - - update - - patch - - delete -- apiGroups: - - "" - resources: - - events - verbs: - - create - - patch + - apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch + - create + - update + - patch + - delete + - apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - get + - list + - watch + - create + - update + - patch + - delete + - apiGroups: + - "" + resources: + - events + verbs: + - create + - patch --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -176,108 +177,108 @@ metadata: creationTimestamp: null name: ixiatg-op-manager-role rules: -- apiGroups: - - "" - resources: - - configmaps - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - pods - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - secrets - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - services - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - network.keysight.com - resources: - - ixiatgs - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - network.keysight.com - resources: - - ixiatgs/finalizers - verbs: - - update -- apiGroups: - - network.keysight.com - resources: - - ixiatgs/status - verbs: - - get - - patch - - update + - apiGroups: + - "" + resources: + - configmaps + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - "" + resources: + - pods + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - "" + resources: + - secrets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - "" + resources: + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - network.keysight.com + resources: + - ixiatgs + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - network.keysight.com + resources: + - ixiatgs/finalizers + verbs: + - update + - apiGroups: + - network.keysight.com + resources: + - ixiatgs/status + verbs: + - get + - patch + - update --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: ixiatg-op-metrics-reader rules: -- nonResourceURLs: - - /metrics - verbs: - - get + - nonResourceURLs: + - /metrics + verbs: + - get --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: ixiatg-op-proxy-role rules: -- apiGroups: - - authentication.k8s.io - resources: - - tokenreviews - verbs: - - create -- apiGroups: - - authorization.k8s.io - resources: - - subjectaccessreviews - verbs: - - create + - apiGroups: + - authentication.k8s.io + resources: + - tokenreviews + verbs: + - create + - apiGroups: + - authorization.k8s.io + resources: + - subjectaccessreviews + verbs: + - create --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding @@ -289,9 +290,9 @@ roleRef: kind: Role name: ixiatg-op-leader-election-role subjects: -- kind: ServiceAccount - name: ixiatg-op-controller-manager - namespace: ixiatg-op-system + - kind: ServiceAccount + name: ixiatg-op-controller-manager + namespace: ixiatg-op-system --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding @@ -302,9 +303,9 @@ roleRef: kind: ClusterRole name: ixiatg-op-manager-role subjects: -- kind: ServiceAccount - name: ixiatg-op-controller-manager - namespace: ixiatg-op-system + - kind: ServiceAccount + name: ixiatg-op-controller-manager + namespace: ixiatg-op-system --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding @@ -315,9 +316,9 @@ roleRef: kind: ClusterRole name: ixiatg-op-proxy-role subjects: -- kind: ServiceAccount - name: ixiatg-op-controller-manager - namespace: ixiatg-op-system + - kind: ServiceAccount + name: ixiatg-op-controller-manager + namespace: ixiatg-op-system --- apiVersion: v1 data: @@ -347,9 +348,9 @@ metadata: namespace: ixiatg-op-system spec: ports: - - name: https - port: 8443 - targetPort: https + - name: https + port: 8443 + targetPort: https selector: control-plane: controller-manager --- @@ -371,47 +372,47 @@ spec: control-plane: controller-manager spec: containers: - - args: - - --secure-listen-address=0.0.0.0:8443 - - --upstream=http://127.0.0.1:8080/ - - --logtostderr=true - - --v=10 - image: registry.k8s.io/kubebuilder/kube-rbac-proxy:v0.8.0 - name: kube-rbac-proxy - ports: - - containerPort: 8443 - name: https - - args: - - --health-probe-bind-address=:8081 - - --metrics-bind-address=127.0.0.1:8080 - - --leader-elect - command: - - /manager - image: ghcr.io/open-traffic-generator/keng-operator:0.3.34 - livenessProbe: - httpGet: - path: /healthz - port: 8081 - initialDelaySeconds: 15 - periodSeconds: 20 - name: manager - readinessProbe: - httpGet: - path: /readyz - port: 8081 - initialDelaySeconds: 5 - periodSeconds: 10 - resources: - limits: - cpu: 100m - memory: 200Mi - requests: - cpu: 100m - memory: 20Mi - securityContext: - allowPrivilegeEscalation: false + - args: + - --secure-listen-address=0.0.0.0:8443 + - --upstream=http://127.0.0.1:8080/ + - --logtostderr=true + - --v=10 + image: registry.k8s.io/kubebuilder/kube-rbac-proxy:v0.8.0 + name: kube-rbac-proxy + ports: + - containerPort: 8443 + name: https + - args: + - --health-probe-bind-address=:8081 + - --metrics-bind-address=127.0.0.1:8080 + - --leader-elect + command: + - /manager + image: ghcr.io/open-traffic-generator/keng-operator:0.3.34 + livenessProbe: + httpGet: + path: /healthz + port: 8081 + initialDelaySeconds: 15 + periodSeconds: 20 + name: manager + readinessProbe: + httpGet: + path: /readyz + port: 8081 + initialDelaySeconds: 5 + periodSeconds: 10 + resources: + limits: + cpu: 100m + memory: 200Mi + requests: + cpu: 100m + memory: 20Mi + securityContext: + allowPrivilegeEscalation: false imagePullSecrets: - - name: ixia-pull-secret + - name: ixia-pull-secret securityContext: runAsNonRoot: true serviceAccountName: ixiatg-op-controller-manager diff --git a/manifests/kind/bridge.yaml b/manifests/kind/bridge.yaml index 84915de60..2f7d6991d 100644 --- a/manifests/kind/bridge.yaml +++ b/manifests/kind/bridge.yaml @@ -13,7 +13,7 @@ rules: - watch - patch - apiGroups: - - "" + - "" resources: - configmaps verbs: @@ -28,9 +28,9 @@ roleRef: kind: ClusterRole name: kindnet subjects: -- kind: ServiceAccount - name: kindnet - namespace: kube-system + - kind: ServiceAccount + name: kindnet + namespace: kube-system --- apiVersion: v1 kind: ServiceAccount @@ -60,66 +60,71 @@ spec: spec: hostNetwork: true tolerations: - - operator: Exists - effect: NoSchedule + - operator: Exists + effect: NoSchedule serviceAccountName: kindnet initContainers: - - name: install-cni-bin - image: ghcr.io/aojea/kindnetd:v1.7.0 - command: ['sh', '-c', 'cd /opt/cni/bin; for i in * ; do cat $i > /cni/$i ; chmod +x /cni/$i ; done'] - volumeMounts: - - name: cni-bin - mountPath: /cni + - name: install-cni-bin + image: ghcr.io/aojea/kindnetd:v1.7.0 + command: + [ + "sh", + "-c", + "cd /opt/cni/bin; for i in * ; do cat $i > /cni/$i ; chmod +x /cni/$i ; done", + ] + volumeMounts: + - name: cni-bin + mountPath: /cni containers: - - name: kindnet-cni - image: ghcr.io/aojea/kindnetd:v1.7.0 - env: - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: POD_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: CNI_BRIDGE - value: "true" - - name: DISABLE_CNI_BRIDGE_OFFLOAD - value: "true" - volumeMounts: + - name: kindnet-cni + image: ghcr.io/aojea/kindnetd:v1.7.0 + env: + - name: HOST_IP + valueFrom: + fieldRef: + fieldPath: status.hostIP + - name: POD_IP + valueFrom: + fieldRef: + fieldPath: status.podIP + - name: CNI_BRIDGE + value: "true" + - name: DISABLE_CNI_BRIDGE_OFFLOAD + value: "true" + volumeMounts: + - name: cni-cfg + mountPath: /etc/cni/net.d + - name: xtables-lock + mountPath: /run/xtables.lock + readOnly: false + - name: lib-modules + mountPath: /lib/modules + readOnly: true + resources: + requests: + cpu: "100m" + memory: "50Mi" + limits: + cpu: "100m" + memory: "50Mi" + securityContext: + privileged: false + capabilities: + add: ["NET_RAW", "NET_ADMIN"] + volumes: + - name: cni-bin + hostPath: + path: /opt/cni/bin + type: DirectoryOrCreate - name: cni-cfg - mountPath: /etc/cni/net.d + hostPath: + path: /etc/cni/net.d + type: DirectoryOrCreate - name: xtables-lock - mountPath: /run/xtables.lock - readOnly: false + hostPath: + path: /run/xtables.lock + type: FileOrCreate - name: lib-modules - mountPath: /lib/modules - readOnly: true - resources: - requests: - cpu: "100m" - memory: "50Mi" - limits: - cpu: "100m" - memory: "50Mi" - securityContext: - privileged: false - capabilities: - add: ["NET_RAW", "NET_ADMIN"] - volumes: - - name: cni-bin - hostPath: - path: /opt/cni/bin - type: DirectoryOrCreate - - name: cni-cfg - hostPath: - path: /etc/cni/net.d - type: DirectoryOrCreate - - name: xtables-lock - hostPath: - path: /run/xtables.lock - type: FileOrCreate - - name: lib-modules - hostPath: - path: /lib/modules + hostPath: + path: /lib/modules --- diff --git a/manifests/kind/config.yaml b/manifests/kind/config.yaml index 332ed08cb..5181a8603 100644 --- a/manifests/kind/config.yaml +++ b/manifests/kind/config.yaml @@ -1,11 +1,12 @@ +--- kind: Cluster apiVersion: kind.x-k8s.io/v1alpha4 networking: # the default CNI will not be installed disableDefaultCNI: true nodes: -- role: control-plane - # add a mount from /tmp/kne on the host to /tmp/kne on the node - extraMounts: - - hostPath: /tmp/kne - containerPath: /tmp/kne + - role: control-plane + # add a mount from /tmp/kne on the host to /tmp/kne on the node + extraMounts: + - hostPath: /tmp/kne + containerPath: /tmp/kne diff --git a/manifests/kind/kind-bridge.yaml b/manifests/kind/kind-bridge.yaml index 84915de60..2f7d6991d 100644 --- a/manifests/kind/kind-bridge.yaml +++ b/manifests/kind/kind-bridge.yaml @@ -13,7 +13,7 @@ rules: - watch - patch - apiGroups: - - "" + - "" resources: - configmaps verbs: @@ -28,9 +28,9 @@ roleRef: kind: ClusterRole name: kindnet subjects: -- kind: ServiceAccount - name: kindnet - namespace: kube-system + - kind: ServiceAccount + name: kindnet + namespace: kube-system --- apiVersion: v1 kind: ServiceAccount @@ -60,66 +60,71 @@ spec: spec: hostNetwork: true tolerations: - - operator: Exists - effect: NoSchedule + - operator: Exists + effect: NoSchedule serviceAccountName: kindnet initContainers: - - name: install-cni-bin - image: ghcr.io/aojea/kindnetd:v1.7.0 - command: ['sh', '-c', 'cd /opt/cni/bin; for i in * ; do cat $i > /cni/$i ; chmod +x /cni/$i ; done'] - volumeMounts: - - name: cni-bin - mountPath: /cni + - name: install-cni-bin + image: ghcr.io/aojea/kindnetd:v1.7.0 + command: + [ + "sh", + "-c", + "cd /opt/cni/bin; for i in * ; do cat $i > /cni/$i ; chmod +x /cni/$i ; done", + ] + volumeMounts: + - name: cni-bin + mountPath: /cni containers: - - name: kindnet-cni - image: ghcr.io/aojea/kindnetd:v1.7.0 - env: - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: POD_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: CNI_BRIDGE - value: "true" - - name: DISABLE_CNI_BRIDGE_OFFLOAD - value: "true" - volumeMounts: + - name: kindnet-cni + image: ghcr.io/aojea/kindnetd:v1.7.0 + env: + - name: HOST_IP + valueFrom: + fieldRef: + fieldPath: status.hostIP + - name: POD_IP + valueFrom: + fieldRef: + fieldPath: status.podIP + - name: CNI_BRIDGE + value: "true" + - name: DISABLE_CNI_BRIDGE_OFFLOAD + value: "true" + volumeMounts: + - name: cni-cfg + mountPath: /etc/cni/net.d + - name: xtables-lock + mountPath: /run/xtables.lock + readOnly: false + - name: lib-modules + mountPath: /lib/modules + readOnly: true + resources: + requests: + cpu: "100m" + memory: "50Mi" + limits: + cpu: "100m" + memory: "50Mi" + securityContext: + privileged: false + capabilities: + add: ["NET_RAW", "NET_ADMIN"] + volumes: + - name: cni-bin + hostPath: + path: /opt/cni/bin + type: DirectoryOrCreate - name: cni-cfg - mountPath: /etc/cni/net.d + hostPath: + path: /etc/cni/net.d + type: DirectoryOrCreate - name: xtables-lock - mountPath: /run/xtables.lock - readOnly: false + hostPath: + path: /run/xtables.lock + type: FileOrCreate - name: lib-modules - mountPath: /lib/modules - readOnly: true - resources: - requests: - cpu: "100m" - memory: "50Mi" - limits: - cpu: "100m" - memory: "50Mi" - securityContext: - privileged: false - capabilities: - add: ["NET_RAW", "NET_ADMIN"] - volumes: - - name: cni-bin - hostPath: - path: /opt/cni/bin - type: DirectoryOrCreate - - name: cni-cfg - hostPath: - path: /etc/cni/net.d - type: DirectoryOrCreate - - name: xtables-lock - hostPath: - path: /run/xtables.lock - type: FileOrCreate - - name: lib-modules - hostPath: - path: /lib/modules + hostPath: + path: /lib/modules --- diff --git a/manifests/kube/credential-provider-config.yaml b/manifests/kube/credential-provider-config.yaml index bd5cdd310..074161856 100644 --- a/manifests/kube/credential-provider-config.yaml +++ b/manifests/kube/credential-provider-config.yaml @@ -1,15 +1,16 @@ +--- kind: CredentialProviderConfig apiVersion: kubelet.config.k8s.io/v1 providers: -- name: auth-provider-gcp - apiVersion: credentialprovider.kubelet.k8s.io/v1 - matchImages: - - "container.cloud.google.com" - - "gcr.io" - - "*.gcr.io" - - "*.pkg.dev" - - "registry.k8s.io" - args: - - get-credentials - - --v=3 - defaultCacheDuration: 1m + - name: auth-provider-gcp + apiVersion: credentialprovider.kubelet.k8s.io/v1 + matchImages: + - "container.cloud.google.com" + - "gcr.io" + - "*.gcr.io" + - "*.pkg.dev" + - "registry.k8s.io" + args: + - get-credentials + - --v=3 + defaultCacheDuration: 1m diff --git a/manifests/meshnet/grpc/manifest.yaml b/manifests/meshnet/grpc/manifest.yaml index bcda4c210..76bb464b3 100644 --- a/manifests/meshnet/grpc/manifest.yaml +++ b/manifests/meshnet/grpc/manifest.yaml @@ -1,3 +1,4 @@ +--- apiVersion: v1 kind: Namespace metadata: @@ -23,100 +24,110 @@ spec: singular: gwirekobj scope: Namespaced versions: - - name: v1beta1 - schema: - openAPIV3Schema: - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource - this object represents. Servers may infer this from the endpoint - the client submits requests to. Cannot be updated. In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - uids: - description: unique link id - items: - type: integer - type: array - type: object - status: - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - grpcWireItems: - items: - properties: - gwire_peer_node_ip: - description: peer node IP address - type: string - link_id: - description: Unique link id as assigned by meshnet - format: int64 - type: integer - local_pod_iface_name: - description: Local pod interface name that is specified in topology - CR and is created by meshnet - type: string - local_pod_ip: - description: Local pod ip as specified in topology CR - type: string - local_pod_name: - description: Local pod name as specified in topology CR - type: string - local_pod_net_ns: - description: Netwokr namespace of the local pod holding the - wire end - type: string - node_name: - description: Name of the node holding the wire end - type: string - topo_namespace: - description: The topology namespace. - type: string - wire_iface_id_on_peer_node: - description: The interface id, in the peer node adn is connected - with remote pod. This is used for de-multiplexing received - packet from grpcwire - format: int64 - type: integer - wire_iface_name_on_local_node: - description: The interface(name) in the local node and is connected - with local pod - type: string - type: object - type: array - kind: - description: 'Kind is a string value representing the REST resource - this object represents. Servers may infer this from the endpoint - the client submits requests to. Cannot be updated. In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - type: object - type: object - served: true - storage: true + - name: v1beta1 + schema: + openAPIV3Schema: + properties: + apiVersion: + description: + "APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources" + type: string + kind: + description: + "Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" + type: string + metadata: + type: object + spec: + properties: + apiVersion: + description: + "APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources" + type: string + kind: + description: + "Kind is a string value representing the REST resource + this object represents. Servers may infer this from the endpoint + the client submits requests to. Cannot be updated. In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" + type: string + uids: + description: unique link id + items: + type: integer + type: array + type: object + status: + properties: + apiVersion: + description: + "APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources" + type: string + grpcWireItems: + items: + properties: + gwire_peer_node_ip: + description: peer node IP address + type: string + link_id: + description: Unique link id as assigned by meshnet + format: int64 + type: integer + local_pod_iface_name: + description: + Local pod interface name that is specified in topology + CR and is created by meshnet + type: string + local_pod_ip: + description: Local pod ip as specified in topology CR + type: string + local_pod_name: + description: Local pod name as specified in topology CR + type: string + local_pod_net_ns: + description: + Netwokr namespace of the local pod holding the + wire end + type: string + node_name: + description: Name of the node holding the wire end + type: string + topo_namespace: + description: The topology namespace. + type: string + wire_iface_id_on_peer_node: + description: + The interface id, in the peer node and is connected + with remote pod. This is used for de-multiplexing received + packet from grpcwire + format: int64 + type: integer + wire_iface_name_on_local_node: + description: + The interface(name) in the local node and is connected + with local pod + type: string + type: object + type: array + kind: + description: + "Kind is a string value representing the REST resource + this object represents. Servers may infer this from the endpoint + the client submits requests to. Cannot be updated. In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" + type: string + type: object + type: object + served: true + storage: true --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition @@ -130,75 +141,75 @@ spec: kind: Topology plural: topologies shortNames: - - topo + - topo singular: topology scope: Namespaced versions: - - name: v1beta1 - schema: - openAPIV3Schema: - properties: - spec: - properties: - links: - items: - description: A complete definition of a p2p link - properties: - local_intf: - description: Local interface name - type: string - local_ip: - description: (Optional) Peer IP address - type: string - peer_intf: - description: Peer interface name - type: string - peer_ip: - description: (Optional) Local IP address - type: string - peer_pod: - description: Name of the peer pod - type: string - uid: - description: Unique identified of a p2p link - type: integer - required: - - uid - - peer_pod - - local_intf - - peer_intf - type: object - type: array - type: object - status: - properties: - container_id: - description: Sandbox ID of the POD - type: string - net_ns: - description: Network namespace of the POD - type: string - skipped: - description: List of pods/interfaces that are skipped by local pod - items: - properties: - link_id: - format: int64 - type: integer - pod_name: - description: peer pod name - type: string - type: object - type: array - src_ip: - description: Source IP of the POD - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} + - name: v1beta1 + schema: + openAPIV3Schema: + properties: + spec: + properties: + links: + items: + description: A complete definition of a p2p link + properties: + local_intf: + description: Local interface name + type: string + local_ip: + description: (Optional) Peer IP address + type: string + peer_intf: + description: Peer interface name + type: string + peer_ip: + description: (Optional) Local IP address + type: string + peer_pod: + description: Name of the peer pod + type: string + uid: + description: Unique identified of a p2p link + type: integer + required: + - uid + - peer_pod + - local_intf + - peer_intf + type: object + type: array + type: object + status: + properties: + container_id: + description: Sandbox ID of the POD + type: string + net_ns: + description: Network namespace of the POD + type: string + skipped: + description: List of pods/interfaces that are skipped by local pod + items: + properties: + link_id: + format: int64 + type: integer + pod_name: + description: peer pod name + type: string + type: object + type: array + src_ip: + description: Source IP of the POD + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} status: acceptedNames: kind: "" @@ -221,21 +232,21 @@ metadata: app: meshnet name: meshnet-clusterrole rules: -- apiGroups: - - networkop.co.uk - resources: - - topologies - - gwirekobjs - verbs: - - '*' -- apiGroups: - - networkop.co.uk - resources: - - topologies/status - - gwirekobjs/spec - - gwirekobjs/status - verbs: - - '*' + - apiGroups: + - networkop.co.uk + resources: + - topologies + - gwirekobjs + verbs: + - "*" + - apiGroups: + - networkop.co.uk + resources: + - topologies/status + - gwirekobjs/spec + - gwirekobjs/status + verbs: + - "*" --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding @@ -248,9 +259,9 @@ roleRef: kind: ClusterRole name: meshnet-clusterrole subjects: -- kind: ServiceAccount - name: meshnet - namespace: meshnet + - kind: ServiceAccount + name: meshnet + namespace: meshnet --- apiVersion: apps/v1 kind: DaemonSet @@ -272,46 +283,46 @@ spec: name: meshnet spec: containers: - - command: - - ./entrypoint.sh - env: - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: INTER_NODE_LINK_TYPE - value: GRPC - - name: NODE_NAME - valueFrom: - fieldRef: - fieldPath: spec.nodeName - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - image: us-west1-docker.pkg.dev/kne-external/kne/networkop/meshnet:v0.3.2 - imagePullPolicy: IfNotPresent - name: meshnet - resources: - limits: - memory: 10G - requests: - cpu: 200m - memory: 1G - securityContext: - privileged: true - volumeMounts: - - mountPath: /etc/cni/net.d - name: cni-cfg - - mountPath: /opt/cni/bin - name: cni-bin - - mountPath: /var/run/netns - mountPropagation: Bidirectional - name: var-run-netns + - command: + - ./entrypoint.sh + env: + - name: HOST_IP + valueFrom: + fieldRef: + fieldPath: status.hostIP + - name: INTER_NODE_LINK_TYPE + value: GRPC + - name: NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + image: us-west1-docker.pkg.dev/kne-external/kne/networkop/meshnet:v0.3.2 + imagePullPolicy: IfNotPresent + name: meshnet + resources: + limits: + memory: 10G + requests: + cpu: 200m + memory: 1G + securityContext: + privileged: true + volumeMounts: + - mountPath: /etc/cni/net.d + name: cni-cfg + - mountPath: /opt/cni/bin + name: cni-bin + - mountPath: /var/run/netns + mountPropagation: Bidirectional + name: var-run-netns hostIPC: true hostNetwork: true hostPID: true @@ -320,15 +331,15 @@ spec: serviceAccountName: meshnet terminationGracePeriodSeconds: 30 tolerations: - - effect: NoSchedule - operator: Exists + - effect: NoSchedule + operator: Exists volumes: - - hostPath: - path: /opt/cni/bin - name: cni-bin - - hostPath: - path: /etc/cni/net.d - name: cni-cfg - - hostPath: - path: /var/run/netns - name: var-run-netns + - hostPath: + path: /opt/cni/bin + name: cni-bin + - hostPath: + path: /etc/cni/net.d + name: cni-cfg + - hostPath: + path: /var/run/netns + name: var-run-netns diff --git a/manifests/meshnet/vxlan/manifest.yaml b/manifests/meshnet/vxlan/manifest.yaml index e9e99e9f5..5a6824b93 100644 --- a/manifests/meshnet/vxlan/manifest.yaml +++ b/manifests/meshnet/vxlan/manifest.yaml @@ -1,3 +1,4 @@ +--- apiVersion: v1 kind: Namespace metadata: @@ -23,100 +24,110 @@ spec: singular: gwirekobj scope: Namespaced versions: - - name: v1beta1 - schema: - openAPIV3Schema: - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource - this object represents. Servers may infer this from the endpoint - the client submits requests to. Cannot be updated. In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - uids: - description: unique link id - items: - type: integer - type: array - type: object - status: - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - grpcWireItems: - items: - properties: - gwire_peer_node_ip: - description: peer node IP address - type: string - link_id: - description: Unique link id as assigned by meshnet - format: int64 - type: integer - local_pod_iface_name: - description: Local pod interface name that is specified in topology - CR and is created by meshnet - type: string - local_pod_ip: - description: Local pod ip as specified in topology CR - type: string - local_pod_name: - description: Local pod name as specified in topology CR - type: string - local_pod_net_ns: - description: Netwokr namespace of the local pod holding the - wire end - type: string - node_name: - description: Name of the node holding the wire end - type: string - topo_namespace: - description: The topology namespace. - type: string - wire_iface_id_on_peer_node: - description: The interface id, in the peer node adn is connected - with remote pod. This is used for de-multiplexing received - packet from grpcwire - format: int64 - type: integer - wire_iface_name_on_local_node: - description: The interface(name) in the local node and is connected - with local pod - type: string - type: object - type: array - kind: - description: 'Kind is a string value representing the REST resource - this object represents. Servers may infer this from the endpoint - the client submits requests to. Cannot be updated. In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - type: object - type: object - served: true - storage: true + - name: v1beta1 + schema: + openAPIV3Schema: + properties: + apiVersion: + description: + "APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources" + type: string + kind: + description: + "Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" + type: string + metadata: + type: object + spec: + properties: + apiVersion: + description: + "APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources" + type: string + kind: + description: + "Kind is a string value representing the REST resource + this object represents. Servers may infer this from the endpoint + the client submits requests to. Cannot be updated. In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" + type: string + uids: + description: unique link id + items: + type: integer + type: array + type: object + status: + properties: + apiVersion: + description: + "APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources" + type: string + grpcWireItems: + items: + properties: + gwire_peer_node_ip: + description: peer node IP address + type: string + link_id: + description: Unique link id as assigned by meshnet + format: int64 + type: integer + local_pod_iface_name: + description: + Local pod interface name that is specified in topology + CR and is created by meshnet + type: string + local_pod_ip: + description: Local pod ip as specified in topology CR + type: string + local_pod_name: + description: Local pod name as specified in topology CR + type: string + local_pod_net_ns: + description: + Netwokr namespace of the local pod holding the + wire end + type: string + node_name: + description: Name of the node holding the wire end + type: string + topo_namespace: + description: The topology namespace. + type: string + wire_iface_id_on_peer_node: + description: + The interface id, in the peer node and is connected + with remote pod. This is used for de-multiplexing received + packet from grpcwire + format: int64 + type: integer + wire_iface_name_on_local_node: + description: + The interface(name) in the local node and is connected + with local pod + type: string + type: object + type: array + kind: + description: + "Kind is a string value representing the REST resource + this object represents. Servers may infer this from the endpoint + the client submits requests to. Cannot be updated. In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" + type: string + type: object + type: object + served: true + storage: true --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition @@ -130,75 +141,75 @@ spec: kind: Topology plural: topologies shortNames: - - topo + - topo singular: topology scope: Namespaced versions: - - name: v1beta1 - schema: - openAPIV3Schema: - properties: - spec: - properties: - links: - items: - description: A complete definition of a p2p link - properties: - local_intf: - description: Local interface name - type: string - local_ip: - description: (Optional) Peer IP address - type: string - peer_intf: - description: Peer interface name - type: string - peer_ip: - description: (Optional) Local IP address - type: string - peer_pod: - description: Name of the peer pod - type: string - uid: - description: Unique identified of a p2p link - type: integer - required: - - uid - - peer_pod - - local_intf - - peer_intf - type: object - type: array - type: object - status: - properties: - container_id: - description: Sandbox ID of the POD - type: string - net_ns: - description: Network namespace of the POD - type: string - skipped: - description: List of pods/interfaces that are skipped by local pod - items: - properties: - link_id: - format: int64 - type: integer - pod_name: - description: peer pod name - type: string - type: object - type: array - src_ip: - description: Source IP of the POD - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} + - name: v1beta1 + schema: + openAPIV3Schema: + properties: + spec: + properties: + links: + items: + description: A complete definition of a p2p link + properties: + local_intf: + description: Local interface name + type: string + local_ip: + description: (Optional) Peer IP address + type: string + peer_intf: + description: Peer interface name + type: string + peer_ip: + description: (Optional) Local IP address + type: string + peer_pod: + description: Name of the peer pod + type: string + uid: + description: Unique identified of a p2p link + type: integer + required: + - uid + - peer_pod + - local_intf + - peer_intf + type: object + type: array + type: object + status: + properties: + container_id: + description: Sandbox ID of the POD + type: string + net_ns: + description: Network namespace of the POD + type: string + skipped: + description: List of pods/interfaces that are skipped by local pod + items: + properties: + link_id: + format: int64 + type: integer + pod_name: + description: peer pod name + type: string + type: object + type: array + src_ip: + description: Source IP of the POD + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} status: acceptedNames: kind: "" @@ -221,21 +232,21 @@ metadata: app: meshnet name: meshnet-clusterrole rules: -- apiGroups: - - networkop.co.uk - resources: - - topologies - - gwirekobjs - verbs: - - '*' -- apiGroups: - - networkop.co.uk - resources: - - topologies/status - - gwirekobjs/spec - - gwirekobjs/status - verbs: - - '*' + - apiGroups: + - networkop.co.uk + resources: + - topologies + - gwirekobjs + verbs: + - "*" + - apiGroups: + - networkop.co.uk + resources: + - topologies/status + - gwirekobjs/spec + - gwirekobjs/status + verbs: + - "*" --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding @@ -248,9 +259,9 @@ roleRef: kind: ClusterRole name: meshnet-clusterrole subjects: -- kind: ServiceAccount - name: meshnet - namespace: meshnet + - kind: ServiceAccount + name: meshnet + namespace: meshnet --- apiVersion: apps/v1 kind: DaemonSet @@ -272,46 +283,46 @@ spec: name: meshnet spec: containers: - - command: - - ./entrypoint.sh - env: - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: INTER_NODE_LINK_TYPE - value: VXLAN - - name: NODE_NAME - valueFrom: - fieldRef: - fieldPath: spec.nodeName - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - image: us-west1-docker.pkg.dev/kne-external/kne/networkop/meshnet:v0.3.2 - imagePullPolicy: IfNotPresent - name: meshnet - resources: - limits: - memory: 10G - requests: - cpu: 200m - memory: 1G - securityContext: - privileged: true - volumeMounts: - - mountPath: /etc/cni/net.d - name: cni-cfg - - mountPath: /opt/cni/bin - name: cni-bin - - mountPath: /var/run/netns - mountPropagation: Bidirectional - name: var-run-netns + - command: + - ./entrypoint.sh + env: + - name: HOST_IP + valueFrom: + fieldRef: + fieldPath: status.hostIP + - name: INTER_NODE_LINK_TYPE + value: VXLAN + - name: NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + image: us-west1-docker.pkg.dev/kne-external/kne/networkop/meshnet:v0.3.2 + imagePullPolicy: IfNotPresent + name: meshnet + resources: + limits: + memory: 10G + requests: + cpu: 200m + memory: 1G + securityContext: + privileged: true + volumeMounts: + - mountPath: /etc/cni/net.d + name: cni-cfg + - mountPath: /opt/cni/bin + name: cni-bin + - mountPath: /var/run/netns + mountPropagation: Bidirectional + name: var-run-netns hostIPC: true hostNetwork: true hostPID: true @@ -320,15 +331,15 @@ spec: serviceAccountName: meshnet terminationGracePeriodSeconds: 30 tolerations: - - effect: NoSchedule - operator: Exists + - effect: NoSchedule + operator: Exists volumes: - - hostPath: - path: /opt/cni/bin - name: cni-bin - - hostPath: - path: /etc/cni/net.d - name: cni-cfg - - hostPath: - path: /var/run/netns - name: var-run-netns + - hostPath: + path: /opt/cni/bin + name: cni-bin + - hostPath: + path: /etc/cni/net.d + name: cni-cfg + - hostPath: + path: /var/run/netns + name: var-run-netns diff --git a/manifests/metallb/manifest.yaml b/manifests/metallb/manifest.yaml index 48454a684..e005898a2 100644 --- a/manifests/metallb/manifest.yaml +++ b/manifests/metallb/manifest.yaml @@ -1,3 +1,4 @@ +--- apiVersion: v1 kind: Namespace metadata: @@ -22,111 +23,111 @@ spec: singular: bfdprofile scope: Namespaced versions: - - additionalPrinterColumns: - - jsonPath: .spec.passiveMode - name: Passive Mode - type: boolean - - jsonPath: .spec.transmitInterval - name: Transmit Interval - type: integer - - jsonPath: .spec.receiveInterval - name: Receive Interval - type: integer - - jsonPath: .spec.detectMultiplier - name: Multiplier - type: integer - name: v1beta1 - schema: - openAPIV3Schema: - description: |- - BFDProfile represents the settings of the bfd session that can be - optionally associated with a BGP session. - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: BFDProfileSpec defines the desired state of BFDProfile. - properties: - detectMultiplier: - description: |- - Configures the detection multiplier to determine - packet loss. The remote transmission interval will be multiplied - by this value to determine the connection loss detection timer. - format: int32 - maximum: 255 - minimum: 2 - type: integer - echoInterval: - description: |- - Configures the minimal echo receive transmission - interval that this system is capable of handling in milliseconds. - Defaults to 50ms - format: int32 - maximum: 60000 - minimum: 10 - type: integer - echoMode: - description: |- - Enables or disables the echo transmission mode. - This mode is disabled by default, and not supported on multi - hops setups. - type: boolean - minimumTtl: - description: |- - For multi hop sessions only: configure the minimum - expected TTL for an incoming BFD control packet. - format: int32 - maximum: 254 - minimum: 1 - type: integer - passiveMode: - description: |- - Mark session as passive: a passive session will not - attempt to start the connection and will wait for control packets - from peer before it begins replying. - type: boolean - receiveInterval: - description: |- - The minimum interval that this system is capable of - receiving control packets in milliseconds. - Defaults to 300ms. - format: int32 - maximum: 60000 - minimum: 10 - type: integer - transmitInterval: - description: |- - The minimum transmission interval (less jitter) - that this system wants to use to send BFD control packets in - milliseconds. Defaults to 300ms - format: int32 - maximum: 60000 - minimum: 10 - type: integer - type: object - status: - description: BFDProfileStatus defines the observed state of BFDProfile. - type: object - type: object - served: true - storage: true - subresources: - status: {} + - additionalPrinterColumns: + - jsonPath: .spec.passiveMode + name: Passive Mode + type: boolean + - jsonPath: .spec.transmitInterval + name: Transmit Interval + type: integer + - jsonPath: .spec.receiveInterval + name: Receive Interval + type: integer + - jsonPath: .spec.detectMultiplier + name: Multiplier + type: integer + name: v1beta1 + schema: + openAPIV3Schema: + description: |- + BFDProfile represents the settings of the bfd session that can be + optionally associated with a BGP session. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: BFDProfileSpec defines the desired state of BFDProfile. + properties: + detectMultiplier: + description: |- + Configures the detection multiplier to determine + packet loss. The remote transmission interval will be multiplied + by this value to determine the connection loss detection timer. + format: int32 + maximum: 255 + minimum: 2 + type: integer + echoInterval: + description: |- + Configures the minimal echo receive transmission + interval that this system is capable of handling in milliseconds. + Defaults to 50ms + format: int32 + maximum: 60000 + minimum: 10 + type: integer + echoMode: + description: |- + Enables or disables the echo transmission mode. + This mode is disabled by default, and not supported on multi + hops setups. + type: boolean + minimumTtl: + description: |- + For multi hop sessions only: configure the minimum + expected TTL for an incoming BFD control packet. + format: int32 + maximum: 254 + minimum: 1 + type: integer + passiveMode: + description: |- + Mark session as passive: a passive session will not + attempt to start the connection and will wait for control packets + from peer before it begins replying. + type: boolean + receiveInterval: + description: |- + The minimum interval that this system is capable of + receiving control packets in milliseconds. + Defaults to 300ms. + format: int32 + maximum: 60000 + minimum: 10 + type: integer + transmitInterval: + description: |- + The minimum transmission interval (less jitter) + that this system wants to use to send BFD control packets in + milliseconds. Defaults to 300ms + format: int32 + maximum: 60000 + minimum: 10 + type: integer + type: object + status: + description: BFDProfileStatus defines the observed state of BFDProfile. + type: object + type: object + served: true + storage: true + subresources: + status: {} --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition @@ -143,207 +144,215 @@ spec: singular: bgpadvertisement scope: Namespaced versions: - - additionalPrinterColumns: - - jsonPath: .spec.ipAddressPools - name: IPAddressPools - type: string - - jsonPath: .spec.ipAddressPoolSelectors - name: IPAddressPool Selectors - type: string - - jsonPath: .spec.peers - name: Peers - type: string - - jsonPath: .spec.nodeSelectors - name: Node Selectors - priority: 10 - type: string - name: v1beta1 - schema: - openAPIV3Schema: - description: |- - BGPAdvertisement allows to advertise the IPs coming - from the selected IPAddressPools via BGP, setting the parameters of the - BGP Advertisement. - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: BGPAdvertisementSpec defines the desired state of BGPAdvertisement. - properties: - aggregationLength: - default: 32 - description: The aggregation-length advertisement option lets you - “roll up” the /32s into a larger prefix. Defaults to 32. Works for - IPv4 addresses. - format: int32 - minimum: 1 - type: integer - aggregationLengthV6: - default: 128 - description: The aggregation-length advertisement option lets you - “roll up” the /128s into a larger prefix. Defaults to 128. Works - for IPv6 addresses. - format: int32 - type: integer - communities: - description: |- - The BGP communities to be associated with the announcement. Each item can be a standard community of the - form 1234:1234, a large community of the form large:1234:1234:1234 or the name of an alias defined in the - Community CRD. - items: - type: string - type: array - ipAddressPoolSelectors: - description: |- - A selector for the IPAddressPools which would get advertised via this advertisement. - If no IPAddressPool is selected by this or by the list, the advertisement is applied to all the IPAddressPools. - items: + - additionalPrinterColumns: + - jsonPath: .spec.ipAddressPools + name: IPAddressPools + type: string + - jsonPath: .spec.ipAddressPoolSelectors + name: IPAddressPool Selectors + type: string + - jsonPath: .spec.peers + name: Peers + type: string + - jsonPath: .spec.nodeSelectors + name: Node Selectors + priority: 10 + type: string + name: v1beta1 + schema: + openAPIV3Schema: + description: |- + BGPAdvertisement allows to advertise the IPs coming + from the selected IPAddressPools via BGP, setting the parameters of the + BGP Advertisement. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: BGPAdvertisementSpec defines the desired state of BGPAdvertisement. + properties: + aggregationLength: + default: 32 + description: + The aggregation-length advertisement option lets you + “roll up” the /32s into a larger prefix. Defaults to 32. Works for + IPv4 addresses. + format: int32 + minimum: 1 + type: integer + aggregationLengthV6: + default: 128 + description: + The aggregation-length advertisement option lets you + “roll up” the /128s into a larger prefix. Defaults to 128. Works + for IPv6 addresses. + format: int32 + type: integer + communities: description: |- - A label selector is a label query over a set of resources. The result of matchLabels and - matchExpressions are ANDed. An empty label selector matches all objects. A null - label selector matches no objects. - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. - The requirements are ANDed. - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label key that the selector applies - to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - items: + The BGP communities to be associated with the announcement. Each item can be a standard community of the + form 1234:1234, a large community of the form large:1234:1234:1234 or the name of an alias defined in the + Community CRD. + items: + type: string + type: array + ipAddressPoolSelectors: + description: |- + A selector for the IPAddressPools which would get advertised via this advertisement. + If no IPAddressPool is selected by this or by the list, the advertisement is applied to all the IPAddressPools. + items: + description: |- + A label selector is a label query over a set of resources. The result of matchLabels and + matchExpressions are ANDed. An empty label selector matches all objects. A null + label selector matches no objects. + properties: + matchExpressions: + description: + matchExpressions is a list of label selector requirements. + The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: + key is the label key that the selector applies + to. type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - type: array - ipAddressPools: - description: The list of IPAddressPools to advertise via this advertisement, - selected by name. - items: - type: string - type: array - localPref: - description: |- - The BGP LOCAL_PREF attribute which is used by BGP best path algorithm, - Path with higher localpref is preferred over one with lower localpref. - format: int32 - type: integer - nodeSelectors: - description: NodeSelectors allows to limit the nodes to announce as - next hops for the LoadBalancer IP. When empty, all the nodes having are - announced as next hops. - items: + type: object + x-kubernetes-map-type: atomic + type: array + ipAddressPools: + description: + The list of IPAddressPools to advertise via this advertisement, + selected by name. + items: + type: string + type: array + localPref: description: |- - A label selector is a label query over a set of resources. The result of matchLabels and - matchExpressions are ANDed. An empty label selector matches all objects. A null - label selector matches no objects. - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. - The requirements are ANDed. - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label key that the selector applies - to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - items: + The BGP LOCAL_PREF attribute which is used by BGP best path algorithm, + Path with higher localpref is preferred over one with lower localpref. + format: int32 + type: integer + nodeSelectors: + description: + NodeSelectors allows to limit the nodes to announce as + next hops for the LoadBalancer IP. When empty, all the nodes having are + announced as next hops. + items: + description: |- + A label selector is a label query over a set of resources. The result of matchLabels and + matchExpressions are ANDed. An empty label selector matches all objects. A null + label selector matches no objects. + properties: + matchExpressions: + description: + matchExpressions is a list of label selector requirements. + The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: + key is the label key that the selector applies + to. type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - type: array - peers: - description: |- - Peers limits the bgppeer to advertise the ips of the selected pools to. - When empty, the loadbalancer IP is announced to all the BGPPeers configured. - items: - type: string - type: array - type: object - status: - description: BGPAdvertisementStatus defines the observed state of BGPAdvertisement. - type: object - type: object - served: true - storage: true - subresources: - status: {} + type: object + x-kubernetes-map-type: atomic + type: array + peers: + description: |- + Peers limits the bgppeer to advertise the ips of the selected pools to. + When empty, the loadbalancer IP is announced to all the BGPPeers configured. + items: + type: string + type: array + type: object + status: + description: BGPAdvertisementStatus defines the observed state of BGPAdvertisement. + type: object + type: object + served: true + storage: true + subresources: + status: {} --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition @@ -362,8 +371,8 @@ spec: namespace: metallb-system path: /convert conversionReviewVersions: - - v1beta1 - - v1beta2 + - v1beta1 + - v1beta2 group: metallb.io names: kind: BGPPeer @@ -372,340 +381,351 @@ spec: singular: bgppeer scope: Namespaced versions: - - additionalPrinterColumns: - - jsonPath: .spec.peerAddress - name: Address - type: string - - jsonPath: .spec.peerASN - name: ASN - type: string - - jsonPath: .spec.bfdProfile - name: BFD Profile - type: string - - jsonPath: .spec.ebgpMultiHop - name: Multi Hops - type: string - deprecated: true - deprecationWarning: v1beta1 is deprecated, please use v1beta2 - name: v1beta1 - schema: - openAPIV3Schema: - description: BGPPeer is the Schema for the peers API. - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: BGPPeerSpec defines the desired state of Peer. - properties: - bfdProfile: - type: string - ebgpMultiHop: - description: EBGP peer is multi-hops away - type: boolean - holdTime: - description: Requested BGP hold time, per RFC4271. - type: string - keepaliveTime: - description: Requested BGP keepalive time, per RFC4271. - type: string - myASN: - description: AS number to use for the local end of the session. - format: int32 - maximum: 4294967295 - minimum: 0 - type: integer - nodeSelectors: - description: |- - Only connect to this peer on nodes that match one of these - selectors. - items: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: + - additionalPrinterColumns: + - jsonPath: .spec.peerAddress + name: Address + type: string + - jsonPath: .spec.peerASN + name: ASN + type: string + - jsonPath: .spec.bfdProfile + name: BFD Profile + type: string + - jsonPath: .spec.ebgpMultiHop + name: Multi Hops + type: string + deprecated: true + deprecationWarning: v1beta1 is deprecated, please use v1beta2 + name: v1beta1 + schema: + openAPIV3Schema: + description: BGPPeer is the Schema for the peers API. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: BGPPeerSpec defines the desired state of Peer. + properties: + bfdProfile: + type: string + ebgpMultiHop: + description: EBGP peer is multi-hops away + type: boolean + holdTime: + description: Requested BGP hold time, per RFC4271. + type: string + keepaliveTime: + description: Requested BGP keepalive time, per RFC4271. + type: string + myASN: + description: AS number to use for the local end of the session. + format: int32 + maximum: 4294967295 + minimum: 0 + type: integer + nodeSelectors: + description: |- + Only connect to this peer on nodes that match one of these + selectors. + items: + properties: + matchExpressions: + items: + properties: + key: type: string - minItems: 1 - type: array - required: - - key - - operator - - values + operator: + type: string + values: + items: + type: string + minItems: 1 + type: array + required: + - key + - operator + - values + type: object + type: array + matchLabels: + additionalProperties: + type: string type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - type: array - password: - description: Authentication password for routers enforcing TCP MD5 - authenticated sessions - type: string - peerASN: - description: AS number to expect from the remote end of the session. - format: int32 - maximum: 4294967295 - minimum: 0 - type: integer - peerAddress: - description: Address to dial when establishing the session. - type: string - peerPort: - description: Port to dial when establishing the session. - maximum: 16384 - minimum: 0 - type: integer - routerID: - description: BGP router ID to advertise to the peer - type: string - sourceAddress: - description: Source address to use when establishing the session. - type: string - required: - - myASN - - peerASN - - peerAddress - type: object - status: - description: BGPPeerStatus defines the observed state of Peer. - type: object - type: object - served: true - storage: false - subresources: - status: {} - - additionalPrinterColumns: - - jsonPath: .spec.peerAddress - name: Address - type: string - - jsonPath: .spec.peerASN - name: ASN - type: string - - jsonPath: .spec.bfdProfile - name: BFD Profile - type: string - - jsonPath: .spec.ebgpMultiHop - name: Multi Hops - type: string - name: v1beta2 - schema: - openAPIV3Schema: - description: BGPPeer is the Schema for the peers API. - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: BGPPeerSpec defines the desired state of Peer. - properties: - bfdProfile: - description: The name of the BFD Profile to be used for the BFD session - associated to the BGP session. If not set, the BFD session won't - be set up. - type: string - connectTime: - description: Requested BGP connect time, controls how long BGP waits - between connection attempts to a neighbor. - type: string - x-kubernetes-validations: - - message: connect time should be between 1 seconds to 65535 - rule: duration(self).getSeconds() >= 1 && duration(self).getSeconds() - <= 65535 - - message: connect time should contain a whole number of seconds - rule: duration(self).getMilliseconds() % 1000 == 0 - disableMP: - default: false - description: To set if we want to disable MP BGP that will separate - IPv4 and IPv6 route exchanges into distinct BGP sessions. - type: boolean - dynamicASN: - description: |- - DynamicASN detects the AS number to use for the remote end of the session - without explicitly setting it via the ASN field. Limited to: - internal - if the neighbor's ASN is different than MyASN connection is denied. - external - if the neighbor's ASN is the same as MyASN the connection is denied. - ASN and DynamicASN are mutually exclusive and one of them must be specified. - enum: - - internal - - external - type: string - ebgpMultiHop: - description: To set if the BGPPeer is multi-hops away. Needed for - FRR mode only. - type: boolean - enableGracefulRestart: - description: |- - EnableGracefulRestart allows BGP peer to continue to forward data packets - along known routes while the routing protocol information is being - restored. This field is immutable because it requires restart of the BGP - session. Supported for FRR mode only. - type: boolean - x-kubernetes-validations: - - message: EnableGracefulRestart cannot be changed after creation - rule: self == oldSelf - holdTime: - description: Requested BGP hold time, per RFC4271. - type: string - keepaliveTime: - description: Requested BGP keepalive time, per RFC4271. - type: string - myASN: - description: AS number to use for the local end of the session. - format: int32 - maximum: 4294967295 - minimum: 0 - type: integer - nodeSelectors: - description: |- - Only connect to this peer on nodes that match one of these - selectors. - items: + type: object + type: array + password: + description: + Authentication password for routers enforcing TCP MD5 + authenticated sessions + type: string + peerASN: + description: AS number to expect from the remote end of the session. + format: int32 + maximum: 4294967295 + minimum: 0 + type: integer + peerAddress: + description: Address to dial when establishing the session. + type: string + peerPort: + description: Port to dial when establishing the session. + maximum: 16384 + minimum: 0 + type: integer + routerID: + description: BGP router ID to advertise to the peer + type: string + sourceAddress: + description: Source address to use when establishing the session. + type: string + required: + - myASN + - peerASN + - peerAddress + type: object + status: + description: BGPPeerStatus defines the observed state of Peer. + type: object + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .spec.peerAddress + name: Address + type: string + - jsonPath: .spec.peerASN + name: ASN + type: string + - jsonPath: .spec.bfdProfile + name: BFD Profile + type: string + - jsonPath: .spec.ebgpMultiHop + name: Multi Hops + type: string + name: v1beta2 + schema: + openAPIV3Schema: + description: BGPPeer is the Schema for the peers API. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: BGPPeerSpec defines the desired state of Peer. + properties: + bfdProfile: + description: + The name of the BFD Profile to be used for the BFD session + associated to the BGP session. If not set, the BFD session won't + be set up. + type: string + connectTime: + description: + Requested BGP connect time, controls how long BGP waits + between connection attempts to a neighbor. + type: string + x-kubernetes-validations: + - message: connect time should be between 1 seconds to 65535 + rule: + duration(self).getSeconds() >= 1 && duration(self).getSeconds() + <= 65535 + - message: connect time should contain a whole number of seconds + rule: duration(self).getMilliseconds() % 1000 == 0 + disableMP: + default: false + description: + To set if we want to disable MP BGP that will separate + IPv4 and IPv6 route exchanges into distinct BGP sessions. + type: boolean + dynamicASN: description: |- - A label selector is a label query over a set of resources. The result of matchLabels and - matchExpressions are ANDed. An empty label selector matches all objects. A null - label selector matches no objects. - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. - The requirements are ANDed. - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label key that the selector applies - to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - items: + DynamicASN detects the AS number to use for the remote end of the session + without explicitly setting it via the ASN field. Limited to: + internal - if the neighbor's ASN is different than MyASN connection is denied. + external - if the neighbor's ASN is the same as MyASN the connection is denied. + ASN and DynamicASN are mutually exclusive and one of them must be specified. + enum: + - internal + - external + type: string + ebgpMultiHop: + description: + To set if the BGPPeer is multi-hops away. Needed for + FRR mode only. + type: boolean + enableGracefulRestart: + description: |- + EnableGracefulRestart allows BGP peer to continue to forward data packets + along known routes while the routing protocol information is being + restored. This field is immutable because it requires restart of the BGP + session. Supported for FRR mode only. + type: boolean + x-kubernetes-validations: + - message: EnableGracefulRestart cannot be changed after creation + rule: self == oldSelf + holdTime: + description: Requested BGP hold time, per RFC4271. + type: string + keepaliveTime: + description: Requested BGP keepalive time, per RFC4271. + type: string + myASN: + description: AS number to use for the local end of the session. + format: int32 + maximum: 4294967295 + minimum: 0 + type: integer + nodeSelectors: + description: |- + Only connect to this peer on nodes that match one of these + selectors. + items: + description: |- + A label selector is a label query over a set of resources. The result of matchLabels and + matchExpressions are ANDed. An empty label selector matches all objects. A null + label selector matches no objects. + properties: + matchExpressions: + description: + matchExpressions is a list of label selector requirements. + The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: + key is the label key that the selector applies + to. type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object + type: object + x-kubernetes-map-type: atomic + type: array + password: + description: + Authentication password for routers enforcing TCP MD5 + authenticated sessions + type: string + passwordSecret: + description: |- + passwordSecret is name of the authentication secret for BGP Peer. + the secret must be of type "kubernetes.io/basic-auth", and created in the + same namespace as the MetalLB deployment. The password is stored in the + secret as the key "password". + properties: + name: + description: + name is unique within a namespace to reference a + secret resource. + type: string + namespace: + description: + namespace defines the space within which the secret + name must be unique. + type: string type: object x-kubernetes-map-type: atomic - type: array - password: - description: Authentication password for routers enforcing TCP MD5 - authenticated sessions - type: string - passwordSecret: - description: |- - passwordSecret is name of the authentication secret for BGP Peer. - the secret must be of type "kubernetes.io/basic-auth", and created in the - same namespace as the MetalLB deployment. The password is stored in the - secret as the key "password". - properties: - name: - description: name is unique within a namespace to reference a - secret resource. - type: string - namespace: - description: namespace defines the space within which the secret - name must be unique. - type: string - type: object - x-kubernetes-map-type: atomic - peerASN: - description: |- - AS number to expect from the remote end of the session. - ASN and DynamicASN are mutually exclusive and one of them must be specified. - format: int32 - maximum: 4294967295 - minimum: 0 - type: integer - peerAddress: - description: Address to dial when establishing the session. - type: string - peerPort: - default: 179 - description: Port to dial when establishing the session. - maximum: 16384 - minimum: 0 - type: integer - routerID: - description: BGP router ID to advertise to the peer - type: string - sourceAddress: - description: Source address to use when establishing the session. - type: string - vrf: - description: |- - To set if we want to peer with the BGPPeer using an interface belonging to - a host vrf - type: string - required: - - myASN - - peerAddress - type: object - status: - description: BGPPeerStatus defines the observed state of Peer. - type: object - type: object - served: true - storage: true - subresources: - status: {} + peerASN: + description: |- + AS number to expect from the remote end of the session. + ASN and DynamicASN are mutually exclusive and one of them must be specified. + format: int32 + maximum: 4294967295 + minimum: 0 + type: integer + peerAddress: + description: Address to dial when establishing the session. + type: string + peerPort: + default: 179 + description: Port to dial when establishing the session. + maximum: 16384 + minimum: 0 + type: integer + routerID: + description: BGP router ID to advertise to the peer + type: string + sourceAddress: + description: Source address to use when establishing the session. + type: string + vrf: + description: |- + To set if we want to peer with the BGPPeer using an interface belonging to + a host vrf + type: string + required: + - myASN + - peerAddress + type: object + status: + description: BGPPeerStatus defines the observed state of Peer. + type: object + type: object + served: true + storage: true + subresources: + status: {} --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition @@ -722,55 +742,55 @@ spec: singular: community scope: Namespaced versions: - - name: v1beta1 - schema: - openAPIV3Schema: - description: |- - Community is a collection of aliases for communities. - Users can define named aliases to be used in the BGPPeer CRD. - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: CommunitySpec defines the desired state of Community. - properties: - communities: - items: - properties: - name: - description: The name of the alias for the community. - type: string - value: - description: |- - The BGP community value corresponding to the given name. Can be a standard community of the form 1234:1234 - or a large community of the form large:1234:1234:1234. - type: string - type: object - type: array - type: object - status: - description: CommunityStatus defines the observed state of Community. - type: object - type: object - served: true - storage: true - subresources: - status: {} + - name: v1beta1 + schema: + openAPIV3Schema: + description: |- + Community is a collection of aliases for communities. + Users can define named aliases to be used in the BGPPeer CRD. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: CommunitySpec defines the desired state of Community. + properties: + communities: + items: + properties: + name: + description: The name of the alias for the community. + type: string + value: + description: |- + The BGP community value corresponding to the given name. Can be a standard community of the form 1234:1234 + or a large community of the form large:1234:1234:1234. + type: string + type: object + type: array + type: object + status: + description: CommunityStatus defines the observed state of Community. + type: object + type: object + served: true + storage: true + subresources: + status: {} --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition @@ -787,206 +807,212 @@ spec: singular: ipaddresspool scope: Namespaced versions: - - additionalPrinterColumns: - - jsonPath: .spec.autoAssign - name: Auto Assign - type: boolean - - jsonPath: .spec.avoidBuggyIPs - name: Avoid Buggy IPs - type: boolean - - jsonPath: .spec.addresses - name: Addresses - type: string - name: v1beta1 - schema: - openAPIV3Schema: - description: |- - IPAddressPool represents a pool of IP addresses that can be allocated - to LoadBalancer services. - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: IPAddressPoolSpec defines the desired state of IPAddressPool. - properties: - addresses: - description: |- - A list of IP address ranges over which MetalLB has authority. - You can list multiple ranges in a single pool, they will all share the - same settings. Each range can be either a CIDR prefix, or an explicit - start-end range of IPs. - items: - type: string - type: array - autoAssign: - default: true - description: |- - AutoAssign flag used to prevent MetallB from automatic allocation - for a pool. - type: boolean - avoidBuggyIPs: - default: false - description: |- - AvoidBuggyIPs prevents addresses ending with .0 and .255 - to be used by a pool. - type: boolean - serviceAllocation: - description: |- - AllocateTo makes ip pool allocation to specific namespace and/or service. - The controller will use the pool with lowest value of priority in case of - multiple matches. A pool with no priority set will be used only if the - pools with priority can't be used. If multiple matching IPAddressPools are - available it will check for the availability of IPs sorting the matching - IPAddressPools by priority, starting from the highest to the lowest. If - multiple IPAddressPools have the same priority, choice will be random. - properties: - namespaceSelectors: - description: |- - NamespaceSelectors list of label selectors to select namespace(s) for ip pool, - an alternative to using namespace list. - items: + - additionalPrinterColumns: + - jsonPath: .spec.autoAssign + name: Auto Assign + type: boolean + - jsonPath: .spec.avoidBuggyIPs + name: Avoid Buggy IPs + type: boolean + - jsonPath: .spec.addresses + name: Addresses + type: string + name: v1beta1 + schema: + openAPIV3Schema: + description: |- + IPAddressPool represents a pool of IP addresses that can be allocated + to LoadBalancer services. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: IPAddressPoolSpec defines the desired state of IPAddressPool. + properties: + addresses: + description: |- + A list of IP address ranges over which MetalLB has authority. + You can list multiple ranges in a single pool, they will all share the + same settings. Each range can be either a CIDR prefix, or an explicit + start-end range of IPs. + items: + type: string + type: array + autoAssign: + default: true + description: |- + AutoAssign flag used to prevent MetallB from automatic allocation + for a pool. + type: boolean + avoidBuggyIPs: + default: false + description: |- + AvoidBuggyIPs prevents addresses ending with .0 and .255 + to be used by a pool. + type: boolean + serviceAllocation: + description: |- + AllocateTo makes ip pool allocation to specific namespace and/or service. + The controller will use the pool with lowest value of priority in case of + multiple matches. A pool with no priority set will be used only if the + pools with priority can't be used. If multiple matching IPAddressPools are + available it will check for the availability of IPs sorting the matching + IPAddressPools by priority, starting from the highest to the lowest. If + multiple IPAddressPools have the same priority, choice will be random. + properties: + namespaceSelectors: description: |- - A label selector is a label query over a set of resources. The result of matchLabels and - matchExpressions are ANDed. An empty label selector matches all objects. A null - label selector matches no objects. - properties: - matchExpressions: - description: matchExpressions is a list of label selector - requirements. The requirements are ANDed. - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label key that the selector - applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - items: + NamespaceSelectors list of label selectors to select namespace(s) for ip pool, + an alternative to using namespace list. + items: + description: |- + A label selector is a label query over a set of resources. The result of matchLabels and + matchExpressions are ANDed. An empty label selector matches all objects. A null + label selector matches no objects. + properties: + matchExpressions: + description: + matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: + key is the label key that the selector + applies to. type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - type: array - namespaces: - description: Namespaces list of namespace(s) on which ip pool - can be attached. - items: - type: string - type: array - priority: - description: Priority priority given for ip pool while ip allocation - on a service. - type: integer - serviceSelectors: - description: |- - ServiceSelectors list of label selector to select service(s) for which ip pool - can be used for ip allocation. - items: + type: object + x-kubernetes-map-type: atomic + type: array + namespaces: + description: + Namespaces list of namespace(s) on which ip pool + can be attached. + items: + type: string + type: array + priority: + description: + Priority priority given for ip pool while ip allocation + on a service. + type: integer + serviceSelectors: description: |- - A label selector is a label query over a set of resources. The result of matchLabels and - matchExpressions are ANDed. An empty label selector matches all objects. A null - label selector matches no objects. - properties: - matchExpressions: - description: matchExpressions is a list of label selector - requirements. The requirements are ANDed. - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label key that the selector - applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - items: + ServiceSelectors list of label selector to select service(s) for which ip pool + can be used for ip allocation. + items: + description: |- + A label selector is a label query over a set of resources. The result of matchLabels and + matchExpressions are ANDed. An empty label selector matches all objects. A null + label selector matches no objects. + properties: + matchExpressions: + description: + matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: + key is the label key that the selector + applies to. type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - type: array - type: object - required: - - addresses - type: object - status: - description: IPAddressPoolStatus defines the observed state of IPAddressPool. - type: object - required: - - spec - type: object - served: true - storage: true - subresources: - status: {} + type: object + x-kubernetes-map-type: atomic + type: array + type: object + required: + - addresses + type: object + status: + description: IPAddressPoolStatus defines the observed state of IPAddressPool. + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition @@ -1003,177 +1029,183 @@ spec: singular: l2advertisement scope: Namespaced versions: - - additionalPrinterColumns: - - jsonPath: .spec.ipAddressPools - name: IPAddressPools - type: string - - jsonPath: .spec.ipAddressPoolSelectors - name: IPAddressPool Selectors - type: string - - jsonPath: .spec.interfaces - name: Interfaces - type: string - - jsonPath: .spec.nodeSelectors - name: Node Selectors - priority: 10 - type: string - name: v1beta1 - schema: - openAPIV3Schema: - description: |- - L2Advertisement allows to advertise the LoadBalancer IPs provided - by the selected pools via L2. - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: L2AdvertisementSpec defines the desired state of L2Advertisement. - properties: - interfaces: - description: |- - A list of interfaces to announce from. The LB IP will be announced only from these interfaces. - If the field is not set, we advertise from all the interfaces on the host. - items: - type: string - type: array - ipAddressPoolSelectors: - description: |- - A selector for the IPAddressPools which would get advertised via this advertisement. - If no IPAddressPool is selected by this or by the list, the advertisement is applied to all the IPAddressPools. - items: + - additionalPrinterColumns: + - jsonPath: .spec.ipAddressPools + name: IPAddressPools + type: string + - jsonPath: .spec.ipAddressPoolSelectors + name: IPAddressPool Selectors + type: string + - jsonPath: .spec.interfaces + name: Interfaces + type: string + - jsonPath: .spec.nodeSelectors + name: Node Selectors + priority: 10 + type: string + name: v1beta1 + schema: + openAPIV3Schema: + description: |- + L2Advertisement allows to advertise the LoadBalancer IPs provided + by the selected pools via L2. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: L2AdvertisementSpec defines the desired state of L2Advertisement. + properties: + interfaces: description: |- - A label selector is a label query over a set of resources. The result of matchLabels and - matchExpressions are ANDed. An empty label selector matches all objects. A null - label selector matches no objects. - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. - The requirements are ANDed. - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label key that the selector applies - to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - type: array - ipAddressPools: - description: The list of IPAddressPools to advertise via this advertisement, - selected by name. - items: - type: string - type: array - nodeSelectors: - description: NodeSelectors allows to limit the nodes to announce as - next hops for the LoadBalancer IP. When empty, all the nodes having are - announced as next hops. - items: + A list of interfaces to announce from. The LB IP will be announced only from these interfaces. + If the field is not set, we advertise from all the interfaces on the host. + items: + type: string + type: array + ipAddressPoolSelectors: description: |- - A label selector is a label query over a set of resources. The result of matchLabels and - matchExpressions are ANDed. An empty label selector matches all objects. A null - label selector matches no objects. - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. - The requirements are ANDed. - items: + A selector for the IPAddressPools which would get advertised via this advertisement. + If no IPAddressPool is selected by this or by the list, the advertisement is applied to all the IPAddressPools. + items: + description: |- + A label selector is a label query over a set of resources. The result of matchLabels and + matchExpressions are ANDed. An empty label selector matches all objects. A null + label selector matches no objects. + properties: + matchExpressions: + description: + matchExpressions is a list of label selector requirements. + The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: + key is the label key that the selector applies + to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label key that the selector applies - to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - items: + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + type: array + ipAddressPools: + description: + The list of IPAddressPools to advertise via this advertisement, + selected by name. + items: + type: string + type: array + nodeSelectors: + description: + NodeSelectors allows to limit the nodes to announce as + next hops for the LoadBalancer IP. When empty, all the nodes having are + announced as next hops. + items: + description: |- + A label selector is a label query over a set of resources. The result of matchLabels and + matchExpressions are ANDed. An empty label selector matches all objects. A null + label selector matches no objects. + properties: + matchExpressions: + description: + matchExpressions is a list of label selector requirements. + The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: + key is the label key that the selector applies + to. type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - type: array - type: object - status: - description: L2AdvertisementStatus defines the observed state of L2Advertisement. - type: object - type: object - served: true - storage: true - subresources: - status: {} + type: object + x-kubernetes-map-type: atomic + type: array + type: object + status: + description: L2AdvertisementStatus defines the observed state of L2Advertisement. + type: object + type: object + served: true + storage: true + subresources: + status: {} --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition @@ -1190,80 +1222,82 @@ spec: singular: servicel2status scope: Namespaced versions: - - additionalPrinterColumns: - - jsonPath: .status.node - name: Allocated Node - type: string - - jsonPath: .status.serviceName - name: Service Name - type: string - - jsonPath: .status.serviceNamespace - name: Service Namespace - type: string - name: v1beta1 - schema: - openAPIV3Schema: - description: ServiceL2Status reveals the actual traffic status of loadbalancer - services in layer2 mode. - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: ServiceL2StatusSpec defines the desired state of ServiceL2Status. - type: object - status: - description: MetalLBServiceL2Status defines the observed state of ServiceL2Status. - properties: - interfaces: - description: Interfaces indicates the interfaces that receive the - directed traffic - items: - description: InterfaceInfo defines interface info of layer2 announcement. - properties: - name: - description: Name the name of network interface card - type: string - type: object - type: array - node: - description: Node indicates the node that receives the directed traffic - type: string - x-kubernetes-validations: - - message: Value is immutable - rule: self == oldSelf - serviceName: - description: ServiceName indicates the service this status represents - type: string - x-kubernetes-validations: - - message: Value is immutable - rule: self == oldSelf - serviceNamespace: - description: ServiceNamespace indicates the namespace of the service - type: string - x-kubernetes-validations: - - message: Value is immutable - rule: self == oldSelf - type: object - type: object - served: true - storage: true - subresources: - status: {} + - additionalPrinterColumns: + - jsonPath: .status.node + name: Allocated Node + type: string + - jsonPath: .status.serviceName + name: Service Name + type: string + - jsonPath: .status.serviceNamespace + name: Service Namespace + type: string + name: v1beta1 + schema: + openAPIV3Schema: + description: + ServiceL2Status reveals the actual traffic status of loadbalancer + services in layer2 mode. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: ServiceL2StatusSpec defines the desired state of ServiceL2Status. + type: object + status: + description: MetalLBServiceL2Status defines the observed state of ServiceL2Status. + properties: + interfaces: + description: + Interfaces indicates the interfaces that receive the + directed traffic + items: + description: InterfaceInfo defines interface info of layer2 announcement. + properties: + name: + description: Name the name of network interface card + type: string + type: object + type: array + node: + description: Node indicates the node that receives the directed traffic + type: string + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + serviceName: + description: ServiceName indicates the service this status represents + type: string + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + serviceNamespace: + description: ServiceNamespace indicates the namespace of the service + type: string + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + type: object + type: object + served: true + storage: true + subresources: + status: {} --- apiVersion: v1 kind: ServiceAccount @@ -1289,81 +1323,81 @@ metadata: name: controller namespace: metallb-system rules: -- apiGroups: - - "" - resources: - - secrets - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resourceNames: - - memberlist - resources: - - secrets - verbs: - - list -- apiGroups: - - apps - resourceNames: - - controller - resources: - - deployments - verbs: - - get -- apiGroups: - - metallb.io - resources: - - bgppeers - verbs: - - get - - list -- apiGroups: - - metallb.io - resources: - - bfdprofiles - verbs: - - get - - list - - watch -- apiGroups: - - metallb.io - resources: - - ipaddresspools - verbs: - - get - - list - - watch -- apiGroups: - - metallb.io - resources: - - bgpadvertisements - verbs: - - get - - list - - watch -- apiGroups: - - metallb.io - resources: - - l2advertisements - verbs: - - get - - list - - watch -- apiGroups: - - metallb.io - resources: - - communities - verbs: - - get - - list - - watch + - apiGroups: + - "" + resources: + - secrets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - "" + resourceNames: + - memberlist + resources: + - secrets + verbs: + - list + - apiGroups: + - apps + resourceNames: + - controller + resources: + - deployments + verbs: + - get + - apiGroups: + - metallb.io + resources: + - bgppeers + verbs: + - get + - list + - apiGroups: + - metallb.io + resources: + - bfdprofiles + verbs: + - get + - list + - watch + - apiGroups: + - metallb.io + resources: + - ipaddresspools + verbs: + - get + - list + - watch + - apiGroups: + - metallb.io + resources: + - bgpadvertisements + verbs: + - get + - list + - watch + - apiGroups: + - metallb.io + resources: + - l2advertisements + verbs: + - get + - list + - watch + - apiGroups: + - metallb.io + resources: + - communities + verbs: + - get + - list + - watch --- apiVersion: rbac.authorization.k8s.io/v1 kind: Role @@ -1373,77 +1407,77 @@ metadata: name: pod-lister namespace: metallb-system rules: -- apiGroups: - - "" - resources: - - pods - verbs: - - list - - get -- apiGroups: - - "" - resources: - - secrets - verbs: - - get - - list - - watch -- apiGroups: - - "" - resources: - - configmaps - verbs: - - get - - list - - watch -- apiGroups: - - metallb.io - resources: - - bfdprofiles - verbs: - - get - - list - - watch -- apiGroups: - - metallb.io - resources: - - bgppeers - verbs: - - get - - list - - watch -- apiGroups: - - metallb.io - resources: - - l2advertisements - verbs: - - get - - list - - watch -- apiGroups: - - metallb.io - resources: - - bgpadvertisements - verbs: - - get - - list - - watch -- apiGroups: - - metallb.io - resources: - - ipaddresspools - verbs: - - get - - list - - watch -- apiGroups: - - metallb.io - resources: - - communities - verbs: - - get - - list - - watch + - apiGroups: + - "" + resources: + - pods + verbs: + - list + - get + - apiGroups: + - "" + resources: + - secrets + verbs: + - get + - list + - watch + - apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch + - apiGroups: + - metallb.io + resources: + - bfdprofiles + verbs: + - get + - list + - watch + - apiGroups: + - metallb.io + resources: + - bgppeers + verbs: + - get + - list + - watch + - apiGroups: + - metallb.io + resources: + - l2advertisements + verbs: + - get + - list + - watch + - apiGroups: + - metallb.io + resources: + - bgpadvertisements + verbs: + - get + - list + - watch + - apiGroups: + - metallb.io + resources: + - ipaddresspools + verbs: + - get + - list + - watch + - apiGroups: + - metallb.io + resources: + - communities + verbs: + - get + - list + - watch --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -1452,89 +1486,89 @@ metadata: app: metallb name: metallb-system:controller rules: -- apiGroups: - - "" - resources: - - services - - namespaces - verbs: - - get - - list - - watch -- apiGroups: - - "" - resources: - - nodes - verbs: - - list -- apiGroups: - - "" - resources: - - services/status - verbs: - - update -- apiGroups: - - "" - resources: - - events - verbs: - - create - - patch -- apiGroups: - - policy - resourceNames: - - controller - resources: - - podsecuritypolicies - verbs: - - use -- apiGroups: - - admissionregistration.k8s.io - resourceNames: - - metallb-webhook-configuration - resources: - - validatingwebhookconfigurations - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - admissionregistration.k8s.io - resources: - - validatingwebhookconfigurations - verbs: - - list - - watch -- apiGroups: - - apiextensions.k8s.io - resourceNames: - - bfdprofiles.metallb.io - - bgpadvertisements.metallb.io - - bgppeers.metallb.io - - ipaddresspools.metallb.io - - l2advertisements.metallb.io - - communities.metallb.io - resources: - - customresourcedefinitions - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - apiextensions.k8s.io - resources: - - customresourcedefinitions - verbs: - - list - - watch + - apiGroups: + - "" + resources: + - services + - namespaces + verbs: + - get + - list + - watch + - apiGroups: + - "" + resources: + - nodes + verbs: + - list + - apiGroups: + - "" + resources: + - services/status + verbs: + - update + - apiGroups: + - "" + resources: + - events + verbs: + - create + - patch + - apiGroups: + - policy + resourceNames: + - controller + resources: + - podsecuritypolicies + verbs: + - use + - apiGroups: + - admissionregistration.k8s.io + resourceNames: + - metallb-webhook-configuration + resources: + - validatingwebhookconfigurations + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - admissionregistration.k8s.io + resources: + - validatingwebhookconfigurations + verbs: + - list + - watch + - apiGroups: + - apiextensions.k8s.io + resourceNames: + - bfdprofiles.metallb.io + - bgpadvertisements.metallb.io + - bgppeers.metallb.io + - ipaddresspools.metallb.io + - l2advertisements.metallb.io + - communities.metallb.io + resources: + - customresourcedefinitions + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - apiextensions.k8s.io + resources: + - customresourcedefinitions + verbs: + - list + - watch --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -1543,47 +1577,47 @@ metadata: app: metallb name: metallb-system:speaker rules: -- apiGroups: - - metallb.io - resources: - - servicel2statuses - - servicel2statuses/status - verbs: - - '*' -- apiGroups: - - "" - resources: - - services - - endpoints - - nodes - - namespaces - verbs: - - get - - list - - watch -- apiGroups: - - discovery.k8s.io - resources: - - endpointslices - verbs: - - get - - list - - watch -- apiGroups: - - "" - resources: - - events - verbs: - - create - - patch -- apiGroups: - - policy - resourceNames: - - speaker - resources: - - podsecuritypolicies - verbs: - - use + - apiGroups: + - metallb.io + resources: + - servicel2statuses + - servicel2statuses/status + verbs: + - "*" + - apiGroups: + - "" + resources: + - services + - endpoints + - nodes + - namespaces + verbs: + - get + - list + - watch + - apiGroups: + - discovery.k8s.io + resources: + - endpointslices + verbs: + - get + - list + - watch + - apiGroups: + - "" + resources: + - events + verbs: + - create + - patch + - apiGroups: + - policy + resourceNames: + - speaker + resources: + - podsecuritypolicies + verbs: + - use --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding @@ -1597,9 +1631,9 @@ roleRef: kind: Role name: controller subjects: -- kind: ServiceAccount - name: controller - namespace: metallb-system + - kind: ServiceAccount + name: controller + namespace: metallb-system --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding @@ -1613,9 +1647,9 @@ roleRef: kind: Role name: pod-lister subjects: -- kind: ServiceAccount - name: speaker - namespace: metallb-system + - kind: ServiceAccount + name: speaker + namespace: metallb-system --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding @@ -1628,9 +1662,9 @@ roleRef: kind: ClusterRole name: metallb-system:controller subjects: -- kind: ServiceAccount - name: controller - namespace: metallb-system + - kind: ServiceAccount + name: controller + namespace: metallb-system --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding @@ -1643,9 +1677,9 @@ roleRef: kind: ClusterRole name: metallb-system:speaker subjects: -- kind: ServiceAccount - name: speaker - namespace: metallb-system + - kind: ServiceAccount + name: speaker + namespace: metallb-system --- apiVersion: v1 data: @@ -1669,8 +1703,8 @@ metadata: namespace: metallb-system spec: ports: - - port: 443 - targetPort: 9443 + - port: 443 + targetPort: 9443 selector: component: controller --- @@ -1698,51 +1732,51 @@ spec: component: controller spec: containers: - - args: - - --port=7472 - - --log-level=info - - --tls-min-version=VersionTLS12 - env: - - name: METALLB_ML_SECRET_NAME - value: memberlist - - name: METALLB_DEPLOYMENT - value: controller - image: quay.io/metallb/controller:v0.14.9 - livenessProbe: - failureThreshold: 3 - httpGet: - path: /metrics - port: monitoring - initialDelaySeconds: 10 - periodSeconds: 10 - successThreshold: 1 - timeoutSeconds: 1 - name: controller - ports: - - containerPort: 7472 - name: monitoring - - containerPort: 9443 - name: webhook-server - protocol: TCP - readinessProbe: - failureThreshold: 3 - httpGet: - path: /metrics - port: monitoring - initialDelaySeconds: 10 - periodSeconds: 10 - successThreshold: 1 - timeoutSeconds: 1 - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - all - readOnlyRootFilesystem: true - volumeMounts: - - mountPath: /tmp/k8s-webhook-server/serving-certs - name: cert - readOnly: true + - args: + - --port=7472 + - --log-level=info + - --tls-min-version=VersionTLS12 + env: + - name: METALLB_ML_SECRET_NAME + value: memberlist + - name: METALLB_DEPLOYMENT + value: controller + image: quay.io/metallb/controller:v0.14.9 + livenessProbe: + failureThreshold: 3 + httpGet: + path: /metrics + port: monitoring + initialDelaySeconds: 10 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + name: controller + ports: + - containerPort: 7472 + name: monitoring + - containerPort: 9443 + name: webhook-server + protocol: TCP + readinessProbe: + failureThreshold: 3 + httpGet: + path: /metrics + port: monitoring + initialDelaySeconds: 10 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - all + readOnlyRootFilesystem: true + volumeMounts: + - mountPath: /tmp/k8s-webhook-server/serving-certs + name: cert + readOnly: true nodeSelector: kubernetes.io/os: linux securityContext: @@ -1752,10 +1786,10 @@ spec: serviceAccountName: controller terminationGracePeriodSeconds: 0 volumes: - - name: cert - secret: - defaultMode: 420 - secretName: metallb-webhook-cert + - name: cert + secret: + defaultMode: 420 + secretName: metallb-webhook-cert --- apiVersion: apps/v1 kind: DaemonSet @@ -1780,94 +1814,94 @@ spec: component: speaker spec: containers: - - args: - - --port=7472 - - --log-level=info - env: - - name: METALLB_NODE_NAME - valueFrom: - fieldRef: - fieldPath: spec.nodeName - - name: METALLB_POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: METALLB_HOST - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: METALLB_ML_BIND_ADDR - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: METALLB_ML_LABELS - value: app=metallb,component=speaker - - name: METALLB_ML_SECRET_KEY_PATH - value: /etc/ml_secret_key - image: quay.io/metallb/speaker:v0.14.9 - livenessProbe: - failureThreshold: 3 - httpGet: - path: /metrics - port: monitoring - initialDelaySeconds: 10 - periodSeconds: 10 - successThreshold: 1 - timeoutSeconds: 1 - name: speaker - ports: - - containerPort: 7472 - name: monitoring - - containerPort: 7946 - name: memberlist-tcp - - containerPort: 7946 - name: memberlist-udp - protocol: UDP - readinessProbe: - failureThreshold: 3 - httpGet: - path: /metrics - port: monitoring - initialDelaySeconds: 10 - periodSeconds: 10 - successThreshold: 1 - timeoutSeconds: 1 - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_RAW - drop: - - ALL - readOnlyRootFilesystem: true - volumeMounts: - - mountPath: /etc/ml_secret_key - name: memberlist - readOnly: true - - mountPath: /etc/metallb - name: metallb-excludel2 - readOnly: true + - args: + - --port=7472 + - --log-level=info + env: + - name: METALLB_NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + - name: METALLB_POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: METALLB_HOST + valueFrom: + fieldRef: + fieldPath: status.hostIP + - name: METALLB_ML_BIND_ADDR + valueFrom: + fieldRef: + fieldPath: status.podIP + - name: METALLB_ML_LABELS + value: app=metallb,component=speaker + - name: METALLB_ML_SECRET_KEY_PATH + value: /etc/ml_secret_key + image: quay.io/metallb/speaker:v0.14.9 + livenessProbe: + failureThreshold: 3 + httpGet: + path: /metrics + port: monitoring + initialDelaySeconds: 10 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + name: speaker + ports: + - containerPort: 7472 + name: monitoring + - containerPort: 7946 + name: memberlist-tcp + - containerPort: 7946 + name: memberlist-udp + protocol: UDP + readinessProbe: + failureThreshold: 3 + httpGet: + path: /metrics + port: monitoring + initialDelaySeconds: 10 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + securityContext: + allowPrivilegeEscalation: false + capabilities: + add: + - NET_RAW + drop: + - ALL + readOnlyRootFilesystem: true + volumeMounts: + - mountPath: /etc/ml_secret_key + name: memberlist + readOnly: true + - mountPath: /etc/metallb + name: metallb-excludel2 + readOnly: true hostNetwork: true nodeSelector: kubernetes.io/os: linux serviceAccountName: speaker terminationGracePeriodSeconds: 2 tolerations: - - effect: NoSchedule - key: node-role.kubernetes.io/master - operator: Exists - - effect: NoSchedule - key: node-role.kubernetes.io/control-plane - operator: Exists + - effect: NoSchedule + key: node-role.kubernetes.io/master + operator: Exists + - effect: NoSchedule + key: node-role.kubernetes.io/control-plane + operator: Exists volumes: - - name: memberlist - secret: - defaultMode: 420 - secretName: memberlist - - configMap: - defaultMode: 256 + - name: memberlist + secret: + defaultMode: 420 + secretName: memberlist + - configMap: + defaultMode: 256 + name: metallb-excludel2 name: metallb-excludel2 - name: metallb-excludel2 --- apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingWebhookConfiguration @@ -1875,123 +1909,123 @@ metadata: creationTimestamp: null name: metallb-webhook-configuration webhooks: -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: metallb-webhook-service - namespace: metallb-system - path: /validate-metallb-io-v1beta2-bgppeer - failurePolicy: Fail - name: bgppeersvalidationwebhook.metallb.io - rules: - - apiGroups: - - metallb.io - apiVersions: - - v1beta2 - operations: - - CREATE - - UPDATE - resources: - - bgppeers - sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: metallb-webhook-service - namespace: metallb-system - path: /validate-metallb-io-v1beta1-bfdprofile - failurePolicy: Fail - name: bfdprofilevalidationwebhook.metallb.io - rules: - - apiGroups: - - metallb.io - apiVersions: - - v1beta1 - operations: - - CREATE - - DELETE - resources: - - bfdprofiles - sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: metallb-webhook-service - namespace: metallb-system - path: /validate-metallb-io-v1beta1-bgpadvertisement - failurePolicy: Fail - name: bgpadvertisementvalidationwebhook.metallb.io - rules: - - apiGroups: - - metallb.io - apiVersions: - - v1beta1 - operations: - - CREATE - - UPDATE - resources: - - bgpadvertisements - sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: metallb-webhook-service - namespace: metallb-system - path: /validate-metallb-io-v1beta1-community - failurePolicy: Fail - name: communityvalidationwebhook.metallb.io - rules: - - apiGroups: - - metallb.io - apiVersions: - - v1beta1 - operations: - - CREATE - - UPDATE - resources: - - communities - sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: metallb-webhook-service - namespace: metallb-system - path: /validate-metallb-io-v1beta1-ipaddresspool - failurePolicy: Fail - name: ipaddresspoolvalidationwebhook.metallb.io - rules: - - apiGroups: - - metallb.io - apiVersions: - - v1beta1 - operations: - - CREATE - - UPDATE - resources: - - ipaddresspools - sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: metallb-webhook-service - namespace: metallb-system - path: /validate-metallb-io-v1beta1-l2advertisement - failurePolicy: Fail - name: l2advertisementvalidationwebhook.metallb.io - rules: - - apiGroups: - - metallb.io - apiVersions: - - v1beta1 - operations: - - CREATE - - UPDATE - resources: - - l2advertisements - sideEffects: None + - admissionReviewVersions: + - v1 + clientConfig: + service: + name: metallb-webhook-service + namespace: metallb-system + path: /validate-metallb-io-v1beta2-bgppeer + failurePolicy: Fail + name: bgppeersvalidationwebhook.metallb.io + rules: + - apiGroups: + - metallb.io + apiVersions: + - v1beta2 + operations: + - CREATE + - UPDATE + resources: + - bgppeers + sideEffects: None + - admissionReviewVersions: + - v1 + clientConfig: + service: + name: metallb-webhook-service + namespace: metallb-system + path: /validate-metallb-io-v1beta1-bfdprofile + failurePolicy: Fail + name: bfdprofilevalidationwebhook.metallb.io + rules: + - apiGroups: + - metallb.io + apiVersions: + - v1beta1 + operations: + - CREATE + - DELETE + resources: + - bfdprofiles + sideEffects: None + - admissionReviewVersions: + - v1 + clientConfig: + service: + name: metallb-webhook-service + namespace: metallb-system + path: /validate-metallb-io-v1beta1-bgpadvertisement + failurePolicy: Fail + name: bgpadvertisementvalidationwebhook.metallb.io + rules: + - apiGroups: + - metallb.io + apiVersions: + - v1beta1 + operations: + - CREATE + - UPDATE + resources: + - bgpadvertisements + sideEffects: None + - admissionReviewVersions: + - v1 + clientConfig: + service: + name: metallb-webhook-service + namespace: metallb-system + path: /validate-metallb-io-v1beta1-community + failurePolicy: Fail + name: communityvalidationwebhook.metallb.io + rules: + - apiGroups: + - metallb.io + apiVersions: + - v1beta1 + operations: + - CREATE + - UPDATE + resources: + - communities + sideEffects: None + - admissionReviewVersions: + - v1 + clientConfig: + service: + name: metallb-webhook-service + namespace: metallb-system + path: /validate-metallb-io-v1beta1-ipaddresspool + failurePolicy: Fail + name: ipaddresspoolvalidationwebhook.metallb.io + rules: + - apiGroups: + - metallb.io + apiVersions: + - v1beta1 + operations: + - CREATE + - UPDATE + resources: + - ipaddresspools + sideEffects: None + - admissionReviewVersions: + - v1 + clientConfig: + service: + name: metallb-webhook-service + namespace: metallb-system + path: /validate-metallb-io-v1beta1-l2advertisement + failurePolicy: Fail + name: l2advertisementvalidationwebhook.metallb.io + rules: + - apiGroups: + - metallb.io + apiVersions: + - v1beta1 + operations: + - CREATE + - UPDATE + resources: + - l2advertisements + sideEffects: None diff --git a/metrics/metrics_test.go b/metrics/metrics_test.go index 5a8501ec7..f31c4db35 100644 --- a/metrics/metrics_test.go +++ b/metrics/metrics_test.go @@ -25,6 +25,7 @@ import ( epb "github.com/openconfig/kne/proto/event" "google.golang.org/api/option" "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" "google.golang.org/protobuf/proto" ) @@ -36,7 +37,7 @@ func newTestReporter(t *testing.T, ctx context.Context) (*Reporter, *pstest.Serv // Start a fake server running locally. srv := pstest.NewServer() // Connect to the server without using TLS. - conn, err := grpc.Dial(srv.Addr, grpc.WithInsecure()) + conn, err := grpc.NewClient(srv.Addr, grpc.WithTransportCredentials(insecure.NewCredentials())) if err != nil { srv.Close() t.Fatalf("failed to start fake PubSub server: %v", err) @@ -93,9 +94,9 @@ func TestReportDeployClusterStart(t *testing.T) { if _, ok := e.Event.(*epb.KNEEvent_DeployClusterStart); !ok { t.Fatalf("event is not a DeployClusterStart event") } - te := e.GetDeployClusterStart() - if te.Cluster.Cluster != epb.Cluster_CLUSTER_TYPE_EXTERNAL { - t.Errorf("event has wrong cluster type, got %v, want %v", te.Cluster.Cluster, epb.Cluster_CLUSTER_TYPE_EXTERNAL) + gotEvent := e.GetDeployClusterStart() + if gotEvent.Cluster.Cluster != epb.Cluster_CLUSTER_TYPE_EXTERNAL { + t.Errorf("event has wrong cluster type, got %v, want %v", gotEvent.Cluster.Cluster, epb.Cluster_CLUSTER_TYPE_EXTERNAL) } } @@ -129,9 +130,9 @@ func TestReportDeployClusterEnd(t *testing.T) { if _, ok := e.Event.(*epb.KNEEvent_DeployClusterEnd); !ok { t.Fatalf("event is not a DeployClusterEnd event") } - te := e.GetDeployClusterEnd() - if te.Error != eventErr.Error() { - t.Errorf("event has wrong error message, got %v, want %v", te.Error, eventErr.Error()) + gotEvent := e.GetDeployClusterEnd() + if gotEvent.Error != eventErr.Error() { + t.Errorf("event has wrong error message, got %v, want %v", gotEvent.Error, eventErr.Error()) } } @@ -164,9 +165,9 @@ func TestReportCreateTopologyStart(t *testing.T) { if _, ok := e.Event.(*epb.KNEEvent_CreateTopologyStart); !ok { t.Fatalf("event is not a CreateTopologyStart event") } - te := e.GetCreateTopologyStart() - if te.Topology.LinkCount != 3 { - t.Errorf("event has wrong link count, got %v, want 3", te.Topology.LinkCount) + gotEvent := e.GetCreateTopologyStart() + if gotEvent.Topology.LinkCount != 3 { + t.Errorf("event has wrong link count, got %v, want 3", gotEvent.Topology.LinkCount) } } @@ -200,9 +201,9 @@ func TestReportCreateTopologyEnd(t *testing.T) { if _, ok := e.Event.(*epb.KNEEvent_CreateTopologyEnd); !ok { t.Fatalf("event is not a CreateTopologyEnd event") } - te := e.GetCreateTopologyEnd() - if te.Error != eventErr.Error() { - t.Errorf("event has wrong error message, got %v, want %v", te.Error, eventErr.Error()) + gotEvent := e.GetCreateTopologyEnd() + if gotEvent.Error != eventErr.Error() { + t.Errorf("event has wrong error message, got %v, want %v", gotEvent.Error, eventErr.Error()) } } @@ -212,7 +213,7 @@ func TestNewReporter(t *testing.T) { defer srv.Close() // Test missing topic - conn1, err := grpc.Dial(srv.Addr, grpc.WithInsecure()) + conn1, err := grpc.NewClient(srv.Addr, grpc.WithTransportCredentials(insecure.NewCredentials())) if err != nil { t.Fatalf("failed to dial fake PubSub server: %v", err) } @@ -222,7 +223,7 @@ func TestNewReporter(t *testing.T) { } // Create default topic for default project/topic test - conn2, err := grpc.Dial(srv.Addr, grpc.WithInsecure()) + conn2, err := grpc.NewClient(srv.Addr, grpc.WithTransportCredentials(insecure.NewCredentials())) if err != nil { t.Fatalf("failed to dial fake PubSub server: %v", err) } @@ -241,7 +242,7 @@ func TestNewReporter(t *testing.T) { client.Close() // Test NewReporter success with default project/topic - conn3, err := grpc.Dial(srv.Addr, grpc.WithInsecure()) + conn3, err := grpc.NewClient(srv.Addr, grpc.WithTransportCredentials(insecure.NewCredentials())) if err != nil { t.Fatalf("failed to dial fake PubSub server: %v", err) } diff --git a/pods/pods.go b/pods/pods.go index a1d33b5f7..9a79b0852 100644 --- a/pods/pods.go +++ b/pods/pods.go @@ -121,11 +121,11 @@ func (c *ContainerStatus) String() string { } func (c *ContainerStatus) Equal(oc *ContainerStatus) bool { - return !(c.Name != oc.Name || - c.Image != oc.Image || - c.Ready != oc.Ready || - c.Reason != oc.Reason || - c.Message != oc.Message) + return c.Name == oc.Name && + c.Image == oc.Image && + c.Ready == oc.Ready && + c.Reason == oc.Reason && + c.Message == oc.Message } // GetPodStatus returns the status of the pods found in the supplied namespace. @@ -183,9 +183,9 @@ func WatchPodStatus(ctx context.Context, client kubernetes.Interface, namespace // PodToStatus returns a pointer to a new PodStatus for pod. func PodToStatus(pod *corev1.Pod) *PodStatus { s := PodStatus{ - Name: pod.ObjectMeta.Name, - Namespace: pod.ObjectMeta.Namespace, - UID: pod.ObjectMeta.UID, + Name: pod.Name, + Namespace: pod.Namespace, + UID: pod.UID, Phase: pod.Status.Phase, // Ready will be set to false below if one of the containers is not ready Ready: len(pod.Status.ContainerStatuses)+len(pod.Status.InitContainerStatuses) > 0, diff --git a/pods/status.go b/pods/status.go index 6f30310b7..73ac4bf8f 100644 --- a/pods/status.go +++ b/pods/status.go @@ -71,7 +71,7 @@ func newWatcher(ctx context.Context, cancel func(), ch chan *PodStatus, stop fun return w } -// SetProgress determins if progress output should be displayed while watching. +// SetProgress determines if progress output should be displayed while watching. func (w *Watcher) SetProgress(value bool) { w.mu.Lock() w.progress = value @@ -176,7 +176,7 @@ func (w *Watcher) updatePod(s *PodStatus) bool { w.podStates[s.UID] = newState } if newState == "failed" { - w.errCh <- fmt.Errorf("Pod %s failed to deploy", s.Name) + w.errCh <- fmt.Errorf("pod %s failed to deploy", s.Name) w.cancel() return false } diff --git a/pods/status_test.go b/pods/status_test.go index f127b4cd6..c38f2b611 100644 --- a/pods/status_test.go +++ b/pods/status_test.go @@ -100,7 +100,7 @@ func TestUpdatePod(t *testing.T) { want: ` 01:23:45 POD: pod1 is now failed `[1:], - errch: `Pod pod1 failed to deploy`, + errch: `pod pod1 failed to deploy`, canceled: true, }, { diff --git a/third_party/meshnet/.gitignore b/third_party/meshnet/.gitignore new file mode 100644 index 000000000..bd6d44d83 --- /dev/null +++ b/third_party/meshnet/.gitignore @@ -0,0 +1,4 @@ +cache/ +main.go +.vscode +.envrc diff --git a/third_party/meshnet/.mk/buf.mk b/third_party/meshnet/.mk/buf.mk new file mode 100644 index 000000000..15e58e108 --- /dev/null +++ b/third_party/meshnet/.mk/buf.mk @@ -0,0 +1,15 @@ +# buf-specific targets. See https://github.com/bufbuild/buf +# NOTE: make sure you've got protoc-gen-go installed. See https://grpc.io/docs/languages/go/quickstart/ + +.PHONY: buf-ensure +buf-ensure: + @which buf >/dev/null 2>&1 || \ + echo 'Install buf with "go install github.com/bufbuild/buf/cmd/buf@latest"' + +.PHONY: lint +buf-lint: buf-ensure + buf lint + +.PHONY: buf-generate +buf-generate: buf-lint + buf generate -v diff --git a/third_party/meshnet/.mk/ci.mk b/third_party/meshnet/.mk/ci.mk new file mode 100644 index 000000000..f4b7197a7 --- /dev/null +++ b/third_party/meshnet/.mk/ci.mk @@ -0,0 +1,2 @@ +ci: + act -P ubuntu-latest=nektos/act-environments-ubuntu:20.04 diff --git a/third_party/meshnet/.mk/kind.mk b/third_party/meshnet/.mk/kind.mk new file mode 100644 index 000000000..b9fb2290f --- /dev/null +++ b/third_party/meshnet/.mk/kind.mk @@ -0,0 +1,33 @@ +# KIND cluster name +KIND_CLUSTER_NAME := "meshnet" + +.PHONY: kind-install +kind-install: + go install sigs.k8s.io/kind@v0.17.0 + +.PHONY: kind-stop +kind-stop: + @$(GOPATH)/bin/kind delete cluster --name $(KIND_CLUSTER_NAME) || \ + echo "kind cluster is not running" + +.PHONY: kind-ensure +kind-ensure: + @which $(GOPATH)/bin/kind >/dev/null 2>&1 || \ + make kind-install + +.PHONY: kind-start +kind-start: kind-ensure + @$(GOPATH)/bin/kind get clusters | grep $(KIND_CLUSTER_NAME) >/dev/null 2>&1 || \ + $(GOPATH)/bin/kind create cluster --name $(KIND_CLUSTER_NAME) --config ./kind.yaml + +.PHONY: kind-wait-for-cni +kind-wait-for-cni: + kubectl wait --timeout=60s --for condition=Ready pod -l app=kindnet -n kube-system + +.PHONY: kind-connect +kind-connect: + kubectl cluster-info --context kind-meshnet >/dev/null + +.PHONY: kind-load +kind-load: + $(GOPATH)/bin/kind load docker-image --name $(KIND_CLUSTER_NAME) ${DOCKER_IMAGE}:${COMMIT} diff --git a/third_party/meshnet/.mk/kustomize.mk b/third_party/meshnet/.mk/kustomize.mk new file mode 100644 index 000000000..6ed29a339 --- /dev/null +++ b/third_party/meshnet/.mk/kustomize.mk @@ -0,0 +1,19 @@ +.PHONY: kust-install +kust-install: + go install sigs.k8s.io/kustomize/kustomize/v5@v5.0.0 + +.PHONY: kust-ensure +kust-ensure: + @which $(GOPATH)/bin/kustomize >/dev/null 2>&1 || \ + make kust-install + +.PHONY: kustomize +kustomize: kust-ensure + cd manifests/overlays/e2e && $(GOPATH)/bin/kustomize edit set image ${DOCKER_IMAGE}:${COMMIT} + cd manifests/overlays/grpc-link-e2e && $(GOPATH)/bin/kustomize edit set image ${DOCKER_IMAGE}:${COMMIT} + cd manifests/overlays/grpc-link && $(GOPATH)/bin/kustomize edit set image ${DOCKER_IMAGE}:${COMMIT} + + +.PHONY: kustomize-kops +kustomize-kops: kust-ensure + kubectl apply -k manifests/overlays/kops/ diff --git a/third_party/meshnet/LICENSE b/third_party/meshnet/LICENSE new file mode 100644 index 000000000..ef428df13 --- /dev/null +++ b/third_party/meshnet/LICENSE @@ -0,0 +1,29 @@ +BSD 3-Clause License + +Copyright (c) 2018, Michael Kashin +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/third_party/meshnet/Makefile b/third_party/meshnet/Makefile new file mode 100644 index 000000000..6c5c77f71 --- /dev/null +++ b/third_party/meshnet/Makefile @@ -0,0 +1,128 @@ +DOCKER_IMAGE := openconfig/meshnet +GOPATH ?= ${HOME}/go +ARCHS := "linux/amd64,linux/arm64" +#ARCHS := "linux/amd64" + +COMMIT := $(shell git describe --dirty --always) +TAG := $(shell git describe --tags --abbrev=0 || echo latest) + +include .mk/kind.mk +include .mk/ci.mk +include .mk/kustomize.mk +include .mk/buf.mk + +.PHONY: all +all: docker + +## Run unit tests +test: + go test ./... + +## Run unit tests for Reconciliation +recon-test: + sudo go test -count=1 -v -run '' github.com/openconfig/kne/third_party/meshnet/daemon/grpcwire + #sudo go test -count=1 -run '' github.com/openconfig/kne/third_party/meshnet/daemon/grpcwire + +# Build local binaries +local-build: + CGO_ENABLED=0 GOOS=linux go build -o meshnet github.com/openconfig/kne/third_party/meshnet/plugin + CGO_ENABLED=1 GOOS=linux go build -o meshnetd github.com/openconfig/kne/third_party/meshnet/daemon + +# Remove local binaries +local-clean: + @[ -f ./meshnet ] && rm meshnet || true + @[ -f ./meshnetd ] && rm meshnetd || true + + +.PHONY: docker +## Build the docker image +docker: + @echo 'Creating docker image ${DOCKER_IMAGE}:${COMMIT}' + docker buildx create --use --name=multiarch --driver-opt network=host --buildkitd-flags '--allow-insecure-entitlement network.host' --node multiarch && \ + docker buildx build --load \ + --build-arg LDFLAGS=${LDFLAGS} \ + --platform "linux/amd64" \ + --tag ${DOCKER_IMAGE}:${COMMIT} \ + -f docker/Dockerfile \ + . + + +.PHONY: release +## Release the current code with git tag and `latest` +release: + docker run --rm --privileged tonistiigi/binfmt --install all + docker buildx build --push \ + --build-arg LDFLAGS=${LDFLAGS} \ + --platform ${ARCHS} \ + -t ${DOCKER_IMAGE}:${TAG} \ + -t ${DOCKER_IMAGE}:latest \ + -f docker/Dockerfile \ + . + +## Generate GRPC code +proto: buf-generate + +## Targets below are for integration testing only + +.PHONY: up +## Build test environment +up: kind-start + +.PHONY: down +## Destroy test environment +down: kind-stop + +.PHONY: e2e +## Run the end-to-end test +e2e: wait-for-meshnet + kubectl apply -f tests/3node.yml + kubectl wait --timeout=120s --for condition=Ready pod -l test=3node + kubectl exec r1 -- ping -c 1 12.12.12.2 + kubectl exec r1 -- ping -c 1 13.13.13.3 + kubectl exec r2 -- ping -c 1 23.23.23.3 + +.PHONY: e2e-mlink +## Run the end-to-end test with multi-links between a pair of nodes. +e2e-mlink: wait-for-meshnet + kubectl apply -f tests/3node-mlink.yml + kubectl wait --timeout=120s --for condition=Ready pod -l test=3node-mlink --namespace=mlink + kubectl exec r1 -n mlink -- ping -c 1 10.10.10.2 + kubectl exec r1 -n mlink -- ping -c 1 11.11.11.2 + kubectl exec r1 -n mlink -- ping -c 1 12.12.12.2 + kubectl exec r1 -n mlink -- ping -c 1 13.13.13.2 + kubectl exec r1 -n mlink -- ping -c 1 20.20.20.2 + kubectl exec r1 -n mlink -- ping -c 1 21.21.21.2 + kubectl exec r1 -n mlink -- ping -c 1 22.22.22.2 + kubectl exec r1 -n mlink -- ping -c 1 23.23.23.2 + kubectl exec r2 -n mlink -- ping -c 1 30.30.30.2 + kubectl exec r2 -n mlink -- ping -c 1 31.31.31.2 + kubectl exec r2 -n mlink -- ping -c 1 32.32.32.2 + kubectl exec r2 -n mlink -- ping -c 1 33.33.33.2 + +wait-for-meshnet: + kubectl wait --for condition=Ready pod -l name=meshnet -n meshnet + sleep 5 + +.PHONY: install +## Install meshnet into a test cluster +install: kind-load kind-wait-for-cni kustomize kind-connect +ifdef grpc + kustomize build manifests/overlays/grpc-link-e2e | kubectl apply -f - +else + kustomize build manifests/overlays/e2e | kubectl apply -f - +endif + +.PHONY: uninstall +## Uninstall meshnet from a test cluster +uninstall: kind-connect +ifdef grpc + -kustomize build manifests/overlays/grpc-link-e2e | kubectl delete -f - +else + -kustomize build manifests/overlays/e2e | kubectl delete -f - +endif + +github-ci: kust-ensure build clean local upload install e2e + +# From: https://gist.github.com/klmr/575726c7e05d8780505a +help: + @echo "$$(tput sgr0)";sed -ne"/^## /{h;s/.*//;:d" -e"H;n;s/^## //;td" -e"s/:.*//;G;s/\\n## /---/;s/\\n/ /g;p;}" ${MAKEFILE_LIST}|awk -F --- -v n=$$(tput cols) -v i=15 -v a="$$(tput setaf 6)" -v z="$$(tput sgr0)" '{printf"%s%*s%s ",a,-i,$$1,z;m=split($$2,w," ");l=n-i;for(j=1;j<=m;j++){l-=length(w[j])+1;if(l<= 0){l=n-i-length(w[j])-1;printf"\n%*s ",-i," ";}printf"%s ",w[j];}printf"\n";}' diff --git a/third_party/meshnet/README.md b/third_party/meshnet/README.md new file mode 100644 index 000000000..ee00f14ba --- /dev/null +++ b/third_party/meshnet/README.md @@ -0,0 +1,396 @@ +# meshnet CNI (KNE Fork) + +> [!NOTE] This directory contains a fork of the archived +> [networkop/meshnet-cni](https://github.com/networkop/meshnet-cni) project, now +> integrated and maintained directly within the OpenConfig KNE repository. +> +> Future development, bug fixes, and CI/CD testing are managed as part of the +> parent [KNE project](https://github.com/openconfig/kne). Please file any +> issues or pull requests in the main KNE repository. + +**meshnet** is a (K8s) CNI plugin to create arbitrary network topologies out of +point-to-point links with the help of +[koko](https://github.com/redhat-nfvpe/koko). Heavily inspired by +[Ratchet-CNI](https://github.com/dougbtv/ratchet-cni), +[kokonet](https://github.com/s1061123/kokonet) and +[Multus](https://github.com/intel/multus-cni). + +## New in version 0.2.0 + +- Using K8s etcd as datastore via Custom Resources +- All internal communication now happens over gRPC +- Support for macvlan links to connect to external resources + +## Architecture + +The goal of this plugin is to interconnect pods via direct point-to-point links +according to a pre-define topology. To do that, the plugin uses three types of +links: + +- **veth** - used to connect two pods running on the same host +- **vxlan** - used to connected two pods running on different hosts + - Optionally users can opt in to use **gRPC** instead, for this use case. + Check [Installation](#installation). +- **macvlan** - used to connect to external resources, i.e. any physical or + virtual device outside of the Kubernetes cluster + +Topology information, represented as a list of links per pod, is stored in k8s's +etcd datastore as custom resources: + +```yaml +apiVersion: networkop.co.uk/v1beta1 +kind: Topology +metadata: + name: r1 +spec: + links: + - uid: 1 + peer_pod: r2 + local_intf: eth1 + local_ip: 12.12.12.1/24 + peer_intf: eth1 + peer_ip: 12.12.12.2/24 +``` + +The plugin configuration file contains a "chained" `meshnet` in the list of +plugins: + +```yaml +{ + "cniVersion": "0.3.1", + "name": "kindnet", + "plugins": + [ + { + "ipMasq": false, + "ipam": + { + "dataDir": "/run/cni-ipam-state", + "ranges": [[{ "subnet": "10.244.0.0/24" }]], + "routes": [{ "dst": "0.0.0.0/0" }], + "type": "host-local", + }, + "mtu": 1500, + "type": "ptp", + }, + { "capabilities": { "portMappings": true }, "type": "portmap" }, + { "name": "meshnet", "type": "meshnet", "ipam": {}, "dns": {} }, + ], +} +``` + +The plugin consists of three main components: + +- **datastore** - a k8s native etcd backend cluster storing topology + information and runtime pod metadata (e.g. pod IP address and NetNS) +- **meshnet** - a CNI binary responsible for pod's network configuration +- **meshnetd** - a daemon responsible for communication with k8s and vxlan (or + grpc) link configuration updates + +![architecture](arch_v0_2_0.png) + +Below is the order of operation of the plugin from the perspective of +kube-node-1: + +1. Kubernetes cluster gets populated with the topology information via custom + resources +2. pod-1/pod-2 come up, local kubelet calls the `meshnet` binary for each pod + to setup their networking. +3. Based on the CNI configuration file, Kubelet calls meshnet to set up + additional interfaces. + + > Note that `eth0` is **always** setup by one of the existing CNI plugins. + > It is used to provide external connectivity to and from the pod + +4. meshnet binary updates the topology data with pod's runtime metadata + (namespace filepath and primary IP address). + +5. meshnet binary (via a local meshnet daemon) retrieves the list of `links` + and looks up peer pod's metadata to identify what kind of link to setup - + veth, vxlan or macvlan. + +6. If the peer is on the same node, it calls koko to setup a `veth` link + between the two pods. + +7. If the peer is on the remote node, it does two things: + - 7.1 It calls koko to setup a local `vxlan` link. + - 7.2 It makes a gRPC `Update` call to the remote node's meshnet daemon, + specifying this link's metadata (e.g. VTEP IP and VNI). + +8. Upon receipt of this information, remote node's `meshnetd` idepmotently + updates the local vxlan link, i.e. it creates a new link, updates the + existing link if there's a change or does nothing if the link attributes are + the same. + +## Local Demo + +Clone this project and build a local 3-node Kubernetes cluster + +```sh +make up +``` + +Build the meshnet docker image + +```sh +make docker +``` + +Install meshnet plugin + +```sh +# install meshnet with VXLAN link +make install +# or install meshnet with gRPC link +make grpc=1 install +``` + +Verify that meshnet is up and `READY` + +```sh +kubectl get daemonset -n meshnet +``` + +Install a 3-node test topology + +```sh +kubectl apply -f tests/3node.yml +``` + +Check that all pods are running + +```sh +kubectl get pods -l test=3node +NAME READY STATUS RESTARTS AGE +r1 1/1 Running 0 40m +r2 1/1 Running 0 40m +r3 1/1 Running 0 40s +``` + +Test connectivity between pods + +```sh +kubectl exec r1 -- ping -c 1 12.12.12.2 +kubectl exec r2 -- ping -c 1 23.23.23.3 +kubectl exec r3 -- ping -c 1 13.13.13.1 +``` + +Cleanup + +```sh +kubectl delete --grace-period=0 --force -f tests/3node.yml +``` + +Destroy the local kind cluster + +```sh +make down +``` + +## Installation + +The following manifest will create all that's required for meshnet plugin to +function, i.e.: + +- A `meshnet` namespace +- A Custom Resource Definition for network topologies +- A set of RBAC rules to allow meshnet to interact with new custom resources +- A daemonset with meshnet plugin and configuration files + +```sh +# to install meshnet with VXLAN link +kubectl apply -k manifests/base +# to install meshnet with gRPC link +kubectl apply -k manifests/overlays/grpc-link +``` + +#### Interaction with existing resources + +Meshnet plugin was designed to work alongside any other existing or future +Kubernetes resources that may not require any special topology to be set up for +them. Every pod coming up will have its first interface setup by an existing CNI +plugin (e.g. flannel, weave, calico) and will only have additional interfaces +connected if there's a matching custom `Topology` resource. + +During the initial installation process, meshnet will try to insert itself into +the list of CNI plugins. For example, assuming the following configuration is +present in `/etc/cni/net.d/weave.conf`: + +```json +{ + "cniVersion": "0.2.0", + "name": "weave", + "type": "weave-net" +} +``` + +Meshnet will convert the above to conflist and produce the file +`/etc/cni/net.d/00-meshnet.conflist` with the following content: + +```json +{ + "cniVersion": "0.2.0", + "name": "weave", + "plugins": [ + { + "cniVersion": "0.2.0", + "name": "weave", + "type": "weave-net" + }, + { + "name": "meshnet", + "type": "meshnet", + "ipam": {}, + "dns": {} + } + ] +} +``` + +### Customising installation paths + +In some cases, Kubernetes distros may store CNI configuration files and binaries +in non-standard directories and override them with `--cni-bin-dir` and +`--cni-conf-dir` flags. In order to install meshnet into the right directories, +create a new overlay under `manifests/overlays` and patch the `cni-dir` or +`cni-bin` volumes with the correct location. See +[kops overlay](manifests/overlays/kops) for an example. + +### Resilient topologies + +If you need to have Pods restarted and re-scheduled by the kube-controller, it's +possible to deploy them as StatefulSets with replica number = 1. See +[this example](/tests/2node-sts.yml). + +### Examples + +Inside the `tests` directory there are 4 manifests with the following test +topologies: + +- A simple point-to-point 2-node topology +- A 3-node topology connected as a triangle +- A 5-node topology connected as + [quincunx](https://en.wikipedia.org/wiki/Quincunx) +- A 2-node topology with 2nd node connected to a macvlan interface + +#### Use k8s-topo to orchestrate network topologies + +Login the K8s master node and + +```sh +git clone https://github.com/networkop/k8s-topo.git && cd k8s-topo +``` + +Deploy k8s-topo pod + +```sh +kubectl create -f manifest.yml +``` + +Connect to the k8s-topo pod + +```sh +kubectl exec -it k8s-topo sh +``` + +Create a random 20-node network topology + +```text +./examples/builder/builder 20 0 +Total number of links generated: 19 +``` + +Create the topology inside K8s + +```sh +k8s-topo --create examples/builder/random.yml +``` + +Optionally, you can generate a D3.js network topology graph + +```sh +k8s-topo --graph examples/builder/random.yml +``` + +View the generated topology graph at `http://:32000` + +Verify that the topology has been deployed (from the master node) + +```text +kubectl get pods -o wide | grep qrtr +qrtr-1 1/1 Running 0 11s 10.233.65.231 node3 +qrtr-10 1/1 Running 0 11s 10.233.65.234 node3 +qrtr-11 1/1 Running 0 10s 10.233.66.246 node4 +``` + +Login the first node and verify connectivity to every other loopback + +```text +$ qrtr-1 +/ # for i in `seq 0 20`; do echo "192.0.2.$i =>" $(ping -c 1 -W 1 192.0.2.$i|grep loss); done +192.0.2.0 => 1 packets transmitted, 1 packets received, 0% packet loss +192.0.2.1 => 1 packets transmitted, 1 packets received, 0% packet loss +192.0.2.2 => 1 packets transmitted, 1 packets received, 0% packet loss +192.0.2.3 => 1 packets transmitted, 1 packets received, 0% packet loss +192.0.2.4 => 1 packets transmitted, 1 packets received, 0% packet loss +192.0.2.5 => 1 packets transmitted, 1 packets received, 0% packet loss +192.0.2.6 => 1 packets transmitted, 1 packets received, 0% packet loss +192.0.2.7 => 1 packets transmitted, 1 packets received, 0% packet loss +192.0.2.8 => 1 packets transmitted, 1 packets received, 0% packet loss +192.0.2.9 => 1 packets transmitted, 1 packets received, 0% packet loss +192.0.2.10 => 1 packets transmitted, 1 packets received, 0% packet loss +192.0.2.11 => 1 packets transmitted, 1 packets received, 0% packet loss +192.0.2.12 => 1 packets transmitted, 1 packets received, 0% packet loss +192.0.2.13 => 1 packets transmitted, 1 packets received, 0% packet loss +192.0.2.14 => 1 packets transmitted, 1 packets received, 0% packet loss +192.0.2.15 => 1 packets transmitted, 1 packets received, 0% packet loss +192.0.2.16 => 1 packets transmitted, 1 packets received, 0% packet loss +192.0.2.17 => 1 packets transmitted, 1 packets received, 0% packet loss +192.0.2.18 => 1 packets transmitted, 1 packets received, 0% packet loss +192.0.2.19 => 1 packets transmitted, 1 packets received, 0% packet loss +``` + +Destroy the topology + +```sh +k8s-topo --destroy examples/builder/random.yml +``` + +## Troubleshooting + +There are two places to collect meshnet logs: + +1. Meshnet daemon logs can be collected outside of the Kubernetes cluster. For + example, the below command will collect logs from all meshnet daemons using + [stern](https://github.com/wercker/stern) + + ```sh + stern meshnet -n meshnet + ``` + +2. Meshnet plugin (binary) logs can be collected on the respective Kubernetes + nodes, e.g. + + ```text + root@kind-worker:/# journalctl -u kubelet + ``` + +--- + +Each POD is supposed to run an `init-wait` container that waits for the right +number of interface to be connected before passing the ball to the main +container. However, sometimes, PODs restart resulting in the missing interfaces +inside the main container process, since they may have been added _AFTER_ the +process that reads the container interface list (e.g. qemu-kvm for VM-based +containers). This is the procedure I use to identify the cause of the failure: + +1. Identify which POD is at fault. This will most likely be the incorrect + number of interfaces. +2. Identify which interface is missing or was added last. +3. Identify the correlation between the pair of containers interconnected by + the missing interface +4. Look for the peer container's failures using `kubectl get events +--sort-by=.metadata.creationTimestamp'` +5. Identify which k8s node this POD is running on `kubectl get pods +acme-scs1001-a -o yaml | grep node` +6. On that node check the `journalctl` for any errors associated with the POD diff --git a/third_party/meshnet/api/clientset/v1beta1/fake/fake.go b/third_party/meshnet/api/clientset/v1beta1/fake/fake.go new file mode 100644 index 000000000..91195787e --- /dev/null +++ b/third_party/meshnet/api/clientset/v1beta1/fake/fake.go @@ -0,0 +1,32 @@ +// Copyright 2023 Google LLC +// +// 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 fake + +import ( + toplogyv1client "github.com/openconfig/kne/third_party/meshnet/api/clientset/v1beta1" + topologyv1 "github.com/openconfig/kne/third_party/meshnet/api/types/v1beta1" + "k8s.io/apimachinery/pkg/runtime" + dfake "k8s.io/client-go/dynamic/fake" + "k8s.io/client-go/rest" +) + +func NewSimpleClientset(objects ...runtime.Object) (*toplogyv1client.Clientset, error) { + cs, err := toplogyv1client.NewForConfig(&rest.Config{}) + if err != nil { + return nil, err + } + c := dfake.NewSimpleDynamicClient(topologyv1.Scheme, objects...) + cs.SetDynamicClient(c.Resource(toplogyv1client.GVR())) + return cs, nil +} diff --git a/third_party/meshnet/api/clientset/v1beta1/topology.go b/third_party/meshnet/api/clientset/v1beta1/topology.go new file mode 100644 index 000000000..4a0a7154e --- /dev/null +++ b/third_party/meshnet/api/clientset/v1beta1/topology.go @@ -0,0 +1,179 @@ +// Copyright 2021 Google LLC +// +// 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 v1beta1 + +import ( + "context" + "fmt" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/watch" + "k8s.io/client-go/dynamic" + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" + "sigs.k8s.io/controller-runtime/pkg/client/apiutil" + + topologyv1 "github.com/openconfig/kne/third_party/meshnet/api/types/v1beta1" +) + +// TopologyInterface provides access to the Topology CRD. +type TopologyInterface interface { + List(ctx context.Context, opts metav1.ListOptions) (*topologyv1.TopologyList, error) + Get(ctx context.Context, name string, opts metav1.GetOptions) (*topologyv1.Topology, error) + Create(ctx context.Context, topology *topologyv1.Topology, opts metav1.CreateOptions) (*topologyv1.Topology, error) + Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error + Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) + Unstructured(ctx context.Context, name string, opts metav1.GetOptions, subresources ...string) (*unstructured.Unstructured, error) + Update(ctx context.Context, obj *unstructured.Unstructured, opts metav1.UpdateOptions) (*topologyv1.Topology, error) +} + +// Interface is the clientset interface for topology. +type Interface interface { + Topology(namespace string) TopologyInterface +} + +// Clientset is a client for the topology crds. +type Clientset struct { + dInterface dynamic.NamespaceableResourceInterface +} + +var gvr = schema.GroupVersionResource{ + Group: topologyv1.GroupName, + Version: topologyv1.GroupVersion, + Resource: "topologies", +} + +func GVR() schema.GroupVersionResource { + return gvr +} + +var ( + groupVersion = &schema.GroupVersion{ + Group: topologyv1.GroupName, + Version: topologyv1.GroupVersion, + } +) + +func GV() *schema.GroupVersion { + return groupVersion +} + +// NewForConfig returns a new Clientset based on c. +func NewForConfig(c *rest.Config) (*Clientset, error) { + config := *c + config.ContentConfig.GroupVersion = groupVersion + config.APIPath = "/apis" + config.NegotiatedSerializer = scheme.Codecs.WithoutConversion() + config.UserAgent = rest.DefaultKubernetesUserAgent() + dClient, err := dynamic.NewForConfig(c) + if err != nil { + return nil, err + } + dInterface := dClient.Resource(gvr) + return &Clientset{dInterface: dInterface}, nil +} + +// SetDynamicClient is only exposed for integration testing. +func (c *Clientset) SetDynamicClient(d dynamic.NamespaceableResourceInterface) { + c.dInterface = d +} + +func (c *Clientset) Topology(namespace string) TopologyInterface { + return &topologyClient{ + dInterface: c.dInterface, + ns: namespace, + } +} + +type topologyClient struct { + dInterface dynamic.NamespaceableResourceInterface + ns string +} + +func (t *topologyClient) List(ctx context.Context, opts metav1.ListOptions) (*topologyv1.TopologyList, error) { + u, err := t.dInterface.Namespace(t.ns).List(ctx, opts) + if err != nil { + return nil, err + } + result := topologyv1.TopologyList{} + if err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.UnstructuredContent(), &result); err != nil { + return nil, fmt.Errorf("failed to type assert return to TopologyList: %w", err) + } + return &result, nil +} + +func (t *topologyClient) Get(ctx context.Context, name string, opts metav1.GetOptions) (*topologyv1.Topology, error) { + u, err := t.dInterface.Namespace(t.ns).Get(ctx, name, opts) + if err != nil { + return nil, err + } + result := topologyv1.Topology{} + if err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.UnstructuredContent(), &result); err != nil { + return nil, fmt.Errorf("failed to type assert return to Topology: %w", err) + } + return &result, nil +} + +func (t *topologyClient) Create(ctx context.Context, topology *topologyv1.Topology, opts metav1.CreateOptions) (*topologyv1.Topology, error) { + gvk, err := apiutil.GVKForObject(topology, topologyv1.Scheme) + if err != nil { + return nil, fmt.Errorf("failed to get gvk for Topology: %w", err) + } + topology.TypeMeta = metav1.TypeMeta{ + Kind: gvk.Kind, + APIVersion: gvk.GroupVersion().String(), + } + obj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(topology) + if err != nil { + return nil, fmt.Errorf("failed to convert Topology to unstructured: %w", err) + } + u, err := t.dInterface.Namespace(t.ns).Create(ctx, &unstructured.Unstructured{Object: obj}, opts) + if err != nil { + return nil, err + } + result := topologyv1.Topology{} + if err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.UnstructuredContent(), &result); err != nil { + return nil, fmt.Errorf("failed to type assert return to Topology: %w", err) + } + return &result, nil +} + +func (t *topologyClient) Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) { + opts.Watch = true + return t.dInterface.Namespace(t.ns).Watch(ctx, opts) +} + +func (t *topologyClient) Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error { + return t.dInterface.Namespace(t.ns).Delete(ctx, name, opts) +} + +func (t *topologyClient) Update(ctx context.Context, obj *unstructured.Unstructured, opts metav1.UpdateOptions) (*topologyv1.Topology, error) { + obj, err := t.dInterface.Namespace(t.ns).UpdateStatus(ctx, obj, metav1.UpdateOptions{}) + if err != nil { + return nil, err + } + result := topologyv1.Topology{} + if err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.UnstructuredContent(), &result); err != nil { + return nil, fmt.Errorf("failed to type assert return to Topology: %w", err) + } + return &result, nil +} + +func (t *topologyClient) Unstructured(ctx context.Context, name string, opts metav1.GetOptions, subresources ...string) (*unstructured.Unstructured, error) { + return t.dInterface.Namespace(t.ns).Get(ctx, name, opts, subresources...) +} diff --git a/third_party/meshnet/api/clientset/v1beta1/topology_test.go b/third_party/meshnet/api/clientset/v1beta1/topology_test.go new file mode 100644 index 000000000..75555825d --- /dev/null +++ b/third_party/meshnet/api/clientset/v1beta1/topology_test.go @@ -0,0 +1,414 @@ +// Copyright 2023 Google LLC +// +// 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 v1beta1 + +import ( + "context" + "fmt" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/h-fam/errdiff" + topologyv1 "github.com/openconfig/kne/third_party/meshnet/api/types/v1beta1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/watch" + dynamicfake "k8s.io/client-go/dynamic/fake" + "k8s.io/client-go/rest" + ktest "k8s.io/client-go/testing" +) + +var ( + objNew = &topologyv1.Topology{ + TypeMeta: metav1.TypeMeta{ + Kind: "Topology", + APIVersion: "networkop.co.uk/v1beta1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "newObj", + Namespace: "test", + Generation: 1, + }, + Status: topologyv1.TopologyStatus{}, + Spec: topologyv1.TopologySpec{ + Links: []topologyv1.Link{{ + LocalIntf: "int1", + PeerIntf: "int1", + PeerPod: "obj2", + UID: 0, + }}, + }, + } + obj1 = &topologyv1.Topology{ + TypeMeta: metav1.TypeMeta{ + Kind: "Topology", + APIVersion: "networkop.co.uk/v1beta1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "obj1", + Namespace: "test", + Generation: 1, + }, + Status: topologyv1.TopologyStatus{}, + Spec: topologyv1.TopologySpec{ + Links: []topologyv1.Link{{ + LocalIntf: "int1", + PeerIntf: "int1", + PeerPod: "obj2", + UID: 0, + }}, + }, + } + obj2 = &topologyv1.Topology{ + TypeMeta: metav1.TypeMeta{ + Kind: "Topology", + APIVersion: "networkop.co.uk/v1beta1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "obj2", + Namespace: "test", + Generation: 1, + }, + Status: topologyv1.TopologyStatus{}, + Spec: topologyv1.TopologySpec{ + Links: []topologyv1.Link{{ + LocalIntf: "int1", + PeerIntf: "int1", + PeerPod: "obj1", + UID: 1, + }}, + }, + } +) + +type fakeWatch struct { + e []watch.Event + ch chan watch.Event + done chan struct{} +} + +func newFakeWatch(e []watch.Event) *fakeWatch { + f := &fakeWatch{ + e: e, + ch: make(chan watch.Event, 1), + done: make(chan struct{}), + } + go func() { + for len(f.e) != 0 { + e := f.e[0] + f.e = f.e[1:] + select { + case f.ch <- e: + case <-f.done: + return + } + } + }() + return f +} +func (f *fakeWatch) Stop() { + close(f.done) +} + +func (f *fakeWatch) ResultChan() <-chan watch.Event { + return f.ch +} + +func setUp(t *testing.T) *Clientset { + t.Helper() + objs := []runtime.Object{obj1, obj2} + cs, err := NewForConfig(&rest.Config{}) + if err != nil { + t.Fatalf("failed to create client set") + } + f := dynamicfake.NewSimpleDynamicClient(topologyv1.Scheme, objs...) + f.PrependWatchReactor("*", func(action ktest.Action) (bool, watch.Interface, error) { + wAction, ok := action.(ktest.WatchAction) + if !ok { + return false, nil, nil + } + if wAction.GetWatchRestrictions().ResourceVersion == "doesnotexist" { + return true, nil, fmt.Errorf("cannot watch unknown resource version") + } + f := newFakeWatch([]watch.Event{ + { + Type: watch.Added, + Object: obj1, + }, + }) + return true, f, nil + }) + cs.dInterface = f.Resource(gvr) + return cs +} + +func TestCreate(t *testing.T) { + cs := setUp(t) + objWithoutTypeMetaOut := objNew.DeepCopy() + objWithoutTypeMetaOut.Name = "newObjWithoutTypeMeta" + objWithoutTypeMetaIn := objWithoutTypeMetaOut.DeepCopy() + objWithoutTypeMetaIn.TypeMeta.Reset() + tests := []struct { + desc string + in *topologyv1.Topology + want *topologyv1.Topology + wantErr string + }{{ + desc: "already exists", + in: obj1, + wantErr: "already exists", + }, { + desc: "success", + in: objNew, + want: objNew, + }, { + desc: "success without typemeta", + in: objWithoutTypeMetaIn, + want: objWithoutTypeMetaOut, + }} + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + tc := cs.Topology("test") + got, err := tc.Create(context.Background(), tt.in, metav1.CreateOptions{}) + if s := errdiff.Substring(err, tt.wantErr); s != "" { + t.Fatalf("unexpected error: %s", s) + } + if tt.wantErr != "" { + return + } + if s := cmp.Diff(tt.want, got); s != "" { + t.Fatalf("Create(%+v) failed: %s", tt.want, s) + } + }) + } +} + +func TestList(t *testing.T) { + cs := setUp(t) + tests := []struct { + desc string + want *topologyv1.TopologyList + wantErr string + }{{ + desc: "success", + want: &topologyv1.TopologyList{ + Items: []topologyv1.Topology{*obj1, *obj2}, + }, + }} + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + tc := cs.Topology("test") + got, err := tc.List(context.Background(), metav1.ListOptions{}) + if s := errdiff.Substring(err, tt.wantErr); s != "" { + t.Fatalf("unexpected error: %s", s) + } + if tt.wantErr != "" { + return + } + if s := cmp.Diff(tt.want, got, cmpopts.IgnoreFields(topologyv1.TopologyList{}, "TypeMeta")); s != "" { + t.Fatalf("List() failed: %s", s) + } + }) + } +} + +func TestGet(t *testing.T) { + cs := setUp(t) + tests := []struct { + desc string + in string + want *topologyv1.Topology + wantErr string + }{{ + desc: "failure", + in: "doesnotexist", + wantErr: `"doesnotexist" not found`, + }, { + desc: "success 1", + in: "obj1", + want: obj1, + }, { + desc: "success 2", + in: "obj2", + want: obj2, + }} + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + tc := cs.Topology("test") + got, err := tc.Get(context.Background(), tt.in, metav1.GetOptions{}) + if s := errdiff.Substring(err, tt.wantErr); s != "" { + t.Fatalf("unexpected error: %s", s) + } + if tt.wantErr != "" { + return + } + if s := cmp.Diff(tt.want, got); s != "" { + t.Fatalf("Get(%q) failed: %s", tt.in, s) + } + }) + } +} + +func TestDelete(t *testing.T) { + cs := setUp(t) + tests := []struct { + desc string + in string + wantErr string + }{{ + desc: "failure", + in: "doesnotexist", + wantErr: `"doesnotexist" not found`, + }, { + desc: "success", + in: "obj1", + }} + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + tc := cs.Topology("test") + err := tc.Delete(context.Background(), tt.in, metav1.DeleteOptions{}) + if s := errdiff.Substring(err, tt.wantErr); s != "" { + t.Fatalf("unexpected error: %s", s) + } + if tt.wantErr != "" { + return + } + }) + } +} + +func TestWatch(t *testing.T) { + cs := setUp(t) + tests := []struct { + desc string + ver string + want watch.Event + wantErr string + }{{ + desc: "failure", + ver: "doesnotexist", + wantErr: "cannot watch unknown resource version", + }, { + desc: "success", + want: watch.Event{ + Type: watch.Added, + Object: obj1, + }, + }} + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + tc := cs.Topology("test") + w, err := tc.Watch(context.Background(), metav1.ListOptions{ResourceVersion: tt.ver}) + if s := errdiff.Substring(err, tt.wantErr); s != "" { + t.Fatalf("unexpected error: %s", s) + } + if tt.wantErr != "" { + return + } + e := <-w.ResultChan() + if s := cmp.Diff(tt.want, e); s != "" { + t.Fatalf("Watch() failed: %s", s) + } + }) + } +} + +func TestUpdate(t *testing.T) { + cs := setUp(t) + tests := []struct { + desc string + want *topologyv1.Topology + wantErr string + }{{ + desc: "Error", + want: &topologyv1.Topology{ + TypeMeta: metav1.TypeMeta{ + Kind: "Topology", + APIVersion: "networkop.co.uk/v1beta1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "doesnotexist", + Namespace: "test", + }, + }, + wantErr: "doesnotexist", + }, { + desc: "Valid Topology", + want: obj1, + }} + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + tc := cs.Topology("test") + updateObj := tt.want.DeepCopy() + updateObj.Spec.Links = append(updateObj.Spec.Links, topologyv1.Link{UID: 1000}) + update, err := runtime.DefaultUnstructuredConverter.ToUnstructured(updateObj) + if err != nil { + t.Fatalf("failed to generate update: %v", err) + } + got, err := tc.Update(context.Background(), &unstructured.Unstructured{Object: update}, metav1.UpdateOptions{}) + if s := errdiff.Substring(err, tt.wantErr); s != "" { + t.Fatalf("unexpected error: %s", s) + } + if tt.wantErr != "" { + return + } + if s := cmp.Diff(updateObj, got); s != "" { + t.Fatalf("Update() failed: %s", s) + } + }) + } +} + +func TestUnstructured(t *testing.T) { + cs := setUp(t) + tests := []struct { + desc string + in string + want *topologyv1.Topology + wantErr string + }{{ + desc: "failure", + in: "missingObj", + wantErr: `"missingObj" not found`, + }, { + desc: "success 1", + in: "obj1", + want: obj1, + }, { + desc: "success 2", + in: "obj2", + want: obj2, + }} + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + tc := cs.Topology("test") + got, err := tc.Unstructured(context.Background(), tt.in, metav1.GetOptions{}) + if s := errdiff.Substring(err, tt.wantErr); s != "" { + t.Fatalf("unexpected error: %s", s) + } + if tt.wantErr != "" { + return + } + uObj1 := &topologyv1.Topology{} + if err := runtime.DefaultUnstructuredConverter.FromUnstructured(got.Object, uObj1); err != nil { + t.Fatalf("failed to turn response into a topology: %v", err) + } + if s := cmp.Diff(uObj1, tt.want); s != "" { + t.Fatalf("Unstructured(%q) failed: %s", tt.in, s) + } + }) + } +} diff --git a/third_party/meshnet/api/types/v1beta1/gwire_types.go b/third_party/meshnet/api/types/v1beta1/gwire_types.go new file mode 100644 index 000000000..aaf7032ad --- /dev/null +++ b/third_party/meshnet/api/types/v1beta1/gwire_types.go @@ -0,0 +1,93 @@ +// Copyright 2023 Google LLC +// +// 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. + +// This is required to generate CRD using controller-gen +// +groupName=networkop.co.uk + +package v1beta1 + +import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + +//go:generate controller-gen object paths=$GOFILE + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// +optional +type GWireKNodeSpec struct { + metav1.TypeMeta `json:",inline"` + // unique link id + // +optional + UIDs []int `json:"uids"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// +optional +type GWireKNodeStatus struct { + metav1.TypeMeta `json:",inline"` + + // +optional + GWireKItems []GWireStatus `json:"grpcWireItems"` +} + +type GWireStatus struct { + // +optional + // Name of the node holding the wire end + LocalNodeName string `json:"node_name"` + // +optional + // Unique link id as assigned by meshnet + LinkId int64 `json:"link_id"` + // +optional + // The topology namespace. + TopoNamespace string `json:"topo_namespace"` + // +optional + //Network namespace of the local pod holding the wire end + LocalPodNetNs string `json:"local_pod_net_ns"` + // +optional + // Local pod name as specified in topology CR + LocalPodName string `json:"local_pod_name"` + // +optional + // Local pod ip as specified in topology CR + LocalPodIp string `json:"local_pod_ip"` + // +optional + // Local pod interface name that is specified in topology CR and is created by meshnet + LocalPodIfaceName string `json:"local_pod_iface_name"` + // +optional + // The interface(name) in the local node and is connected with local pod + WireIfaceNameOnLocalNode string `json:"wire_iface_name_on_local_node"` + // +optional + // The interface id, in the peer node and is connected with remote pod. + // This is used for de-multiplexing received packet from grpcwire + WireIfaceIdOnPeerNode int64 `json:"wire_iface_id_on_peer_node"` + // +optional + // peer node IP address + GWirePeerNodeIp string `json:"gwire_peer_node_ip"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +type GWireKObj struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + // +optional + Status GWireKNodeStatus `json:"status"` + // +optional + Spec GWireKNodeSpec `json:"spec"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +type GWireKObjList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + + Items []GWireKObj `json:"items"` +} diff --git a/third_party/meshnet/api/types/v1beta1/register.go b/third_party/meshnet/api/types/v1beta1/register.go new file mode 100644 index 000000000..1be80c43f --- /dev/null +++ b/third_party/meshnet/api/types/v1beta1/register.go @@ -0,0 +1,41 @@ +// Copyright 2021 Google LLC +// +// 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 v1beta1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +const ( + GroupName = "networkop.co.uk" + GroupVersion = "v1beta1" + GWireResNamePlural = "gwirekobjs" +) + +var ( + SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: GroupVersion} + Scheme = runtime.NewScheme() +) + +func init() { + Scheme.AddKnownTypes(SchemeGroupVersion, + &Topology{}, + &TopologyList{}, + ) + metav1.AddToGroupVersion(Scheme, SchemeGroupVersion) + metav1.AddMetaToScheme(Scheme) +} diff --git a/third_party/meshnet/api/types/v1beta1/topology.go b/third_party/meshnet/api/types/v1beta1/topology.go new file mode 100644 index 000000000..745c3f32d --- /dev/null +++ b/third_party/meshnet/api/types/v1beta1/topology.go @@ -0,0 +1,63 @@ +// Copyright 2021 Google LLC +// +// 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 v1beta1 + +import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + +//go:generate controller-gen object paths=$GOFILE + +type TopologySpec struct { + metav1.TypeMeta `json:",inline"` + Links []Link `json:"links"` +} + +type TopologyStatus struct { + metav1.TypeMeta `json:",inline"` + Skipped []Skipped `json:"skipped"` + SrcIP string `json:"src_ip"` + NetNS string `json:"net_ns"` + ContainerID string `json:"container_id"` +} + +type Skipped struct { + PodName string `json:"pod_name"` + LinkId int64 `json:"link_id"` +} + +type Link struct { + LocalIntf string `json:"local_intf"` + LocalIP string `json:"local_ip"` + PeerIntf string `json:"peer_intf"` + PeerIP string `json:"peer_ip"` + PeerPod string `json:"peer_pod"` + UID int `json:"uid"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +type Topology struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Status TopologyStatus `json:"status"` + Spec TopologySpec `json:"spec"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +type TopologyList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + + Items []Topology `json:"items"` +} diff --git a/third_party/meshnet/api/types/v1beta1/zz_generated.deepcopy.go b/third_party/meshnet/api/types/v1beta1/zz_generated.deepcopy.go new file mode 100644 index 000000000..1137b08db --- /dev/null +++ b/third_party/meshnet/api/types/v1beta1/zz_generated.deepcopy.go @@ -0,0 +1,141 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +// Copyright 2021 Google LLC +// +// 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. + +// Code generated by controller-gen. DO NOT EDIT. + +package v1beta1 + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Topology) DeepCopyInto(out *Topology) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Status.DeepCopyInto(&out.Status) + in.Spec.DeepCopyInto(&out.Spec) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Topology. +func (in *Topology) DeepCopy() *Topology { + if in == nil { + return nil + } + out := new(Topology) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Topology) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TopologyList) DeepCopyInto(out *TopologyList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Topology, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TopologyList. +func (in *TopologyList) DeepCopy() *TopologyList { + if in == nil { + return nil + } + out := new(TopologyList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *TopologyList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TopologySpec) DeepCopyInto(out *TopologySpec) { + *out = *in + out.TypeMeta = in.TypeMeta + if in.Links != nil { + in, out := &in.Links, &out.Links + *out = make([]Link, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TopologySpec. +func (in *TopologySpec) DeepCopy() *TopologySpec { + if in == nil { + return nil + } + out := new(TopologySpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *TopologySpec) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TopologyStatus) DeepCopyInto(out *TopologyStatus) { + *out = *in + out.TypeMeta = in.TypeMeta + if in.Skipped != nil { + in, out := &in.Skipped, &out.Skipped + *out = make([]Skipped, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TopologyStatus. +func (in *TopologyStatus) DeepCopy() *TopologyStatus { + if in == nil { + return nil + } + out := new(TopologyStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *TopologyStatus) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} diff --git a/third_party/meshnet/api/types/v1beta1/zz_generated_grpcwire.deepcopy.go b/third_party/meshnet/api/types/v1beta1/zz_generated_grpcwire.deepcopy.go new file mode 100644 index 000000000..4f0de4310 --- /dev/null +++ b/third_party/meshnet/api/types/v1beta1/zz_generated_grpcwire.deepcopy.go @@ -0,0 +1,127 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +// Code generated by controller-gen. DO NOT EDIT. + +package v1beta1 + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GWireKNodeSpec) DeepCopyInto(out *GWireKNodeSpec) { + *out = *in + out.TypeMeta = in.TypeMeta + if in.UIDs != nil { + in, out := &in.UIDs, &out.UIDs + *out = make([]int, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GWireKNodeSpec. +func (in *GWireKNodeSpec) DeepCopy() *GWireKNodeSpec { + if in == nil { + return nil + } + out := new(GWireKNodeSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *GWireKNodeSpec) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GWireKNodeStatus) DeepCopyInto(out *GWireKNodeStatus) { + *out = *in + out.TypeMeta = in.TypeMeta + if in.GWireKItems != nil { + in, out := &in.GWireKItems, &out.GWireKItems + *out = make([]GWireStatus, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GWireKNodeStatus. +func (in *GWireKNodeStatus) DeepCopy() *GWireKNodeStatus { + if in == nil { + return nil + } + out := new(GWireKNodeStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *GWireKNodeStatus) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GWireKObj) DeepCopyInto(out *GWireKObj) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Status.DeepCopyInto(&out.Status) + in.Spec.DeepCopyInto(&out.Spec) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GWireKObj. +func (in *GWireKObj) DeepCopy() *GWireKObj { + if in == nil { + return nil + } + out := new(GWireKObj) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *GWireKObj) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GWireKObjList) DeepCopyInto(out *GWireKObjList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]GWireKObj, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GWireKObjList. +func (in *GWireKObjList) DeepCopy() *GWireKObjList { + if in == nil { + return nil + } + out := new(GWireKObjList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *GWireKObjList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} diff --git a/third_party/meshnet/arch.png b/third_party/meshnet/arch.png new file mode 100644 index 000000000..a2153d082 Binary files /dev/null and b/third_party/meshnet/arch.png differ diff --git a/third_party/meshnet/arch_v0_2_0.png b/third_party/meshnet/arch_v0_2_0.png new file mode 100644 index 000000000..7a6d740a9 Binary files /dev/null and b/third_party/meshnet/arch_v0_2_0.png differ diff --git a/third_party/meshnet/buf.gen.yaml b/third_party/meshnet/buf.gen.yaml new file mode 100644 index 000000000..f63695468 --- /dev/null +++ b/third_party/meshnet/buf.gen.yaml @@ -0,0 +1,8 @@ +version: v1beta1 +plugins: + - name: go-grpc + out: . + opt: paths=source_relative + - name: go + out: . + opt: paths=source_relative diff --git a/third_party/meshnet/buf.yaml b/third_party/meshnet/buf.yaml new file mode 100644 index 000000000..2bedabfd4 --- /dev/null +++ b/third_party/meshnet/buf.yaml @@ -0,0 +1,17 @@ +version: v1beta1 +build: + roots: + - . +lint: + use: + - BASIC + - FILE_LOWER_SNAKE_CASE + except: + - ENUM_NO_ALLOW_ALIAS + - IMPORT_NO_PUBLIC + - PACKAGE_AFFINITY + - PACKAGE_DIRECTORY_MATCH + - PACKAGE_SAME_DIRECTORY +breaking: + use: + - WIRE_JSON diff --git a/third_party/meshnet/daemon/cni/cni.go b/third_party/meshnet/daemon/cni/cni.go new file mode 100644 index 000000000..b3611855c --- /dev/null +++ b/third_party/meshnet/daemon/cni/cni.go @@ -0,0 +1,145 @@ +package cni + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "sort" + "strings" + + "github.com/containernetworking/cni/libcni" + "github.com/containernetworking/cni/pkg/types" + log "github.com/sirupsen/logrus" +) + +const ( + defaultNetDir = "/etc/cni/net.d" + defaultCNIFile = "00-meshnet.conflist" + interNodeLinkConf = "/etc/cni/net.d/meshnet-inter-node-link-type" + defaultPluginName = "meshnet" +) + +var meshnetCNIPath = filepath.Join(defaultNetDir, defaultCNIFile) + +// This is borrowed from https://tinyurl.com/khjhf9xd +func loadConfList() (map[string]interface{}, error) { + files, err := libcni.ConfFiles(defaultNetDir, []string{".conf", ".conflist", ".json"}) + switch { + case err != nil: + return nil, err + case len(files) == 0: + return nil, libcni.NoConfigsFoundError{Dir: defaultNetDir} + } + + // Ignore any existing meshnet config files + var confFiles []string + for _, f := range files { + if strings.Contains(f, "meshnet") { + continue + } + confFiles = append(confFiles, f) + } + + sort.Strings(confFiles) + // Iterate over existing confFiles and pick the first one that's valid, borrowed from https://tinyurl.com/977uyx5m + for _, confFile := range confFiles { + var confList *libcni.NetworkConfigList + if strings.HasSuffix(confFile, ".conflist") { + confList, err = libcni.ConfListFromFile(confFile) + if err != nil { + log.Infof("Error loading %q CNI config list file: %s", confFile, err) + continue + } + } else { + conf, err := libcni.ConfFromFile(confFile) + if err != nil { + log.Infof("Error loading %q CNI config file: %s", confFile, err) + continue + } + // Ensure the config has a "type" so we know what plugin to run. + // Also catches the case where somebody put a conflist into a conf file. + if conf.Network.Type == "" { + log.Infof("Error loading %q CNI config file: no 'type'; perhaps this is a .conflist?", confFile) + continue + } + + confList, err = libcni.ConfListFromConf(conf) + if err != nil { + log.Infof("Error converting CNI config file %q to list: %s", confFile, err) + continue + } + } + if len(confList.Plugins) == 0 { + log.Infof("%q CNI config list has no plugins, skipping", confFile) + continue + } + + // only pre-parse the top of the CNI file without using the types.NetConfList + // this is because some generic types do not define the complete config struct + // e.g. IPAM config will not be parsed at all beyond the `type` + var conf map[string]interface{} + err = json.Unmarshal(confList.Bytes, &conf) + + return conf, err + } + + return nil, fmt.Errorf("no valid network configurations found in %q", defaultNetDir) +} + +func saveConfList(m map[string]interface{}) error { + bytes, err := json.MarshalIndent(m, "", "\t") + if err != nil { + return err + } + return ioutil.WriteFile(meshnetCNIPath, bytes, os.FileMode(06444)) +} + +func saveInterNodeLinkConf() error { + return ioutil.WriteFile(interNodeLinkConf, []byte(os.Getenv("INTER_NODE_LINK_TYPE")), os.FileMode(06444)) +} + +func removeInterNodeLinkConf() error { + if err := os.Remove(interNodeLinkConf); err != nil { + return fmt.Errorf("failed to remove %s: %v", interNodeLinkConf, err) + } + return nil +} + +// Init installs meshnet CNI configuration +func Init() error { + + conf, err := loadConfList() + if err != nil { + return err + } + + // We can safely access and type-cast since all of the checks have already been done in the `loadConfList()` + plugins := conf["plugins"].([]interface{}) + + plugins = append(plugins, &types.NetConf{ + Type: defaultPluginName, + Name: defaultPluginName, + }) + + conf["cniVersion"] = "0.3.0" + conf["plugins"] = plugins + + // TODO: check if we can avoid creating a custom file for propagating value of env INTER_NODE_LINK_TYPE + if err := saveInterNodeLinkConf(); err != nil { + return err + } + + return saveConfList(conf) +} + +// Cleanup removes meshnet CNI configuration +func Cleanup() { + if err := os.Remove(meshnetCNIPath); err != nil { + log.Infof("Failed to remove file %s: %v", meshnetCNIPath, err) + } + if err := removeInterNodeLinkConf(); err != nil { + log.Infof("Failed to remove inter node link conf: %v", err) + } +} diff --git a/third_party/meshnet/daemon/grpcwire/grpcwire.go b/third_party/meshnet/daemon/grpcwire/grpcwire.go new file mode 100644 index 000000000..06f4f050b --- /dev/null +++ b/third_party/meshnet/daemon/grpcwire/grpcwire.go @@ -0,0 +1,390 @@ +package grpcwire + +import ( + "context" + "fmt" + "io" + "net" + "strings" + "sync" + + "github.com/google/gopacket" + "github.com/google/gopacket/pcap" + "github.com/openconfig/gnmi/errlist" + koko "github.com/redhat-nfvpe/koko/api" + log "github.com/sirupsen/logrus" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + + mpb "github.com/openconfig/kne/third_party/meshnet/daemon/proto/meshnet/v1beta1" + "github.com/openconfig/kne/third_party/meshnet/utils/wireutil" +) + +var grpcOvrlyLogger *log.Entry = nil + +func InitLogger() { + grpcOvrlyLogger = log.WithFields(log.Fields{"daemon": "meshnetd", "overlay": "gRPC"}) +} + +type intfIndex struct { + mu sync.Mutex + currId int64 +} + +/* + In a given node a veth-pair connects a pod with the meshnet daemon hosted in the node. This meshnet + +daemon provides the grpc-wire service to connect the local pod with the remote pod over grpc. The node +end of the veth-pair must have unique name with in the node. A node can have multiple pods. So there +will be multiple veth-pairs for connecting multiple nodes to meshnet daemon and each of them (the node end) must have unique +names. IntfIndex provides the sequentially increasing number which makes the name unique when added as +suffix to the name. +*/ +var indexGen intfIndex + +func NextIndex() int64 { + indexGen.mu.Lock() + defer indexGen.mu.Unlock() + indexGen.currId++ + return indexGen.currId +} + +/*+++tbf: These constants has no utility other that helping in debugging. These can be removed later. */ +type grpcWireOriginator int + +func (g grpcWireOriginator) String() string { + switch g { + case HOST_CREATED_WIRE: + return "host originated" + case PEER_CREATED_WIRE: + return "peer originated" + } + return "unknown originator" +} + +const ( + HOST_CREATED_WIRE grpcWireOriginator = iota + PEER_CREATED_WIRE +) + +type GRPCWire struct { + UID int // uid identify a particular link in a topology as per meshnet crd + TopoNamespace string // K8s namespace this wire belongs to + + /* Node information */ + LocalNodeIfaceID int64 // OS assigned interface ID of local node interface + LocalNodeIfaceName string // name of local node interface + + /* Pod information : where this wire is terminating in this node */ + LocalPodIP string // IP address of the local container who will consume packets over this wire. + LocalPodName string // Name the local pod who will consume packets over this wire. + LocalPodIfaceName string // Name the interface which is inside the local pod who will consume packets over this wire. This is for debugging + LocalPodNetNS string + + /*Peer pod information*/ + WireIfaceIDOnPeerNode int64 // Peer end of the wire interface ID which is present in peer node + PeerNodeIP string // Peer node IP + + IsReady bool // Is this wire ip. + Originator grpcWireOriginator // create by local host or create on trigger from remote host. This is for debugging. + OriginatorIP string // IP address of the host created it. This is for debugging. + + StopC chan struct{} // the channel to send stop signal to the receive thread. + mu sync.Mutex +} + +type linkKey struct { + namespace string + linkUID int +} + +func CreateGWire(locIfIndex int, locIfNm string, stopC chan struct{}, wireDef *mpb.WireDef) *GRPCWire { + + return &GRPCWire{ + UID: int(wireDef.LinkUid), + + LocalNodeIfaceID: int64(locIfIndex), + LocalNodeIfaceName: locIfNm, + LocalPodIP: wireDef.LocalPodIp, + LocalPodIfaceName: wireDef.IntfNameInPod, + LocalPodName: wireDef.LocalPodName, + LocalPodNetNS: wireDef.LocalPodNetNs, + + WireIfaceIDOnPeerNode: wireDef.WireIfIdOnPeerNode, + PeerNodeIP: wireDef.PeerNodeIp, + + IsReady: true, + Originator: PEER_CREATED_WIRE, + OriginatorIP: wireDef.PeerNodeIp, + + StopC: stopC, + TopoNamespace: wireDef.TopoNs, + } + +} + +// update the ware with the given input and mark the wire ready +func (wire *GRPCWire) UpdateWire(peerIntfId int64, stopC chan struct{}) { + wire.mu.Lock() + defer wire.mu.Unlock() + wire.StopC = stopC + if !wire.IsReady { + wire.WireIfaceIDOnPeerNode = peerIntfId + } + wire.IsReady = true +} + +// Delete a wire from the in-memory wire-map under a lock +func (w *wireMap) Delete(wire *GRPCWire) error { + w.mu.Lock() + defer w.mu.Unlock() + err := w.DeleteWoLock(wire) + return err +} + +// GetWireByUID returns wire matching the provided namespace and linkUID. +func GetWireByUID(namespace string, linkUID int) (*GRPCWire, bool) { + return wires.GetWire(namespace, linkUID) +} + +// For the given uid if the wire exists, then update the wire properties. +// Returns true if a wire exists, also the wire structure that got modified +func UpdateWireByUID(namespace string, linkUID int, peerIntfId int64, stopC chan struct{}) (*GRPCWire, bool) { + wires.mu.Lock() + defer wires.mu.Unlock() + wire, ok := wires.wires[linkKey{ + namespace: namespace, + linkUID: linkUID, + }] + if ok { + wire.StopC = stopC + if !wire.IsReady { + wire.WireIfaceIDOnPeerNode = peerIntfId + } + wire.IsReady = true + } + return wire, ok +} + +// WireDownByUID - stops packet collection from the connected pod +func WireDownByUID(namespace string, linkUID int) error { + wires.mu.Lock() + defer wires.mu.Unlock() + + wire, ok := wires.wires[linkKey{ + namespace: namespace, + linkUID: linkUID, + }] + if ok { + grpcOvrlyLogger.Infof("WireDownByUID: Making wire down from db, %s@%s-%s@%d, peer fid %d, link uid %d", + wire.LocalPodName, wire.LocalPodIfaceName, wire.LocalNodeIfaceName, wire.LocalNodeIfaceID, wire.WireIfaceIDOnPeerNode, linkUID) + if wire.IsReady { + close(wire.StopC) + } + wire.IsReady = false + } else { + grpcOvrlyLogger.Infof("WireDownByUID: Did not find entry to make down from db, uid %d, ns %s", + linkUID, namespace) + } + return nil +} + +// ------------------------------------------------------------------------------------------------- +func AddWireInMemNDataStore(wire *GRPCWire, handle *pcap.Handle) int { + /* Populate the active wire map and returns the number of currently added active wires. */ + + /* if this wire is already present in the map then it will be overwritten. + It seems to be ok to overwrite. Think more in what situation this may + not be the desired behavior and we need to throw an error. */ + wire.IsReady = true + + wires.AddInMemNDataStore(wire, handle) + return len(wires.wires) +} + +// ------------------------------------------------------------------------------------------------- +// DeleteWire cleans up the active wire map and returns the number of currently added active wire. +func DeleteWire(wire *GRPCWire) int { + wires.AtomicDelete(wire) + return len(wires.wires) +} + +// ----------------------------------------------------------------------------------------------------------- +// This function is used for delete operation. It deletes all the wires connected with the pod. +// This function clear up the in-memory data base as well as the K8S Datastore. +func DeletePodWires(namespace string, podName string) error { + var errs errlist.List + for { + aW, _ := ExtractOneWireByPod(namespace, podName) + if aW == nil { + break + } + + // Since this wire is already extracted, so it no more preset in in-memory-map. Next we need to clear only the K8S data store. + if err := RemoveWireAcrosAll(aW, false); err != nil { + grpcOvrlyLogger.Infof("[WIRE-DELETE]:Error Removing local-iface@pod : %s@%s for wire UID: %d, iface id %d : %v", aW.LocalPodIfaceName, aW.LocalPodName, aW.UID, aW.LocalNodeIfaceID, err) + errs.Add(err) + } else { + grpcOvrlyLogger.Infof("[WIRE-DELETE]:Removed local-iface@pod : %s@%s for wire UID: %d, iface id %d", aW.LocalPodIfaceName, aW.LocalPodName, aW.UID, aW.LocalNodeIfaceID) + } + } + if errs.Err() != nil { + return fmt.Errorf("[WIRE-DELETE]:failed to remove all grpc-wires for pod %s@%s: %w", podName, namespace, errs.Err()) + } + grpcOvrlyLogger.Infof("[WIRE-DELETE]:All grpc-wires for pod %s:%s is deleted", namespace, podName) + return nil +} + +// ---------------------------------------------------------------------------------------------------------- +// Cleanup function for clearing up the in-memory wire map anf the K8S data store, when the meshnet cni plugin +// instructs the meshenet daemon to destroy a wire. Before deleting this function stops the thread for receiving +// packets from the pod connected to this wire. +// input parameter imMem set to true to clear the in-memory wire map. +func RemoveWireAcrosAll(wire *GRPCWire, inMem bool) error { + + if wire == nil { + grpcOvrlyLogger.Infof("[WIRE-DELETE]:Null wire. This ware is already removed") + return nil + } + + // stop the packet receive thread for this pod + if wire.IsReady { + close(wire.StopC) + } + wire.IsReady = false + + /* Remove the veth from the node */ + intf, err := net.InterfaceByIndex(int(wire.LocalNodeIfaceID)) + if err != nil { + grpcOvrlyLogger.Infof("[WIRE-DELETE]:Interface index %d for wire %d, is already cleaned up.", wire.LocalNodeIfaceID, wire.UID) + } else { + myVeth := koko.VEth{} + myVeth.LinkName = intf.Name + if err = myVeth.RemoveVethLink(); err != nil { + return fmt.Errorf("[WIRE-DELETE]:failed to remove veth link: %w", err) + } + } + + // clean up im-memory wire-map + if inMem { + wires.AtomicDelete(wire) // Deleting the wire from in-memory data + } + //delete from data-store + wire.K8sDelGWire() + grpcOvrlyLogger.Infof("[WIRE-DELETE]:Successfully removed grpc wire for link %d, iface id %d.", wire.UID, wire.LocalNodeIfaceID) + return nil +} + +// ----------------------------------------------------------------------------------------------------------- +// Generate the name of the interface to be placed on the node +func GenNodeIfaceName(podName string, podIfaceName string) (string, error) { + // Linux has issue if interface name is too long. Generate a smaller name. + // In recent kernel versions this is defined by IFNAMSIZ to be 16 bytes, so 15 user-visible bytes + // (assuming it includes a trailing null). IFNAMSIZ is used in defining struct net_device's name. + // The name must not contain / or any whitespace characters + // + //TODO: This method needs to be robust. It monotonically increases the index and never + // decreases it, even if the interfaces are deleted. So far this will work for accumulated + // 1K interfaces per node under the current naming scheme. This is too small. + // Using 14 digit random number and checking if any interface with generated name exists and if + // exists then generate another random number (try 3 times before giving up). This will make it robust. + // This reduces the readability and correlation between the “pod-interface” and corresponding + // “node-interface”, for example eth1host1-<3-digit-index> will become "12345678901234". + id := NextIndex() + + ifaceName := fmt.Sprintf("%.5s%.5s-%04d", podName, podIfaceName, id) + + return ifaceName, nil +} + +// ----------------------------------------------------------------------------------------------------------- +func RecvFrmLocalPodThread(wire *GRPCWire, locIfNm string) error { + + defaultPort := wireutil.GRPCDefaultPort + pktBuffSz := int32(1024 * 64 * 10) //keep buffer for MAX 10 64K frames + + url := strings.TrimSpace(fmt.Sprintf("%s:%d", wire.PeerNodeIP, defaultPort)) + /* Utilizing google gopacket for polling for packets from the node. This seems to be the + simplest way to get all packets. + As an alternative to google gopacket(pcap), a socket based implementation is possible. + Not sure if socket based implementation can bring any advantage or not. + + Near term will replace pcap by socket. + */ + + // in some rare cases by the time the thread starts K8S may decide to move the pod somewhere else. + // in that case the local interfaced will be cleaned up asynchronously. Detect the situation and return. + _, err := net.InterfaceByName(locIfNm) + if err != nil { + grpcOvrlyLogger.Errorf("[Packet Receive thread]For pod %s failed to retrieve interface %s/%d. error: %v", wire.LocalPodName, wire.LocalNodeIfaceName, wire.LocalNodeIfaceID, err) + return err + } + + rdHandl, err := pcap.OpenLive(wire.LocalNodeIfaceName, pktBuffSz, true, pcap.BlockForever) + if err != nil { + // let the caller handle the error + grpcOvrlyLogger.Errorf("Receive Thread for local pod failed to open interface: %s/%d, PCAP ERROR: %v", wire.LocalNodeIfaceName, wire.LocalNodeIfaceID, err) + return err + } + defer rdHandl.Close() + + err = rdHandl.SetDirection(pcap.Direction(pcap.DirectionIn)) + if err != nil { + // let the caller handle the error + grpcOvrlyLogger.Errorf("Receive Thread for local pod failed to set up capture direction: %s/%d, PCAP ERROR: %v", wire.LocalNodeIfaceName, wire.LocalNodeIfaceID, err) + return err + } + + remote, err := grpc.Dial(url, grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + grpcOvrlyLogger.Infof("RecvFrmLocalPodThread:Failed to connect to remote %s/%d", url, wire.LocalNodeIfaceID) + return err + } + defer remote.Close() + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + source := gopacket.NewPacketSource(rdHandl, rdHandl.LinkType()) + wireClient := mpb.NewWireProtocolClient(remote) + + in := source.Packets() + var packet gopacket.Packet + for { + select { + case <-wire.StopC: + grpcOvrlyLogger.Infof("RecvFrmLocalPodThread: closing connection with remote peer-iface@peer-node-ip: %d@%s/%d from %s@%s", + wire.WireIfaceIDOnPeerNode, wire.PeerNodeIP, wire.LocalNodeIfaceID, wire.LocalPodName, wire.LocalPodIfaceName) + return io.EOF + case packet = <-in: + data := packet.Data() + payload := &mpb.Packet{ + RemotIntfId: wire.WireIfaceIDOnPeerNode, + Frame: data, + } + + /*+++TODO: Ethernet has a minimum frame size of 64 bytes, comprising an 18-byte header and a payload of 46 bytes. + It also has a maximum frame size of 1518 bytes, in which case the payload is 1500 bytes. + This logic needs to be better, take the interface MTU not hardcoded value of 1518. + This is a very unusual condition to receive an packet from the pod with size > MTU. This can only happens if + things gets really messed up. */ + if len(data) > 1518 { + pktType := DecodeFrame(payload.Frame) + grpcOvrlyLogger.Infof("RecvFrmLocalPodThread: unusually large packet received from local pod (may be GRO enabled). size: %d, pkt:%s", len(data), pktType) + /* When Generic Receive Offload (GRO) is enabled then containers can send packets larger than MTU size packet. Do not drop these + packets, deliver it to the receiving container to process. + */ + //continue + } + + ok, err := wireClient.SendToOnce(ctx, payload) + if err != nil || !ok.Response { + grpcOvrlyLogger.Infof("RecvFrmLocalPodThread: Could not deliver pkt %s@%s@%s. Peer not ready, remote iface id %d. err=%v", + wire.LocalPodName, wire.LocalPodIfaceName, wire.LocalNodeIfaceName, wire.WireIfaceIDOnPeerNode, err) + /* we generate information and continue. As the above errors will happen when the remote end is not yet ready. + It will eventually get ready and if it can't then someone else will stop this thread. + */ + } + } + } +} diff --git a/third_party/meshnet/daemon/grpcwire/gwire_map.go b/third_party/meshnet/daemon/grpcwire/gwire_map.go new file mode 100644 index 000000000..b58fbc332 --- /dev/null +++ b/third_party/meshnet/daemon/grpcwire/gwire_map.go @@ -0,0 +1,142 @@ +package grpcwire + +import ( + "fmt" + "sync" + + "github.com/google/gopacket/pcap" +) + +type wireMap struct { + mu sync.Mutex + wires map[linkKey]*GRPCWire + handles map[int64]*pcap.Handle +} + +func (w *wireMap) GetWire(namespace string, linkUID int) (*GRPCWire, bool) { + w.mu.Lock() + defer w.mu.Unlock() + wire, ok := w.wires[linkKey{ + namespace: namespace, + linkUID: linkUID, + }] + return wire, ok +} + +func (w *wireMap) GetHandle(key int64) (*pcap.Handle, bool) { + w.mu.Lock() + defer w.mu.Unlock() + handle, ok := w.handles[key] + return handle, ok +} + +func (w *wireMap) AddInMem(wire *GRPCWire, handle *pcap.Handle) error { + w.mu.Lock() + defer w.mu.Unlock() + w.wires[linkKey{ + namespace: wire.LocalPodNetNS, + linkUID: wire.UID, + }] = wire + + w.handles[wire.LocalNodeIfaceID] = handle + return nil +} + +func (w *wireMap) AddInMemNDataStore(wire *GRPCWire, handle *pcap.Handle) error { + w.mu.Lock() + defer w.mu.Unlock() + w.wires[linkKey{ + namespace: wire.LocalPodNetNS, + linkUID: wire.UID, + }] = wire + + wire.K8sStoreGWire() + + w.handles[wire.LocalNodeIfaceID] = handle + return nil +} + +// Clear the in-memory wire map +func (w *wireMap) AtomicDelete(wire *GRPCWire) error { + w.mu.Lock() + defer w.mu.Unlock() + delete(w.wires, linkKey{ + namespace: wire.LocalPodNetNS, + linkUID: wire.UID, + }) + + delete(w.handles, wire.LocalNodeIfaceID) + + return nil +} + +// Delete a wire from the in-memory wire-map without a lock +func (w *wireMap) DeleteWoLock(wire *GRPCWire) error { + delete(w.wires, linkKey{ + namespace: wire.LocalPodNetNS, + linkUID: wire.UID, + }) + delete(w.handles, wire.LocalNodeIfaceID) + return nil +} + +/* A grpc-wire creation (between pod A and pod B) can be triggered by either host hosting pod A, B. They + * can even trigger it simultaneously. Irrespective of who triggers, successful wire creation needs + * activities at both hosts end. Our intention is to finish the wire creation at the first trigger. + * This map keeps the list of wires which are already created and must not be recreated, if any second + * trigger is received. This situation occurs when both the host triggers wire creation almost simultaneously. + */ +var wires = &wireMap{ + wires: map[linkKey]*GRPCWire{}, + /* Used when a packet is received, then we know the id of the interface to which the packet to be delivered. + This map take interface-id as key and returns the corresponding handle for delivering the packet. + map[interface-id]->handle */ + handles: map[int64]*pcap.Handle{}, +} + +// FindWiresByPod returns a list of wires matching the namespace and pod. +func GetWiresByPod(namespace string, podName string) ([]*GRPCWire, bool) { + wires.mu.Lock() + defer wires.mu.Unlock() + var rWires []*GRPCWire + + for _, wire := range wires.wires { + if wire.LocalPodName == podName && wire.TopoNamespace == namespace { + rWires = append(rWires, wire) + } + } + return rWires, true +} + +// For a given pod, this atomic function extracts and returns the first wire from the wire map. Note the wire is +// removed from the wire-map. This function is expected to be used for deleting and wire. +func ExtractOneWireByPod(namespace string, podName string) (*GRPCWire, bool) { + wires.mu.Lock() + defer wires.mu.Unlock() + //var rWires *GRPCWire + + for _, wire := range wires.wires { + if wire.LocalPodName == podName && wire.TopoNamespace == namespace { + // delete this wire from wire map. + delete(wires.wires, linkKey{ + namespace: wire.LocalPodNetNS, + linkUID: wire.UID, + }) + + // also clean up the pcap handle for the wire that is extracted from the wire-map + delete(wires.handles, wire.LocalNodeIfaceID) + return wire, true + } + } + return nil, true // no wire found is not a failure, so return true +} + +func GetHostIntfHndl(intfID int64) (*pcap.Handle, error) { + + val, ok := wires.GetHandle(intfID) + if ok { + return val, nil + } + return nil, fmt.Errorf("node interface %d is not found in local db", intfID) + +} diff --git a/third_party/meshnet/daemon/grpcwire/gwire_recon.go b/third_party/meshnet/daemon/grpcwire/gwire_recon.go new file mode 100644 index 000000000..a97cb8bd6 --- /dev/null +++ b/third_party/meshnet/daemon/grpcwire/gwire_recon.go @@ -0,0 +1,467 @@ +package grpcwire + +import ( + "context" + "fmt" + "net" + "os" + "reflect" + + "github.com/google/gopacket/pcap" + grpcwirev1 "github.com/openconfig/kne/third_party/meshnet/api/types/v1beta1" + mpb "github.com/openconfig/kne/third_party/meshnet/daemon/proto/meshnet/v1beta1" + log "github.com/sirupsen/logrus" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/fields" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/dynamic" + "k8s.io/client-go/util/retry" +) + +// GWireClient is dynamic client for grpc wire. it is used to read/write grpc wire info from/to k8s api data-store +type GWireClient struct { + di dynamic.NamespaceableResourceInterface + gvr schema.GroupVersionResource +} + +var gWClient GWireClient + +const ( + kStatus = "status" // json name of Status of gwire_type, +++TBD: can we make it dynamic + kGrpcWireItems = "grpcWireItems" // json name of GWireKItems of gwire_type, +++TBD: can we make it dynamic +) + +// ----------------------------------------------------------------------------------------------------------- +func SetGWireClient(gClient *dynamic.DynamicClient) { + // identifier of grpc wire object in k8s apis + gWClient.gvr = schema.GroupVersionResource{ + Group: grpcwirev1.GroupName, + Version: grpcwirev1.GroupVersion, + Resource: grpcwirev1.GWireResNamePlural, + } + gWClient.di = gClient.Resource(gWClient.gvr) +} + +// ----------------------------------------------------------------------------------------------------------- +func SetGWireClientInterface(gClient dynamic.NamespaceableResourceInterface) { + gWClient.di = gClient +} + +// ------------------------------------------------------------------------------------------------------------ +func (gc GWireClient) GetWireObjListUS(ctx context.Context, ndName string) (*unstructured.UnstructuredList, error) { + return gc.di.Namespace("").List(ctx, metav1.ListOptions{ + TypeMeta: metav1.TypeMeta{ + Kind: reflect.TypeOf(grpcwirev1.GWireKObj{}).Name(), + }, + FieldSelector: fields.SelectorFromSet( + fields.Set{metav1.ObjectNameField: ndName}, // need GRPC wire endpoint information for this node only + ).String(), + }) +} + +// ------------------------------------------------------------------------------------------------------------ +func (gc GWireClient) CreatWireObj(ctx context.Context, nSpace string, uWbj map[string]interface{}) (*unstructured.Unstructured, error) { + return gc.di.Namespace(nSpace).Create(ctx, &unstructured.Unstructured{Object: uWbj}, metav1.CreateOptions{}) +} + +// ------------------------------------------------------------------------------------------------------------ +func (gc GWireClient) UpdateWireObj(ctx context.Context, nSpace string, wObjsOnNd *unstructured.Unstructured) (*unstructured.Unstructured, error) { + return gc.di.Namespace(nSpace).Update(ctx, wObjsOnNd, metav1.UpdateOptions{}) + +} + +//------------------------------------------------------------------------------------------------------------ + +func (gc GWireClient) GetWireObjGrpUS(ctx context.Context, wStatus *grpcwirev1.GWireStatus) (*unstructured.Unstructured, error) { + return gc.di.Namespace(wStatus.TopoNamespace).Get(ctx, wStatus.LocalNodeName, metav1.GetOptions{}) +} + +// ----------------------------------------------------------------------------------------------------------- +// Create & populate "GWireStatus" from a "GRPCWire". GWireStatus is stored in K8S data-store +func CreateWireStatus(wire *GRPCWire, nodeName string) *grpcwirev1.GWireStatus { + + return &grpcwirev1.GWireStatus{ + LocalNodeName: nodeName, + LinkId: int64(wire.UID), + TopoNamespace: wire.TopoNamespace, + + //local pod information + LocalPodNetNs: wire.LocalPodNetNS, + WireIfaceNameOnLocalNode: wire.LocalNodeIfaceName, + LocalPodName: wire.LocalPodName, + LocalPodIfaceName: wire.LocalPodIfaceName, + LocalPodIp: wire.LocalPodIP, + + //peer information + WireIfaceIdOnPeerNode: wire.WireIfaceIDOnPeerNode, + GWirePeerNodeIp: wire.PeerNodeIP, + } + +} + +// ----------------------------------------------------------------------------------------------------------- +// K8sStoreGWire writes grpc wire info 'wire' for a specific topology namespace (wire.TopoNamespace) into k8s +// data-store for the current node. It calls updateGRPCWireStatus() to serve the purpose +func (wire *GRPCWire) K8sStoreGWire() error { + nodeName, err := findNodeName() + if err != nil { + grpcOvrlyLogger.Errorf("K8sStoreGWire: could not get node name: %v", err) + return err + } + + ctx := context.Background() + ws := CreateWireStatus(wire, nodeName) + err = updateGRPCWireStatus(ctx, ws) + + if err != nil { + grpcOvrlyLogger.Errorf("K8sStoreGWire: Failed to set status for node %s: %v", nodeName, err) + } + return nil +} + +// ----------------------------------------------------------------------------------------------------------- +// K8sDelGWire deletes grpc wire info 'wire' for a specific namespace from k8s api data-store for the current +// node. namespace is specified in given 'wire' argument. it calls deleteGRPCWireStatus() to serve the purpose +func (wire *GRPCWire) K8sDelGWire() error { + nodeName, err := findNodeName() + if err != nil { + grpcOvrlyLogger.Errorf("K8sDelGWire: could not get node name: %v", err) + } + ctx := context.Background() + + ws := CreateWireStatus(wire, nodeName) + err = deleteGRPCWireStatus(ctx, ws) + + if err != nil { + grpcOvrlyLogger.Errorf("Failed to delete wire status for node %s: %v", nodeName, err) + return err + } + return nil +} + +// ----------------------------------------------------------------------------------------------------------- +// On meshnet daemon reboot ReconGWires reconciles all grpc wires of all namespaces (topologies) in local memory. +// InK8S data store, it looks for +// - gwireKObj for all name-spaces +// - iterate over all wire info list present in gwireKObj +// - call reCreateGWire() with saved wire info to build up the in memory wire map +func ReconGWires() error { + nodeName, err := findNodeName() + if err != nil { + grpcOvrlyLogger.Errorf("ReconGWires: could not get node: %v", err) + return err + } + + ctx := context.Background() + retryErr := retry.RetryOnConflict(retry.DefaultRetry, func() error { + // retrieve list of grpc wire obj list for all namespaces for the current node-name + gwireKObjList, err := gWClient.GetWireObjListUS(ctx, nodeName) + if err != nil { + grpcOvrlyLogger.Errorf("reconGWires: could not get gWireKObjs from k8s: %v", err) + return err + } + // in the unlikely situation where one has multiple topologies running in the same cluster, + // gwireKObjList will have multiple items for this node. + // {(),(),...} + for _, node := range gwireKObjList.Items { + // a node is found and node-Status-GWireKItems exists, so reconcile + grpcWireItems, found, err := unstructured.NestedSlice(node.Object, kStatus, kGrpcWireItems) + if err != nil { + grpcOvrlyLogger.Errorf("ReconGWires: could not retrieve grpcWireItem: %v", err) + continue + } + if !found { + grpcOvrlyLogger.Errorf("ReconGWires: grpcWireItem not found in GWireKObj status, retrieved from k8s data-store") + continue + } + if grpcWireItems == nil { + grpcOvrlyLogger.Errorf("ReconGWires: grpcWireItem is nil in GWireKObj status, retrieved from k8s data-store") + continue + } + for _, grpcWireItem := range grpcWireItems { + wireStatusItem, ok := grpcWireItem.(map[string]interface{}) + if !ok { + grpcOvrlyLogger.Errorf("ReconGWires: unable to retrieve wire status item, %v is not a map", grpcWireItem) + continue + } + + // create the wire structure from the saved data in K8S data store + wireStatus := grpcwirev1.GWireStatus{} + if err := runtime.DefaultUnstructuredConverter.FromUnstructured(wireStatusItem, &wireStatus); err != nil { + grpcOvrlyLogger.Errorf("ReconGWires: unable to retrieve wire status: %v", err) + continue + } + reCreateGWire(wireStatus, ctx) + } + } + return nil + }) + if retryErr != nil { + grpcOvrlyLogger.Errorf("Failed to read status on node %s", nodeName) + return retryErr + } + + return nil +} + +// ----------------------------------------------------------------------------------------------------------- +// updateGRPCWireStatus writes grpc wire 'wStatus' into k8s data-store. 'wStatus' for all existing grpc wires are added +// under 'grpcWireItems' as part of status. Status is part of 'GWireKObj' and identified +// by name=. For the first write, this object for a node does not exist in k8s data- +// store. So for first write, it creates the object and then adds the 'wStatus'. For all subsequent 'wStatus' +// to be added, first get the object from data-store, append the 'wStatus' to the existing list of +// 'grpcWireItems' and write the updated 'grpcWireItems' list back to k8s api data-store. +func updateGRPCWireStatus(ctx context.Context, wStatus *grpcwirev1.GWireStatus) error { + grpcOvrlyLogger.Infof("Updating GRPC wire status on node %s, pod %s@%s", wStatus.LocalNodeName, wStatus.LocalPodName, wStatus.LocalPodIfaceName) + + retryErr := retry.RetryOnConflict(retry.DefaultRetry, func() error { + + // wires are grouped by node name. retrieve the group for this node 'wStatus.LocalNodeName' + wObjsOnNd, err := gWClient.GetWireObjGrpUS(ctx, wStatus) + if err != nil { + // if not found then create it + if errors.IsNotFound(err) { + // WireObj does not exist. add it first + err = CreateGWireStatInDS(ctx, wStatus) + if err != nil { + return err + } + grpcOvrlyLogger.Infof("updateGRPCWireStatus: Created node %s, pod %s@%s into k8s data-store", + wStatus.LocalNodeName, wStatus.LocalPodName, wStatus.LocalPodIfaceName) + return nil + } + + return err // for all error expect 'not found' return the error + } + + // extract status gwire items + gwireItems, found, err := unstructured.NestedSlice(wObjsOnNd.Object, kStatus, kGrpcWireItems) + if err != nil { + grpcOvrlyLogger.Errorf("updateGRPCWireStatus: could not retrieve gWireItems: %v", err) + return err + } + if !found { + grpcOvrlyLogger.Errorf("updateGRPCWireStatus: gwireItems not found in GWireKObj status") + return err + } + if gwireItems == nil { + grpcOvrlyLogger.Errorf("updateGRPCWireStatus: gwireItems is nil in GWireKObj status") + return err + } + newItem, err := runtime.DefaultUnstructuredConverter.ToUnstructured(wStatus) + if err != nil { + grpcOvrlyLogger.Errorf("updateGRPCWireStatus: could not convert to unstructured: %v\n", err) + return err + } + gwireItems = append(gwireItems, newItem) + + if err := unstructured.SetNestedField(wObjsOnNd.Object, gwireItems, kStatus, kGrpcWireItems); err != nil { + grpcOvrlyLogger.Errorf("updateGRPCWireStatus: could not set grpcwireitems status: %v", err) + return err + } + + _, err = gWClient.UpdateWireObj(ctx, wStatus.TopoNamespace, wObjsOnNd) + if err != nil { + grpcOvrlyLogger.Infof("updateGRPCWireStatus: Could not update GRPCWire status for node %s, pod %s@%s into K8s", + wObjsOnNd.GetName(), wStatus.LocalPodName, wStatus.LocalPodIfaceName) + return err + } + return nil + + }) + if retryErr != nil { + log.WithFields(log.Fields{ + "daemon": "meshnetd", + "err": retryErr, + "function": "updateGRPCWireStatus", + }).Errorf("Failed to update status on node %s, pod %s@%s", wStatus.LocalNodeName, wStatus.LocalPodName, wStatus.LocalPodIfaceName) + return retryErr + } + + return nil +} + +// ----------------------------------------------------------------------------------------------------------- +// CreateGWireStatInDS creates grpc wire unstructured object with gvr info populated in it. +func CreateGWireStatInDS(ctx context.Context, wStatus *grpcwirev1.GWireStatus) error { + wObj := &grpcwirev1.GWireKObj{ + TypeMeta: metav1.TypeMeta{ + Kind: reflect.TypeOf(grpcwirev1.GWireKObj{}).Name(), + APIVersion: grpcwirev1.GroupName + "/" + grpcwirev1.GroupVersion, + }, + ObjectMeta: metav1.ObjectMeta{ + Name: wStatus.LocalNodeName, + Namespace: wStatus.TopoNamespace, + }, + Status: grpcwirev1.GWireKNodeStatus{ + GWireKItems: []grpcwirev1.GWireStatus{*wStatus}, + }, + } + uWbj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(wObj) + if err != nil { + grpcOvrlyLogger.Errorf("CreateGWireStatInDS: could not create unstructured for new wire: %v", err) + return err + } + + _, err = gWClient.CreatWireObj(ctx, wStatus.TopoNamespace, uWbj) + if err != nil { + grpcOvrlyLogger.Errorf("CreateGWireStatInDS: Could not create node %s, pod %s@%s into k8s data-store: %v", + wStatus.LocalNodeName, wStatus.LocalPodName, wStatus.LocalPodIfaceName, err) + return err + } + return nil +} + +// ----------------------------------------------------------------------------------------------------------- +// deleteGRPCWireStatus deletes a grpc wire status from 'grpcWireItems' for a specific namespace +// for this node. Topology namespace is derived from given 'wStatus'. +func deleteGRPCWireStatus(ctx context.Context, wStatus *grpcwirev1.GWireStatus) error { + + retryErr := retry.RetryOnConflict(retry.DefaultRetry, func() error { + node, err := gWClient.GetWireObjGrpUS(ctx, wStatus) + if err != nil { + grpcOvrlyLogger.Errorf("deleteGRPCWireStatus: failed to read node %s, pod %s@%s from K8s to delete wire status: %v", + wStatus.LocalNodeName, wStatus.LocalPodName, wStatus.LocalPodIfaceName, err) + return err + } + + // TBD: think about a faster way to remove an entry + + newSList := []interface{}{} + gwireItems, found, err := unstructured.NestedSlice(node.Object, kStatus, kGrpcWireItems) + if err != nil { + grpcOvrlyLogger.Errorf("deleteGRPCWireStatus: could not retrieve gWireItems: %v", err) + return err + } + if !found { + grpcOvrlyLogger.Errorf("deleteGRPCWireStatus: gwireItems not found in GWireKObj status, retrieved from k8s data-store") + return err + } + if gwireItems == nil { + grpcOvrlyLogger.Errorf("deleteGRPCWireStatus: gwireItems is nil in GWireKObj status, retrieved from k8s data-store") + return err + } + + for _, gwireItem := range gwireItems { + gwireStatusItem, ok := gwireItem.(map[string]interface{}) + if !ok { + log.Errorf("deleteGRPCWireStatus: unable to retrieve status, %v is not a map", gwireItem) + continue + } + gwireStatus := grpcwirev1.GWireStatus{} + if err := runtime.DefaultUnstructuredConverter.FromUnstructured(gwireStatusItem, &gwireStatus); err != nil { + log.Errorf("deleteGRPCWireStatus: unable to convert status from object: %v", err) + continue + } + if gwireStatus.LinkId == wStatus.LinkId { + continue + } + newSList = append(newSList, gwireStatusItem) + } + + if err := unstructured.SetNestedField(node.Object, newSList, kStatus, kGrpcWireItems); err != nil { + grpcOvrlyLogger.Errorf("deleteGRPCWireStatus: could not update kGrpcWireItems in status: %v", err) + return err + } + _, err = gWClient.UpdateWireObj(ctx, wStatus.TopoNamespace, node) + if err == nil { + grpcOvrlyLogger.Infof("deleteGRPCWireStatus: Deleted GRPCWire status on node %s, for pod %s@%s", + node.GetName(), wStatus.LocalPodName, wStatus.LocalPodIfaceName) + } + return err + }) + if retryErr != nil { + log.WithFields(log.Fields{ + "daemon": "meshnetd", + "err": retryErr, + "function": "deleteGRPCWireStatus", + }).Errorf("Failed to update status on node %s, pod %s@%s", wStatus.LocalNodeName, wStatus.LocalPodName, wStatus.LocalPodIfaceName) + return retryErr + } + + return nil +} + +// ----------------------------------------------------------------------------------------------------------- +// reCreateGWire writes the wire status 'wStatus' retrieved from k8s data-store into local memory database +// 'in-memory wire-map' and starts pod to daemon packet receive thread for this wire. +func reCreateGWire(wStatus grpcwirev1.GWireStatus, _ context.Context) error { + + grpcWire, ok := GetWireByUID(wStatus.LocalPodNetNs, int(wStatus.LinkId)) + if ok && grpcWire.IsReady { + grpcOvrlyLogger.Infof("reCreateGWire: This grpc-wire is already present in local db, link id %d", wStatus.LinkId) + return nil + } + + wireDef := mpb.WireDef{ + LinkUid: wStatus.LinkId, + WireIfNameOnLocalNode: wStatus.WireIfaceNameOnLocalNode, + LocalPodIp: wStatus.LocalPodIp, + IntfNameInPod: wStatus.LocalPodIfaceName, + LocalPodName: wStatus.LocalPodName, + LocalPodNetNs: wStatus.LocalPodNetNs, + WireIfIdOnPeerNode: wStatus.WireIfaceIdOnPeerNode, + PeerNodeIp: wStatus.GWirePeerNodeIp, + TopoNs: wStatus.TopoNamespace, + } + err := reconLocalGRPCWire(&wireDef) + if err != nil { + return fmt.Errorf("reCreateGWire: Failed to reconciliate local end of the GRPC channel: %v", err) + } + + grpcOvrlyLogger.Infof("Reconciliated grpc-wire (local-pod:%s:%s@node:%s <----link uid: %d----> remote-peer:%s:%d)", + wStatus.LocalPodName, wStatus.LocalPodIfaceName, wStatus.WireIfaceNameOnLocalNode, + wStatus.LinkId, wStatus.GWirePeerNodeIp, wStatus.WireIfaceIdOnPeerNode) + + return nil +} + +// ----------------------------------------------------------------------------------------------------------- +// Recreate the wire in-memory wire-map and start the pod to daemon packet receive thread for this wire. +func reconLocalGRPCWire(wireDef *mpb.WireDef) error { + locInf, err := net.InterfaceByName(wireDef.WireIfNameOnLocalNode) + if err != nil { + grpcOvrlyLogger.Errorf("[RECONCILE:LOCAL-END]For pod %s failed to retrieve interface ID for interface %v. error:%v", wireDef.LocalPodName, wireDef.WireIfNameOnLocalNode, err) + return err + } + + //Using google gopacket for packet receive. An alternative could be using socket. Not sure it it provides any advantage over gopacket. + wrHandle, err := pcap.OpenLive(wireDef.WireIfNameOnLocalNode, 65365, true, pcap.BlockForever) + if err != nil { + grpcOvrlyLogger.Errorf("[RECONCILE:LOCAL-END]Could not open interface for send/recv packets for containers local iface id %d. error:%v", locInf.Index, err) + return err + } + aWire := CreateGWire(locInf.Index, wireDef.WireIfNameOnLocalNode, make(chan struct{}), wireDef) + aWire.IsReady = true + // reconciling, so add only in memory + wires.AddInMem(aWire, wrHandle) + + // TODO: handle error here + go RecvFrmLocalPodThread(aWire, aWire.LocalNodeIfaceName) + + return nil +} + +// Finds out the node name in which a pod is running. A running pod can call this function to find +// out the node in which it's currently running. This function must be called from within the cluster. +// Returns the "node name" and error +func findNodeName() (string, error) { + var err error + + //Ref - Expose Pod Information to Containers Through Environment Variables + //https://kubernetes.io/docs/tasks/inject-data-application/environment-variable-expose-pod-information/ + + // NODE_NAME for meshnet daemon set it carries the "spec.nodeName" for the daemon set. + ndNm := os.Getenv("NODE_NAME") + if len(ndNm) == 0 { + //grpcOvrlyLogger.Infof("Couldn't find node name from environment. Check the daemonset.yaml has NODE_NAME env set to spec.nodeName. Retrieving it from OS.\n") + ndNm, err = os.Hostname() + if err != nil { + return "", fmt.Errorf("findNodeName: could not get node name from OS: %v", err) + } + } + return ndNm, nil +} diff --git a/third_party/meshnet/daemon/grpcwire/gwire_recon_test.go b/third_party/meshnet/daemon/grpcwire/gwire_recon_test.go new file mode 100644 index 000000000..bdbc6bffa --- /dev/null +++ b/third_party/meshnet/daemon/grpcwire/gwire_recon_test.go @@ -0,0 +1,454 @@ +package grpcwire + +import ( + "context" + "net" + "os" + "reflect" + "testing" + + "github.com/containernetworking/plugins/pkg/ns" + "github.com/google/go-cmp/cmp" + grpcwirev1 "github.com/openconfig/kne/third_party/meshnet/api/types/v1beta1" + "github.com/vishvananda/netlink" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/fields" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/dynamic" + dynamicfake "k8s.io/client-go/dynamic/fake" +) + +var ( + gWireKObj1 = &grpcwirev1.GWireKObj{ + TypeMeta: metav1.TypeMeta{ + Kind: reflect.TypeOf(grpcwirev1.GWireKObj{}).Name(), + APIVersion: grpcwirev1.GroupName + "/" + grpcwirev1.GroupVersion, + }, + ObjectMeta: metav1.ObjectMeta{ + Name: must(findNodeName()), + Namespace: "test", + }, + Status: grpcwirev1.GWireKNodeStatus{ + GWireKItems: []grpcwirev1.GWireStatus{ + { + LocalNodeName: must(findNodeName()), + LinkId: 1, + TopoNamespace: "test", + LocalPodNetNs: "testNetNs", + LocalPodName: "pod1", + LocalPodIp: "1.1.1.1", + LocalPodIfaceName: "eth1", + WireIfaceNameOnLocalNode: "eth1-node1", + WireIfaceIdOnPeerNode: 101, + GWirePeerNodeIp: "2.2.2.2", + }, + { + LocalNodeName: must(findNodeName()), + LinkId: 2, + TopoNamespace: "test", + LocalPodNetNs: "testNetNs", + LocalPodName: "pod2", + LocalPodIp: "1.1.1.2", + LocalPodIfaceName: "eth2", + WireIfaceNameOnLocalNode: "eth2-node1", + WireIfaceIdOnPeerNode: 102, + GWirePeerNodeIp: "2.2.2.2", + }, + }, + }, + Spec: grpcwirev1.GWireKNodeSpec{}, + } + gWireKObj2 = &grpcwirev1.GWireKObj{ + TypeMeta: metav1.TypeMeta{ + Kind: reflect.TypeOf(grpcwirev1.GWireKObj{}).Name(), + APIVersion: grpcwirev1.GroupName + "/" + grpcwirev1.GroupVersion, + }, + ObjectMeta: metav1.ObjectMeta{ + Name: must(findNodeName()), + Namespace: "test2", + }, + Status: grpcwirev1.GWireKNodeStatus{ + GWireKItems: []grpcwirev1.GWireStatus{ + { + LocalNodeName: must(findNodeName()), + LinkId: 3, + TopoNamespace: "test2", + LocalPodNetNs: "testNetNs", + LocalPodName: "pod3", + LocalPodIp: "2.1.1.1", + LocalPodIfaceName: "eth3", + WireIfaceNameOnLocalNode: "eth3-node2", + WireIfaceIdOnPeerNode: 201, + GWirePeerNodeIp: "2.2.2.2", + }, + }, + }, + Spec: grpcwirev1.GWireKNodeSpec{}, + } +) + +func must[T any](v T, err error) T { + if err != nil { + panic(err) + } + return v +} + +func setUp(t *testing.T, objs ...runtime.Object) dynamic.NamespaceableResourceInterface { + //t.Helper() + InitLogger() + //objs := []runtime.Object{} + + var gvr schema.GroupVersionResource = schema.GroupVersionResource{ + Group: grpcwirev1.GroupName, + Version: grpcwirev1.GroupVersion, + Resource: grpcwirev1.GWireResNamePlural, + } + f := dynamicfake.NewSimpleDynamicClient(grpcwirev1.Scheme, objs...) + resInterface := f.Resource(gvr) + SetGWireClientInterface(resInterface) + + return resInterface +} + +func createWireFromWireStatus(wireStatus *grpcwirev1.GWireStatus) *GRPCWire { + return &GRPCWire{ + // LocalNodeName: nodeName, + UID: int(wireStatus.LinkId), + TopoNamespace: wireStatus.TopoNamespace, + + // local pod information + LocalPodNetNS: wireStatus.LocalPodNetNs, + LocalNodeIfaceName: wireStatus.WireIfaceNameOnLocalNode, + LocalPodName: wireStatus.LocalPodName, + LocalPodIfaceName: wireStatus.LocalPodIfaceName, + LocalPodIP: wireStatus.LocalPodIp, + + // peer information + WireIfaceIDOnPeerNode: wireStatus.WireIfaceIdOnPeerNode, + PeerNodeIP: wireStatus.GWirePeerNodeIp, + } +} + +func getWireObjListUSForNs(cs dynamic.NamespaceableResourceInterface, ndName, ns string) (*unstructured.UnstructuredList, error) { + return cs.Namespace(ns).List(context.Background(), metav1.ListOptions{ + TypeMeta: metav1.TypeMeta{ + Kind: reflect.TypeOf(grpcwirev1.GWireKObj{}).Name(), + }, + FieldSelector: fields.SelectorFromSet( + fields.Set{metav1.ObjectNameField: ndName}, + ).String(), + }) +} + +// -------------------------------------------------------------------------------------------------- +func isRoot(t *testing.T) { + if os.Getuid() != 0 { + t.Skip("Test requires root privileges. Run test with sudo") + } +} + +// Create a veth pair in the current name space. It also makes the links up +func createVethPair(t *testing.T, name string, peerName string) error { + veth := &netlink.Veth{ + LinkAttrs: netlink.LinkAttrs{ + Name: name, + Flags: net.FlagUp, + }, + PeerName: peerName, + } + err := netlink.LinkAdd(veth) + if err != nil { + switch { + case os.IsExist(err): + t.Logf("veth name (%v) already exists", name) + default: + t.Logf("netlink failed to make veth pair: %v", err) + } + return err + } + link, err := netlink.LinkByName(name) + if err != nil { + t.Logf("failed to get interface %s, err:%v", name, err) + return err + } + + if err = netlink.LinkSetUp(link); err != nil { + t.Logf("failed to set interface %s up: %v", name, err) + return err + } + + link, err = netlink.LinkByName(peerName) + if err != nil { + t.Logf("failed to get interface %s, err:%v", peerName, err) + return err + } + + if err = netlink.LinkSetUp(link); err != nil { + t.Logf("failed to set interface %s up: %v", peerName, err) + return err + } + return nil +} + +// -------------------------------------------------------------------------------------------------- +func cleanupVethPair(t *testing.T, netNs ns.NetNS, ifaceName string) error { + var err error + + isRoot(t) + + err = netNs.Do(func(_ ns.NetNS) error { + // deleting only one will delete the pair. + link2, err := netlink.LinkByName(ifaceName) + if err != nil { + t.Errorf("failed to lookup %q in %q: %v", ifaceName, netNs.Path(), err) + return err + } + if err = netlink.LinkDel(link2); err != nil { + t.Errorf("failed to remove link %q in %q: %v", ifaceName, netNs.Path(), err) + return err + } + return nil + }) + + if err != nil { + t.Errorf("cleanup: failed to remove link : %v", err) + return err + } + return nil +} + +// TestK8sStoreGWire covers gwire status add, update and get commands +func TestK8sStoreGWire(t *testing.T) { + cs := setUp(t) + + test_cases := []struct { + desc string + store *GRPCWire + want *GRPCWire + wantErr string + }{ + { + desc: "Create", + store: &GRPCWire{ + UID: 1, + TopoNamespace: "testNs1", + LocalPodNetNS: "gwireNetNs", + LocalNodeIfaceName: "localNodeIfaceName", + LocalPodName: "pod1", + LocalPodIfaceName: "localPodIfaceName", + LocalPodIP: "10.10.10.1", + WireIfaceIDOnPeerNode: 100, + PeerNodeIP: "100.100.100.100", + }, + }, + { + desc: "Update", + store: &GRPCWire{ + UID: 2, + TopoNamespace: "testNs1", + LocalPodNetNS: "gwireNetNs", + LocalNodeIfaceName: "localNodeIfaceName1", + LocalPodName: "pod2", + LocalPodIfaceName: "localPodIfaceName1", + LocalPodIP: "10.10.10.2", + WireIfaceIDOnPeerNode: 101, + PeerNodeIP: "100.100.100.101", + }, + }, + } + + nodeName, err := findNodeName() + if err != nil { + t.Fatalf("could not retrieve node name") + } + //t.Logf("----------node name %s\n", nodeName) + var storedWStatus []interface{} + for _, tc := range test_cases { + t.Run(tc.desc, func(t *testing.T) { + err := tc.store.K8sStoreGWire() + if err != nil { + t.Fatalf("could not add gwire status into k8s data-store") + } + storedWStatus = append(storedWStatus, *CreateWireStatus(tc.store, nodeName)) + wObjsOnNd, err := cs.Namespace(tc.store.TopoNamespace).Get(context.Background(), nodeName, metav1.GetOptions{}) + if err != nil { + t.Fatalf("error in retrieving wire obj from k8s data-store: %v", err) + } + var retrievedWStatus []interface{} + grpcWireItems, _, _ := unstructured.NestedSlice(wObjsOnNd.Object, kStatus, kGrpcWireItems) + for _, gWireItem := range grpcWireItems { + var wireStatus = grpcwirev1.GWireStatus{} + err = runtime.DefaultUnstructuredConverter.FromUnstructured(gWireItem.(map[string]interface{}), &wireStatus) + if err != nil { + t.Fatalf("could not convert to wire status") + } + retrievedWStatus = append(retrievedWStatus, wireStatus) + } + if !cmp.Equal(storedWStatus, retrievedWStatus) { + t.Errorf("could not retrieve correct gwire info") + if s := cmp.Diff(storedWStatus, retrievedWStatus); s != "" { + t.Logf("Retrieved info:\n(%+v)\n", retrievedWStatus) + t.Logf("Stored info:\n(%+v)\n", storedWStatus) + t.Logf("Diff info:\n(%+v)\n", s) + } + } + }) + } + //t.Logf("TestK8sStoreGWire: passed") +} + +// TestK8sDelGWire covers gwire status delete, update and get commands +func TestK8sDelGWire(t *testing.T) { + //objs := []runtime.Object{gWireKObj1, gWireKObj2} + cs := setUp(t, gWireKObj1, gWireKObj2) + + test_cases := []struct { + desc string + stored *grpcwirev1.GWireKObj + exptCnt int + }{ + { + desc: "Multiple wires", + stored: gWireKObj1, + exptCnt: 2, + }, + { + desc: "Single wire", + stored: gWireKObj2, + exptCnt: 1, + }, + } + + for _, tc := range test_cases { + t.Run(tc.desc, func(t *testing.T) { + usObjList, err := getWireObjListUSForNs(cs, tc.stored.GetName(), tc.stored.GetNamespace()) + if err != nil || len(usObjList.Items) != 1 { + t.Fatalf("could not retrieve gwire status from k8s data-store") + } + // usObjList.Items[0] returns gwireKObj. there is only one kobj for this NS and name + grpcWireItems, _, _ := unstructured.NestedSlice(usObjList.Items[0].Object, kStatus, kGrpcWireItems) + if len(grpcWireItems) != tc.exptCnt { + t.Logf("stored wire status count (%d) is not matching with expected count (%d)", + len(grpcWireItems), tc.exptCnt) + t.FailNow() + } + for _, gWireStatus := range tc.stored.Status.GWireKItems { + gWire := createWireFromWireStatus(&gWireStatus) + err := gWire.K8sDelGWire() + if err != nil { + t.Fatalf("could not delete gwire status from k8s data-store") + } + } + usObjList2, err := getWireObjListUSForNs(cs, tc.stored.GetName(), tc.stored.GetNamespace()) + if err != nil || len(usObjList2.Items) != 1 { + t.Fatalf("could not retrieve gwire status from k8s data-store after delete") + } + grpcWireItems2, _, _ := unstructured.NestedSlice(usObjList2.Items[0].Object, kStatus, kGrpcWireItems) + if len(grpcWireItems2) != 0 { + t.Logf("stored wire status count (%d) is not matching with expected count (0) after delete", + len(usObjList2.Items)) + t.FailNow() + } + }) + } +} + +// TestReconGWires covers gwire reconciliation into local memory +func TestReconGWires(t *testing.T) { + cs := setUp(t, gWireKObj1, gWireKObj2) + + test_cases := []struct { + desc string + stored *grpcwirev1.GWireKObj + exptCnt int + }{ + { + desc: "Multiple wires", + stored: gWireKObj1, + }, + { + desc: "Single wire", + stored: gWireKObj2, + }, + } + + isRoot(t) + + // create interface first + for _, tc := range test_cases { + for _, gWireStatus := range tc.stored.Status.GWireKItems { + err := createVethPair(t, gWireStatus.LocalPodIfaceName, gWireStatus.WireIfaceNameOnLocalNode) + if err != nil { + t.Logf("createVethPair returned error: %v", err) + t.FailNow() + } + } + } + //clean up + currNs, err := ns.GetCurrentNS() + if err != nil { + t.Logf("failed to get current namespace : %v", err) + t.FailNow() + } + defer currNs.Close() + + // cleanup interfaces + defer func() { + for _, tc := range test_cases { + for _, gWireStatus := range tc.stored.Status.GWireKItems { + err := cleanupVethPair(t, currNs, gWireStatus.LocalPodIfaceName) + if err != nil { + t.Logf("cleanup: failed to remove link : %v", err) + t.FailNow() + } + } + } + }() + + err = ReconGWires() + if err != nil { + t.Logf("reconciliation fails: %v", err) + t.FailNow() + } + + //verify local memory database + for _, tc := range test_cases { + t.Run(tc.desc, func(t *testing.T) { + usObjList, err := getWireObjListUSForNs(cs, tc.stored.GetName(), tc.stored.GetNamespace()) + if err != nil { + t.Fatalf("could not retrieve gwire status from k8s data-store") + } + // usObjList.Items[0] returns gwireKObj. there is only one kobj for this NS and name + grpcWireItems, found, err := unstructured.NestedSlice(usObjList.Items[0].Object, kStatus, kGrpcWireItems) + if err != nil || !found { + t.Logf("GWire status item not found in k8s data store") + t.FailNow() + } + for _, gWireStatus := range grpcWireItems { + var wireStatus = grpcwirev1.GWireStatus{} + err = runtime.DefaultUnstructuredConverter.FromUnstructured(gWireStatus.(map[string]interface{}), &wireStatus) + if err != nil { + t.Fatalf("could not convert to wire status") + } + gWireLocal, found := wires.GetWire(wireStatus.LocalPodNetNs, int(wireStatus.LinkId)) + if !found { + t.Logf("could not write into local memory from k8s data-store") + t.FailNow() + } + gWireStatusLocal := CreateWireStatus(gWireLocal, tc.stored.GetName()) + if !cmp.Equal(wireStatus, *gWireStatusLocal) { + t.Errorf("could not retrieve correct gwire status") + if s := cmp.Diff(wireStatus, gWireStatusLocal); s != "" { + //t.Logf("Data-store info:\n(%+v)\n", wireStatus) + //t.Logf("Local memory info:\n(%+v)\n", gWireStatusLocal) + t.Logf("Diff info:\n(%+v)\n", s) + t.FailNow() + } + } + } + }) + } +} diff --git a/third_party/meshnet/daemon/grpcwire/gwire_rpc_handlers.go b/third_party/meshnet/daemon/grpcwire/gwire_rpc_handlers.go new file mode 100644 index 000000000..a5d91ebdd --- /dev/null +++ b/third_party/meshnet/daemon/grpcwire/gwire_rpc_handlers.go @@ -0,0 +1,164 @@ +package grpcwire + +import ( + "context" + "fmt" + "net" + + log "github.com/sirupsen/logrus" + + "github.com/containernetworking/plugins/pkg/ns" + "github.com/google/gopacket/pcap" + mpb "github.com/openconfig/kne/third_party/meshnet/daemon/proto/meshnet/v1beta1" + "github.com/openconfig/kne/third_party/meshnet/utils/wireutil" + koko "github.com/redhat-nfvpe/koko/api" +) + +func CreateGRPCWireLocal(ctx context.Context, wireDef *mpb.WireDef) (*mpb.BoolResponse, error) { + locInf, err := net.InterfaceByName(wireDef.WireIfNameOnLocalNode) + if err != nil { + log.WithFields(log.Fields{ + "daemon": "meshnetd", + "overlay": "gRPC", + }).Errorf("[ADD-WIRE:LOCAL-END]For pod %s failed to retrieve interface ID for interface %v. error:%v", wireDef.LocalPodName, wireDef.WireIfNameOnLocalNode, err) + return &mpb.BoolResponse{Response: false}, err + } + + // update tx checksumming to off + err = wireutil.SetTxChecksumOff(wireDef.IntfNameInPod, wireDef.LocalPodNetNs) + if err != nil { + log.Errorf("Error in setting tx checksum-off on interface %s, ns %s, pod %s: %v", wireDef.IntfNameInPod, wireDef.LocalPodNetNs, wireDef.LocalPodName, err) + // generate error and continue + } else { + log.Infof("Setting tx checksum-off on interface %s, pod %s is successful", wireDef.IntfNameInPod, wireDef.LocalPodName) + } + + //Using google gopacket for packet receive. An alternative could be using socket. Not sure it it provides any advantage over gopacket. + wrHandle, err := pcap.OpenLive(wireDef.WireIfNameOnLocalNode, 65365, true, pcap.BlockForever) + if err != nil { + log.WithFields(log.Fields{ + "daemon": "meshnetd", + "overlay": "gRPC", + }).Errorf("[ADD-WIRE:LOCAL-END]Could not open interface for send/recv packets for containers local iface id %d. error:%v", locInf.Index, err) + return &mpb.BoolResponse{Response: false}, err + } + + aWire := CreateGWire(locInf.Index, wireDef.WireIfNameOnLocalNode, make(chan struct{}), wireDef) + aWire.IsReady = false + aWire.Originator = HOST_CREATED_WIRE + aWire.OriginatorIP = "unknown" + + // Add the newly created wire in the in memory wire-map and k8S data store + AddWireInMemNDataStore(aWire, wrHandle) + + log.WithFields(log.Fields{ + "daemon": "meshnetd", + "overlay": "gRPC", + }).Infof("[ADD-WIRE:LOCAL-END]For pod %s@%s, node iface id %d starting the local packet receive thread", wireDef.LocalPodName, wireDef.IntfNameInPod, locInf.Index) + // TODO: handle error here + go RecvFrmLocalPodThread(aWire, aWire.LocalNodeIfaceName) + + return &mpb.BoolResponse{Response: true}, nil +} + +// A remote peer can tell the local node to create/update the local end of the grpc-wire. +// At the local end if the wire is already created then update the wire properties. +// This updation can happen when a pod is deleted and recreated again. This is not very uncommon in K8S to move +// a pod from node A to node B dynamically +func CreateUpdateGRPCWireRemoteTriggered(wireDef *mpb.WireDef, stopC chan struct{}) (*GRPCWire, error) { + + var err error + + // If this wire is already created, then only update the already created wire properties like stopC. + // This can happen due to a race between the local and remote peer. + // This can also happen when a pod in one end of the wire is deleted and created again. + // In all cases link creation happen only once but it can get updated multiple times. + grpcWire, ok := UpdateWireByUID(wireDef.LocalPodNetNs, int(wireDef.LinkUid), wireDef.WireIfIdOnPeerNode, stopC) + if ok { + grpcOvrlyLogger.Infof("[CREATE-UPDATE-WIRE] At remote end this grpc-wire is already created by %s. Local interface id : %d peer interface id : %d", grpcWire.Originator, grpcWire.LocalNodeIfaceID, grpcWire.WireIfaceIDOnPeerNode) + return grpcWire, nil + } + + outIfNm, err := GenNodeIfaceName(wireDef.LocalPodName, wireDef.IntfNameInPod) + if err != nil { + return nil, fmt.Errorf("[ADD-WIRE:REMOTE-END] could not get current network namespace: %v", err) + } + + currNs, err := ns.GetCurrentNS() + if err != nil { + return nil, fmt.Errorf("[ADD-WIRE:REMOTE-END] could not get current network namespace: %v", err) + } + + /* Create the veth to connect the pod with the meshnet daemon running on the node */ + hostEndVeth := koko.VEth{ + NsName: currNs.Path(), + LinkName: outIfNm, + } + + inIfNm := wireDef.IntfNameInPod + inContainerVeth := koko.VEth{ + NsName: wireDef.LocalPodNetNs, + LinkName: inIfNm, + } + + if wireDef.LocalPodIp != "" { + ipAddr, ipSubnet, err := net.ParseCIDR(wireDef.LocalPodIp) + if err != nil { + return nil, fmt.Errorf("failed to create remote end of GRPC wire(%s@%s), failed to parse CIDR %s: %w", + inIfNm, wireDef.LocalPodName, wireDef.LocalPodIp, err) + } + inContainerVeth.IPAddr = []net.IPNet{{ + IP: ipAddr, + Mask: ipSubnet.Mask, + }} + } + + if err = koko.MakeVeth(inContainerVeth, hostEndVeth); err != nil { + grpcOvrlyLogger.Errorf("[ADD-WIRE:REMOTE-END] Error creating vEth pair (in:%s <--> out:%s). Error-> %s", inIfNm, outIfNm, err) + return nil, err + } + if err := wireutil.SetTxChecksumOff(inContainerVeth.LinkName, inContainerVeth.NsName); err != nil { + grpcOvrlyLogger.Errorf("Error in setting tx checksum-off on interface %s, pod %s: %v", inContainerVeth.LinkName, wireDef.LocalPodName, err) + // not returning + } + locIface, err := net.InterfaceByName(hostEndVeth.LinkName) + if err != nil { + // let the caller handle the error + grpcOvrlyLogger.Errorf("[ADD-WIRE:REMOTE-END] Remote end could not get interface index for %s. error:%v", hostEndVeth.LinkName, err) + return nil, err + } + grpcOvrlyLogger.Infof("[ADD-WIRE:REMOTE-END] Trigger from %s:%d : Successfully created remote pod to node vEth pair %s@%s <--> %s(%d).", + wireDef.PeerNodeIp, wireDef.WireIfIdOnPeerNode, inIfNm, wireDef.LocalPodName, outIfNm, locIface.Index) + aWire := CreateGWire(locIface.Index, hostEndVeth.LinkName, stopC, wireDef) + /* Utilizing google gopacket for polling for packets from the node. This seems to be the + simplest way to get all packets. + As an alternative to google gopacket(pcap), a socket based implementation is possible. + Not sure if socket based implementation can bring any advantage or not. + + Near term will replace pcap by socket. + */ + wrHandle, err := pcap.OpenLive(hostEndVeth.LinkName, 65365, true, pcap.BlockForever) + if err != nil { + // let the caller handle the error + grpcOvrlyLogger.Errorf("[ADD-WIRE:REMOTE-END] At remote end could not open interface (%d) for sed/recv packets for containers. error:%v", locIface.Index, err) + return nil, err + } + + // Add the created wire in the in memory wire-map and k8S data store + AddWireInMemNDataStore(aWire, wrHandle) + + return aWire, nil +} + +// When the remote peer tells the local node to remove the local end of the grpc-wire info +func GRPCWireDownRemoteTriggered(wireDef *mpb.WireDef) error { + + err := WireDownByUID(wireDef.LocalPodNetNs, int(wireDef.LinkUid)) + if err != nil { + grpcOvrlyLogger.Infof("[WIRE-DOWN] Remote end failed in making down wire end in pod %s@%s,. Link uid : %d", + wireDef.LocalPodName, wireDef.IntfNameInPod, wireDef.LinkUid) + return nil + } + + return nil +} diff --git a/third_party/meshnet/daemon/grpcwire/wire-decode.go b/third_party/meshnet/daemon/grpcwire/wire-decode.go new file mode 100644 index 000000000..e3e73277c --- /dev/null +++ b/third_party/meshnet/daemon/grpcwire/wire-decode.go @@ -0,0 +1,158 @@ +package grpcwire + +import ( + "fmt" + + "github.com/google/gopacket" + "github.com/google/gopacket/layers" +) + +func DecodeFrame(frame []byte) string { + pktTypeStr := "" + numPkts := 1 + totalLen := len(frame) + etherHdrLen := 14 + totalDecodedLen := 0 + + for { + packet := gopacket.NewPacket(frame, layers.LayerTypeEthernet, gopacket.Default) + ethernetLayer := packet.Layer(layers.LayerTypeEthernet) + if ethernetLayer != nil { + ethernetPacket, _ := ethernetLayer.(*layers.Ethernet) + pktTypeStr += fmt.Sprintf("Pkt no %d: ", numPkts) + "Ethernet" + decodedLen, typeStr := DecodePkt(packet, ethernetPacket.NextLayerType(), ethernetPacket.Length) + pktTypeStr += typeStr + totalDecodedLen += etherHdrLen + decodedLen + remainingLen := totalLen - totalDecodedLen + if remainingLen >= 14 { + numPkts++ + frame = frame[totalDecodedLen:] + pktTypeStr += "\n " + } else { + break + } + } else { + break + } + } + if numPkts > 1 { + pktTypeStr = "Multi Pkts: " + pktTypeStr + } + + return pktTypeStr +} + +func DecodePkt(packet gopacket.Packet, layerType gopacket.LayerType, length uint16) (int, string) { + var typeStr, pktTypeStr string + decodedLen := 0 + + if layerType == layers.LayerTypeIPv4 { + decodedLen, typeStr = decodeIPv4Pkt(packet) + pktTypeStr += typeStr + } else if layerType == layers.LayerTypeIPv6 { + decodedLen, typeStr = decodeIPv6Pkt(packet) + pktTypeStr += typeStr + } else if layerType == layers.LayerTypeLLC { + llcLayer := packet.Layer(layers.LayerTypeLLC) + if llcLayer != nil { + pktTypeStr += ":LLC" + llcPacket, _ := llcLayer.(*layers.LLC) + if llcPacket.DSAP == 0xFE && llcPacket.SSAP == 0xFE && llcPacket.Control == 0x3 { + if llcPacket.Payload[0] == 0x83 { + pktTypeStr += ":ISIS" + } + } + } + decodedLen = int(length) + } else if layerType == layers.LayerTypeARP { + pktTypeStr += ":ARP" + decodedLen = 28 + } else if layerType == layers.LayerTypeDot1Q { + pktTypeStr += ":VLAN" + //fmt.Printf("VLAN\n") + vlanHdrLen := 4 + vlanLayer := packet.Layer(layers.LayerTypeDot1Q) + if vlanLayer != nil { + vlanPacket, _ := vlanLayer.(*layers.Dot1Q) + nextLayer := vlanPacket.NextLayerType() + if nextLayer == gopacket.LayerTypeZero { + // this may be LLC layer. try to match with known LLC 0xFEFE03 + if vlanPacket.Payload[0] == 0xFE && vlanPacket.Payload[1] == 0xFE && vlanPacket.Payload[2] == 0x03 { + packet := gopacket.NewPacket(vlanPacket.Payload, layers.LayerTypeLLC, gopacket.Default) + decodedLen, typeStr = DecodePkt(packet, layers.LayerTypeLLC, uint16(vlanPacket.Type)) + pktTypeStr += typeStr + decodedLen += vlanHdrLen + } + } else { + packet := gopacket.NewPacket(vlanPacket.Payload, nextLayer, gopacket.Default) + decodedLen, typeStr = DecodePkt(packet, nextLayer, 0) + pktTypeStr += typeStr + decodedLen += vlanHdrLen + } + } else { + pktTypeStr += ":No VLAN Hdr" + } + } + + return decodedLen, pktTypeStr +} + +func decodeIPv4Pkt(packet gopacket.Packet) (int, string) { + var decodedLen int = 0 + pktTypeStr := ":IPv4" + + ipLayer := packet.Layer(layers.LayerTypeIPv4) + if ipLayer != nil { + ipPacket, _ := ipLayer.(*layers.IPv4) + decodedLen = int(ipPacket.Length) + + pktTypeStr += fmt.Sprintf("[s:%s, d:%s]", ipPacket.SrcIP.String(), ipPacket.DstIP.String()) + if ipPacket.Protocol == layers.IPProtocolICMPv4 { + pktTypeStr += ":ICMP" + } else if ipPacket.Protocol == layers.IPProtocolTCP { + pktTypeStr += ":TCP" + tcpLayer := packet.Layer(layers.LayerTypeTCP) + if tcpLayer != nil { + tcpPkt := tcpLayer.(*layers.TCP) + if tcpPkt.DstPort == 179 { + pktTypeStr += ":BGP" + } else { + pktTypeStr += fmt.Sprintf(":[Port:%d]", tcpPkt.DstPort) + } + } + } else { + pktTypeStr += fmt.Sprintf(":IPv4 with protocol : %d", ipPacket.Protocol) + } + } + return decodedLen, pktTypeStr +} + +func decodeIPv6Pkt(packet gopacket.Packet) (int, string) { + var decodedLen int = 0 + pktTypeStr := ":IPv6" + + ipLayer := packet.Layer(layers.LayerTypeIPv6) + if ipLayer != nil { + ipPacket, _ := ipLayer.(*layers.IPv6) + decodedLen = int(ipPacket.Length) + + pktTypeStr += fmt.Sprintf("[s:%s, d:%s]", ipPacket.SrcIP.String(), ipPacket.DstIP.String()) + if ipPacket.NextHeader == layers.IPProtocolICMPv6 { + pktTypeStr += ":ICMPv6" + } else if ipPacket.NextHeader == layers.IPProtocolTCP { + pktTypeStr += ":TCP" + tcpLayer := packet.Layer(layers.LayerTypeTCP) + if tcpLayer != nil { + tcpPkt := tcpLayer.(*layers.TCP) + if tcpPkt.DstPort == 179 { + pktTypeStr += ":BGP" + } else { + pktTypeStr += fmt.Sprintf("[Port:%d]", tcpPkt.DstPort) + } + } + } else { + pktTypeStr += fmt.Sprintf(":IPv6 with protocol : %d", ipPacket.NextHeader) + } + } + return decodedLen, pktTypeStr +} diff --git a/third_party/meshnet/daemon/main.go b/third_party/meshnet/daemon/main.go new file mode 100644 index 000000000..ca86d66ff --- /dev/null +++ b/third_party/meshnet/daemon/main.go @@ -0,0 +1,62 @@ +package main + +import ( + "flag" + "os" + "strconv" + + "github.com/openconfig/kne/third_party/meshnet/daemon/cni" + "github.com/openconfig/kne/third_party/meshnet/daemon/grpcwire" + "github.com/openconfig/kne/third_party/meshnet/daemon/meshnet" + "github.com/openconfig/kne/third_party/meshnet/daemon/vxlan" + "github.com/openconfig/kne/third_party/meshnet/utils/wireutil" + log "github.com/sirupsen/logrus" +) + +func main() { + + if err := cni.Init(); err != nil { + log.Errorf("Failed to initialise CNI plugin: %v", err) + os.Exit(1) + } + defer cni.Cleanup() + + isDebug := flag.Bool("d", false, "enable degugging") + grpcPort, err := strconv.Atoi(os.Getenv("GRPC_PORT")) + if err != nil || grpcPort == 0 { + grpcPort = wireutil.GRPCDefaultPort + } + flag.Parse() + log.SetLevel(log.InfoLevel) + if *isDebug { + log.SetLevel(log.DebugLevel) + log.Debug("Verbose logging enabled") + } + + meshnet.InitLogger() + grpcwire.InitLogger() + vxlan.InitLogger() + + m, err := meshnet.New(meshnet.Config{ + Port: grpcPort, + }) + if err != nil { + log.Errorf("failed to create meshnet: %v", err) + os.Exit(1) + } + log.Info("Starting meshnet daemon...with grpc support") + + grpcwire.SetGWireClient(m.GWireDynClient) + + // read grpcwire info (if any) from data store and update local db + err = grpcwire.ReconGWires() + if err != nil { + log.Errorf("could not reconcile grpc wire: %v", err) + // generate error and continue + } + + if err := m.Serve(); err != nil { + log.Errorf("daemon exited badly: %v", err) + os.Exit(1) + } +} diff --git a/third_party/meshnet/daemon/meshnet/handler.go b/third_party/meshnet/daemon/meshnet/handler.go new file mode 100644 index 000000000..af57d29fb --- /dev/null +++ b/third_party/meshnet/daemon/meshnet/handler.go @@ -0,0 +1,451 @@ +package meshnet + +import ( + "context" + "os" + + "github.com/openconfig/kne/third_party/meshnet/api/types/v1beta1" + "github.com/openconfig/kne/third_party/meshnet/daemon/grpcwire" + "github.com/openconfig/kne/third_party/meshnet/daemon/vxlan" + + log "github.com/sirupsen/logrus" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/util/retry" + + mpb "github.com/openconfig/kne/third_party/meshnet/daemon/proto/meshnet/v1beta1" +) + +func (m *Meshnet) getPod(ctx context.Context, name, ns string) (*unstructured.Unstructured, error) { + mnetdLogger.Infof("Reading pod %s from K8s", name) + return m.tClient.Topology(ns).Unstructured(ctx, name, metav1.GetOptions{}) +} + +func (m *Meshnet) updateStatus(ctx context.Context, obj *unstructured.Unstructured, ns string) error { + mnetdLogger.Infof("Update pod status %s from K8s", obj.GetName()) + _, err := m.tClient.Topology(ns).Update(ctx, obj, metav1.UpdateOptions{}) + return err +} + +func (m *Meshnet) Get(ctx context.Context, pod *mpb.PodQuery) (*mpb.Pod, error) { + mnetdLogger.Infof("Retrieving %s's metadata from K8s...", pod.Name) + + result, err := m.getPod(ctx, pod.Name, pod.KubeNs) + if err != nil { + mnetdLogger.Errorf("Failed to read pod %s from K8s", pod.Name) + return nil, err + } + + remoteLinks, found, err := unstructured.NestedSlice(result.Object, "spec", "links") + if err != nil || !found || remoteLinks == nil { + mnetdLogger.Errorf("could not find 'Link' array in pod's spec") + return nil, err + } + + links := make([]*mpb.Link, len(remoteLinks)) + for i := range links { + remoteLink, ok := remoteLinks[i].(map[string]interface{}) + if !ok { + mnetdLogger.Errorf("Unrecognised 'Link' structure") + return nil, err + } + newLink := &mpb.Link{} + newLink.PeerPod, _, _ = unstructured.NestedString(remoteLink, "peer_pod") + newLink.PeerIntf, _, _ = unstructured.NestedString(remoteLink, "peer_intf") + newLink.LocalIntf, _, _ = unstructured.NestedString(remoteLink, "local_intf") + newLink.LocalIp, _, _ = unstructured.NestedString(remoteLink, "local_ip") + newLink.PeerIp, _, _ = unstructured.NestedString(remoteLink, "peer_ip") + newLink.Uid, _, _ = unstructured.NestedInt64(remoteLink, "uid") + links[i] = newLink + } + + srcIP, _, _ := unstructured.NestedString(result.Object, "status", "src_ip") + netNs, _, _ := unstructured.NestedString(result.Object, "status", "net_ns") + containerId, _, _ := unstructured.NestedString(result.Object, "status", "container_id") + nodeIP := os.Getenv("HOST_IP") + nodeIntf := os.Getenv("HOST_INTF") + + return &mpb.Pod{ + Name: pod.Name, + SrcIp: srcIP, + NetNs: netNs, + KubeNs: pod.KubeNs, + Links: links, + NodeIp: nodeIP, + NodeIntf: nodeIntf, + ContainerId: containerId, + }, nil +} + +func (m *Meshnet) SetAlive(ctx context.Context, pod *mpb.Pod) (*mpb.BoolResponse, error) { + mnetdLogger.Infof("Setting %s's SrcIp=%s and NetNs=%s", pod.Name, pod.SrcIp, pod.NetNs) + + retryErr := retry.RetryOnConflict(retry.DefaultRetry, func() error { + result, err := m.getPod(ctx, pod.Name, pod.KubeNs) + if err != nil { + mnetdLogger.Errorf("Failed to read pod %s from K8s", pod.Name) + return err + } + + if err = unstructured.SetNestedField(result.Object, pod.SrcIp, "status", "src_ip"); err != nil { + mnetdLogger.Errorf("Failed to update pod's src_ip") + } + + if err = unstructured.SetNestedField(result.Object, pod.NetNs, "status", "net_ns"); err != nil { + mnetdLogger.Errorf("Failed to update pod's net_ns") + } + + if err = unstructured.SetNestedField(result.Object, pod.ContainerId, "status", "container_id"); err != nil { + mnetdLogger.Errorf("Failed to update pod's container_id") + } + + return m.updateStatus(ctx, result, pod.KubeNs) + }) + + if retryErr != nil { + log.WithFields(log.Fields{ + "daemon": "meshnetd", + "err": retryErr, + "function": "SetAlive", + }).Errorf("Failed to update pod %s alive status", pod.Name) + return &mpb.BoolResponse{Response: false}, retryErr + } + + return &mpb.BoolResponse{Response: true}, nil +} + +// A point to point link between two pods is created only when both the pods are alive. +// While creating a link, when a pod does not find its peer, it marks the peer as skipped for this link UID. +// The pod stores the list of skipped peers and its corresponding link UID in it's skip list. +// For example - when a pod A want to create a link and it does not find it's peer B, +// then pod A adds the peer pod B and corresponding link UID in pod A's skip list. +func (m *Meshnet) Skip(ctx context.Context, skip *mpb.SkipQuery) (*mpb.BoolResponse, error) { + mnetdLogger.Infof("Pod %s, skipping peer pod %s for link UID %d ", skip.Pod, skip.Peer, skip.LinkId) + + retryErr := retry.RetryOnConflict(retry.DefaultRetry, func() error { + result, err := m.getPod(ctx, skip.Pod, skip.KubeNs) + if err != nil { + mnetdLogger.Errorf("Failed to read pod %s from K8s", skip.Pod) + return err + } + skipped, found, err := unstructured.NestedSlice(result.Object, "status", "skipped") + if found && err != nil { + mnetdLogger.Errorf("skip: error in retrieving skipped list from status, object found: %t, err: %v", found, err) + return err + } + // create a new skip item to append the peer in the skip list + newItem, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&v1beta1.Skipped{PodName: skip.Peer, LinkId: skip.LinkId}) + if err != nil { + mnetdLogger.Errorf("skip: could not convert to unstructured: %v\n", err) + return err + } + //append the peer information in skip list and update the data store + newSkipped := append(skipped, newItem) + if err := unstructured.SetNestedField(result.Object, newSkipped, "status", "skipped"); err != nil { + mnetdLogger.Errorf("failed to updated skipped list") + return err + } + + return m.updateStatus(ctx, result, skip.KubeNs) + }) + if retryErr != nil { + log.WithFields(log.Fields{ + "daemon": "meshnetd", + "err": retryErr, + "function": "Skip", + }).Errorf("Failed to update skip pod %s status", skip.Pod) + return &mpb.BoolResponse{Response: false}, retryErr + } + + return &mpb.BoolResponse{Response: true}, nil +} + +// Clean up function SkipReverse is called when a pod is getting deleted. It gets called once for every link the pod has. +// Anytime a pod is destroyed, for a link, it inserts itself along with its link-id in the skip list of its' peers on the link. +// It removes the the peer (and connecting link UID) from its own skip list. +// This data update in "data-store" helps to recreate the links in future when the pod is recreated by K8S +func (m *Meshnet) SkipReverse(ctx context.Context, skip *mpb.SkipQuery) (*mpb.BoolResponse, error) { + mnetdLogger.Infof("Reverse-skip for pod %s on link uid %d with peer pod %s", skip.Pod, skip.LinkId, skip.Peer) + + var podName string + retryErr := retry.RetryOnConflict(retry.DefaultRetry, func() error { + // get the peer pod if it is alive + peerPod, err := m.getPod(ctx, skip.Peer, skip.KubeNs) + if err != nil { + mnetdLogger.Errorf("Failed to read pod %s from K8s", skip.Pod) + return err + } + srcIP, _, _ := unstructured.NestedString(peerPod.Object, "status", "src_ip") + netNs, _, _ := unstructured.NestedString(peerPod.Object, "status", "net_ns") + if srcIP == "" || netNs == "" { + // peer pod is not alive as container. so no need to update peer's skipped list in Topology CRD. + return nil + } + podName = peerPod.GetName() + + // Extracting peer pod's skipped list and insert this pod's name in it. + // This is needed as in future this pod comes back again, it will find out that it has + // been skipped by the peer. As a result this pod will re-initiate link creation. + peerSkipped, found, err := unstructured.NestedSlice(peerPod.Object, "status", "skipped") + if found && err != nil { + mnetdLogger.Errorf("skipReverse: error in retrieving skipped list from peer pod's status: object found: %t, err: %v", found, err) + return err + } + + // If the pod is already present in skipped list we don't need to append it again. + // For example - The pod can be already present in the Peer's skip list if this was a low priority + // pod and it came up after the high priority peer. + // For example :- when a pod is repeatedly destroyed (create->destroy->create->destroy.... while + // the topology is still alive), it try to insert itself multiple times in peers skip list. + // Prevent multiple entry of same pod. + found = false + for _, el := range peerSkipped { + elSkipped, ok := el.(map[string]interface{}) + if !ok { + mnetdLogger.Errorf("skipReverse: 'Skipped' not recognized") + continue + } + skipped := v1beta1.Skipped{} + if err := runtime.DefaultUnstructuredConverter.FromUnstructured(elSkipped, &skipped); err != nil { + mnetdLogger.Errorf("skipReverse: unable to retrieve Skipped: %v", err) + continue + } + if skip.Pod == skipped.PodName && skip.LinkId == skipped.LinkId { + found = true + break + } + } + newPeerSkipped := peerSkipped + if !found { + newItem, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&v1beta1.Skipped{PodName: skip.Pod, LinkId: skip.LinkId}) + if err != nil { + mnetdLogger.Errorf("skipReverse: could not convert to unstructured: %v\n", err) + return err + } + newPeerSkipped = append(newPeerSkipped, newItem) + } + + // updating peer pod's skipped list locally + if err := unstructured.SetNestedField(peerPod.Object, newPeerSkipped, "status", "skipped"); err != nil { + mnetdLogger.Errorf("skipReverse: Failed to updated reverse-skipped list for peer pod %s", peerPod.GetName()) + return err + } + + // sending peer pod's updates to k8s + return m.updateStatus(ctx, peerPod, skip.KubeNs) + }) + if retryErr != nil { + log.WithFields(log.Fields{ + "daemon": "meshnetd", + "err": retryErr, + "function": "SkipReverse", + }).Errorf("Failed to update peer pod %s skipreverse status", podName) + return &mpb.BoolResponse{Response: false}, retryErr + } + + retryErr = retry.RetryOnConflict(retry.DefaultRetry, func() error { + // setting the value for this pod + thisPod, err := m.getPod(ctx, skip.Pod, skip.KubeNs) + if err != nil { + mnetdLogger.Errorf("skipReverse: Failed to read pod %s from K8s", skip.Pod) + return err + } + + // extracting this pod's skipped list and removing peer pod's name from it + thisSkipped, found, err := unstructured.NestedSlice(thisPod.Object, "status", "skipped") + if found && err != nil { + mnetdLogger.Errorf("skipreverse: error in retrieving skipped list from local pod's status: object found: %t, err: %v", found, err) + return err + } + newThisSkipped := make([]interface{}, 0) + + log.WithFields(log.Fields{ + "daemon": "meshnetd", + "SkipReverse": thisSkipped, + }).Info("THIS SkipReverse:") + + for _, el := range thisSkipped { + elSkipped, ok := el.(map[string]interface{}) + if !ok { + mnetdLogger.Errorf("skip-reverse: unrecongnized 'Skipped'") + continue + } + skipped := v1beta1.Skipped{} + if err := runtime.DefaultUnstructuredConverter.FromUnstructured(elSkipped, &skipped); err != nil { + mnetdLogger.Errorf("skipReverse : unable to retrieve Skipped: %v", err) + continue + } + if skipped.PodName != skip.Peer && skipped.LinkId != skip.LinkId { + mnetdLogger.Infof("Appending new element %s@%d", skipped.PodName, skipped.LinkId) + newThisSkipped = append(newThisSkipped, elSkipped) + } + } + + log.WithFields(log.Fields{ + "daemon": "meshnetd", + "SkipReverse": newThisSkipped, + }).Info("NEW THIS SkipReverse:") + + // updating this pod's skipped list locally after removing the peer from the current skip list. + // Length check for newThisSkipped is not needed even if it is empty. we should update data-store with empty list. + if err := unstructured.SetNestedField(thisPod.Object, newThisSkipped, "status", "skipped"); err != nil { + mnetdLogger.Errorf("skipReverse: Failed to cleanup skipped list for pod %s", thisPod.GetName()) + return err + } + + // sending this pod's updates to k8s + return m.updateStatus(ctx, thisPod, skip.KubeNs) + }) + if retryErr != nil { + log.WithFields(log.Fields{ + "daemon": "meshnetd", + "err": retryErr, + "function": "SkipReverse", + }).Error("Failed to update this pod skipreverse status") + return &mpb.BoolResponse{Response: false}, retryErr + } + + return &mpb.BoolResponse{Response: true}, nil +} + +func (m *Meshnet) IsSkipped(ctx context.Context, skip *mpb.SkipQuery) (*mpb.BoolResponse, error) { + // mnetdLogger.Infof("Checking if %s is skipped by %s", skip.Peer, skip.Pod) + mnetdLogger.Infof("Checking if %s, link id %d is skipped by %s", skip.Pod, skip.LinkId, skip.Peer) + + result, err := m.getPod(ctx, skip.Peer, skip.KubeNs) + if err != nil { + mnetdLogger.Errorf("isSkipped: Failed to read pod %s from K8s", skip.Pod) + return nil, err + } + + skipped, found, err := unstructured.NestedSlice(result.Object, "status", "skipped") + if found && err != nil { + mnetdLogger.Errorf("isSkipped: error in retrieving skipped list from peer pod's status, object found: %t, err: %v", found, err) + return nil, err + } + + for _, peerSkipped := range skipped { + elSkipped, ok := peerSkipped.(map[string]interface{}) + if !ok { + mnetdLogger.Errorf("isSkipped: 'Skipped' not recognized") + continue + } + skipped := v1beta1.Skipped{} + if err := runtime.DefaultUnstructuredConverter.FromUnstructured(elSkipped, &skipped); err != nil { + mnetdLogger.Errorf("isSkipped: unable to retrieve Skipped: %v", err) + continue + } + if skip.Pod == skipped.PodName && skip.LinkId == skipped.LinkId { + return &mpb.BoolResponse{Response: true}, nil + } + } + return &mpb.BoolResponse{Response: false}, nil +} + +func (m *Meshnet) Update(ctx context.Context, pod *mpb.RemotePod) (*mpb.BoolResponse, error) { + if err := vxlan.CreateOrUpdate(pod); err != nil { + mnetdLogger.Errorf("Failed to Update Vxlan") + return &mpb.BoolResponse{Response: false}, nil + } + return &mpb.BoolResponse{Response: true}, nil +} + +// ------------------------------------------------------------------------------------------------------ +func (m *Meshnet) RemGRPCWire(ctx context.Context, wireDef *mpb.WireDef) (*mpb.BoolResponse, error) { + //if err := grpcwire.DeleteWiresByPod(wireDef.KubeNs, wireDef.LocalPodName); err != nil + if err := grpcwire.DeletePodWires(wireDef.TopoNs, wireDef.LocalPodName); err != nil { + return &mpb.BoolResponse{Response: false}, err + } + return &mpb.BoolResponse{Response: true}, nil +} + +func (m *Meshnet) AddGRPCWireLocal(ctx context.Context, wireDef *mpb.WireDef) (*mpb.BoolResponse, error) { + return grpcwire.CreateGRPCWireLocal(ctx, wireDef) +} + +// ------------------------------------------------------------------------------------------------------ +func (m *Meshnet) SendToOnce(ctx context.Context, pkt *mpb.Packet) (*mpb.BoolResponse, error) { + wrHandle, err := grpcwire.GetHostIntfHndl(pkt.RemotIntfId) + if err != nil { + log.WithFields(log.Fields{ + "daemon": "meshnetd", + "overlay": "gRPC", + }).Errorf("SendToOnce (wire id - %v): Could not find local handle. err:%v", pkt.RemotIntfId, err) + return &mpb.BoolResponse{Response: false}, err + } + + // In case any per packet log need to be generated. + // pktType := grpcwire.DecodePkt(pkt.Frame) + // log.Printf("Daemon(SendToOnce): Received [pkt: %s, bytes: %d, for local interface id: %d]. Sending it to local container", pktType, len(pkt.Frame), pkt.RemotIntfId) + // log.Printf("Daemon(SendToOnce): Received [bytes: %d, for local interface id: %d]. Sending it to local container", len(pkt.Frame), pkt.RemotIntfId) + + err = wrHandle.WritePacketData(pkt.Frame) + if err != nil { + log.WithFields(log.Fields{ + "daemon": "meshnetd", + "overlay": "gRPC", + }).Errorf("SendToOnce (wire id - %v): Could not write packet(%d bytes) to local interface. err:%v", pkt.RemotIntfId, len(pkt.Frame), err) + return &mpb.BoolResponse{Response: false}, err + } + + return &mpb.BoolResponse{Response: true}, nil +} + +// --------------------------------------------------------------------------------------------------------------- +func (m *Meshnet) AddGRPCWireRemote(ctx context.Context, wireDef *mpb.WireDef) (*mpb.WireCreateResponse, error) { + stopC := make(chan struct{}) + wire, err := grpcwire.CreateUpdateGRPCWireRemoteTriggered(wireDef, stopC) + if err == nil { + log.WithFields(log.Fields{ + "daemon": "meshnetd", + "overlay": "gRPC", + }).Infof("[ADD-WIRE:REMOTE-END]For pod %s@%s starting the local packet receive thread", wireDef.LocalPodName, wireDef.IntfNameInPod) + go grpcwire.RecvFrmLocalPodThread(wire, wire.LocalNodeIfaceName) + + return &mpb.WireCreateResponse{Response: true, PeerIntfId: wire.LocalNodeIfaceID}, nil + } + log.WithFields(log.Fields{ + "daemon": "meshnetd", + "overlay": "gRPC", + }).Errorf("[ADD-WIRE:REMOTE-END] err: %v", err) + return &mpb.WireCreateResponse{Response: false, PeerIntfId: wireDef.WireIfIdOnPeerNode}, err +} + +// --------------------------------------------------------------------------------------------------------------- +func (m *Meshnet) GRPCWireDownRemote(ctx context.Context, wireDef *mpb.WireDef) (*mpb.WireDownResponse, error) { + err := grpcwire.GRPCWireDownRemoteTriggered(wireDef) + if err == nil { + log.WithFields(log.Fields{ + "daemon": "meshnetd", + "overlay": "gRPC", + }).Infof("[WIRE-DOWN]At remote end for pod %s@%s", wireDef.LocalPodName, wireDef.IntfNameInPod) + return &mpb.WireDownResponse{Response: true}, nil + } + log.WithFields(log.Fields{ + "daemon": "meshnetd", + "overlay": "gRPC", + }).Errorf("[WIRE-DOWN]Remote end err: %v", err) + return &mpb.WireDownResponse{Response: false}, err +} + +// --------------------------------------------------------------------------------------------------------------- +// GRPCWireExists will return the wire if it exists. +func (m *Meshnet) GRPCWireExists(ctx context.Context, wireDef *mpb.WireDef) (*mpb.WireCreateResponse, error) { + wire, ok := grpcwire.GetWireByUID(wireDef.LocalPodNetNs, int(wireDef.LinkUid)) + if !ok || wire == nil { + return &mpb.WireCreateResponse{Response: false, PeerIntfId: wireDef.WireIfIdOnPeerNode}, nil + } + return &mpb.WireCreateResponse{Response: ok, PeerIntfId: wire.WireIfaceIDOnPeerNode}, nil +} + +// --------------------------------------------------------------------------------------------------------------- +// Given the pod name and the pod interface, GenerateNodeInterfaceName generates the corresponding interface name in the node. +// This pod interface and the node interface later become the two end of a veth-pair +func (m *Meshnet) GenerateNodeInterfaceName(ctx context.Context, in *mpb.GenerateNodeInterfaceNameRequest) (*mpb.GenerateNodeInterfaceNameResponse, error) { + locIfNm, err := grpcwire.GenNodeIfaceName(in.PodName, in.PodIntfName) + if err != nil { + return &mpb.GenerateNodeInterfaceNameResponse{Ok: false, NodeIntfName: ""}, err + } + return &mpb.GenerateNodeInterfaceNameResponse{Ok: true, NodeIntfName: locIfNm}, nil +} diff --git a/third_party/meshnet/daemon/meshnet/meshnet.go b/third_party/meshnet/daemon/meshnet/meshnet.go new file mode 100644 index 000000000..a8342691c --- /dev/null +++ b/third_party/meshnet/daemon/meshnet/meshnet.go @@ -0,0 +1,154 @@ +package meshnet + +import ( + "fmt" + "io" + "net" + "os" + "path/filepath" + + grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware" + grpc_ctxtags "github.com/grpc-ecosystem/go-grpc-middleware/tags" + + glogrus "github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus" + log "github.com/sirupsen/logrus" + "google.golang.org/grpc" + "google.golang.org/grpc/grpclog" + "google.golang.org/grpc/reflection" + "k8s.io/client-go/dynamic" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" + "k8s.io/client-go/util/homedir" + + topologyclientv1 "github.com/openconfig/kne/third_party/meshnet/api/clientset/v1beta1" + "github.com/openconfig/kne/third_party/meshnet/utils/wireutil" + + mpb "github.com/openconfig/kne/third_party/meshnet/daemon/proto/meshnet/v1beta1" +) + +type Config struct { + Port int + GRPCOpts []grpc.ServerOption +} + +type Meshnet struct { + mpb.UnimplementedLocalServer + mpb.UnimplementedRemoteServer + mpb.UnimplementedWireProtocolServer + config Config + kClient kubernetes.Interface + tClient topologyclientv1.Interface + GWireDynClient *dynamic.DynamicClient + rCfg *rest.Config + s *grpc.Server + lis net.Listener +} + +var mnetdLogger *log.Entry = nil + +func InitLogger() { + mnetdLogger = log.WithFields(log.Fields{"daemon": "meshnetd"}) +} + +func restConfig() (*rest.Config, error) { + mnetdLogger.Infof("Trying in-cluster configuration") + rCfg, err := rest.InClusterConfig() + if err != nil { + kubecfg := filepath.Join(".kube", "config") + if home := homedir.HomeDir(); home != "" { + kubecfg = filepath.Join(home, kubecfg) + } + mnetdLogger.Infof("Falling back to kubeconfig: %q", kubecfg) + rCfg, err = clientcmd.BuildConfigFromFlags("", kubecfg) + if err != nil { + mnetdLogger.Infof("error in Falling back to kubeconfig: %v", err) + return nil, err + } + } + return rCfg, nil +} + +func New(cfg Config) (*Meshnet, error) { + rCfg, err := restConfig() + if err != nil { + return nil, err + } + kClient, err := kubernetes.NewForConfig(rCfg) + if err != nil { + return nil, err + } + tClient, err := topologyclientv1.NewForConfig(rCfg) + if err != nil { + return nil, err + } + gwireDynClient, err := dynamic.NewForConfig(rCfg) + if err != nil { + return nil, err + } + + lis, err := net.Listen("tcp", fmt.Sprintf(":%d", cfg.Port)) + if err != nil { + return nil, err + } + // If the link type is GRPC then set the GRPC logging level to LevelNone + // Otherwise there will be GRPC log for every packet sent as for link type GRPC, GRPC is also the data-plane. This is too + // much of log that does not help in debugging and K8S does log rotation very frequently. + var svr *grpc.Server + lnkTyp := os.Getenv("INTER_NODE_LINK_TYPE") + if lnkTyp == wireutil.INTER_NODE_LINK_GRPC { + svr = grpc.NewServer(cfg.GRPCOpts...) + } else { + svr = newServerWithLogging(cfg.GRPCOpts...) + } + + m := &Meshnet{ + config: cfg, + rCfg: rCfg, + kClient: kClient, + tClient: tClient, + GWireDynClient: gwireDynClient, + lis: lis, + s: svr, + } + mpb.RegisterLocalServer(m.s, m) + mpb.RegisterRemoteServer(m.s, m) + mpb.RegisterWireProtocolServer(m.s, m) + reflection.Register(m.s) + + // After server is registered, reduce logging if link type is GRPC + if lnkTyp == wireutil.INTER_NODE_LINK_GRPC { + // Stop all Info, Warning, Error + grpclog.SetLoggerV2(grpclog.NewLoggerV2WithVerbosity(io.Discard, io.Discard, io.Discard, 0)) + // see Error/Fatal, but opt out on Info + //grpclog.SetLoggerV2(grpclog.NewLoggerV2WithVerbosity(ioutil.Discard, ioutil.Discard, os.Stderr, 0)) + mnetdLogger.Infof("Enabled GRPC logging for Error and Fatal only. Disabled Info & Warning logs.") + } + + return m, nil +} + +func (m *Meshnet) Serve() error { + mnetdLogger.Infof("GRPC server has started on port: %d", m.config.Port) + return m.s.Serve(m.lis) +} + +func (m *Meshnet) Stop() { + m.s.Stop() +} + +func newServerWithLogging(opts ...grpc.ServerOption) *grpc.Server { + lEntry := log.NewEntry(log.StandardLogger()) + lOpts := []glogrus.Option{} + glogrus.ReplaceGrpcLogger(lEntry) + opts = append(opts, + grpc_middleware.WithUnaryServerChain( + grpc_ctxtags.UnaryServerInterceptor(grpc_ctxtags.WithFieldExtractor(grpc_ctxtags.CodeGenRequestFieldExtractor)), + glogrus.UnaryServerInterceptor(lEntry, lOpts...), + ), + grpc_middleware.WithStreamServerChain( + grpc_ctxtags.StreamServerInterceptor(grpc_ctxtags.WithFieldExtractor(grpc_ctxtags.CodeGenRequestFieldExtractor)), + glogrus.StreamServerInterceptor(lEntry, lOpts...), + )) + return grpc.NewServer(opts...) +} diff --git a/third_party/meshnet/daemon/proto/meshnet/v1beta1/meshnet.pb.go b/third_party/meshnet/daemon/proto/meshnet/v1beta1/meshnet.pb.go new file mode 100644 index 000000000..902665c37 --- /dev/null +++ b/third_party/meshnet/daemon/proto/meshnet/v1beta1/meshnet.pb.go @@ -0,0 +1,1019 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.11 +// protoc (unknown) +// source: daemon/proto/meshnet/v1beta1/meshnet.proto + +package v1beta1 + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" + unsafe "unsafe" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Pod struct { + state protoimpl.MessageState `protogen:"open.v1"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + SrcIp string `protobuf:"bytes,2,opt,name=src_ip,json=srcIp,proto3" json:"src_ip,omitempty"` + NetNs string `protobuf:"bytes,3,opt,name=net_ns,json=netNs,proto3" json:"net_ns,omitempty"` + KubeNs string `protobuf:"bytes,4,opt,name=kube_ns,json=kubeNs,proto3" json:"kube_ns,omitempty"` + Links []*Link `protobuf:"bytes,5,rep,name=links,proto3" json:"links,omitempty"` + NodeIp string `protobuf:"bytes,6,opt,name=node_ip,json=nodeIp,proto3" json:"node_ip,omitempty"` + NodeIntf string `protobuf:"bytes,7,opt,name=node_intf,json=nodeIntf,proto3" json:"node_intf,omitempty"` + ContainerId string `protobuf:"bytes,8,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Pod) Reset() { + *x = Pod{} + mi := &file_daemon_proto_meshnet_v1beta1_meshnet_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Pod) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Pod) ProtoMessage() {} + +func (x *Pod) ProtoReflect() protoreflect.Message { + mi := &file_daemon_proto_meshnet_v1beta1_meshnet_proto_msgTypes[0] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Pod.ProtoReflect.Descriptor instead. +func (*Pod) Descriptor() ([]byte, []int) { + return file_daemon_proto_meshnet_v1beta1_meshnet_proto_rawDescGZIP(), []int{0} +} + +func (x *Pod) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Pod) GetSrcIp() string { + if x != nil { + return x.SrcIp + } + return "" +} + +func (x *Pod) GetNetNs() string { + if x != nil { + return x.NetNs + } + return "" +} + +func (x *Pod) GetKubeNs() string { + if x != nil { + return x.KubeNs + } + return "" +} + +func (x *Pod) GetLinks() []*Link { + if x != nil { + return x.Links + } + return nil +} + +func (x *Pod) GetNodeIp() string { + if x != nil { + return x.NodeIp + } + return "" +} + +func (x *Pod) GetNodeIntf() string { + if x != nil { + return x.NodeIntf + } + return "" +} + +func (x *Pod) GetContainerId() string { + if x != nil { + return x.ContainerId + } + return "" +} + +type Link struct { + state protoimpl.MessageState `protogen:"open.v1"` + PeerPod string `protobuf:"bytes,1,opt,name=peer_pod,json=peerPod,proto3" json:"peer_pod,omitempty"` + LocalIntf string `protobuf:"bytes,2,opt,name=local_intf,json=localIntf,proto3" json:"local_intf,omitempty"` + PeerIntf string `protobuf:"bytes,3,opt,name=peer_intf,json=peerIntf,proto3" json:"peer_intf,omitempty"` + LocalIp string `protobuf:"bytes,4,opt,name=local_ip,json=localIp,proto3" json:"local_ip,omitempty"` + PeerIp string `protobuf:"bytes,5,opt,name=peer_ip,json=peerIp,proto3" json:"peer_ip,omitempty"` + Uid int64 `protobuf:"varint,6,opt,name=uid,proto3" json:"uid,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Link) Reset() { + *x = Link{} + mi := &file_daemon_proto_meshnet_v1beta1_meshnet_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Link) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Link) ProtoMessage() {} + +func (x *Link) ProtoReflect() protoreflect.Message { + mi := &file_daemon_proto_meshnet_v1beta1_meshnet_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Link.ProtoReflect.Descriptor instead. +func (*Link) Descriptor() ([]byte, []int) { + return file_daemon_proto_meshnet_v1beta1_meshnet_proto_rawDescGZIP(), []int{1} +} + +func (x *Link) GetPeerPod() string { + if x != nil { + return x.PeerPod + } + return "" +} + +func (x *Link) GetLocalIntf() string { + if x != nil { + return x.LocalIntf + } + return "" +} + +func (x *Link) GetPeerIntf() string { + if x != nil { + return x.PeerIntf + } + return "" +} + +func (x *Link) GetLocalIp() string { + if x != nil { + return x.LocalIp + } + return "" +} + +func (x *Link) GetPeerIp() string { + if x != nil { + return x.PeerIp + } + return "" +} + +func (x *Link) GetUid() int64 { + if x != nil { + return x.Uid + } + return 0 +} + +type PodQuery struct { + state protoimpl.MessageState `protogen:"open.v1"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + KubeNs string `protobuf:"bytes,2,opt,name=kube_ns,json=kubeNs,proto3" json:"kube_ns,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *PodQuery) Reset() { + *x = PodQuery{} + mi := &file_daemon_proto_meshnet_v1beta1_meshnet_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *PodQuery) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PodQuery) ProtoMessage() {} + +func (x *PodQuery) ProtoReflect() protoreflect.Message { + mi := &file_daemon_proto_meshnet_v1beta1_meshnet_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PodQuery.ProtoReflect.Descriptor instead. +func (*PodQuery) Descriptor() ([]byte, []int) { + return file_daemon_proto_meshnet_v1beta1_meshnet_proto_rawDescGZIP(), []int{2} +} + +func (x *PodQuery) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *PodQuery) GetKubeNs() string { + if x != nil { + return x.KubeNs + } + return "" +} + +type SkipQuery struct { + state protoimpl.MessageState `protogen:"open.v1"` + Pod string `protobuf:"bytes,1,opt,name=pod,proto3" json:"pod,omitempty"` + Peer string `protobuf:"bytes,2,opt,name=peer,proto3" json:"peer,omitempty"` + KubeNs string `protobuf:"bytes,3,opt,name=kube_ns,json=kubeNs,proto3" json:"kube_ns,omitempty"` + LinkId int64 `protobuf:"varint,4,opt,name=link_id,json=linkId,proto3" json:"link_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SkipQuery) Reset() { + *x = SkipQuery{} + mi := &file_daemon_proto_meshnet_v1beta1_meshnet_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SkipQuery) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SkipQuery) ProtoMessage() {} + +func (x *SkipQuery) ProtoReflect() protoreflect.Message { + mi := &file_daemon_proto_meshnet_v1beta1_meshnet_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SkipQuery.ProtoReflect.Descriptor instead. +func (*SkipQuery) Descriptor() ([]byte, []int) { + return file_daemon_proto_meshnet_v1beta1_meshnet_proto_rawDescGZIP(), []int{3} +} + +func (x *SkipQuery) GetPod() string { + if x != nil { + return x.Pod + } + return "" +} + +func (x *SkipQuery) GetPeer() string { + if x != nil { + return x.Peer + } + return "" +} + +func (x *SkipQuery) GetKubeNs() string { + if x != nil { + return x.KubeNs + } + return "" +} + +func (x *SkipQuery) GetLinkId() int64 { + if x != nil { + return x.LinkId + } + return 0 +} + +type BoolResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Response bool `protobuf:"varint,1,opt,name=response,proto3" json:"response,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *BoolResponse) Reset() { + *x = BoolResponse{} + mi := &file_daemon_proto_meshnet_v1beta1_meshnet_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *BoolResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BoolResponse) ProtoMessage() {} + +func (x *BoolResponse) ProtoReflect() protoreflect.Message { + mi := &file_daemon_proto_meshnet_v1beta1_meshnet_proto_msgTypes[4] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BoolResponse.ProtoReflect.Descriptor instead. +func (*BoolResponse) Descriptor() ([]byte, []int) { + return file_daemon_proto_meshnet_v1beta1_meshnet_proto_rawDescGZIP(), []int{4} +} + +func (x *BoolResponse) GetResponse() bool { + if x != nil { + return x.Response + } + return false +} + +type RemotePod struct { + state protoimpl.MessageState `protogen:"open.v1"` + NetNs string `protobuf:"bytes,1,opt,name=net_ns,json=netNs,proto3" json:"net_ns,omitempty"` + IntfName string `protobuf:"bytes,2,opt,name=intf_name,json=intfName,proto3" json:"intf_name,omitempty"` + IntfIp string `protobuf:"bytes,3,opt,name=intf_ip,json=intfIp,proto3" json:"intf_ip,omitempty"` + PeerVtep string `protobuf:"bytes,4,opt,name=peer_vtep,json=peerVtep,proto3" json:"peer_vtep,omitempty"` + KubeNs string `protobuf:"bytes,5,opt,name=kube_ns,json=kubeNs,proto3" json:"kube_ns,omitempty"` + Vni int64 `protobuf:"varint,6,opt,name=vni,proto3" json:"vni,omitempty"` + NodeIntf string `protobuf:"bytes,7,opt,name=node_intf,json=nodeIntf,proto3" json:"node_intf,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RemotePod) Reset() { + *x = RemotePod{} + mi := &file_daemon_proto_meshnet_v1beta1_meshnet_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RemotePod) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RemotePod) ProtoMessage() {} + +func (x *RemotePod) ProtoReflect() protoreflect.Message { + mi := &file_daemon_proto_meshnet_v1beta1_meshnet_proto_msgTypes[5] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RemotePod.ProtoReflect.Descriptor instead. +func (*RemotePod) Descriptor() ([]byte, []int) { + return file_daemon_proto_meshnet_v1beta1_meshnet_proto_rawDescGZIP(), []int{5} +} + +func (x *RemotePod) GetNetNs() string { + if x != nil { + return x.NetNs + } + return "" +} + +func (x *RemotePod) GetIntfName() string { + if x != nil { + return x.IntfName + } + return "" +} + +func (x *RemotePod) GetIntfIp() string { + if x != nil { + return x.IntfIp + } + return "" +} + +func (x *RemotePod) GetPeerVtep() string { + if x != nil { + return x.PeerVtep + } + return "" +} + +func (x *RemotePod) GetKubeNs() string { + if x != nil { + return x.KubeNs + } + return "" +} + +func (x *RemotePod) GetVni() int64 { + if x != nil { + return x.Vni + } + return 0 +} + +func (x *RemotePod) GetNodeIntf() string { + if x != nil { + return x.NodeIntf + } + return "" +} + +// The proto describes both end of a grpc-wire, the local end and the remote +// end. +type WireDef struct { + state protoimpl.MessageState `protogen:"open.v1"` + // The remote machine interface id, to which this wire is connected to. + // + // When local machine sends a packet to remote machine over grpc-wire, + // then along with the packet data, local machine also sends the interface id + // to which this packet needs to delivered in remote machine. It avoids any + // per packet interface lookup at the remote end. Packet delivery becomes + // an O(1) operation at remote end. + WireIfIdOnPeerNode int64 `protobuf:"varint,1,opt,name=wire_if_id_on_peer_node,json=wireIfIdOnPeerNode,proto3" json:"wire_if_id_on_peer_node,omitempty"` + // The remote machine interface IP, to which this grpc wire is connected to. + PeerNodeIp string `protobuf:"bytes,2,opt,name=peer_node_ip,json=peerNodeIp,proto3" json:"peer_node_ip,omitempty"` + // Interface name, which comes from topology definition and to be put + // inside container. + // This filed is used when grpc-wire to be created. + IntfNameInPod string `protobuf:"bytes,3,opt,name=intf_name_in_pod,json=intfNameInPod,proto3" json:"intf_name_in_pod,omitempty"` + // Network name space of the local pod which is connected to this + // grpc-wire + LocalPodNetNs string `protobuf:"bytes,4,opt,name=local_pod_net_ns,json=localPodNetNs,proto3" json:"local_pod_net_ns,omitempty"` + // Each meshnet link has a uid. + LinkUid int64 `protobuf:"varint,5,opt,name=link_uid,json=linkUid,proto3" json:"link_uid,omitempty"` + // Name of the local pod where this wire is getting added. + LocalPodName string `protobuf:"bytes,6,opt,name=local_pod_name,json=localPodName,proto3" json:"local_pod_name,omitempty"` + // Every interface inside a pod is one end of a veth pair. The other end of + // the veth pair is with the local node. This is the name of veth end, which + // is with the node. Packets coming from the pod will be picked up from this + // veth end and will be transported to the remote node over grpc wire. + WireIfNameOnLocalNode string `protobuf:"bytes,7,opt,name=wire_if_name_on_local_node,json=wireIfNameOnLocalNode,proto3" json:"wire_if_name_on_local_node,omitempty"` + TopoNs string `protobuf:"bytes,8,opt,name=topo_ns,json=topoNs,proto3" json:"topo_ns,omitempty"` + LocalPodIp string `protobuf:"bytes,9,opt,name=local_pod_ip,json=localPodIp,proto3" json:"local_pod_ip,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *WireDef) Reset() { + *x = WireDef{} + mi := &file_daemon_proto_meshnet_v1beta1_meshnet_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *WireDef) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WireDef) ProtoMessage() {} + +func (x *WireDef) ProtoReflect() protoreflect.Message { + mi := &file_daemon_proto_meshnet_v1beta1_meshnet_proto_msgTypes[6] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WireDef.ProtoReflect.Descriptor instead. +func (*WireDef) Descriptor() ([]byte, []int) { + return file_daemon_proto_meshnet_v1beta1_meshnet_proto_rawDescGZIP(), []int{6} +} + +func (x *WireDef) GetWireIfIdOnPeerNode() int64 { + if x != nil { + return x.WireIfIdOnPeerNode + } + return 0 +} + +func (x *WireDef) GetPeerNodeIp() string { + if x != nil { + return x.PeerNodeIp + } + return "" +} + +func (x *WireDef) GetIntfNameInPod() string { + if x != nil { + return x.IntfNameInPod + } + return "" +} + +func (x *WireDef) GetLocalPodNetNs() string { + if x != nil { + return x.LocalPodNetNs + } + return "" +} + +func (x *WireDef) GetLinkUid() int64 { + if x != nil { + return x.LinkUid + } + return 0 +} + +func (x *WireDef) GetLocalPodName() string { + if x != nil { + return x.LocalPodName + } + return "" +} + +func (x *WireDef) GetWireIfNameOnLocalNode() string { + if x != nil { + return x.WireIfNameOnLocalNode + } + return "" +} + +func (x *WireDef) GetTopoNs() string { + if x != nil { + return x.TopoNs + } + return "" +} + +func (x *WireDef) GetLocalPodIp() string { + if x != nil { + return x.LocalPodIp + } + return "" +} + +type WireCreateResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Response bool `protobuf:"varint,1,opt,name=response,proto3" json:"response,omitempty"` + // the interface id, that was created. + PeerIntfId int64 `protobuf:"varint,2,opt,name=peer_intf_id,json=peerIntfId,proto3" json:"peer_intf_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *WireCreateResponse) Reset() { + *x = WireCreateResponse{} + mi := &file_daemon_proto_meshnet_v1beta1_meshnet_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *WireCreateResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WireCreateResponse) ProtoMessage() {} + +func (x *WireCreateResponse) ProtoReflect() protoreflect.Message { + mi := &file_daemon_proto_meshnet_v1beta1_meshnet_proto_msgTypes[7] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WireCreateResponse.ProtoReflect.Descriptor instead. +func (*WireCreateResponse) Descriptor() ([]byte, []int) { + return file_daemon_proto_meshnet_v1beta1_meshnet_proto_rawDescGZIP(), []int{7} +} + +func (x *WireCreateResponse) GetResponse() bool { + if x != nil { + return x.Response + } + return false +} + +func (x *WireCreateResponse) GetPeerIntfId() int64 { + if x != nil { + return x.PeerIntfId + } + return 0 +} + +type WireDownResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Response bool `protobuf:"varint,1,opt,name=response,proto3" json:"response,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *WireDownResponse) Reset() { + *x = WireDownResponse{} + mi := &file_daemon_proto_meshnet_v1beta1_meshnet_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *WireDownResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WireDownResponse) ProtoMessage() {} + +func (x *WireDownResponse) ProtoReflect() protoreflect.Message { + mi := &file_daemon_proto_meshnet_v1beta1_meshnet_proto_msgTypes[8] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WireDownResponse.ProtoReflect.Descriptor instead. +func (*WireDownResponse) Descriptor() ([]byte, []int) { + return file_daemon_proto_meshnet_v1beta1_meshnet_proto_rawDescGZIP(), []int{8} +} + +func (x *WireDownResponse) GetResponse() bool { + if x != nil { + return x.Response + } + return false +} + +type Packet struct { + state protoimpl.MessageState `protogen:"open.v1"` + // the remote machine interface id, to which this packet should be delivered. + RemotIntfId int64 `protobuf:"varint,1,opt,name=remot_intf_id,json=remotIntfId,proto3" json:"remot_intf_id,omitempty"` + Frame []byte `protobuf:"bytes,2,opt,name=frame,proto3" json:"frame,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Packet) Reset() { + *x = Packet{} + mi := &file_daemon_proto_meshnet_v1beta1_meshnet_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Packet) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Packet) ProtoMessage() {} + +func (x *Packet) ProtoReflect() protoreflect.Message { + mi := &file_daemon_proto_meshnet_v1beta1_meshnet_proto_msgTypes[9] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Packet.ProtoReflect.Descriptor instead. +func (*Packet) Descriptor() ([]byte, []int) { + return file_daemon_proto_meshnet_v1beta1_meshnet_proto_rawDescGZIP(), []int{9} +} + +func (x *Packet) GetRemotIntfId() int64 { + if x != nil { + return x.RemotIntfId + } + return 0 +} + +func (x *Packet) GetFrame() []byte { + if x != nil { + return x.Frame + } + return nil +} + +type GenerateNodeInterfaceNameRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + PodIntfName string `protobuf:"bytes,1,opt,name=pod_intf_name,json=podIntfName,proto3" json:"pod_intf_name,omitempty"` + PodName string `protobuf:"bytes,2,opt,name=pod_name,json=podName,proto3" json:"pod_name,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GenerateNodeInterfaceNameRequest) Reset() { + *x = GenerateNodeInterfaceNameRequest{} + mi := &file_daemon_proto_meshnet_v1beta1_meshnet_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GenerateNodeInterfaceNameRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GenerateNodeInterfaceNameRequest) ProtoMessage() {} + +func (x *GenerateNodeInterfaceNameRequest) ProtoReflect() protoreflect.Message { + mi := &file_daemon_proto_meshnet_v1beta1_meshnet_proto_msgTypes[10] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GenerateNodeInterfaceNameRequest.ProtoReflect.Descriptor instead. +func (*GenerateNodeInterfaceNameRequest) Descriptor() ([]byte, []int) { + return file_daemon_proto_meshnet_v1beta1_meshnet_proto_rawDescGZIP(), []int{10} +} + +func (x *GenerateNodeInterfaceNameRequest) GetPodIntfName() string { + if x != nil { + return x.PodIntfName + } + return "" +} + +func (x *GenerateNodeInterfaceNameRequest) GetPodName() string { + if x != nil { + return x.PodName + } + return "" +} + +type GenerateNodeInterfaceNameResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Ok bool `protobuf:"varint,1,opt,name=ok,proto3" json:"ok,omitempty"` + NodeIntfName string `protobuf:"bytes,2,opt,name=node_intf_name,json=nodeIntfName,proto3" json:"node_intf_name,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GenerateNodeInterfaceNameResponse) Reset() { + *x = GenerateNodeInterfaceNameResponse{} + mi := &file_daemon_proto_meshnet_v1beta1_meshnet_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GenerateNodeInterfaceNameResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GenerateNodeInterfaceNameResponse) ProtoMessage() {} + +func (x *GenerateNodeInterfaceNameResponse) ProtoReflect() protoreflect.Message { + mi := &file_daemon_proto_meshnet_v1beta1_meshnet_proto_msgTypes[11] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GenerateNodeInterfaceNameResponse.ProtoReflect.Descriptor instead. +func (*GenerateNodeInterfaceNameResponse) Descriptor() ([]byte, []int) { + return file_daemon_proto_meshnet_v1beta1_meshnet_proto_rawDescGZIP(), []int{11} +} + +func (x *GenerateNodeInterfaceNameResponse) GetOk() bool { + if x != nil { + return x.Ok + } + return false +} + +func (x *GenerateNodeInterfaceNameResponse) GetNodeIntfName() string { + if x != nil { + return x.NodeIntfName + } + return "" +} + +var File_daemon_proto_meshnet_v1beta1_meshnet_proto protoreflect.FileDescriptor + +const file_daemon_proto_meshnet_v1beta1_meshnet_proto_rawDesc = "" + + "\n" + + "*daemon/proto/meshnet/v1beta1/meshnet.proto\x12\x0fmeshnet.v1beta1\"\xe6\x01\n" + + "\x03Pod\x12\x12\n" + + "\x04name\x18\x01 \x01(\tR\x04name\x12\x15\n" + + "\x06src_ip\x18\x02 \x01(\tR\x05srcIp\x12\x15\n" + + "\x06net_ns\x18\x03 \x01(\tR\x05netNs\x12\x17\n" + + "\akube_ns\x18\x04 \x01(\tR\x06kubeNs\x12+\n" + + "\x05links\x18\x05 \x03(\v2\x15.meshnet.v1beta1.LinkR\x05links\x12\x17\n" + + "\anode_ip\x18\x06 \x01(\tR\x06nodeIp\x12\x1b\n" + + "\tnode_intf\x18\a \x01(\tR\bnodeIntf\x12!\n" + + "\fcontainer_id\x18\b \x01(\tR\vcontainerId\"\xa3\x01\n" + + "\x04Link\x12\x19\n" + + "\bpeer_pod\x18\x01 \x01(\tR\apeerPod\x12\x1d\n" + + "\n" + + "local_intf\x18\x02 \x01(\tR\tlocalIntf\x12\x1b\n" + + "\tpeer_intf\x18\x03 \x01(\tR\bpeerIntf\x12\x19\n" + + "\blocal_ip\x18\x04 \x01(\tR\alocalIp\x12\x17\n" + + "\apeer_ip\x18\x05 \x01(\tR\x06peerIp\x12\x10\n" + + "\x03uid\x18\x06 \x01(\x03R\x03uid\"7\n" + + "\bPodQuery\x12\x12\n" + + "\x04name\x18\x01 \x01(\tR\x04name\x12\x17\n" + + "\akube_ns\x18\x02 \x01(\tR\x06kubeNs\"c\n" + + "\tSkipQuery\x12\x10\n" + + "\x03pod\x18\x01 \x01(\tR\x03pod\x12\x12\n" + + "\x04peer\x18\x02 \x01(\tR\x04peer\x12\x17\n" + + "\akube_ns\x18\x03 \x01(\tR\x06kubeNs\x12\x17\n" + + "\alink_id\x18\x04 \x01(\x03R\x06linkId\"*\n" + + "\fBoolResponse\x12\x1a\n" + + "\bresponse\x18\x01 \x01(\bR\bresponse\"\xbd\x01\n" + + "\tRemotePod\x12\x15\n" + + "\x06net_ns\x18\x01 \x01(\tR\x05netNs\x12\x1b\n" + + "\tintf_name\x18\x02 \x01(\tR\bintfName\x12\x17\n" + + "\aintf_ip\x18\x03 \x01(\tR\x06intfIp\x12\x1b\n" + + "\tpeer_vtep\x18\x04 \x01(\tR\bpeerVtep\x12\x17\n" + + "\akube_ns\x18\x05 \x01(\tR\x06kubeNs\x12\x10\n" + + "\x03vni\x18\x06 \x01(\x03R\x03vni\x12\x1b\n" + + "\tnode_intf\x18\a \x01(\tR\bnodeIntf\"\xe9\x02\n" + + "\aWireDef\x123\n" + + "\x17wire_if_id_on_peer_node\x18\x01 \x01(\x03R\x12wireIfIdOnPeerNode\x12 \n" + + "\fpeer_node_ip\x18\x02 \x01(\tR\n" + + "peerNodeIp\x12'\n" + + "\x10intf_name_in_pod\x18\x03 \x01(\tR\rintfNameInPod\x12'\n" + + "\x10local_pod_net_ns\x18\x04 \x01(\tR\rlocalPodNetNs\x12\x19\n" + + "\blink_uid\x18\x05 \x01(\x03R\alinkUid\x12$\n" + + "\x0elocal_pod_name\x18\x06 \x01(\tR\flocalPodName\x129\n" + + "\x1awire_if_name_on_local_node\x18\a \x01(\tR\x15wireIfNameOnLocalNode\x12\x17\n" + + "\atopo_ns\x18\b \x01(\tR\x06topoNs\x12 \n" + + "\flocal_pod_ip\x18\t \x01(\tR\n" + + "localPodIp\"R\n" + + "\x12WireCreateResponse\x12\x1a\n" + + "\bresponse\x18\x01 \x01(\bR\bresponse\x12 \n" + + "\fpeer_intf_id\x18\x02 \x01(\x03R\n" + + "peerIntfId\".\n" + + "\x10WireDownResponse\x12\x1a\n" + + "\bresponse\x18\x01 \x01(\bR\bresponse\"B\n" + + "\x06Packet\x12\"\n" + + "\rremot_intf_id\x18\x01 \x01(\x03R\vremotIntfId\x12\x14\n" + + "\x05frame\x18\x02 \x01(\fR\x05frame\"a\n" + + " GenerateNodeInterfaceNameRequest\x12\"\n" + + "\rpod_intf_name\x18\x01 \x01(\tR\vpodIntfName\x12\x19\n" + + "\bpod_name\x18\x02 \x01(\tR\apodName\"Y\n" + + "!GenerateNodeInterfaceNameResponse\x12\x0e\n" + + "\x02ok\x18\x01 \x01(\bR\x02ok\x12$\n" + + "\x0enode_intf_name\x18\x02 \x01(\tR\fnodeIntfName2\xc0\x05\n" + + "\x05Local\x126\n" + + "\x03Get\x12\x19.meshnet.v1beta1.PodQuery\x1a\x14.meshnet.v1beta1.Pod\x12?\n" + + "\bSetAlive\x12\x14.meshnet.v1beta1.Pod\x1a\x1d.meshnet.v1beta1.BoolResponse\x12H\n" + + "\vSkipReverse\x12\x1a.meshnet.v1beta1.SkipQuery\x1a\x1d.meshnet.v1beta1.BoolResponse\x12A\n" + + "\x04Skip\x12\x1a.meshnet.v1beta1.SkipQuery\x1a\x1d.meshnet.v1beta1.BoolResponse\x12F\n" + + "\tIsSkipped\x12\x1a.meshnet.v1beta1.SkipQuery\x1a\x1d.meshnet.v1beta1.BoolResponse\x12O\n" + + "\x0eGRPCWireExists\x12\x18.meshnet.v1beta1.WireDef\x1a#.meshnet.v1beta1.WireCreateResponse\x12K\n" + + "\x10AddGRPCWireLocal\x12\x18.meshnet.v1beta1.WireDef\x1a\x1d.meshnet.v1beta1.BoolResponse\x12F\n" + + "\vRemGRPCWire\x12\x18.meshnet.v1beta1.WireDef\x1a\x1d.meshnet.v1beta1.BoolResponse\x12\x82\x01\n" + + "\x19GenerateNodeInterfaceName\x121.meshnet.v1beta1.GenerateNodeInterfaceNameRequest\x1a2.meshnet.v1beta1.GenerateNodeInterfaceNameResponse2\xf4\x01\n" + + "\x06Remote\x12C\n" + + "\x06Update\x12\x1a.meshnet.v1beta1.RemotePod\x1a\x1d.meshnet.v1beta1.BoolResponse\x12R\n" + + "\x11AddGRPCWireRemote\x12\x18.meshnet.v1beta1.WireDef\x1a#.meshnet.v1beta1.WireCreateResponse\x12Q\n" + + "\x12GRPCWireDownRemote\x12\x18.meshnet.v1beta1.WireDef\x1a!.meshnet.v1beta1.WireDownResponse2\x9e\x01\n" + + "\fWireProtocol\x12D\n" + + "\n" + + "SendToOnce\x12\x17.meshnet.v1beta1.Packet\x1a\x1d.meshnet.v1beta1.BoolResponse\x12H\n" + + "\fSendToStream\x12\x17.meshnet.v1beta1.Packet\x1a\x1d.meshnet.v1beta1.BoolResponse(\x01B&Z$github.com/networkop/meshnet/v1beta1b\x06proto3" + +var ( + file_daemon_proto_meshnet_v1beta1_meshnet_proto_rawDescOnce sync.Once + file_daemon_proto_meshnet_v1beta1_meshnet_proto_rawDescData []byte +) + +func file_daemon_proto_meshnet_v1beta1_meshnet_proto_rawDescGZIP() []byte { + file_daemon_proto_meshnet_v1beta1_meshnet_proto_rawDescOnce.Do(func() { + file_daemon_proto_meshnet_v1beta1_meshnet_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_daemon_proto_meshnet_v1beta1_meshnet_proto_rawDesc), len(file_daemon_proto_meshnet_v1beta1_meshnet_proto_rawDesc))) + }) + return file_daemon_proto_meshnet_v1beta1_meshnet_proto_rawDescData +} + +var file_daemon_proto_meshnet_v1beta1_meshnet_proto_msgTypes = make([]protoimpl.MessageInfo, 12) +var file_daemon_proto_meshnet_v1beta1_meshnet_proto_goTypes = []any{ + (*Pod)(nil), // 0: meshnet.v1beta1.Pod + (*Link)(nil), // 1: meshnet.v1beta1.Link + (*PodQuery)(nil), // 2: meshnet.v1beta1.PodQuery + (*SkipQuery)(nil), // 3: meshnet.v1beta1.SkipQuery + (*BoolResponse)(nil), // 4: meshnet.v1beta1.BoolResponse + (*RemotePod)(nil), // 5: meshnet.v1beta1.RemotePod + (*WireDef)(nil), // 6: meshnet.v1beta1.WireDef + (*WireCreateResponse)(nil), // 7: meshnet.v1beta1.WireCreateResponse + (*WireDownResponse)(nil), // 8: meshnet.v1beta1.WireDownResponse + (*Packet)(nil), // 9: meshnet.v1beta1.Packet + (*GenerateNodeInterfaceNameRequest)(nil), // 10: meshnet.v1beta1.GenerateNodeInterfaceNameRequest + (*GenerateNodeInterfaceNameResponse)(nil), // 11: meshnet.v1beta1.GenerateNodeInterfaceNameResponse +} +var file_daemon_proto_meshnet_v1beta1_meshnet_proto_depIdxs = []int32{ + 1, // 0: meshnet.v1beta1.Pod.links:type_name -> meshnet.v1beta1.Link + 2, // 1: meshnet.v1beta1.Local.Get:input_type -> meshnet.v1beta1.PodQuery + 0, // 2: meshnet.v1beta1.Local.SetAlive:input_type -> meshnet.v1beta1.Pod + 3, // 3: meshnet.v1beta1.Local.SkipReverse:input_type -> meshnet.v1beta1.SkipQuery + 3, // 4: meshnet.v1beta1.Local.Skip:input_type -> meshnet.v1beta1.SkipQuery + 3, // 5: meshnet.v1beta1.Local.IsSkipped:input_type -> meshnet.v1beta1.SkipQuery + 6, // 6: meshnet.v1beta1.Local.GRPCWireExists:input_type -> meshnet.v1beta1.WireDef + 6, // 7: meshnet.v1beta1.Local.AddGRPCWireLocal:input_type -> meshnet.v1beta1.WireDef + 6, // 8: meshnet.v1beta1.Local.RemGRPCWire:input_type -> meshnet.v1beta1.WireDef + 10, // 9: meshnet.v1beta1.Local.GenerateNodeInterfaceName:input_type -> meshnet.v1beta1.GenerateNodeInterfaceNameRequest + 5, // 10: meshnet.v1beta1.Remote.Update:input_type -> meshnet.v1beta1.RemotePod + 6, // 11: meshnet.v1beta1.Remote.AddGRPCWireRemote:input_type -> meshnet.v1beta1.WireDef + 6, // 12: meshnet.v1beta1.Remote.GRPCWireDownRemote:input_type -> meshnet.v1beta1.WireDef + 9, // 13: meshnet.v1beta1.WireProtocol.SendToOnce:input_type -> meshnet.v1beta1.Packet + 9, // 14: meshnet.v1beta1.WireProtocol.SendToStream:input_type -> meshnet.v1beta1.Packet + 0, // 15: meshnet.v1beta1.Local.Get:output_type -> meshnet.v1beta1.Pod + 4, // 16: meshnet.v1beta1.Local.SetAlive:output_type -> meshnet.v1beta1.BoolResponse + 4, // 17: meshnet.v1beta1.Local.SkipReverse:output_type -> meshnet.v1beta1.BoolResponse + 4, // 18: meshnet.v1beta1.Local.Skip:output_type -> meshnet.v1beta1.BoolResponse + 4, // 19: meshnet.v1beta1.Local.IsSkipped:output_type -> meshnet.v1beta1.BoolResponse + 7, // 20: meshnet.v1beta1.Local.GRPCWireExists:output_type -> meshnet.v1beta1.WireCreateResponse + 4, // 21: meshnet.v1beta1.Local.AddGRPCWireLocal:output_type -> meshnet.v1beta1.BoolResponse + 4, // 22: meshnet.v1beta1.Local.RemGRPCWire:output_type -> meshnet.v1beta1.BoolResponse + 11, // 23: meshnet.v1beta1.Local.GenerateNodeInterfaceName:output_type -> meshnet.v1beta1.GenerateNodeInterfaceNameResponse + 4, // 24: meshnet.v1beta1.Remote.Update:output_type -> meshnet.v1beta1.BoolResponse + 7, // 25: meshnet.v1beta1.Remote.AddGRPCWireRemote:output_type -> meshnet.v1beta1.WireCreateResponse + 8, // 26: meshnet.v1beta1.Remote.GRPCWireDownRemote:output_type -> meshnet.v1beta1.WireDownResponse + 4, // 27: meshnet.v1beta1.WireProtocol.SendToOnce:output_type -> meshnet.v1beta1.BoolResponse + 4, // 28: meshnet.v1beta1.WireProtocol.SendToStream:output_type -> meshnet.v1beta1.BoolResponse + 15, // [15:29] is the sub-list for method output_type + 1, // [1:15] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_daemon_proto_meshnet_v1beta1_meshnet_proto_init() } +func file_daemon_proto_meshnet_v1beta1_meshnet_proto_init() { + if File_daemon_proto_meshnet_v1beta1_meshnet_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_daemon_proto_meshnet_v1beta1_meshnet_proto_rawDesc), len(file_daemon_proto_meshnet_v1beta1_meshnet_proto_rawDesc)), + NumEnums: 0, + NumMessages: 12, + NumExtensions: 0, + NumServices: 3, + }, + GoTypes: file_daemon_proto_meshnet_v1beta1_meshnet_proto_goTypes, + DependencyIndexes: file_daemon_proto_meshnet_v1beta1_meshnet_proto_depIdxs, + MessageInfos: file_daemon_proto_meshnet_v1beta1_meshnet_proto_msgTypes, + }.Build() + File_daemon_proto_meshnet_v1beta1_meshnet_proto = out.File + file_daemon_proto_meshnet_v1beta1_meshnet_proto_goTypes = nil + file_daemon_proto_meshnet_v1beta1_meshnet_proto_depIdxs = nil +} diff --git a/third_party/meshnet/daemon/proto/meshnet/v1beta1/meshnet.proto b/third_party/meshnet/daemon/proto/meshnet/v1beta1/meshnet.proto new file mode 100644 index 000000000..4c02711ed --- /dev/null +++ b/third_party/meshnet/daemon/proto/meshnet/v1beta1/meshnet.proto @@ -0,0 +1,147 @@ +syntax = "proto3"; + +package meshnet.v1beta1; + +// The Go package name is the version. +option go_package = "github.com/networkop/meshnet/v1beta1"; + +message Pod { + string name = 1; + string src_ip = 2; + string net_ns = 3; + string kube_ns = 4; + repeated Link links = 5; + string node_ip = 6; + string node_intf = 7; + string container_id = 8; +} + +message Link { + string peer_pod = 1; + string local_intf = 2; + string peer_intf = 3; + string local_ip = 4; + string peer_ip = 5; + int64 uid = 6; +} + +message PodQuery { + string name = 1; + string kube_ns = 2; +} + +message SkipQuery { + string pod = 1; + string peer = 2; + string kube_ns = 3; + int64 link_id = 4; +} + +message BoolResponse { + bool response = 1; +} + +message RemotePod { + string net_ns = 1; + string intf_name = 2; + string intf_ip = 3; + string peer_vtep = 4; + string kube_ns = 5; + int64 vni = 6; + string node_intf = 7; +} + +// The proto describes both end of a grpc-wire, the local end and the remote +// end. +message WireDef { + // The remote machine interface id, to which this wire is connected to. + // When local machine sends a packet to remote machine over grpc-wire, + // then along with the packet data, local machine also sends the interface id + // to which this packet needs to delivered in remote machine. It avoids any + // per packet interface lookup at the remote end. Packet delivery becomes + // an O(1) operation at remote end. + int64 wire_if_id_on_peer_node = 1; + + // The remote machine interface IP, to which this grpc wire is connected to. + string peer_node_ip = 2; + + // Interface name, which comes from topology definition and to be put + // inside container. + // This filed is used when grpc-wire to be created. + string intf_name_in_pod = 3; + + // Network name space of the local pod which is connected to this + // grpc-wire + string local_pod_net_ns = 4; + + // Each meshnet link has a uid. + int64 link_uid = 5; + + // Name of the local pod where this wire is getting added. + string local_pod_name = 6; + + // Every interface inside a pod is one end of a veth pair. The other end of + // the veth pair is with the local node. This is the name of veth end, which + // is with the node. Packets coming from the pod will be picked up from this + // veth end and will be transported to the remote node over grpc wire. + string wire_if_name_on_local_node = 7; + + string topo_ns = 8; + + string local_pod_ip = 9; +} + +message WireCreateResponse { + bool response = 1; + // the interface id, that was created. + int64 peer_intf_id = 2; +} + +message WireDownResponse { + bool response = 1; +} + +message Packet { + // the remote machine interface id, to which this packet should be delivered. + int64 remot_intf_id = 1; + bytes frame = 2; +} + +message GenerateNodeInterfaceNameRequest { + string pod_intf_name = 1; + string pod_name = 2; +} + +message GenerateNodeInterfaceNameResponse { + bool ok = 1; + string node_intf_name = 2; +} + +service Local { + rpc Get(PodQuery) returns (Pod); + rpc SetAlive(Pod) returns (BoolResponse); + rpc SkipReverse(SkipQuery) returns (BoolResponse); + rpc Skip(SkipQuery) returns (BoolResponse); + rpc IsSkipped(SkipQuery) returns (BoolResponse); + + rpc GRPCWireExists(WireDef) returns (WireCreateResponse); + rpc AddGRPCWireLocal(WireDef) returns (BoolResponse); + rpc RemGRPCWire(WireDef) returns (BoolResponse); + + // A node is going to hold multiple veth to connect to multiple containers. + // Each veth name must be unique with in a node. Daemon generates an ID that + // is unique in this node. + rpc GenerateNodeInterfaceName(GenerateNodeInterfaceNameRequest) + returns (GenerateNodeInterfaceNameResponse); +} + +service Remote { + rpc Update(RemotePod) returns (BoolResponse); + rpc AddGRPCWireRemote(WireDef) returns (WireCreateResponse); + rpc GRPCWireDownRemote(WireDef) returns (WireDownResponse); +} + +service WireProtocol { + rpc SendToOnce(Packet) returns (BoolResponse); + rpc SendToStream(stream Packet) returns (BoolResponse); +} diff --git a/third_party/meshnet/daemon/proto/meshnet/v1beta1/meshnet_grpc.pb.go b/third_party/meshnet/daemon/proto/meshnet/v1beta1/meshnet_grpc.pb.go new file mode 100644 index 000000000..6ca1fdc39 --- /dev/null +++ b/third_party/meshnet/daemon/proto/meshnet/v1beta1/meshnet_grpc.pb.go @@ -0,0 +1,743 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.6.0 +// - protoc (unknown) +// source: daemon/proto/meshnet/v1beta1/meshnet.proto + +package v1beta1 + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 + +const ( + Local_Get_FullMethodName = "/meshnet.v1beta1.Local/Get" + Local_SetAlive_FullMethodName = "/meshnet.v1beta1.Local/SetAlive" + Local_SkipReverse_FullMethodName = "/meshnet.v1beta1.Local/SkipReverse" + Local_Skip_FullMethodName = "/meshnet.v1beta1.Local/Skip" + Local_IsSkipped_FullMethodName = "/meshnet.v1beta1.Local/IsSkipped" + Local_GRPCWireExists_FullMethodName = "/meshnet.v1beta1.Local/GRPCWireExists" + Local_AddGRPCWireLocal_FullMethodName = "/meshnet.v1beta1.Local/AddGRPCWireLocal" + Local_RemGRPCWire_FullMethodName = "/meshnet.v1beta1.Local/RemGRPCWire" + Local_GenerateNodeInterfaceName_FullMethodName = "/meshnet.v1beta1.Local/GenerateNodeInterfaceName" +) + +// LocalClient is the client API for Local service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type LocalClient interface { + Get(ctx context.Context, in *PodQuery, opts ...grpc.CallOption) (*Pod, error) + SetAlive(ctx context.Context, in *Pod, opts ...grpc.CallOption) (*BoolResponse, error) + SkipReverse(ctx context.Context, in *SkipQuery, opts ...grpc.CallOption) (*BoolResponse, error) + Skip(ctx context.Context, in *SkipQuery, opts ...grpc.CallOption) (*BoolResponse, error) + IsSkipped(ctx context.Context, in *SkipQuery, opts ...grpc.CallOption) (*BoolResponse, error) + GRPCWireExists(ctx context.Context, in *WireDef, opts ...grpc.CallOption) (*WireCreateResponse, error) + AddGRPCWireLocal(ctx context.Context, in *WireDef, opts ...grpc.CallOption) (*BoolResponse, error) + RemGRPCWire(ctx context.Context, in *WireDef, opts ...grpc.CallOption) (*BoolResponse, error) + // A node is going to hold multiple veth to connect to multiple containers. + // Each veth name must be unique with in a node. Daemon generates an ID that + // is unique in this node. + GenerateNodeInterfaceName(ctx context.Context, in *GenerateNodeInterfaceNameRequest, opts ...grpc.CallOption) (*GenerateNodeInterfaceNameResponse, error) +} + +type localClient struct { + cc grpc.ClientConnInterface +} + +func NewLocalClient(cc grpc.ClientConnInterface) LocalClient { + return &localClient{cc} +} + +func (c *localClient) Get(ctx context.Context, in *PodQuery, opts ...grpc.CallOption) (*Pod, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(Pod) + err := c.cc.Invoke(ctx, Local_Get_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *localClient) SetAlive(ctx context.Context, in *Pod, opts ...grpc.CallOption) (*BoolResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(BoolResponse) + err := c.cc.Invoke(ctx, Local_SetAlive_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *localClient) SkipReverse(ctx context.Context, in *SkipQuery, opts ...grpc.CallOption) (*BoolResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(BoolResponse) + err := c.cc.Invoke(ctx, Local_SkipReverse_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *localClient) Skip(ctx context.Context, in *SkipQuery, opts ...grpc.CallOption) (*BoolResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(BoolResponse) + err := c.cc.Invoke(ctx, Local_Skip_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *localClient) IsSkipped(ctx context.Context, in *SkipQuery, opts ...grpc.CallOption) (*BoolResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(BoolResponse) + err := c.cc.Invoke(ctx, Local_IsSkipped_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *localClient) GRPCWireExists(ctx context.Context, in *WireDef, opts ...grpc.CallOption) (*WireCreateResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(WireCreateResponse) + err := c.cc.Invoke(ctx, Local_GRPCWireExists_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *localClient) AddGRPCWireLocal(ctx context.Context, in *WireDef, opts ...grpc.CallOption) (*BoolResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(BoolResponse) + err := c.cc.Invoke(ctx, Local_AddGRPCWireLocal_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *localClient) RemGRPCWire(ctx context.Context, in *WireDef, opts ...grpc.CallOption) (*BoolResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(BoolResponse) + err := c.cc.Invoke(ctx, Local_RemGRPCWire_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *localClient) GenerateNodeInterfaceName(ctx context.Context, in *GenerateNodeInterfaceNameRequest, opts ...grpc.CallOption) (*GenerateNodeInterfaceNameResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(GenerateNodeInterfaceNameResponse) + err := c.cc.Invoke(ctx, Local_GenerateNodeInterfaceName_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +// LocalServer is the server API for Local service. +// All implementations must embed UnimplementedLocalServer +// for forward compatibility. +type LocalServer interface { + Get(context.Context, *PodQuery) (*Pod, error) + SetAlive(context.Context, *Pod) (*BoolResponse, error) + SkipReverse(context.Context, *SkipQuery) (*BoolResponse, error) + Skip(context.Context, *SkipQuery) (*BoolResponse, error) + IsSkipped(context.Context, *SkipQuery) (*BoolResponse, error) + GRPCWireExists(context.Context, *WireDef) (*WireCreateResponse, error) + AddGRPCWireLocal(context.Context, *WireDef) (*BoolResponse, error) + RemGRPCWire(context.Context, *WireDef) (*BoolResponse, error) + // A node is going to hold multiple veth to connect to multiple containers. + // Each veth name must be unique with in a node. Daemon generates an ID that + // is unique in this node. + GenerateNodeInterfaceName(context.Context, *GenerateNodeInterfaceNameRequest) (*GenerateNodeInterfaceNameResponse, error) + mustEmbedUnimplementedLocalServer() +} + +// UnimplementedLocalServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedLocalServer struct{} + +func (UnimplementedLocalServer) Get(context.Context, *PodQuery) (*Pod, error) { + return nil, status.Error(codes.Unimplemented, "method Get not implemented") +} +func (UnimplementedLocalServer) SetAlive(context.Context, *Pod) (*BoolResponse, error) { + return nil, status.Error(codes.Unimplemented, "method SetAlive not implemented") +} +func (UnimplementedLocalServer) SkipReverse(context.Context, *SkipQuery) (*BoolResponse, error) { + return nil, status.Error(codes.Unimplemented, "method SkipReverse not implemented") +} +func (UnimplementedLocalServer) Skip(context.Context, *SkipQuery) (*BoolResponse, error) { + return nil, status.Error(codes.Unimplemented, "method Skip not implemented") +} +func (UnimplementedLocalServer) IsSkipped(context.Context, *SkipQuery) (*BoolResponse, error) { + return nil, status.Error(codes.Unimplemented, "method IsSkipped not implemented") +} +func (UnimplementedLocalServer) GRPCWireExists(context.Context, *WireDef) (*WireCreateResponse, error) { + return nil, status.Error(codes.Unimplemented, "method GRPCWireExists not implemented") +} +func (UnimplementedLocalServer) AddGRPCWireLocal(context.Context, *WireDef) (*BoolResponse, error) { + return nil, status.Error(codes.Unimplemented, "method AddGRPCWireLocal not implemented") +} +func (UnimplementedLocalServer) RemGRPCWire(context.Context, *WireDef) (*BoolResponse, error) { + return nil, status.Error(codes.Unimplemented, "method RemGRPCWire not implemented") +} +func (UnimplementedLocalServer) GenerateNodeInterfaceName(context.Context, *GenerateNodeInterfaceNameRequest) (*GenerateNodeInterfaceNameResponse, error) { + return nil, status.Error(codes.Unimplemented, "method GenerateNodeInterfaceName not implemented") +} +func (UnimplementedLocalServer) mustEmbedUnimplementedLocalServer() {} +func (UnimplementedLocalServer) testEmbeddedByValue() {} + +// UnsafeLocalServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to LocalServer will +// result in compilation errors. +type UnsafeLocalServer interface { + mustEmbedUnimplementedLocalServer() +} + +func RegisterLocalServer(s grpc.ServiceRegistrar, srv LocalServer) { + // If the following call panics, it indicates UnimplementedLocalServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } + s.RegisterService(&Local_ServiceDesc, srv) +} + +func _Local_Get_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(PodQuery) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(LocalServer).Get(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Local_Get_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(LocalServer).Get(ctx, req.(*PodQuery)) + } + return interceptor(ctx, in, info, handler) +} + +func _Local_SetAlive_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Pod) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(LocalServer).SetAlive(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Local_SetAlive_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(LocalServer).SetAlive(ctx, req.(*Pod)) + } + return interceptor(ctx, in, info, handler) +} + +func _Local_SkipReverse_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SkipQuery) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(LocalServer).SkipReverse(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Local_SkipReverse_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(LocalServer).SkipReverse(ctx, req.(*SkipQuery)) + } + return interceptor(ctx, in, info, handler) +} + +func _Local_Skip_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SkipQuery) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(LocalServer).Skip(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Local_Skip_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(LocalServer).Skip(ctx, req.(*SkipQuery)) + } + return interceptor(ctx, in, info, handler) +} + +func _Local_IsSkipped_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SkipQuery) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(LocalServer).IsSkipped(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Local_IsSkipped_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(LocalServer).IsSkipped(ctx, req.(*SkipQuery)) + } + return interceptor(ctx, in, info, handler) +} + +func _Local_GRPCWireExists_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(WireDef) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(LocalServer).GRPCWireExists(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Local_GRPCWireExists_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(LocalServer).GRPCWireExists(ctx, req.(*WireDef)) + } + return interceptor(ctx, in, info, handler) +} + +func _Local_AddGRPCWireLocal_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(WireDef) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(LocalServer).AddGRPCWireLocal(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Local_AddGRPCWireLocal_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(LocalServer).AddGRPCWireLocal(ctx, req.(*WireDef)) + } + return interceptor(ctx, in, info, handler) +} + +func _Local_RemGRPCWire_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(WireDef) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(LocalServer).RemGRPCWire(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Local_RemGRPCWire_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(LocalServer).RemGRPCWire(ctx, req.(*WireDef)) + } + return interceptor(ctx, in, info, handler) +} + +func _Local_GenerateNodeInterfaceName_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GenerateNodeInterfaceNameRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(LocalServer).GenerateNodeInterfaceName(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Local_GenerateNodeInterfaceName_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(LocalServer).GenerateNodeInterfaceName(ctx, req.(*GenerateNodeInterfaceNameRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// Local_ServiceDesc is the grpc.ServiceDesc for Local service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var Local_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "meshnet.v1beta1.Local", + HandlerType: (*LocalServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Get", + Handler: _Local_Get_Handler, + }, + { + MethodName: "SetAlive", + Handler: _Local_SetAlive_Handler, + }, + { + MethodName: "SkipReverse", + Handler: _Local_SkipReverse_Handler, + }, + { + MethodName: "Skip", + Handler: _Local_Skip_Handler, + }, + { + MethodName: "IsSkipped", + Handler: _Local_IsSkipped_Handler, + }, + { + MethodName: "GRPCWireExists", + Handler: _Local_GRPCWireExists_Handler, + }, + { + MethodName: "AddGRPCWireLocal", + Handler: _Local_AddGRPCWireLocal_Handler, + }, + { + MethodName: "RemGRPCWire", + Handler: _Local_RemGRPCWire_Handler, + }, + { + MethodName: "GenerateNodeInterfaceName", + Handler: _Local_GenerateNodeInterfaceName_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "daemon/proto/meshnet/v1beta1/meshnet.proto", +} + +const ( + Remote_Update_FullMethodName = "/meshnet.v1beta1.Remote/Update" + Remote_AddGRPCWireRemote_FullMethodName = "/meshnet.v1beta1.Remote/AddGRPCWireRemote" + Remote_GRPCWireDownRemote_FullMethodName = "/meshnet.v1beta1.Remote/GRPCWireDownRemote" +) + +// RemoteClient is the client API for Remote service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type RemoteClient interface { + Update(ctx context.Context, in *RemotePod, opts ...grpc.CallOption) (*BoolResponse, error) + AddGRPCWireRemote(ctx context.Context, in *WireDef, opts ...grpc.CallOption) (*WireCreateResponse, error) + GRPCWireDownRemote(ctx context.Context, in *WireDef, opts ...grpc.CallOption) (*WireDownResponse, error) +} + +type remoteClient struct { + cc grpc.ClientConnInterface +} + +func NewRemoteClient(cc grpc.ClientConnInterface) RemoteClient { + return &remoteClient{cc} +} + +func (c *remoteClient) Update(ctx context.Context, in *RemotePod, opts ...grpc.CallOption) (*BoolResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(BoolResponse) + err := c.cc.Invoke(ctx, Remote_Update_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *remoteClient) AddGRPCWireRemote(ctx context.Context, in *WireDef, opts ...grpc.CallOption) (*WireCreateResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(WireCreateResponse) + err := c.cc.Invoke(ctx, Remote_AddGRPCWireRemote_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *remoteClient) GRPCWireDownRemote(ctx context.Context, in *WireDef, opts ...grpc.CallOption) (*WireDownResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(WireDownResponse) + err := c.cc.Invoke(ctx, Remote_GRPCWireDownRemote_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +// RemoteServer is the server API for Remote service. +// All implementations must embed UnimplementedRemoteServer +// for forward compatibility. +type RemoteServer interface { + Update(context.Context, *RemotePod) (*BoolResponse, error) + AddGRPCWireRemote(context.Context, *WireDef) (*WireCreateResponse, error) + GRPCWireDownRemote(context.Context, *WireDef) (*WireDownResponse, error) + mustEmbedUnimplementedRemoteServer() +} + +// UnimplementedRemoteServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedRemoteServer struct{} + +func (UnimplementedRemoteServer) Update(context.Context, *RemotePod) (*BoolResponse, error) { + return nil, status.Error(codes.Unimplemented, "method Update not implemented") +} +func (UnimplementedRemoteServer) AddGRPCWireRemote(context.Context, *WireDef) (*WireCreateResponse, error) { + return nil, status.Error(codes.Unimplemented, "method AddGRPCWireRemote not implemented") +} +func (UnimplementedRemoteServer) GRPCWireDownRemote(context.Context, *WireDef) (*WireDownResponse, error) { + return nil, status.Error(codes.Unimplemented, "method GRPCWireDownRemote not implemented") +} +func (UnimplementedRemoteServer) mustEmbedUnimplementedRemoteServer() {} +func (UnimplementedRemoteServer) testEmbeddedByValue() {} + +// UnsafeRemoteServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to RemoteServer will +// result in compilation errors. +type UnsafeRemoteServer interface { + mustEmbedUnimplementedRemoteServer() +} + +func RegisterRemoteServer(s grpc.ServiceRegistrar, srv RemoteServer) { + // If the following call panics, it indicates UnimplementedRemoteServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } + s.RegisterService(&Remote_ServiceDesc, srv) +} + +func _Remote_Update_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RemotePod) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(RemoteServer).Update(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Remote_Update_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(RemoteServer).Update(ctx, req.(*RemotePod)) + } + return interceptor(ctx, in, info, handler) +} + +func _Remote_AddGRPCWireRemote_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(WireDef) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(RemoteServer).AddGRPCWireRemote(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Remote_AddGRPCWireRemote_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(RemoteServer).AddGRPCWireRemote(ctx, req.(*WireDef)) + } + return interceptor(ctx, in, info, handler) +} + +func _Remote_GRPCWireDownRemote_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(WireDef) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(RemoteServer).GRPCWireDownRemote(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Remote_GRPCWireDownRemote_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(RemoteServer).GRPCWireDownRemote(ctx, req.(*WireDef)) + } + return interceptor(ctx, in, info, handler) +} + +// Remote_ServiceDesc is the grpc.ServiceDesc for Remote service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var Remote_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "meshnet.v1beta1.Remote", + HandlerType: (*RemoteServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Update", + Handler: _Remote_Update_Handler, + }, + { + MethodName: "AddGRPCWireRemote", + Handler: _Remote_AddGRPCWireRemote_Handler, + }, + { + MethodName: "GRPCWireDownRemote", + Handler: _Remote_GRPCWireDownRemote_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "daemon/proto/meshnet/v1beta1/meshnet.proto", +} + +const ( + WireProtocol_SendToOnce_FullMethodName = "/meshnet.v1beta1.WireProtocol/SendToOnce" + WireProtocol_SendToStream_FullMethodName = "/meshnet.v1beta1.WireProtocol/SendToStream" +) + +// WireProtocolClient is the client API for WireProtocol service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type WireProtocolClient interface { + SendToOnce(ctx context.Context, in *Packet, opts ...grpc.CallOption) (*BoolResponse, error) + SendToStream(ctx context.Context, opts ...grpc.CallOption) (grpc.ClientStreamingClient[Packet, BoolResponse], error) +} + +type wireProtocolClient struct { + cc grpc.ClientConnInterface +} + +func NewWireProtocolClient(cc grpc.ClientConnInterface) WireProtocolClient { + return &wireProtocolClient{cc} +} + +func (c *wireProtocolClient) SendToOnce(ctx context.Context, in *Packet, opts ...grpc.CallOption) (*BoolResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(BoolResponse) + err := c.cc.Invoke(ctx, WireProtocol_SendToOnce_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *wireProtocolClient) SendToStream(ctx context.Context, opts ...grpc.CallOption) (grpc.ClientStreamingClient[Packet, BoolResponse], error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + stream, err := c.cc.NewStream(ctx, &WireProtocol_ServiceDesc.Streams[0], WireProtocol_SendToStream_FullMethodName, cOpts...) + if err != nil { + return nil, err + } + x := &grpc.GenericClientStream[Packet, BoolResponse]{ClientStream: stream} + return x, nil +} + +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type WireProtocol_SendToStreamClient = grpc.ClientStreamingClient[Packet, BoolResponse] + +// WireProtocolServer is the server API for WireProtocol service. +// All implementations must embed UnimplementedWireProtocolServer +// for forward compatibility. +type WireProtocolServer interface { + SendToOnce(context.Context, *Packet) (*BoolResponse, error) + SendToStream(grpc.ClientStreamingServer[Packet, BoolResponse]) error + mustEmbedUnimplementedWireProtocolServer() +} + +// UnimplementedWireProtocolServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedWireProtocolServer struct{} + +func (UnimplementedWireProtocolServer) SendToOnce(context.Context, *Packet) (*BoolResponse, error) { + return nil, status.Error(codes.Unimplemented, "method SendToOnce not implemented") +} +func (UnimplementedWireProtocolServer) SendToStream(grpc.ClientStreamingServer[Packet, BoolResponse]) error { + return status.Error(codes.Unimplemented, "method SendToStream not implemented") +} +func (UnimplementedWireProtocolServer) mustEmbedUnimplementedWireProtocolServer() {} +func (UnimplementedWireProtocolServer) testEmbeddedByValue() {} + +// UnsafeWireProtocolServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to WireProtocolServer will +// result in compilation errors. +type UnsafeWireProtocolServer interface { + mustEmbedUnimplementedWireProtocolServer() +} + +func RegisterWireProtocolServer(s grpc.ServiceRegistrar, srv WireProtocolServer) { + // If the following call panics, it indicates UnimplementedWireProtocolServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } + s.RegisterService(&WireProtocol_ServiceDesc, srv) +} + +func _WireProtocol_SendToOnce_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Packet) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(WireProtocolServer).SendToOnce(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: WireProtocol_SendToOnce_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(WireProtocolServer).SendToOnce(ctx, req.(*Packet)) + } + return interceptor(ctx, in, info, handler) +} + +func _WireProtocol_SendToStream_Handler(srv interface{}, stream grpc.ServerStream) error { + return srv.(WireProtocolServer).SendToStream(&grpc.GenericServerStream[Packet, BoolResponse]{ServerStream: stream}) +} + +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type WireProtocol_SendToStreamServer = grpc.ClientStreamingServer[Packet, BoolResponse] + +// WireProtocol_ServiceDesc is the grpc.ServiceDesc for WireProtocol service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var WireProtocol_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "meshnet.v1beta1.WireProtocol", + HandlerType: (*WireProtocolServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "SendToOnce", + Handler: _WireProtocol_SendToOnce_Handler, + }, + }, + Streams: []grpc.StreamDesc{ + { + StreamName: "SendToStream", + Handler: _WireProtocol_SendToStream_Handler, + ClientStreams: true, + }, + }, + Metadata: "daemon/proto/meshnet/v1beta1/meshnet.proto", +} diff --git a/third_party/meshnet/daemon/vxlan/vxlan.go b/third_party/meshnet/daemon/vxlan/vxlan.go new file mode 100644 index 000000000..715d92c08 --- /dev/null +++ b/third_party/meshnet/daemon/vxlan/vxlan.go @@ -0,0 +1,162 @@ +package vxlan + +import ( + "fmt" + "net" + "strconv" + "strings" + + "github.com/containernetworking/plugins/pkg/ns" + "github.com/redhat-nfvpe/koko/api" + log "github.com/sirupsen/logrus" + "github.com/vishvananda/netlink" + + mpb "github.com/openconfig/kne/third_party/meshnet/daemon/proto/meshnet/v1beta1" +) + +var vxLanOvrlyLogger *log.Entry = nil + +func InitLogger() { + vxLanOvrlyLogger = log.WithFields(log.Fields{"daemon": "meshnetd", "overlay": "vxLAN"}) +} + +// CreateOrUpdate creates or updates the vxlan on the node. +func CreateOrUpdate(v *mpb.RemotePod) error { + var srcIntf string + var err error + srcIntf = v.NodeIntf + if srcIntf == "" { + /// Looking up default interface + _, srcIntf, err = getSource() + if err != nil { + return err + } + } + + // Creating koko Veth struct + veth := api.VEth{ + NsName: v.NetNs, + LinkName: v.IntfName, + } + + // Link IP is optional, only set it when it's provided + if v.IntfIp != "" { + ipAddr, ipSubnet, err := net.ParseCIDR(v.IntfIp) + if err != nil { + return fmt.Errorf(" MESHNETD: Error parsing CIDR %s: %s", v.IntfIp, err) + } + veth.IPAddr = []net.IPNet{{ + IP: ipAddr, + Mask: ipSubnet.Mask, + }} + } + vxLanOvrlyLogger.Infof("Created koko Veth struct %+v", veth) + + // Creating koko vxlan struct + vxlan := api.VxLan{ + ParentIF: srcIntf, + IPAddr: net.ParseIP(v.PeerVtep), + ID: int(v.Vni), + } + vxLanOvrlyLogger.Infof("Created koko vxlan struct %+v", vxlan) + + // Try to read interface attributes from netlink + link := getLinkFromNS(veth.NsName, veth.LinkName) + vxLanOvrlyLogger.Infof("Retrieved %s link from %s Netns: %+v", veth.LinkName, veth.NsName, link) + + // Check if interface already exists + vxlanLink, ok := link.(*netlink.Vxlan) + vxLanOvrlyLogger.Infof("Is link %+v a VXLAN?: %s", vxlanLink, strconv.FormatBool(ok)) + if ok { // the link we've found is a vxlan link + + if !(vxlanLink.VxlanId == vxlan.ID && vxlanLink.Group.Equal(vxlan.IPAddr)) { // If Vxlan attrs are different + + // We remove the existing link and add a new one + vxLanOvrlyLogger.Infof("Vxlan attrs are different: %d!=%d or %v!=%v", vxlanLink.VxlanId, vxlan.ID, vxlanLink.Group, vxlan.IPAddr) + if err = veth.RemoveVethLink(); err != nil { + return fmt.Errorf(" MESHNETD: Error when removing an old Vxlan interface with koko: %s", err) + } + + if err = api.MakeVxLan(veth, vxlan); err != nil { + if strings.Contains(err.Error(), "file exists") { + vxLanOvrlyLogger.Infof(" MESHNETD: Error when creating a Vxlan interface with koko, file exists") + } else { + return fmt.Errorf(" MESHNETD: Error when re-creating a Vxlan interface with koko: %s", err) + } + } + } // If Vxlan attrs are the same, do nothing + + } else { // the link we've found isn't a vxlan or doesn't exist + + vxLanOvrlyLogger.Infof("Link %+v we've found isn't a vxlan or doesn't exist", link) + // If link exists but wasn't matched as vxlan, we need to delete it + if link != nil { + vxLanOvrlyLogger.Infof("Attempting to remove link %+v", veth) + if err = veth.RemoveVethLink(); err != nil { + return fmt.Errorf(" MESHNETD: Error when removing an old non-Vxlan interface with koko: %s", err) + } + } + + // Then we simply create a new one + vxLanOvrlyLogger.Infof("Creating a VXLAN link: %v; inside the pod: %v", vxlan, veth) + if err = api.MakeVxLan(veth, vxlan); err != nil { + if strings.Contains(err.Error(), "file exists") { + vxLanOvrlyLogger.Warnf(" MESHNETD: Error when creating a Vxlan interface with koko, file exists") + } else { + vxLanOvrlyLogger.Errorf(" MESHNETD: Error when creating a new Vxlan interface with koko: %s", err) + return err + } + } + } + + return nil +} + +// getLinkFromNS retrieves netlink.Link from NetNS +func getLinkFromNS(nsName string, linkName string) netlink.Link { + // If namespace doesn't exist, do nothing and return empty result + vethNs, err := ns.GetNS(nsName) + if err != nil { + return nil + } + defer vethNs.Close() + // We can ignore the error returned here as we will create that interface instead + var result netlink.Link + err = vethNs.Do(func(_ ns.NetNS) error { + var err error + result, err = netlink.LinkByName(linkName) + return err + }) + if err != nil { + vxLanOvrlyLogger.Warnf("failed to get link: %s", linkName) + } + + return result +} + +// Uses netlink to query the IP and LinkName of the interface with default route +func getSource() (string, string, error) { + // Looking up a default route to get the intf and IP for vxlan + r, err := netlink.RouteGet(net.IPv4(1, 1, 1, 1)) + if (err != nil) && len(r) < 1 { + return "", "", fmt.Errorf(" MESHNETD: Error getting default route: %s\n%+v", err, r) + } + srcIP := r[0].Src.String() + + link, err := netlink.LinkByIndex(r[0].LinkIndex) + if err != nil { + return "", "", fmt.Errorf(" MESHNETD: Error looking up link by its index: %s", err) + } + srcIntf := link.Attrs().Name + return srcIP, srcIntf, nil +} + +func vxlanDifferent(l1 *netlink.Vxlan, l2 api.VxLan) bool { + if l1.VxlanId != l2.ID { + return false + } + if !l1.Group.Equal(l2.IPAddr) { + return false + } + return true +} diff --git a/third_party/meshnet/daemon/vxlan/vxlan_test.go b/third_party/meshnet/daemon/vxlan/vxlan_test.go new file mode 100644 index 000000000..1c504cb13 --- /dev/null +++ b/third_party/meshnet/daemon/vxlan/vxlan_test.go @@ -0,0 +1,57 @@ +package vxlan + +import ( + "net" + "testing" + + "github.com/redhat-nfvpe/koko/api" + "github.com/vishvananda/netlink" +) + +func TestVxlan(t *testing.T) { + tests := []struct { + expected api.VxLan + found *netlink.Vxlan + same bool + }{ + { + expected: api.VxLan{ + ID: 5001, + IPAddr: net.IPv4(1, 1, 1, 1), + }, + found: &netlink.Vxlan{ + VxlanId: 5001, + Group: net.IPv4(1, 1, 1, 1), + }, + same: true, + }, + { + expected: api.VxLan{ + ID: 5001, + IPAddr: net.IPv4(1, 1, 1, 1), + }, + found: &netlink.Vxlan{ + VxlanId: 5002, + Group: net.IPv4(1, 1, 1, 1), + }, + same: false, + }, + { + expected: api.VxLan{ + ID: 5001, + IPAddr: net.IPv4(1, 1, 1, 1), + }, + found: &netlink.Vxlan{ + VxlanId: 5001, + Group: net.IPv4(2, 2, 2, 2), + }, + same: false, + }, + } + for i, tt := range tests { + result := vxlanDifferent(tt.found, tt.expected) + if result != tt.same { + t.Errorf("#%d test failed", i) + } + } +} diff --git a/third_party/meshnet/docker/Dockerfile b/third_party/meshnet/docker/Dockerfile new file mode 100644 index 000000000..b6431f1b3 --- /dev/null +++ b/third_party/meshnet/docker/Dockerfile @@ -0,0 +1,67 @@ +# GRPC generation +FROM golang:1.25.11 AS proto_base + +WORKDIR /src + +RUN go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.36.11 && \ + go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.5.1 && \ + go install github.com/bufbuild/buf/cmd/buf@v1.13.1 + +COPY daemon/proto . +COPY buf.gen.yaml . +COPY buf.yaml . + +RUN buf lint && buf generate -v + +#---------------------------------------------- Stage 2: Build Base : Build meshnet binaries (Plugin & daemon) - --------------------------------- +# Building the binaries +FROM golang:1.25.11 AS build_base + + +WORKDIR /go/src/github.com/openconfig/kne/third_party/meshnet +ENV CGO_ENABLED=0 +COPY go.mod . +COPY go.sum . +# hadolint ignore=DL3008 +RUN go mod download && \ + apt-get update -y && \ + apt-get install -y --no-install-recommends \ + libpcap-dev \ + libsystemd-dev && \ + rm -rf /var/lib/apt/lists/* + +FROM --platform=${BUILDPLATFORM:-linux/amd64} build_base AS build + +ARG LDFLAGS +ARG TARGETOS +ARG TARGETARCH + +COPY daemon/ daemon/ +COPY api/ api/ +COPY plugin/ plugin/ +COPY utils/ utils/ +COPY --from=proto_base /src/ . + + +RUN GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -o meshnet plugin/meshnet.go plugin/grpcwires-plugin.go && \ + GOOS=${TARGETOS} CGO_ENABLED=1 GOARCH=${TARGETARCH} go build -ldflags="-s -w" -o meshnetd daemon/main.go + + +#----------------------------------------------------- Final Container --------------------------------- +# checkov:skip=CKV_DOCKER_2: CNI daemon image does not require healthcheck +# checkov:skip=CKV_DOCKER_3: CNI daemon must run as root to configure host network namespaces +FROM debian:13-slim +# hadolint ignore=DL3008 +RUN apt-get update && \ + apt-get install -y --no-install-recommends \ + jq \ + libpcap-dev && \ + rm -rf /var/lib/apt/lists/* + +COPY --from=build /go/src/github.com/openconfig/kne/third_party/meshnet/meshnet / +COPY --from=build /go/src/github.com/openconfig/kne/third_party/meshnet/meshnetd / +COPY docker/new-entrypoint.sh /entrypoint.sh +COPY LICENSE / +RUN chmod +x ./entrypoint.sh /meshnetd + +ENTRYPOINT ["./entrypoint.sh"] diff --git a/third_party/meshnet/docker/entrypoint.sh b/third_party/meshnet/docker/entrypoint.sh new file mode 100755 index 000000000..46a4209f3 --- /dev/null +++ b/third_party/meshnet/docker/entrypoint.sh @@ -0,0 +1,29 @@ +#!/bin/sh + +echo "Distributing files" +if [ -d "/opt/cni/bin/" ] && [ -f "./meshnet" ]; then + cp ./meshnet /opt/cni/bin/ +fi + +if [ -d "/etc/cni/net.d/" ] && [ -f "./meshnet.conf" ]; then + cp ./meshnet.conf /etc/cni/net.d/ +fi + +if [ ! -f /etc/cni/net.d/00-meshnet.conf ]; then + echo "Merging existing CNI configuration with meshnet" + existing=$(find /etc/cni/net.d/ -maxdepth 1 -type f | grep -E "flannel|weave|bridge|calico|contiv|cilium|cni|kindnet" | head -n1) + has_plugin_section=$(jq 'has("plugins")' "$existing") + if [ "$has_plugin_section" = true ]; then + jq -s '.[1].delegate = (.[0].plugins[0])' "$existing" /etc/cni/net.d/meshnet.conf | jq '.[1]' >/etc/cni/net.d/00-meshnet.conf + else + jq -s '.[1].delegate = (.[0])' "$existing" /etc/cni/net.d/meshnet.conf | jq '.[1]' >/etc/cni/net.d/00-meshnet.conf + fi +else + echo "Reusing existing CNI config" +fi + +echo 'Making sure the name is set for the master plugin' +jq '.delegate.name = "masterplugin"' /etc/cni/net.d/00-meshnet.conf >/tmp/cni.conf && mv /tmp/cni.conf /etc/cni/net.d/00-meshnet.conf + +echo "Starting meshnetd daemon" +/meshnetd diff --git a/third_party/meshnet/docker/new-entrypoint.sh b/third_party/meshnet/docker/new-entrypoint.sh new file mode 100755 index 000000000..7b504d21c --- /dev/null +++ b/third_party/meshnet/docker/new-entrypoint.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +echo "Distributing files" +if [ -d "/opt/cni/bin/" ] && [ -f "./meshnet" ]; then + cp ./meshnet /opt/cni/bin/ +fi + +if [ -d "/etc/cni/net.d/" ] && [ -f "./meshnet.conf" ]; then + cp ./meshnet.conf /etc/cni/net.d/ +fi + +echo "Starting meshnetd daemon" +/meshnetd diff --git a/third_party/meshnet/docs/implementation.md b/third_party/meshnet/docs/implementation.md new file mode 100644 index 000000000..7a75e883b --- /dev/null +++ b/third_party/meshnet/docs/implementation.md @@ -0,0 +1,94 @@ +# Motivation + +In K8S usually pods across nodes are connected using VxLAN/IPnIP/GRE overlay. +Our objective is to connect these pods using grpc p2p overlay. One of the +advantages is that there is **no reduction of MTU**. For example adding VXLAN +will reduce virtual interface MTU by 20 bytes. With grpc-wire interface MTU +remains 1500 (or can be made higher - not tried yet). Other advantages could be +to add telemetry, generate wire up/down signal, etc. + +## Introduction + +Let's take an example CRD as given in the picture below ![CRD](./pics/crd.png) + +When this crd is deployed with grpc-wire CNI, then the nodes across the K8S pods +will interact over a grpc channel. Each grpc channel will provide a point to +point connection between pods. For example `POD-1:e1 <---> POD-2:e1` will be a +dedicated grpc channel for POD1 & POD-2 communication via their `e1` virtual +interfaces. + +![DEPLOYMENT](./pics/deployment.png) + +Each node in the cluster runs a CNI daemon set. The daemon in the node is +responsible to maintain the GRPC channel and send/receive packets over it. In a +node, each pod is connected with the node daemon using a veth-pair. One end of +the veth pair is inside the pod and the other end is with the daemon. Pods +always writes to or reads from the the interface it has got. Whereas the daemon +is always listening on the other end of the veth pair. As soon as the pod writes +a packet, the daemon gets it and transports it over the grpc channel to the +remote node. Daemon in the remote node delivers it to the destination pod. + +## Details + +When a pod wants to send a packet to a remote pod, it writes it on the interface +inside the pod. Pod is completely unaware of the grpc overlay being used. The +interface that a pod sees is one end of a veth pair. The other end of the veth +pair is with meshnet daemon. The meshnet daemon receives any packet that a pod +wants to send. Meshnet daemon uses the following proto to deliver the packet to +the destination pod (on a different node). + +```go +message Packet { + int64 remot_intf_id = 1; //remote machine interface id, to which packets to be delivered. + bytes frame = 2; //raw bytes +} +``` + +The packet itself carries the id of the destination interface. Destination +interface is an interface in the remote node and the meshnet daemon in the +remote machine has access to this interface. This destination interface is one +end of the veth pair and the other end of this veth pair is within the +destination pod. This is ensured during the wire creation time. So when the +packet reaches the destination daemon, the demon simply writes the received +packet on the interface carried by the packet itself. Since it's a veth pair the +packet goes to the destination pod which is connected at the other end. It +avoids any per packet lookup and packet delivery becomes an O(1) operation. + +Overall Tx/Rx mechanism is depicted in the picture below. + +![DETAIL](./pics/detail.png) + +As shown it the picture above :- + +- When the pod-1 in node-1 has to send a BGP packet, it just writes it on the + pod interface eth1. (_point C1 in the picture_) +- Other end of this veth-pair (`eth1-out1`) is with the daemon, where a thread + is always waiting on `go channel` to read packet. (_point 1 in the picture_) +- Since the wire creation time, the daemon in node-1 knows, any packet + received on `eth1-out1` has to be delivered to the daemon running on node-2 + and the destination interface in node-2 is `eth2-out1` who’s id is `X2`. +- The thread in node-1 (_point 1 in the picture_) makes a GRPC service call + `SendToOnce` to deliver the packet to the service handler in node-2 (_point + 2 in the picture_). During the creation time node-1 and node-2 has + established the GRPC connection. +- `SendToOnce` service in node-2 receives the packet and it also receives the + desired destination interface id along with it. In our case it’s `X2`. It + simply writes the packet on `X2`. Packet reaches the destination pod, which + is at the other end of the veth pair `eth2-out1 (X2) <---> eth1` in node-2. + In this case the destination pod is pod-2 and its interface `eth2`. +- In reverse direction + - When pod-2 in node-2 wants to send a packet to pod-1 in node-1, the same + process continues. Pod-2 writes it it’s interface `eth2`. (_point C2 in + the picture_). The name `eth2` is assigned by meshnet CRD. + - The packet goes to the other end of the veth pair `eth2-out1` + - The daemon in node-2 receive the packet (_point 3 in the picture_) and + makes a GRPC service call `SendToOnce` to deliver the packet to the + service handler in node-1 (_point 4 in the picture_) + - Since the wire creation time, the daemon in node-2 knows, any packet + received on `eth2-out1` has to be delivered to the daemon running on + node-1 and the destination interface in node-1 is `eth1-out1` who’s id + is `X1`. + - `SendToOnce` service in node-1 receives the packet and the desired + destination interface id along with it. In our case it’s `X1`. It simply + writes the packet on `X1`. Packet reaches the destination pod, which is + at the other end of the veth pair `eth1-out1 (X1) <---> eth1` in node-1. diff --git a/third_party/meshnet/docs/meshnet-gwire-recon.pdf b/third_party/meshnet/docs/meshnet-gwire-recon.pdf new file mode 100644 index 000000000..212695286 Binary files /dev/null and b/third_party/meshnet/docs/meshnet-gwire-recon.pdf differ diff --git a/third_party/meshnet/docs/pics/crd.png b/third_party/meshnet/docs/pics/crd.png new file mode 100644 index 000000000..0e72d7d99 Binary files /dev/null and b/third_party/meshnet/docs/pics/crd.png differ diff --git a/third_party/meshnet/docs/pics/deployment.png b/third_party/meshnet/docs/pics/deployment.png new file mode 100644 index 000000000..bfee5b344 Binary files /dev/null and b/third_party/meshnet/docs/pics/deployment.png differ diff --git a/third_party/meshnet/docs/pics/detail.png b/third_party/meshnet/docs/pics/detail.png new file mode 100644 index 000000000..b181f59d6 Binary files /dev/null and b/third_party/meshnet/docs/pics/detail.png differ diff --git a/third_party/meshnet/go.mod b/third_party/meshnet/go.mod new file mode 100644 index 000000000..ecaf40671 --- /dev/null +++ b/third_party/meshnet/go.mod @@ -0,0 +1,76 @@ +module github.com/openconfig/kne/third_party/meshnet + +go 1.25.0 + +require ( + github.com/containernetworking/cni v0.8.1 + github.com/containernetworking/plugins v0.9.1 + github.com/davecgh/go-spew v1.1.1 + github.com/google/go-cmp v0.7.0 + github.com/google/gopacket v1.1.19 + github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 + github.com/h-fam/errdiff v1.0.2 + github.com/openconfig/gnmi v0.0.0-20220920173703-480bf53a74d2 + github.com/redhat-nfvpe/koko v0.0.0-20210415181932-a18aa44814ea + github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8 + github.com/sirupsen/logrus v1.8.1 + github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852 + google.golang.org/grpc v1.81.1 + google.golang.org/protobuf v1.36.11 + k8s.io/apimachinery v0.26.1 + k8s.io/client-go v0.26.1 + sigs.k8s.io/controller-runtime v0.14.5 +) + +require ( + github.com/Microsoft/go-winio v0.4.11 // indirect + github.com/docker/distribution v2.8.0+incompatible // indirect + github.com/docker/docker v0.0.0-20181024220401-bc4c1c238b55 // indirect + github.com/docker/go-connections v0.0.0-20180228141015-7395e3f8aa16 // indirect + github.com/docker/go-units v0.4.0 // indirect + github.com/emicklei/go-restful/v3 v3.9.0 // indirect + github.com/evanphx/json-patch v4.12.0+incompatible // indirect + github.com/go-logr/logr v1.4.3 // indirect + github.com/go-openapi/jsonpointer v0.19.5 // indirect + github.com/go-openapi/jsonreference v0.20.0 // indirect + github.com/go-openapi/swag v0.19.14 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/google/gnostic v0.5.7-v3refs // indirect + github.com/google/gofuzz v1.1.0 // indirect + github.com/imdario/mergo v0.3.6 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/mailru/easyjson v0.7.6 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/opencontainers/go-digest v0.0.0-20170607195333-279bed98673d // indirect + github.com/opencontainers/image-spec v0.0.0-20171030174740-d60099175f88 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae // indirect + golang.org/x/net v0.51.0 // indirect + golang.org/x/oauth2 v0.36.0 // indirect + golang.org/x/sys v0.42.0 // indirect + golang.org/x/term v0.40.0 // indirect + golang.org/x/text v0.34.0 // indirect + golang.org/x/time v0.3.0 // indirect + google.golang.org/genproto v0.0.0-20220714211235-042d03aeabc9 // indirect + gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect + gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + k8s.io/api v0.26.1 // indirect + k8s.io/cri-api v0.0.0-20191204094248-a6f63f369f6d // indirect + k8s.io/klog v1.0.0 // indirect + k8s.io/klog/v2 v2.80.1 // indirect + k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 // indirect + k8s.io/kubernetes v1.14.6 // indirect + k8s.io/utils v0.0.0-20221128185143-99ec85e7a448 // indirect + sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect + sigs.k8s.io/yaml v1.3.0 // indirect +) + +replace github.com/openconfig/kne/third_party/meshnet => ./ diff --git a/third_party/meshnet/go.sum b/third_party/meshnet/go.sum new file mode 100644 index 000000000..fb8d198dd --- /dev/null +++ b/third_party/meshnet/go.sum @@ -0,0 +1,437 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8= +github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/MakeNowJust/heredoc v0.0.0-20171113091838-e9091a26100e/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E= +github.com/Microsoft/go-winio v0.0.0-20180823222421-97e4973ce50b/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= +github.com/Microsoft/go-winio v0.4.11 h1:zoIOcVf0xPN1tnMVbTtEdI+P8OofVk3NObnwOQ6nK2Q= +github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= +github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= +github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= +github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= +github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= +github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0= +github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/containernetworking/cni v0.8.1 h1:7zpDnQ3T3s4ucOuJ/ZCLrYBxzkg0AELFfII3Epo9TmI= +github.com/containernetworking/cni v0.8.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= +github.com/containernetworking/plugins v0.0.0-20180803153142-19f2f28178aa/go.mod h1:dagHaAhNjXjT9QYOklkKJDGaQPTg4pf//FrUcJeb7FU= +github.com/containernetworking/plugins v0.9.1 h1:FD1tADPls2EEi3flPc2OegIY1M9pUa9r2Quag7HMLV8= +github.com/containernetworking/plugins v0.9.1/go.mod h1:xP/idU2ldlzN6m4p5LmGiwRDjeJr6FLK6vuiUwoH7P8= +github.com/coreos/go-iptables v0.5.0/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= +github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ= +github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s= +github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8= +github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4/go.mod h1:bMl4RjIciD2oAxI7DmWRx6gbeqrkoLqv3MV0vzNad+I= +github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +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/docker/distribution v0.0.0-20181024170156-93e082742a00/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/distribution v2.8.0+incompatible h1:l9EaZDICImO1ngI+uTifW+ZYvvz7fKISBAKpg+MbWbY= +github.com/docker/distribution v2.8.0+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker v0.0.0-20181024220401-bc4c1c238b55 h1:t6HHZjjOdUIP8qiTRgxXGBCUpZ/DulDWo3PgtPjfBA4= +github.com/docker/docker v0.0.0-20181024220401-bc4c1c238b55/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.0.0-20180228141015-7395e3f8aa16 h1:4uSgK5h2LeFthjRpG7VUcNsG8cnMBSyUka+aHztsu0k= +github.com/docker/go-connections v0.0.0-20180228141015-7395e3f8aa16/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-units v0.0.0-20180212134657-47565b4f722f/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= +github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= +github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= +github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= +github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE= +github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84= +github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww= +github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= +github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-logr/zapr v1.2.3 h1:a9vnzlIBPQBBkeaR9IuMUfmVOrQlkoC4YfPoFkX3T7A= +github.com/go-logr/zapr v1.2.3/go.mod h1:eIauM6P8qSvTw5o2ez6UEAfGjQKrxQTl5EoK+Qa2oG4= +github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= +github.com/go-openapi/jsonreference v0.20.0 h1:MYlu0sBgChmCfJxxUKZ8g1cPWFOB37YSZqewK7OKeyA= +github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo= +github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= +github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.14 h1:gm3vOOXfiuw5i9p5N9xJvfjvuofpyvLA9Wr6QfK5Fng= +github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= +github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54= +github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= +github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= +github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= +github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw= +github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw= +github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= +github.com/h-fam/errdiff v1.0.2 h1:rPsW4ob2fMOIulwTEoZXaaUIuud7XUudw5SLKTZj3Ss= +github.com/h-fam/errdiff v1.0.2/go.mod h1:FOzgnHXSEE3rRvmGXgmiqWl+H3lwLywYm9CSXqXrSTg= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28= +github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= +github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mattn/go-getopt v0.0.0-20150316012638-824dc755f216/go.mod h1:2uCwQeFuJy1M6CD1U13P4er3sZ8BgifMtkgSIhV1vgg= +github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.2/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1 h1:mFwc4LvZ0xpSvDZ3E+k8Yte0hLOMxXUlP+yXtJqkYfQ= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo/v2 v2.6.0 h1:9t9b9vRUbFq3C4qKFCGkVuq/fIHji802N1nrtkh1mNc= +github.com/onsi/ginkgo/v2 v2.6.0/go.mod h1:63DOGlLAH8+REH8jUGdL3YpCpu7JODesutUjdENfUAc= +github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= +github.com/onsi/gomega v1.24.1 h1:KORJXNNTzJXzu4ScJWssJfJMnJ+2QJqhoQSRwNlze9E= +github.com/onsi/gomega v1.24.1/go.mod h1:3AOiACssS3/MajrniINInwbfOOtfZvplPzuRSmvt1jM= +github.com/openconfig/gnmi v0.0.0-20220920173703-480bf53a74d2 h1:3YLlQFLDsFTvruKoYBbuYqhCgsXMtNewSrLjNXcF/Sg= +github.com/openconfig/gnmi v0.0.0-20220920173703-480bf53a74d2/go.mod h1:Y9os75GmSkhHw2wX8sMsxfI7qRGAEcDh8NTa5a8vj6E= +github.com/opencontainers/go-digest v0.0.0-20170607195333-279bed98673d h1:KdbiNxiNlvqqdRGoWqPq40QvJVnkx0i6boW43dg6Zig= +github.com/opencontainers/go-digest v0.0.0-20170607195333-279bed98673d/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/image-spec v0.0.0-20171030174740-d60099175f88 h1:wHUCmBJYWVPLW+A+82bEi7lJpaUFvUUrJGpBYUvHTYE= +github.com/opencontainers/image-spec v0.0.0-20171030174740-d60099175f88/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/pkg/errors v0.0.0-20160929014801-645ef00459ed/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +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/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/redhat-nfvpe/koko v0.0.0-20210415181932-a18aa44814ea h1:TtTNq6CySsXrbFXgrztJASGdcrRlDlIKt07yqGK//fA= +github.com/redhat-nfvpe/koko v0.0.0-20210415181932-a18aa44814ea/go.mod h1:PE2c8qUUdHpM2GPw9ZAhKMD6HV/QJqTkPTbmsn/NV6c= +github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8 h1:2c1EFnZHIPCW8qKWgHMH/fX2PkSabFc5mrVzfUNdg5U= +github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= +github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= +github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/vishvananda/netlink v0.0.0-20180316044622-a2ad57a690f3/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= +github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852 h1:cPXZWzzG0NllBLdjWoD1nDfaqu98YMv+OneaKc8sPOA= +github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= +github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI= +github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae h1:4hwBBUfQCFe3Cym0ZtKyq7L16eZUtYKs+BaHDN6mAns= +github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I= +go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0= +go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM= +go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY= +go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg= +go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg= +go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw= +go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A= +go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A= +go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= +go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo= +golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= +golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201117170446-d9b008d0a637/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= +golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg= +golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM= +golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk= +golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= +golang.org/x/time v0.0.0-20190921001708-c4c64cad1fd0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= +gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20220714211235-042d03aeabc9 h1:zfXhTgBfGlIh3jMXN06W8qbhFGsh6MJNJiYEuhTddOI= +google.golang.org/genproto v0.0.0-20220714211235-042d03aeabc9/go.mod h1:GkXuJDJ6aQ7lnJcRF+SJVgFdQhypqgl3LB1C9vabdRE= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.81.1 h1:VnnIIZ88UzOOKLukQi+ImGz8O1Wdp8nAGGnvOfEIWQQ= +google.golang.org/grpc v1.81.1/go.mod h1:xGH9GfzOyMTGIOXBJmXt+BX/V0kcdQbdcuwQ/zNw42I= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U= +gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= +gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +k8s.io/api v0.26.1 h1:f+SWYiPd/GsiWwVRz+NbFyCgvv75Pk9NK6dlkZgpCRQ= +k8s.io/api v0.26.1/go.mod h1:xd/GBNgR0f707+ATNyPmQ1oyKSgndzXij81FzWGsejg= +k8s.io/apiextensions-apiserver v0.26.1 h1:cB8h1SRk6e/+i3NOrQgSFij1B2S0Y0wDoNl66bn8RMI= +k8s.io/apiextensions-apiserver v0.26.1/go.mod h1:AptjOSXDGuE0JICx/Em15PaoO7buLwTs0dGleIHixSM= +k8s.io/apimachinery v0.0.0-20191203211716-adc6f4cd9e7d/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg= +k8s.io/apimachinery v0.26.1 h1:8EZ/eGJL+hY/MYCNwhmDzVqq2lPl3N3Bo8rvweJwXUQ= +k8s.io/apimachinery v0.26.1/go.mod h1:tnPmbONNJ7ByJNz9+n9kMjNP8ON+1qoAIIC70lztu74= +k8s.io/client-go v0.26.1 h1:87CXzYJnAMGaa/IDDfRdhTzxk/wzGZ+/HUQpqgVSZXU= +k8s.io/client-go v0.26.1/go.mod h1:IWNSglg+rQ3OcvDkhY6+QLeasV4OYHDjdqeWkDQZwGE= +k8s.io/cri-api v0.0.0-20191204094248-a6f63f369f6d h1:OpF0WA8LtC0MVPG5HzIsY77WinFa7pLXRtlc/ksOW1U= +k8s.io/cri-api v0.0.0-20191204094248-a6f63f369f6d/go.mod h1:BzAkbBHHp81d+aXzbiIcUbilLkbXa40B8mUHOk6EX3s= +k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/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.80.1 h1:atnLQ121W371wYYFawwYx1aEY2eUfs4l3J72wtgAwV4= +k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= +k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 h1:+70TFaan3hfJzs+7VK2o+OGxg8HsuBr/5f6tVAjDu6E= +k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280/go.mod h1:+Axhij7bCpeqhklhUTe3xmOn6bWxolyZEeyaFpjGtl4= +k8s.io/kubernetes v1.14.6 h1:t8Q3aaWanmiariBBr3qYIcAL9o0pv4MB5tZtfbILJGk= +k8s.io/kubernetes v1.14.6/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= +k8s.io/utils v0.0.0-20221128185143-99ec85e7a448 h1:KTgPnR10d5zhztWptI952TNtt/4u5h3IzDXkdIMuo2Y= +k8s.io/utils v0.0.0-20221128185143-99ec85e7a448/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/controller-runtime v0.14.5 h1:6xaWFqzT5KuAQ9ufgUaj1G/+C4Y1GRkhrxl+BJ9i+5s= +sigs.k8s.io/controller-runtime v0.14.5/go.mod h1:WqIdsAY6JBsjfc/CqO0CORmNtoCtE4S6qbPc9s68h+0= +sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k= +sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= +sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= +sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= +sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/third_party/meshnet/kind.yaml b/third_party/meshnet/kind.yaml new file mode 100644 index 000000000..412f5446b --- /dev/null +++ b/third_party/meshnet/kind.yaml @@ -0,0 +1,15 @@ +--- +apiVersion: kind.x-k8s.io/v1alpha4 +kind: Cluster +nodes: + - role: control-plane + - role: worker + - role: worker + - role: worker +kubeadmConfigPatches: + - | + apiVersion: kubelet.config.k8s.io/v1beta1 + kind: KubeletConfiguration + metadata: + name: config + maxPods: 253 diff --git a/third_party/meshnet/manifests/base/crd.yaml b/third_party/meshnet/manifests/base/crd.yaml new file mode 100644 index 000000000..cce0cb1a3 --- /dev/null +++ b/third_party/meshnet/manifests/base/crd.yaml @@ -0,0 +1,81 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: topologies.networkop.co.uk +spec: + group: networkop.co.uk + scope: Namespaced + names: + plural: topologies + singular: topology + kind: Topology + shortNames: + - topo + versions: + - name: v1beta1 + schema: + openAPIV3Schema: + properties: + spec: + properties: + links: + items: + description: "A complete definition of a p2p link" + required: ["uid", "peer_pod", "local_intf", "peer_intf"] + properties: + uid: + description: "Unique identified of a p2p link" + type: integer + peer_pod: + description: "Name of the peer pod" + type: string + local_intf: + description: "Local interface name" + type: string + peer_intf: + description: "Peer interface name" + type: string + peer_ip: + description: "(Optional) Local IP address" + type: string + local_ip: + description: "(Optional) Peer IP address" + type: string + type: object + type: array + type: object + status: + properties: + skipped: + description: "List of pods/interfaces that are skipped by local pod" + items: + properties: + link_id: + format: int64 + type: integer + pod_name: + description: "peer pod name" + type: string + type: object + type: array + src_ip: + description: "Source IP of the POD" + type: string + net_ns: + description: "Network namespace of the POD" + type: string + container_id: + description: "Sandbox ID of the POD" + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/third_party/meshnet/manifests/base/daemonset.yaml b/third_party/meshnet/manifests/base/daemonset.yaml new file mode 100644 index 000000000..3b2a07e0b --- /dev/null +++ b/third_party/meshnet/manifests/base/daemonset.yaml @@ -0,0 +1,76 @@ +apiVersion: apps/v1 +kind: DaemonSet +metadata: + name: meshnet + labels: + k8s-app: meshnet +spec: + selector: + matchLabels: + name: meshnet + template: + metadata: + labels: + name: meshnet + spec: + hostNetwork: true + hostPID: true + hostIPC: true + serviceAccountName: meshnet + nodeSelector: + beta.kubernetes.io/arch: amd64 + tolerations: + - operator: Exists + effect: NoSchedule + containers: + - name: meshnet + securityContext: + privileged: true + image: openconfig/meshnet:latest + imagePullPolicy: IfNotPresent + command: ["./entrypoint.sh"] + resources: + limits: + memory: 10G + requests: + cpu: 200m + memory: 1G + env: + - name: HOST_IP + valueFrom: + fieldRef: + fieldPath: status.hostIP + - name: INTER_NODE_LINK_TYPE + #value: GRPC + value: VXLAN + - name: NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + volumeMounts: + - name: cni-cfg + mountPath: /etc/cni/net.d + - name: cni-bin + mountPath: /opt/cni/bin + - name: var-run-netns + mountPath: /var/run/netns + mountPropagation: Bidirectional + terminationGracePeriodSeconds: 30 + volumes: + - name: cni-bin + hostPath: + path: /opt/cni/bin + - name: cni-cfg + hostPath: + path: /etc/cni/net.d + - name: var-run-netns + hostPath: + path: /var/run/netns diff --git a/third_party/meshnet/manifests/base/gwire_crd.yaml b/third_party/meshnet/manifests/base/gwire_crd.yaml new file mode 100644 index 000000000..e197b3052 --- /dev/null +++ b/third_party/meshnet/manifests/base/gwire_crd.yaml @@ -0,0 +1,121 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.11.1 + creationTimestamp: null + name: gwirekobjs.networkop.co.uk +spec: + group: networkop.co.uk + names: + kind: GWireKObj + listKind: GWireKObjList + plural: gwirekobjs + singular: gwirekobj + scope: Namespaced + versions: + - name: v1beta1 + schema: + openAPIV3Schema: + properties: + apiVersion: + description: + "APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources" + type: string + kind: + description: + "Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" + type: string + metadata: + type: object + spec: + properties: + apiVersion: + description: + "APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources" + type: string + kind: + description: + "Kind is a string value representing the REST resource + this object represents. Servers may infer this from the endpoint + the client submits requests to. Cannot be updated. In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" + type: string + uids: + description: unique link id + items: + type: integer + type: array + type: object + status: + properties: + apiVersion: + description: + "APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources" + type: string + grpcWireItems: + items: + properties: + gwire_peer_node_ip: + description: peer node IP address + type: string + link_id: + description: Unique link id as assigned by meshnet + format: int64 + type: integer + local_pod_iface_name: + description: + Local pod interface name that is specified in topology + CR and is created by meshnet + type: string + local_pod_ip: + description: Local pod ip as specified in topology CR + type: string + local_pod_name: + description: Local pod name as specified in topology CR + type: string + local_pod_net_ns: + description: + Network namespace of the local pod holding the + wire end + type: string + node_name: + description: Name of the node holding the wire end + type: string + topo_namespace: + description: The topology namespace. + type: string + wire_iface_id_on_peer_node: + description: + The interface id, in the peer node and is connected + with remote pod. This is used for de-multiplexing received + packet from grpcwire + format: int64 + type: integer + wire_iface_name_on_local_node: + description: + The interface(name) in the local node and is connected + with local pod + type: string + type: object + type: array + kind: + description: + "Kind is a string value representing the REST resource + this object represents. Servers may infer this from the endpoint + the client submits requests to. Cannot be updated. In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" + type: string + type: object + type: object + served: true + storage: true diff --git a/third_party/meshnet/manifests/base/kustomization.yaml b/third_party/meshnet/manifests/base/kustomization.yaml new file mode 100644 index 000000000..9540a2ba9 --- /dev/null +++ b/third_party/meshnet/manifests/base/kustomization.yaml @@ -0,0 +1,12 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +namespace: meshnet +commonLabels: + app: meshnet +resources: + - namespace.yaml + - serviceaccount.yaml + - crd.yaml + - gwire_crd.yaml + - rbac.yaml + - daemonset.yaml diff --git a/third_party/meshnet/manifests/base/namespace.yaml b/third_party/meshnet/manifests/base/namespace.yaml new file mode 100644 index 000000000..7783a6975 --- /dev/null +++ b/third_party/meshnet/manifests/base/namespace.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: meshnet diff --git a/third_party/meshnet/manifests/base/rbac.yaml b/third_party/meshnet/manifests/base/rbac.yaml new file mode 100644 index 000000000..da7d63eb7 --- /dev/null +++ b/third_party/meshnet/manifests/base/rbac.yaml @@ -0,0 +1,31 @@ +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: meshnet-clusterrole +rules: + - apiGroups: + - "networkop.co.uk" + resources: + - topologies + - gwirekobjs + verbs: ["*"] + - apiGroups: + - "networkop.co.uk" + resources: + - topologies/status + - gwirekobjs/spec + - gwirekobjs/status + verbs: ["*"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: meshnet-clusterrolebinding +roleRef: + kind: ClusterRole + name: meshnet-clusterrole + apiGroup: rbac.authorization.k8s.io +subjects: + - kind: ServiceAccount + name: meshnet diff --git a/third_party/meshnet/manifests/base/serviceaccount.yaml b/third_party/meshnet/manifests/base/serviceaccount.yaml new file mode 100644 index 000000000..4ce60f89e --- /dev/null +++ b/third_party/meshnet/manifests/base/serviceaccount.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: meshnet diff --git a/third_party/meshnet/manifests/overlays/e2e/kustomization.yaml b/third_party/meshnet/manifests/overlays/e2e/kustomization.yaml new file mode 100644 index 000000000..eaf164968 --- /dev/null +++ b/third_party/meshnet/manifests/overlays/e2e/kustomization.yaml @@ -0,0 +1,7 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +images: + - name: openconfig/meshnet + newTag: d0ebd1f-dirty +resources: + - ../../base/ diff --git a/third_party/meshnet/manifests/overlays/grpc-link-e2e/kustomization.yaml b/third_party/meshnet/manifests/overlays/grpc-link-e2e/kustomization.yaml new file mode 100644 index 000000000..398811c52 --- /dev/null +++ b/third_party/meshnet/manifests/overlays/grpc-link-e2e/kustomization.yaml @@ -0,0 +1,7 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +images: + - name: openconfig/meshnet + newTag: d0ebd1f-dirty +resources: + - ../grpc-link/ diff --git a/third_party/meshnet/manifests/overlays/grpc-link/kustomization.yaml b/third_party/meshnet/manifests/overlays/grpc-link/kustomization.yaml new file mode 100644 index 000000000..0368d9622 --- /dev/null +++ b/third_party/meshnet/manifests/overlays/grpc-link/kustomization.yaml @@ -0,0 +1,18 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +patches: + - patch: |- + - op: replace + path: /spec/template/spec/containers/0/env/1 + value: + name: INTER_NODE_LINK_TYPE + value: GRPC + target: + kind: DaemonSet + name: meshnet + namespace: meshnet +resources: + - ../../base/ +images: + - name: openconfig/meshnet + newTag: d0ebd1f-dirty diff --git a/third_party/meshnet/manifests/overlays/kops/kustomization.yaml b/third_party/meshnet/manifests/overlays/kops/kustomization.yaml new file mode 100644 index 000000000..908f11318 --- /dev/null +++ b/third_party/meshnet/manifests/overlays/kops/kustomization.yaml @@ -0,0 +1,8 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: + - ../../base/ +commonLabels: + app: meshnet +patchesStrategicMerge: + - patch.yaml diff --git a/third_party/meshnet/manifests/overlays/kops/patch.yaml b/third_party/meshnet/manifests/overlays/kops/patch.yaml new file mode 100644 index 000000000..8eefb124b --- /dev/null +++ b/third_party/meshnet/manifests/overlays/kops/patch.yaml @@ -0,0 +1,13 @@ +--- +apiVersion: apps/v1 +kind: DaemonSet +metadata: + name: meshnet + namespace: meshnet +spec: + template: + spec: + volumes: + - name: cni-bin + hostPath: + path: /home/kubernetes/bin diff --git a/third_party/meshnet/plugin/grpcwires-plugin.go b/third_party/meshnet/plugin/grpcwires-plugin.go new file mode 100644 index 000000000..ad8a4545a --- /dev/null +++ b/third_party/meshnet/plugin/grpcwires-plugin.go @@ -0,0 +1,295 @@ +package main + +import ( + "context" + "fmt" + "net" + "strings" + "time" + + "github.com/containernetworking/plugins/pkg/ns" + mpb "github.com/openconfig/kne/third_party/meshnet/daemon/proto/meshnet/v1beta1" + "github.com/openconfig/kne/third_party/meshnet/utils/wireutil" + koko "github.com/redhat-nfvpe/koko/api" + log "github.com/sirupsen/logrus" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" +) + +const ( + skipStatusRetryInterval = 2 // sec + skipStatusRetryWarnCount = 5 // generate a warning while continuing further + skipStatusRetryCount = skipStatusRetryWarnCount * 4 // how many times to retry +) + +// -------------------------------------------------------------------------------------------------------- +func CreatGRPCChan(link *mpb.Link, localPod *mpb.Pod, peerPod *mpb.Pod, localClient mpb.LocalClient, cniArgs *k8sArgs, ctx context.Context) error { + // At this point pods attached to both end of this link are both up. They have got the management IP already. + + if link == nil { + return fmt.Errorf("Add-GRPC[%s]: can't establish grpc channel. link not provided. link:%p", localPod.Name, link) + } + + log.Infof("Add-GRPC[%s]: Setting up grpc-wire:(local-pod:%s:%s@node:%s <----link uid: %d----> remote-pod:%s:%s@node:%s)", + localPod.Name, localPod.Name, link.LocalIntf, localPod.SrcIp, + link.Uid, peerPod.Name, link.PeerIntf, peerPod.SrcIp) + + log.Infof("Add-GRPC[%s]: Checking if we've been skipped for link id %d", localPod.Name, link.Uid) + isSkipped, err := localClient.IsSkipped(ctx, &mpb.SkipQuery{ + Pod: localPod.Name, + Peer: peerPod.Name, + LinkId: link.Uid, + KubeNs: string(cniArgs.K8S_POD_NAMESPACE), + }) + + if err != nil { + log.Errorf("Add-GRPC[%s]: Failed to read skipped status with peer pod %s", localPod.Name, peerPod.Name) + return err + } + + wireDef := mpb.WireDef{ + LocalPodNetNs: localPod.NetNs, + LinkUid: link.Uid, + TopoNs: localPod.KubeNs, + } + // Comparing names to determine higher priority + higherPrio := localPod.Name > peerPod.Name + + if !isSkipped.Response && !higherPrio { + /* If peer POD skipped us (booted before us) or we have a higher priority then we initiate the tunnel. + If peer POD has not skipped us (that means yet to boot or just booted) and it has higher priority + then we do not initiate the grpc tunnel. When the high priority peer pod boots up (or get ready) then + it will take care of grpc tunnel creation. This is needed to avoid the race condition when both + the pods are alive, no one has skipped each other and both of them tries to create the tunnel. In + this situation only high priority pod must create the tunnel and not the low priority one. This will + avoid conflict. */ + + ticker := time.NewTicker(time.Second * skipStatusRetryInterval) + defer ticker.Stop() + + iteration := 1 + for range ticker.C { + // Check if it has created the wire while we were waiting + resp, err := localClient.GRPCWireExists(ctx, &wireDef) + if err != nil { + return fmt.Errorf("Add-GRPC[%s]: could not check grpc wire, %s@%s: %v", localPod.Name, localPod.Name, peerPod.Name, err) + } + if resp.Response { + /* Higher priority pod has created the grpc-link. */ + log.Infof("Add-GRPC[%s]: grpc wire is already created by the remote peer. Local interface id:%d, local pod %s, peer pod %s", localPod, resp.PeerIntfId, localPod.Name, peerPod.Name) + return nil + } + + log.Infof("Add-GRPC[%s]: Retrying to read skipped status for pod %s", localPod.Name, localPod.Name) + isSkipped, err = localClient.IsSkipped(ctx, &mpb.SkipQuery{ + Pod: localPod.Name, + Peer: peerPod.Name, + LinkId: link.Uid, + KubeNs: string(cniArgs.K8S_POD_NAMESPACE), + }) + if err != nil { + log.Errorf("Add-GRPC[%s]: Failed to read skipped status from peer pod %s", localPod.Name, peerPod.Name) + return err + } + + if !isSkipped.Response { + if iteration > skipStatusRetryWarnCount { + log.Warnf("Add-GRPC[%s]: Local pod %s is taking longer time (retry %d) to read skip status, skipped by peer %s.", localPod.Name, localPod.Name, iteration, peerPod.Name) + if iteration == skipStatusRetryCount { + log.Infof("Add-GRPC[%s]: Pod %s is not skipped by higher priority pod %s. Link between %s and %s will be created by higher priority pod.", + localPod.Name, localPod.Name, peerPod.Name, localPod.Name, peerPod.Name) + return nil + } + } + iteration++ + } else { + log.Infof("Add-GRPC[%s]: Local pod %s is skipped by peer %s. So we can create wire now", localPod.Name, localPod.Name, peerPod.Name) + break + } + } // end of for + } + + resp, err := localClient.GRPCWireExists(ctx, &wireDef) + if err != nil { + return fmt.Errorf("Add-GRPC[%s]: could not check grpc wire, %s@%s: %v", localPod.Name, localPod.Name, peerPod.Name, err) + } + if resp.Response { + /* While this pod was busy creating other links or was busy with some other task, the remote + pod had finished creating this grpc-link. */ + log.Infof("Add-GRPC[%s]: grpc wire is already set by the remote peer. Local interface id:%d", localPod.Name, resp.PeerIntfId) + return nil + } + + // Create the local end of the grpc-wire + currNs, err := ns.GetCurrentNS() + if err != nil { + return fmt.Errorf("Add-GRPC[%s]: creating GRPC wire for pod %s : failed to get node ns, err: %v", localPod.Name, localPod.Name, err) + } + + // Build koko's veth struct for the intf to be placed inside the pod + inConIntfNm := link.LocalIntf + inContainerVeth, err := makeVeth(localPod.NetNs, inConIntfNm, link.LocalIp) + if err != nil { + log.Errorf("Add-GRPC[%s]: Could not create vEth for local pod %s:%s, peer pod %s, err %v", localPod.Name, localPod.Name, inConIntfNm, peerPod.Name, err) + return err + } + + respIntfName, err := localClient.GenerateNodeInterfaceName(ctx, &mpb.GenerateNodeInterfaceNameRequest{PodIntfName: link.LocalIntf, PodName: localPod.Name}) + if err != nil { + return fmt.Errorf("Add-GRPC[%s]: could not create node interface for local pod %s, peer pod %s: %v", localPod.Name, err, localPod.Name, peerPod.Name) + } + + hostEndVeth := &koko.VEth{ + LinkName: respIntfName.NodeIntfName, + NsName: currNs.Path()} + + if err = koko.MakeVeth(*inContainerVeth, *hostEndVeth); err != nil { + return fmt.Errorf("Add-GRPC[%s]: creating GRPC wire: failed to create vEth-pair inside pod (%s:%s) and on host (%s). err:%s", + localPod.Name, localPod.Name, inContainerVeth.LinkName, hostEndVeth.LinkName, err) + } + + /* Dial the remote peer to create the remote end of the grpc tunnel. */ + + url := fmt.Sprintf("%s:%d", peerPod.SrcIp, wireutil.GRPCDefaultPort) + url = strings.TrimSpace(url) + remote, err := grpc.Dial(url, grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + return fmt.Errorf("Add-GRPC[%s]: creating GRPC wire: failed to dial remote gRPC url %s", localPod.Name, url) + } + remoteClient := mpb.NewRemoteClient(remote) + locInf, err := net.InterfaceByName(hostEndVeth.LinkName) + if err != nil { + return fmt.Errorf("Add-GRPC[%s]: could not get interface by name: %v", localPod.Name, err) + } + + wireDefRemot := mpb.WireDef{ + /*WireIfIdOnPeerNode : is the interface id on which a node receives grpc + packets from the remote pod (hosted in a remote node). Through this interface + the remote packets are delivered to the local pod. + + WireIfIdOnPeerNode is the interface that will be used to send packets to the remote pod also. + Packets coming from local pods will be received on this interface, will be encapsulated + in grpc to send it to the peer node (that is hosting the remote pod). + + The remote pod must send packets to this interface for this grpc-wire, to reach + the connected container in this node. For remote pod this must be the destination + interface id to reach the connected pod in this node. From remote pods perspective, + the local interface of this node is the "PeerIntfId" for the remote pod in remote machine */ + WireIfIdOnPeerNode: int64(locInf.Index), + + /* PeerIp: Ip address of the peer machine/node. + For remote pod this must be the IP address on this host. The remote pod must + Transport packets to this pod (over grpc) in this local node. This is the IP + address of the local node which remote node will do a grpc dial, to send + packets over grpc wire. */ + PeerNodeIp: localPod.SrcIp, + + /* We need to tell the remote node, what is the kne specified in container interface name. + We also need to tell to which network namespace the pod in remote node belongs to. */ + IntfNameInPod: link.PeerIntf, + LocalPodNetNs: peerPod.NetNs, + LocalPodName: peerPod.Name, // name of the remote pod + + /*meshnet assigned unique identifier for this link */ + LinkUid: link.Uid, + TopoNs: peerPod.KubeNs, + LocalPodIp: link.PeerIp, + } + + log.Infof("Add-GRPC[%s]: Create GRPC wire: dialing remote node-->%s@%s", localPod.Name, peerPod.Name, url) + creatResp, err := remoteClient.AddGRPCWireRemote(ctx, &wireDefRemot) + if err != nil { + return fmt.Errorf("Add-GRPC[%s]: failed to create grpc tunnel ar remote end:%s err:%v", localPod.Name, url, err) + } else if !creatResp.Response { + return fmt.Errorf("Add-GRPC[%s]: remote end of the grpc-wire (local-pod:%s:%s@node:%s <----link uid: %d----> remote-pod:%s:%s@node:%s) is not up", + localPod.Name, localPod.Name, link.LocalIntf, localPod.SrcIp, + link.Uid, peerPod.Name, link.PeerIntf, peerPod.SrcIp) + } + + /* remote has finished its job. Register local end of the grpc wire with the daemon + and start the packet sending thread. */ + wireDefLocal := mpb.WireDef{ + /*PeerIntfId : this is the interface id (in the remote machine) to which the host/local machine will send grpc + packets for the remote pod. This interface id will be encoded in every packet + sent over this grpc-wire. This interface id is created in the remote machine and + communicated by the remote machine. Availability of this interface id indicates remote + machine is ready to receive packets over this grpc-wire. Remote machine will use this + interface id to pass the packets to the remote pod. */ + WireIfIdOnPeerNode: creatResp.PeerIntfId, + + /* PeerIp : Ip address of the remote node, to which this local node is sending packets over + this grpc-wire. + */ + PeerNodeIp: peerPod.SrcIp, + + /* WireIfNameOnLocalNode : name of the local machine interface, from where packets generated by the local + pod will be picked up and transported over grpc to remote. local meshnet daemon will receive + packets from local pod on this interface. + */ + WireIfNameOnLocalNode: respIntfName.NodeIntfName, + + /*meshnet assigned unique identifier for this link */ + LinkUid: link.Uid, + LocalPodName: localPod.Name, + IntfNameInPod: link.LocalIntf, + LocalPodNetNs: localPod.NetNs, + LocalPodIp: link.LocalIp, + TopoNs: localPod.KubeNs, + } + log.Infof("Add-GRPC[%s]: Creating GRPC wire: adding the local end of the grpc tunnel.", localPod.Name) + r, err := localClient.AddGRPCWireLocal(ctx, &wireDefLocal) + if err != nil { + return fmt.Errorf("Add-GRPC[%s]: failed to create local end of the tunnel %v", localPod.Name, err) + } else if !r.Response { + return fmt.Errorf("Add-GRPC[%s]: local end of the grpc-wire (local-pod:-%s:%s@node:%s <----link uid: %d----> remote-pod:-%s:%s@node:%s) is not up", + localPod.Name, localPod.Name, link.LocalIntf, localPod.SrcIp, + link.Uid, peerPod.Name, link.PeerIntf, peerPod.SrcIp) + } + + log.Infof("Add-GRPC[%s]: Successfully created grpc-wire (local-pod:%s:%s@node:%s:%d <----link uid: %d----> remote-pod:%s:%s@node:%s:%d)", + localPod.Name, localPod.Name, link.LocalIntf, localPod.SrcIp, locInf.Index, + link.Uid, peerPod.Name, link.PeerIntf, peerPod.SrcIp, creatResp.PeerIntfId) + + return nil +} + +// This function is called when a K8S pod is getting deleted. +func MakeGRPCChanDown(link *mpb.Link, localPod *mpb.Pod, peerPod *mpb.Pod, ctx context.Context) error { + if link == nil { + return fmt.Errorf("can't remove remote grpc info. link not provided. link:%p", link) + } + + /* Dial the remote peer to bring down the remote grpc wire end */ + + url := fmt.Sprintf("%s:%d", peerPod.SrcIp, wireutil.GRPCDefaultPort) + url = strings.TrimSpace(url) + remote, err := grpc.Dial(url, grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + return fmt.Errorf("MakeGRPCChanDown failed to dial remote gRPC url %s", url) + } + remoteClient := mpb.NewRemoteClient(remote) + + wireDefRemot := mpb.WireDef{ + PeerNodeIp: localPod.SrcIp, // for remote pod this pod is the peer pod + IntfNameInPod: link.PeerIntf, + LocalPodNetNs: peerPod.NetNs, + LocalPodName: peerPod.Name, + + /*meshnet assigned unique identifier for this link */ + LinkUid: link.Uid, + TopoNs: peerPod.KubeNs, + LocalPodIp: link.PeerIp, + } + + log.Infof("MakeGRPCChanDown: dialing remote node-->%s@%s", peerPod.Name, url) + removeResp, err := remoteClient.GRPCWireDownRemote(ctx, &wireDefRemot) + if err != nil { + return fmt.Errorf("MakeGRPCChanDown: GRPC communication error for : %s, err:%v", url, err) + } else if !removeResp.Response { + return fmt.Errorf("MakeGRPCChanDown: remote end of the grpc-wire (local-pod:%s:%s@node:%s <----link uid: %d----> remote-pod:%s:%s@node:%s) is not down", + localPod.Name, link.LocalIntf, localPod.SrcIp, + link.Uid, peerPod.Name, link.PeerIntf, peerPod.SrcIp) + } + + return nil +} diff --git a/third_party/meshnet/plugin/meshnet.go b/third_party/meshnet/plugin/meshnet.go new file mode 100644 index 000000000..713b84b8b --- /dev/null +++ b/third_party/meshnet/plugin/meshnet.go @@ -0,0 +1,576 @@ +package main + +import ( + "context" + "encoding/json" + "fmt" + "net" + "os" + "runtime" + + "github.com/containernetworking/cni/pkg/skel" + "github.com/containernetworking/cni/pkg/types" + "github.com/containernetworking/cni/pkg/types/current" + "github.com/containernetworking/cni/pkg/version" + "github.com/davecgh/go-spew/spew" + koko "github.com/redhat-nfvpe/koko/api" + log "github.com/sirupsen/logrus" + "github.com/vishvananda/netlink" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + + mpb "github.com/openconfig/kne/third_party/meshnet/daemon/proto/meshnet/v1beta1" + "github.com/openconfig/kne/third_party/meshnet/utils/wireutil" +) + +const ( + vxlanBase = 5000 + localhost = "localhost" + macvlanMode = netlink.MACVLAN_MODE_BRIDGE +) + +var ( + localDaemon = localhost + ":" + fmt.Sprintf("%d", wireutil.GRPCDefaultPort) +) + +var interNodeLinkType = wireutil.INTER_NODE_LINK_VXLAN + +type netConf struct { + types.NetConf + Delegate map[string]interface{} `json:"delegate"` +} + +type k8sArgs struct { + types.CommonArgs + K8S_POD_NAME types.UnmarshallableString + K8S_POD_NAMESPACE types.UnmarshallableString + K8S_POD_INFRA_CONTAINER_ID types.UnmarshallableString +} + +// ------------------------------------------------------------------------------------------------- +func init() { + // this ensures that main runs only on main thread (thread group leader). + // since namespace ops (unshare, setns) are done for a single thread, we + // must ensure that the goroutine does not jump from OS thread to thread + runtime.LockOSThread() +} + +// ------------------------------------------------------------------------------------------------- +// loadConf loads information from cni.conf +func loadConf(bytes []byte) (*netConf, *current.Result, error) { + n := &netConf{} + if err := json.Unmarshal(bytes, n); err != nil { + return nil, nil, fmt.Errorf("failed to load netconf: %v", err) + } + + // Parse previous result. + if n.RawPrevResult == nil { + // return early if there was no previous result, which is allowed for DEL calls + return n, ¤t.Result{}, nil + } + + // Parse previous result. + var result *current.Result + var err error + if err = version.ParsePrevResult(&n.NetConf); err != nil { + return nil, nil, fmt.Errorf("could not parse prevResult: %v", err) + } + + result, err = current.NewResultFromResult(n.PrevResult) + if err != nil { + return nil, nil, fmt.Errorf("could not convert result to current version: %v", err) + } + return n, result, nil +} + +// getVxlanSource uses netlink to get the iface reliably given an IP address. +// when IP and Interface both are present then Interface is going to take preference +// nodeIntf is specified by the user and it's not auto discovered. The user has to be careful that the peer is reachable though this interface otherwise, VxLAN may not work. +// daemonset.yaml meshnet container env required for host_intf override +// +// env: +// - name: HOST_INTF +// value: breth2 +func getVxlanSource(nodeIP string, nodeIntf string) (string, string, error) { + if nodeIntf == "" && nodeIP == "" { + return "", "", fmt.Errorf("meshnetd provided no HOST_IP address: %s or HOST_INTF: %s", nodeIP, nodeIntf) + } + nIP := net.ParseIP(nodeIP) + if nIP == nil && nodeIntf == "" { + return "", "", fmt.Errorf("parsing failed for meshnetd provided no HOST_IP address: %s and node HOST_INTF: %s", nodeIP, nodeIntf) + } + ifaces, _ := net.Interfaces() + for _, i := range ifaces { + addrs, _ := i.Addrs() + for _, a := range addrs { + var ip net.IP + switch v := a.(type) { + case *net.IPNet: + ip = v.IP + case *net.IPAddr: + ip = v.IP + } + if nodeIntf != "" { + if i.Name == nodeIntf { + return ip.String(), nodeIntf, nil + } + } + if nIP != nil && nIP.Equal(ip) { + log.Infof("Found iface %s for address %s", i.Name, nodeIP) + return nodeIP, i.Name, nil + } + } + } + return "", "", fmt.Errorf("no iface found for address %s", nodeIP) +} + +// ------------------------------------------------------------------------------------------------- +// makeVeth creates koko.Veth from NetNS and LinkName +func makeVeth(netNS, linkName string, ip string) (*koko.VEth, error) { + log.Infof("Creating Veth struct with NetNS:%s and intfName: %s, IP:%s", netNS, linkName, ip) + veth := koko.VEth{} + veth.NsName = netNS + veth.LinkName = linkName + if ip != "" { + ipAddr, ipSubnet, err := net.ParseCIDR(ip) + if err != nil { + return nil, fmt.Errorf("failed to parse CIDR %s: %s", ip, err) + } + veth.IPAddr = []net.IPNet{{ + IP: ipAddr, + Mask: ipSubnet.Mask, + }} + } + return &veth, nil +} + +// ------------------------------------------------------------------------------------------------- +// Creates koko.Vxlan from ParentIF, destination IP and VNI +func makeVxlan(srcIntf string, peerIP string, idx int64) *koko.VxLan { + return &koko.VxLan{ + ParentIF: srcIntf, + IPAddr: net.ParseIP(peerIP), + ID: int(vxlanBase + idx), + } +} + +// ------------------------------------------------------------------------------------------------- +// Adds interfaces to a POD. the POD name and the POD Namespace is passed as arguments. +func cmdAdd(args *skel.CmdArgs) error { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + log.Info("Parsing cni .conf file") + n, result, err := loadConf(args.StdinData) + if err != nil { + return err + } + + log.Info("Parsing CNI_ARGS environment variable") + cniArgs := k8sArgs{} + if err := types.LoadArgs(args.Args, &cniArgs); err != nil { + return err + } + log.Infof("Processing ADD POD %s in namespace %s, container_id %s", string(cniArgs.K8S_POD_NAME), string(cniArgs.K8S_POD_NAMESPACE), string(cniArgs.K8S_POD_INFRA_CONTAINER_ID)) + defer log.Infof("DONE > Processing ADD POD %s in namespace %s, container_id %s", string(cniArgs.K8S_POD_NAME), string(cniArgs.K8S_POD_NAMESPACE), string(cniArgs.K8S_POD_INFRA_CONTAINER_ID)) + + log.Infof("Attempting to connect to local meshnet daemon") + conn, err := grpc.Dial(localDaemon, grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + log.Errorf("Failed to connect to local meshnetd on %s", localDaemon) + return err + } + defer conn.Close() + + meshnetClient := mpb.NewLocalClient(conn) + + log.Infof("Add[%s]: Retrieving local pod information from meshnet daemon", string(cniArgs.K8S_POD_NAME)) + localPod, err := meshnetClient.Get(ctx, &mpb.PodQuery{ + Name: string(cniArgs.K8S_POD_NAME), + KubeNs: string(cniArgs.K8S_POD_NAMESPACE), + }) + if err != nil { + log.Errorf("Add: Pod %s:%s was not a topology pod returning", string(cniArgs.K8S_POD_NAMESPACE), string(cniArgs.K8S_POD_NAME)) + return types.PrintResult(result, n.CNIVersion) + } + + // Finding the source IP and interface for VXLAN VTEP + srcIP, srcIntf, err := getVxlanSource(localPod.NodeIp, localPod.NodeIntf) + if err != nil { + return err + } + //log.Infof("VxLan route is via %s@%s", srcIP, srcIntf) + + // Marking pod as "alive" by setting its srcIP and NetNS and ContainerId + localPod.NetNs = args.Netns + localPod.SrcIp = srcIP + localPod.ContainerId = args.ContainerID + log.Infof("Add[%s]: Setting pod alive status on meshnet daemon", string(cniArgs.K8S_POD_NAME)) + ok, err := meshnetClient.SetAlive(ctx, localPod) + if err != nil || !ok.Response { + log.Errorf("Add[%s]: Failed to set pod alive status", string(cniArgs.K8S_POD_NAME)) + return err + } + + log.Infof("Add[%s]: Starting to traverse all links", string(cniArgs.K8S_POD_NAME)) + for _, link := range localPod.Links { // Iterate over each link of the local pod + // Build koko's veth struct for local intf + myVeth, err := makeVeth(args.Netns, link.LocalIntf, link.LocalIp) + if err != nil { + return err + } + + // First option is macvlan interface + if link.PeerPod == localhost { + log.Infof("Peer link is MacVlan") + macVlan := koko.MacVLan{ + ParentIF: link.PeerIntf, + Mode: macvlanMode, + } + if err = koko.MakeMacVLan(*myVeth, macVlan); err != nil { + log.Errorf("Failed to add macvlan interface") + return err + } + log.Infof("macvlan interface %s@%s has been added", link.LocalIntf, link.PeerIntf) + continue + } + + // Initialising peer pod's metadata + log.Infof("Add[%s]: retrieving peer pod %s information from meshnet daemon", string(cniArgs.K8S_POD_NAME), link.PeerPod) + peerPod, err := meshnetClient.Get(ctx, &mpb.PodQuery{ + Name: link.PeerPod, + KubeNs: string(cniArgs.K8S_POD_NAMESPACE), + }) + if err != nil { + log.Errorf("Add[%s]: Failed to retrieve peer pod %s:%s topology", string(cniArgs.K8S_POD_NAME), string(cniArgs.K8S_POD_NAMESPACE), link.PeerPod) + return err + } + + isAlive := peerPod.SrcIp != "" && peerPod.NetNs != "" + log.Infof("Add[%s]: Is peer pod %s alive?: %t", string(cniArgs.K8S_POD_NAME), peerPod.Name, isAlive) + + if isAlive { // This means we're coming up AFTER our peer so things are pretty easy + log.Infof("Add[%s]: Peer pod %s is alive", string(cniArgs.K8S_POD_NAME), peerPod.Name) + if peerPod.SrcIp == localPod.SrcIp { // This means we're on the same host + log.Infof("Add[%s]: %s and %s are on the same host", string(cniArgs.K8S_POD_NAME), localPod.Name, peerPod.Name) + // Creating koko's Veth struct for peer intf + peerVeth, err := makeVeth(peerPod.NetNs, link.PeerIntf, link.PeerIp) + if err != nil { + log.Errorf("Add[%s]: Failed to build koko Veth struct", string(cniArgs.K8S_POD_NAME)) + return err + } + + // Checking if interfaces already exist + iExist, _ := koko.IsExistLinkInNS(myVeth.NsName, myVeth.LinkName) + pExist, _ := koko.IsExistLinkInNS(peerVeth.NsName, peerVeth.LinkName) + + log.Infof("Add[%s]: Does the link already exist? Local:%t, Peer:%t", string(cniArgs.K8S_POD_NAME), iExist, pExist) + if iExist && pExist { // If both link exist, we don't need to do anything + log.Infof("Add[%s]: Both interfaces already exist in namespace", string(cniArgs.K8S_POD_NAME)) + } else if !iExist && pExist { // If only peer link exists, we need to destroy it first + log.Infof("Add[%s]: Only peer link exists, removing it first", string(cniArgs.K8S_POD_NAME)) + if err := peerVeth.RemoveVethLink(); err != nil { + log.Errorf("Add[%s]: Failed to remove a stale interface %s of my peer %s", string(cniArgs.K8S_POD_NAME), peerVeth.LinkName, link.PeerPod) + return err + } + log.Infof("Add[%s]: Adding the new veth link to both pods", string(cniArgs.K8S_POD_NAME)) + if err = koko.MakeVeth(*myVeth, *peerVeth); err != nil { + log.Errorf("Add[%s]: Error creating VEth pair after peer link remove: %s", string(cniArgs.K8S_POD_NAME), err) + return err + } + } else if iExist && !pExist { // If only local link exists, we need to destroy it first + log.Infof("Add[%s]: Only local link exists, removing it first", string(cniArgs.K8S_POD_NAME)) + if err := myVeth.RemoveVethLink(); err != nil { + log.Errorf("Add[%s]: Failed to remove a local stale VEth interface %s for pod %s", string(cniArgs.K8S_POD_NAME), myVeth.LinkName, localPod.Name) + return err + } + log.Infof("Add[%s]: Adding the new veth link to both pods", string(cniArgs.K8S_POD_NAME)) + if err = koko.MakeVeth(*myVeth, *peerVeth); err != nil { + log.Errorf("Add[%s]: Error creating VEth pair after local link remove: %s", string(cniArgs.K8S_POD_NAME), err) + return err + } + } else { // if neither link exists, we have two options + log.Infof("Add[%s]: Neither link exists. Checking if we've been skipped for interface %s (link id %d)", string(cniArgs.K8S_POD_NAME), link.LocalIntf, link.Uid) + isSkipped, err := meshnetClient.IsSkipped(ctx, &mpb.SkipQuery{ + Pod: localPod.Name, + Peer: peerPod.Name, + LinkId: link.Uid, + KubeNs: string(cniArgs.K8S_POD_NAMESPACE), + }) + if err != nil { + log.Errorf("Add[%s]: Failed to read skipped status from our peer", string(cniArgs.K8S_POD_NAME)) + return err + } + log.Infof("Add[%s]: Have we been skipped for %s (link id %d) by our peer %s? %t", string(cniArgs.K8S_POD_NAME), link.LocalIntf, link.Uid, peerPod.Name, isSkipped.Response) + + // Comparing names to determine higher priority + higherPrio := localPod.Name > peerPod.Name + log.Infof("Add[%s]: Do we have a higher priority? %t", string(cniArgs.K8S_POD_NAME), higherPrio) + + if isSkipped.Response || higherPrio { // If peer POD skipped us (booted before us) or we have a higher priority + log.Infof("Add[%s]: Peer POD has skipped us for %s (link id %d) or we have a higher priority", string(cniArgs.K8S_POD_NAME), link.LocalIntf, link.Uid) + if err = koko.MakeVeth(*myVeth, *peerVeth); err != nil { + log.Errorf("Add[%s]: Error when creating a new VEth pair with koko: %s", string(cniArgs.K8S_POD_NAME), err) + log.Infof("Add[%s]: MY VETH STRUCT: %+v", string(cniArgs.K8S_POD_NAME), spew.Sdump(myVeth)) + log.Infof("Add[%s]: PEER STRUCT: %+v", string(cniArgs.K8S_POD_NAME), spew.Sdump(peerVeth)) + return err + } + } else { // peerPod has higherPrio and hasn't skipped us + // In this case we do nothing, since the pod with a higher IP is supposed to connect veth pair + log.Infof("Add[%s]: Doing nothing, expecting peer pod %s to connect veth pair", string(cniArgs.K8S_POD_NAME), peerPod.Name) + continue + } + } + if err := wireutil.SetTxChecksumOff(myVeth.LinkName, myVeth.NsName); err != nil { + log.Errorf("Add[%s]: Error in setting tx checksum-off on interface %s, ns %s, pod %s: %v", string(cniArgs.K8S_POD_NAME), myVeth.LinkName, myVeth.NsName, localPod.Name, err) + // not returning + } + if err := wireutil.SetTxChecksumOff(peerVeth.LinkName, peerVeth.NsName); err != nil { + log.Errorf("Add[%s]: Error in setting tx checksum-off on interface %s, ns %s, pod %s: %v", string(cniArgs.K8S_POD_NAME), peerVeth.LinkName, peerVeth.NsName, peerPod.Name, err) + // not returning + } + + } else { // This means we're on different hosts + log.Infof("Add[%s]: %s@%s and %s@%s are on different hosts", string(cniArgs.K8S_POD_NAME), localPod.Name, localPod.SrcIp, peerPod.Name, peerPod.SrcIp) + if interNodeLinkType == wireutil.INTER_NODE_LINK_GRPC { + err = CreatGRPCChan(link, localPod, peerPod, meshnetClient, &cniArgs, ctx) + if err != nil { + log.Errorf("Add[%s]: !! Failed to create grpc wire. err: %v", string(cniArgs.K8S_POD_NAME), err) + return err + } + continue + } + // Creating koko's Vxlan struct + vxlan := makeVxlan(srcIntf, peerPod.SrcIp, link.Uid) + // Checking if interface already exists + iExist, _ := koko.IsExistLinkInNS(myVeth.NsName, myVeth.LinkName) + if iExist { // If VXLAN intf exists, we need to remove it first + log.Infof("Add[%s]: VXLAN intf already exists, removing it first", string(cniArgs.K8S_POD_NAME)) + if err := myVeth.RemoveVethLink(); err != nil { + log.Infof("Add[%s], Failed to remove a local stale VXLAN interface %s for pod %s", string(cniArgs.K8S_POD_NAME), myVeth.LinkName, localPod.Name) + return err + } + } + if err = koko.MakeVxLan(*myVeth, *vxlan); err != nil { + log.Infof("Add[%s]: Error when creating a Vxlan interface with koko: %s", string(cniArgs.K8S_POD_NAME), err) + return err + } + + // Now we need to make an API call to update the remote VTEP to point to us + payload := &mpb.RemotePod{ + NetNs: peerPod.NetNs, + IntfName: link.PeerIntf, + IntfIp: link.PeerIp, + PeerVtep: localPod.SrcIp, + Vni: link.Uid + vxlanBase, + KubeNs: string(cniArgs.K8S_POD_NAMESPACE), + NodeIntf: srcIntf, + } + + url := fmt.Sprintf("%s:%d", peerPod.SrcIp, wireutil.GRPCDefaultPort) + log.Infof("Add[%s]: Trying to do a remote update on %s", string(cniArgs.K8S_POD_NAME), url) + + remote, err := grpc.Dial(url, grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + log.Infof("Add[%s]: Failed to dial remote gRPC url %s", string(cniArgs.K8S_POD_NAME), url) + return err + } + remoteClient := mpb.NewRemoteClient(remote) + ok, err := remoteClient.Update(ctx, payload) + if err != nil || !ok.Response { + log.Infof("Add[%s]: Failed to do a remote update", string(cniArgs.K8S_POD_NAME)) + return err + } + log.Infof("Add[%s]: Successfully updated remote meshnet daemon", string(cniArgs.K8S_POD_NAME)) + } + } else { // This means that our peer pod hasn't come up yet + // Since there's no way of telling if our peer is going to be on this host or another, + // the only option is to do nothing, assuming that the peer POD will do all the plumbing when it comes up + log.Infof("Add[%s]: Peer pod %s@%s (link id %d) isn't alive yet, continuing", string(cniArgs.K8S_POD_NAME), peerPod.Name, link.PeerIntf, link.Uid) + // Here we need to set the skipped flag so that our peer can configure VEth interface when it comes up later + ok, err := meshnetClient.Skip(ctx, &mpb.SkipQuery{ + Pod: localPod.Name, + Peer: peerPod.Name, + LinkId: link.Uid, + KubeNs: string(cniArgs.K8S_POD_NAMESPACE), + }) + if err != nil || !ok.Response { + log.Errorf("Add[%s]: Failed to set a skipped flag on peer %s", string(cniArgs.K8S_POD_NAME), peerPod.Name) + return err + } + } + } + + return types.PrintResult(result, n.CNIVersion) +} + +// ------------------------------------------------------------------------------------------------- +// Deletes interfaces from a POD +func cmdDel(args *skel.CmdArgs) error { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + cniArgs := k8sArgs{} + if err := types.LoadArgs(args.Args, &cniArgs); err != nil { + return err + } + log.Infof("========> Processing DEL POD %s in namespace %s, container_id %s", string(cniArgs.K8S_POD_NAME), string(cniArgs.K8S_POD_NAMESPACE), cniArgs.K8S_POD_INFRA_CONTAINER_ID) + defer log.Infof("DONE ========> Processing DEL POD %s in namespace %s, container_id %s", string(cniArgs.K8S_POD_NAME), string(cniArgs.K8S_POD_NAMESPACE), cniArgs.K8S_POD_INFRA_CONTAINER_ID) + + log.WithFields(log.Fields{ + "args": fmt.Sprintf("%+v", args), + }).Info("DEL request arguments") + log.Info("Parsing cni .conf file") + n, result, err := loadConf(args.StdinData) + if err != nil { + return err + } + + conn, err := grpc.Dial(localDaemon, grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + log.Errorf("Failed to connect to local meshnetd on %s", localDaemon) + return err + } + defer conn.Close() + + meshnetClient := mpb.NewLocalClient(conn) + + log.Infof("Del: Retrieving pod's (%s@%s) metadata from meshnet daemon", string(cniArgs.K8S_POD_NAME), string(cniArgs.K8S_POD_NAMESPACE)) + localPod, err := meshnetClient.Get(ctx, &mpb.PodQuery{ + Name: string(cniArgs.K8S_POD_NAME), // getting details of the current pod. + KubeNs: string(cniArgs.K8S_POD_NAMESPACE), + }) + if err != nil { + log.Infof("Del: Pod %s:%s is not in topology returning. err:%v", string(cniArgs.K8S_POD_NAMESPACE), string(cniArgs.K8S_POD_NAME), err) + return types.PrintResult(result, n.CNIVersion) + } + // if the current containerID is not the topo's current containerID, exit here b/c this is a duplicated DEL call. + if localPod.ContainerId != args.ContainerID { + log.Infof("Del: Pod %s:%s is a duplicate; skipping", string(cniArgs.K8S_POD_NAMESPACE), string(cniArgs.K8S_POD_NAME)) + return nil + } + + /* Tell daemon to close the grpc tunnel for this pod netns (if any) */ + log.Infof("Del: Retrieving pod's metadata from meshnet daemon") + wireDef := mpb.WireDef{ + TopoNs: string(cniArgs.K8S_POD_NAMESPACE), + LocalPodName: string(cniArgs.K8S_POD_NAME), + } + + removResp, err := meshnetClient.RemGRPCWire(ctx, &wireDef) + if err != nil || !removResp.Response { + return fmt.Errorf("del: could not remove grpc wire: %v", err) + } + + localPodSrcIp := localPod.SrcIp + log.Infof("Del: Topology data still exists in CRs, cleaning up it's status") + // By setting srcIP and NetNS to "" we're marking this POD as dead + localPod.NetNs = "" + localPod.SrcIp = "" + localPod.ContainerId = "" + _, err = meshnetClient.SetAlive(ctx, localPod) + if err != nil { + return fmt.Errorf("del: could not set alive: %v", err) + } + + log.Infof("Del: Iterating over each link for clean-up") + for _, link := range localPod.Links { // Iterate over each link of the local pod + linkType := "veth" + // Initialising peer pod's metadata. Peer pod information is needed to determine if a link is of type GRPC or VxLAN. + // For GRPC link type there is some additional cleanup work needs to be performed. + log.Infof("Del: Pod %s is retrieving peer pod %s information from meshnet daemon", string(cniArgs.K8S_POD_NAME), link.PeerPod) + peerPod, _ := meshnetClient.Get(ctx, &mpb.PodQuery{ + Name: link.PeerPod, // getting peer pod detail. + KubeNs: string(cniArgs.K8S_POD_NAMESPACE), + }) + + if peerPod.SrcIp != localPodSrcIp { + // they are on different hosts + if interNodeLinkType == wireutil.INTER_NODE_LINK_GRPC { + // for this link bring the grpc wire down + err = MakeGRPCChanDown(link, localPod, peerPod, ctx) + if err != nil { + log.Errorf("Del: !! Failed to remove remote grpc wire. err: %v", err) + // no return + } + linkType = "grpc" + } else { + linkType = "vxlan" + } + } + // Creating koko's Veth struct for local intf + myVeth, err := makeVeth(args.Netns, link.LocalIntf, link.LocalIp) + if err != nil { + log.Infof("Del: Failed to construct koko Veth struct") + return err + } + + log.Infof("Del: Removing link %s", link.LocalIntf) + // API call to koko to remove local Veth link + if err = myVeth.RemoveVethLink(); err != nil { + // instead of failing, just log the error and move on + log.Errorf("Del: Error removing Veth link %s (%s) on pod %s: %v", link.LocalIntf, linkType, localPod.Name, err) + } + + // Setting reversed skipped flag so that this pod will try to connect veth pair on restart + log.Infof("Del: Setting skip-reverse flag on peer %s@%s(link id %d) for local interface %s", link.PeerPod, link.PeerIntf, link.Uid, link.LocalIntf) + ok, err := meshnetClient.SkipReverse(ctx, &mpb.SkipQuery{ + Pod: localPod.Name, + Peer: link.PeerPod, + LinkId: link.Uid, + KubeNs: string(cniArgs.K8S_POD_NAMESPACE), + }) + if err != nil || !ok.Response { + log.Errorf("Del: Failed to set skip reversed flag on our peer %s", link.PeerPod) + return err + } + } + return nil +} + +func SetInterNodeLinkType() { + // TODO: Find a more appropriate (if any) way to figure out intended link type + // As of today, daemon gets the intended link type from env INTER_NODE_LINK_TYPE + // which is set by deployment file. The daemon further propagates this to plugin + // via means of file on host (which is read below) containing the value GRPC or VXLAN + b, err := os.ReadFile("/etc/cni/net.d/meshnet-inter-node-link-type") + if err != nil { + log.Warningf("Could not read iner node link type: %v", err) + // use the default value + return + } + + interNodeLinkType = string(b) +} + +// ------------------------------------------------------------------------------------------------- +func main() { + fp, err := os.OpenFile("/var/log/meshnet-cni.log", os.O_APPEND|os.O_CREATE|os.O_RDWR, 0666) + if err == nil { + log.SetOutput(fp) + } + + SetInterNodeLinkType() + log.Infof("INTER_NODE_LINK_TYPE: %v", interNodeLinkType) + + retCode := 0 + e := skel.PluginMainWithError(cmdAdd, cmdGet, cmdDel, version.All, "CNI plugin meshnet v0.3.0") + if e != nil { + log.Errorf("failed to run meshnet cni: %v", e.Print()) + retCode = 1 + } + log.Infof("K8S invoked meshnet cni") + fp.Close() + os.Exit(retCode) +} + +func cmdGet(args *skel.CmdArgs) error { + cniArgs := k8sArgs{} + if err := types.LoadArgs(args.Args, &cniArgs); err != nil { + return err + } + log.Infof("cmdGet called: %+v", args) + return fmt.Errorf("not implemented") +} + +//------------------------------------------------------------------------------------------------- diff --git a/third_party/meshnet/tests/2node-sts.yml b/third_party/meshnet/tests/2node-sts.yml new file mode 100644 index 000000000..f28879339 --- /dev/null +++ b/third_party/meshnet/tests/2node-sts.yml @@ -0,0 +1,66 @@ +--- +apiVersion: networkop.co.uk/v1beta1 +kind: Topology +metadata: + name: r1-0 +spec: + links: + - uid: 1 + peer_pod: r2-0 + local_intf: eth1 + local_ip: 12.12.12.1/24 + peer_intf: eth1 + peer_ip: 12.12.12.2/24 +--- +apiVersion: networkop.co.uk/v1beta1 +kind: Topology +metadata: + name: r2-0 +spec: + links: + - uid: 1 + peer_pod: r1-0 + local_intf: eth1 + local_ip: 12.12.12.2/24 + peer_intf: eth1 + peer_ip: 12.12.12.1/24 +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: r1 +spec: + selector: + matchLabels: + topo: r1 + replicas: 1 + serviceName: r2 + template: + metadata: + labels: + topo: r1 + spec: + containers: + - image: alpine + name: pod + command: ["/bin/sh", "-c", "sleep 2000000000000"] +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: r2 +spec: + selector: + matchLabels: + topo: r2 + replicas: 1 + serviceName: r2 + template: + metadata: + labels: + topo: r2 + spec: + containers: + - image: alpine + name: pod + command: ["/bin/sh", "-c", "sleep 2000000000000"] diff --git a/third_party/meshnet/tests/2node.yml b/third_party/meshnet/tests/2node.yml new file mode 100644 index 000000000..dd16d2ff5 --- /dev/null +++ b/third_party/meshnet/tests/2node.yml @@ -0,0 +1,69 @@ +--- +apiVersion: v1 +kind: List +items: + - apiVersion: networkop.co.uk/v1beta1 + kind: Topology + metadata: + name: r1 + spec: + links: + - uid: 1 + peer_pod: r2 + local_intf: eth1 + local_ip: 12.12.12.1/24 + peer_intf: eth1 + peer_ip: 12.12.12.2/24 + - apiVersion: networkop.co.uk/v1beta1 + kind: Topology + metadata: + name: r2 + spec: + links: + - uid: 1 + peer_pod: r1 + local_intf: eth1 + local_ip: 12.12.12.2/24 + peer_intf: eth1 + peer_ip: 12.12.12.1/24 + - apiVersion: networkop.co.uk/v1beta1 + kind: Topology + metadata: + name: r3 + spec: + links: + - uid: 2 + peer_pod: r4 + local_intf: eth1 + peer_intf: eth1 + - apiVersion: v1 + kind: Pod + metadata: + name: r1 + spec: + containers: + - image: alpine + name: pod + command: ["/bin/sh", "-c", "sleep 2000000000000"] + - apiVersion: v1 + kind: Pod + metadata: + name: r2 + labels: + test: 2node + spec: + containers: + - image: alpine + name: pod + command: ["/bin/sh", "-c", "sleep 2000000000000"] + - apiVersion: v1 + kind: Pod + metadata: + name: r4 + labels: + test: 2node + spec: + containers: + - image: alpine + name: pod + command: ["/bin/sh", "-c", "sleep 2000000000000"] diff --git a/third_party/meshnet/tests/3node-mlink.yml b/third_party/meshnet/tests/3node-mlink.yml new file mode 100644 index 000000000..f0c3474b0 --- /dev/null +++ b/third_party/meshnet/tests/3node-mlink.yml @@ -0,0 +1,210 @@ +--- +apiVersion: v1 +kind: Namespace +metadata: + name: "mlink" +--- +apiVersion: v1 +kind: List +items: + - apiVersion: networkop.co.uk/v1beta1 + kind: Topology + metadata: + name: r1 + namespace: "mlink" + spec: + links: + - uid: 1 + peer_pod: r2 + local_intf: eth1 + peer_intf: eth1 + local_ip: 10.10.10.1/24 + peer_ip: 10.10.10.2/24 + - uid: 2 + peer_pod: r2 + local_intf: eth2 + peer_intf: eth2 + local_ip: 11.11.11.1/24 + peer_ip: 11.11.11.2/24 + - uid: 3 + peer_pod: r2 + local_intf: eth3 + peer_intf: eth3 + local_ip: 12.12.12.1/24 + peer_ip: 12.12.12.2/24 + - uid: 4 + peer_pod: r2 + local_intf: eth4 + peer_intf: eth4 + local_ip: 13.13.13.1/24 + peer_ip: 13.13.13.2/24 + - uid: 5 + peer_pod: r3 + local_intf: eth5 + peer_intf: eth1 + local_ip: 20.20.20.1/24 + peer_ip: 20.20.20.2/24 + - uid: 6 + peer_pod: r3 + local_intf: eth6 + peer_intf: eth2 + local_ip: 21.21.21.1/24 + peer_ip: 21.21.21.2/24 + - uid: 7 + peer_pod: r3 + local_intf: eth7 + peer_intf: eth3 + local_ip: 22.22.22.1/24 + peer_ip: 22.22.22.2/24 + - uid: 8 + peer_pod: r3 + local_intf: eth8 + peer_intf: eth4 + local_ip: 23.23.23.1/24 + peer_ip: 23.23.23.2/24 + - apiVersion: networkop.co.uk/v1beta1 + kind: Topology + metadata: + name: r2 + namespace: "mlink" + spec: + links: + - uid: 1 + peer_pod: r1 + local_intf: eth1 + peer_intf: eth1 + local_ip: 10.10.10.2/24 + peer_ip: 10.10.10.1/24 + - uid: 2 + peer_pod: r1 + local_intf: eth2 + peer_intf: eth2 + local_ip: 11.11.11.2/24 + peer_ip: 11.11.11.1/24 + - uid: 3 + peer_pod: r1 + local_intf: eth3 + peer_intf: eth3 + local_ip: 12.12.12.2/24 + peer_ip: 12.12.12.1/24 + - uid: 4 + peer_pod: r1 + local_intf: eth4 + peer_intf: eth4 + local_ip: 13.13.13.2/24 + peer_ip: 13.13.13.1/24 + - uid: 9 + peer_pod: r3 + local_intf: eth5 + peer_intf: eth5 + local_ip: 30.30.30.1/24 + peer_ip: 30.30.30.2/24 + - uid: 10 + peer_pod: r3 + local_intf: eth6 + peer_intf: eth6 + local_ip: 31.31.31.1/24 + peer_ip: 31.31.31.2/24 + - uid: 11 + peer_pod: r3 + local_intf: eth7 + peer_intf: eth7 + local_ip: 32.32.32.1/24 + peer_ip: 32.32.32.2/24 + - uid: 12 + peer_pod: r3 + local_intf: eth8 + peer_intf: eth8 + local_ip: 33.33.33.1/24 + peer_ip: 33.33.33.2/24 + - apiVersion: networkop.co.uk/v1beta1 + kind: Topology + metadata: + name: r3 + namespace: "mlink" + spec: + links: + - uid: 5 + peer_pod: r1 + local_intf: eth1 + peer_intf: eth5 + local_ip: 20.20.20.2/24 + peer_ip: 20.20.20.1/24 + - uid: 6 + peer_pod: r1 + local_intf: eth2 + peer_intf: eth6 + local_ip: 21.21.21.2/24 + peer_ip: 21.21.21.1/24 + - uid: 7 + peer_pod: r1 + local_intf: eth3 + peer_intf: eth7 + local_ip: 22.22.22.2/24 + peer_ip: 22.22.22.1/24 + - uid: 8 + peer_pod: r1 + local_intf: eth4 + peer_intf: eth8 + local_ip: 23.23.23.2/24 + peer_ip: 23.23.23.1/24 + - uid: 9 + peer_pod: r2 + local_intf: eth5 + peer_intf: eth5 + local_ip: 30.30.30.2/24 + peer_ip: 30.30.30.1/24 + - uid: 10 + peer_pod: r2 + local_intf: eth6 + peer_intf: eth6 + local_ip: 31.31.31.2/24 + peer_ip: 31.31.31.1/24 + - uid: 11 + peer_pod: r2 + local_intf: eth7 + peer_intf: eth7 + local_ip: 32.32.32.2/24 + peer_ip: 32.32.32.1/24 + - uid: 12 + peer_pod: r2 + local_intf: eth8 + peer_intf: eth8 + local_ip: 33.33.33.2/24 + peer_ip: 33.33.33.1/24 + - apiVersion: v1 + kind: Pod + metadata: + name: r1 + namespace: "mlink" + labels: + test: 3node-mlink + spec: + containers: + - image: alpine + name: pod + command: ["/bin/sh", "-c", "sleep 2000000000000"] + - apiVersion: v1 + kind: Pod + metadata: + name: r2 + namespace: "mlink" + labels: + test: 3node-mlink + spec: + containers: + - image: alpine + name: pod + command: ["/bin/sh", "-c", "sleep 2000000000000"] + - apiVersion: v1 + kind: Pod + metadata: + name: r3 + namespace: "mlink" + labels: + test: 3node-mlink + spec: + containers: + - image: alpine + name: pod + command: ["/bin/sh", "-c", "sleep 2000000000000"] diff --git a/third_party/meshnet/tests/3node.yml b/third_party/meshnet/tests/3node.yml new file mode 100644 index 000000000..d49d746db --- /dev/null +++ b/third_party/meshnet/tests/3node.yml @@ -0,0 +1,91 @@ +--- +apiVersion: v1 +kind: List +items: + - apiVersion: networkop.co.uk/v1beta1 + kind: Topology + metadata: + name: r1 + spec: + links: + - uid: 1 + peer_pod: r2 + local_intf: eth1 + peer_intf: eth1 + local_ip: 12.12.12.1/24 + peer_ip: 12.12.12.2/24 + - uid: 2 + peer_pod: r3 + local_intf: eth2 + peer_intf: eth1 + local_ip: 13.13.13.1/24 + peer_ip: 13.13.13.3/24 + - apiVersion: networkop.co.uk/v1beta1 + kind: Topology + metadata: + name: r2 + spec: + links: + - uid: 1 + peer_pod: r1 + local_intf: eth1 + peer_intf: eth1 + local_ip: 12.12.12.2/24 + peer_ip: 12.12.12.1/24 + - uid: 3 + peer_pod: r3 + local_intf: eth2 + peer_intf: eth2 + local_ip: 23.23.23.2/24 + peer_ip: 23.23.23.3/24 + - apiVersion: networkop.co.uk/v1beta1 + kind: Topology + metadata: + name: r3 + spec: + links: + - uid: 2 + peer_pod: r1 + local_intf: eth1 + peer_intf: eth2 + local_ip: 13.13.13.3/24 + peer_ip: 13.13.13.1/24 + - uid: 3 + peer_pod: r2 + local_intf: eth2 + peer_intf: eth2 + local_ip: 23.23.23.3/24 + peer_ip: 23.23.23.2/24 + - apiVersion: v1 + kind: Pod + metadata: + name: r1 + labels: + test: 3node + spec: + containers: + - image: alpine + name: pod + command: ["/bin/sh", "-c", "sleep 2000000000000"] + - apiVersion: v1 + kind: Pod + metadata: + name: r2 + labels: + test: 3node + spec: + containers: + - image: alpine + name: pod + command: ["/bin/sh", "-c", "sleep 2000000000000"] + - apiVersion: v1 + kind: Pod + metadata: + name: r3 + labels: + test: 3node + spec: + containers: + - image: alpine + name: pod + command: ["/bin/sh", "-c", "sleep 2000000000000"] diff --git a/third_party/meshnet/tests/5node.yml b/third_party/meshnet/tests/5node.yml new file mode 100644 index 000000000..7da3deda4 --- /dev/null +++ b/third_party/meshnet/tests/5node.yml @@ -0,0 +1,175 @@ +--- +apiVersion: v1 +kind: List +items: + - apiVersion: networkop.co.uk/v1beta1 + kind: Topology + metadata: + name: r1 + spec: + links: + - uid: 1 + peer_pod: r2 + local_intf: eth1 + peer_intf: eth1 + local_ip: 12.12.12.1/24 + peer_ip: 12.12.12.2/24 + - uid: 2 + peer_pod: r3 + local_intf: eth2 + peer_intf: eth2 + local_ip: 13.13.13.1/24 + peer_ip: 13.13.13.3/24 + - uid: 3 + peer_pod: r5 + local_intf: eth3 + peer_intf: eth1 + local_ip: 15.15.15.1/24 + peer_ip: 15.15.15.5/24 + - apiVersion: networkop.co.uk/v1beta1 + kind: Topology + metadata: + name: r2 + spec: + links: + - uid: 1 + peer_pod: r1 + local_intf: eth1 + peer_intf: eth1 + local_ip: 12.12.12.2/24 + peer_ip: 12.12.12.1/24 + - uid: 4 + peer_pod: r4 + local_intf: eth2 + peer_intf: eth1 + local_ip: 24.24.24.2/24 + peer_ip: 24.24.24.4/24 + - uid: 5 + peer_pod: r5 + local_intf: eth3 + peer_intf: eth2 + local_ip: 25.25.25.2/24 + peer_ip: 25.25.25.5/24 + - apiVersion: networkop.co.uk/v1beta1 + kind: Topology + metadata: + name: r3 + spec: + links: + - uid: 2 + peer_pod: r1 + local_intf: eth2 + peer_intf: eth2 + local_ip: 13.13.13.3/24 + peer_ip: 13.13.13.1/24 + - uid: 6 + peer_pod: r4 + local_intf: eth1 + peer_intf: eth2 + local_ip: 34.34.34.3/24 + peer_ip: 34.34.34.4/24 + - uid: 8 + peer_pod: r5 + local_intf: eth3 + peer_intf: eth3 + local_ip: 35.35.35.3/24 + peer_ip: 35.35.35.5/24 + - apiVersion: networkop.co.uk/v1beta1 + kind: Topology + metadata: + name: r4 + spec: + links: + - uid: 4 + peer_pod: r2 + local_intf: eth1 + peer_intf: eth2 + local_ip: 24.24.24.4/24 + peer_ip: 24.24.24.2/24 + - uid: 6 + peer_pod: r3 + local_intf: eth2 + peer_intf: eth1 + local_ip: 34.34.34.4/24 + peer_ip: 34.34.34.3/24 + - uid: 7 + peer_pod: r5 + local_intf: eth3 + peer_intf: eth4 + local_ip: 45.45.45.4/24 + peer_ip: 45.45.45.5/24 + - apiVersion: networkop.co.uk/v1beta1 + kind: Topology + metadata: + name: r5 + spec: + links: + - uid: 3 + peer_pod: r1 + local_intf: eth1 + peer_intf: eth3 + local_ip: 15.15.15.5/24 + peer_ip: 15.15.15.1/24 + - uid: 5 + peer_pod: r2 + local_intf: eth2 + peer_intf: eth3 + local_ip: 25.25.25.5/24 + peer_ip: 25.25.25.2/24 + - uid: 7 + peer_pod: r4 + local_intf: eth4 + peer_intf: eth3 + local_ip: 45.45.45.5/24 + peer_ip: 45.45.45.4/24 + - uid: 8 + peer_pod: r3 + local_intf: eth3 + peer_intf: eth3 + local_ip: 35.35.35.5/24 + peer_ip: 35.35.35.3/24 + - apiVersion: v1 + kind: Pod + metadata: + name: r1 + spec: + containers: + - image: alpine + name: pod + command: ["/bin/sh", "-c", "sleep 2000000000000"] + - apiVersion: v1 + kind: Pod + metadata: + name: r2 + spec: + containers: + - image: alpine + name: pod + command: ["/bin/sh", "-c", "sleep 2000000000000"] + - apiVersion: v1 + kind: Pod + metadata: + name: r3 + spec: + containers: + - image: alpine + name: pod + command: ["/bin/sh", "-c", "sleep 2000000000000"] + - apiVersion: v1 + kind: Pod + metadata: + name: r4 + spec: + containers: + - image: alpine + name: pod + command: ["/bin/sh", "-c", "sleep 2000000000000"] + - apiVersion: v1 + kind: Pod + metadata: + name: r5 + spec: + containers: + - image: alpine + name: pod + command: ["/bin/sh", "-c", "sleep 2000000000000"] diff --git a/third_party/meshnet/tests/macvlan.yml b/third_party/meshnet/tests/macvlan.yml new file mode 100644 index 000000000..c56e25009 --- /dev/null +++ b/third_party/meshnet/tests/macvlan.yml @@ -0,0 +1,60 @@ +--- +apiVersion: v1 +kind: List +items: + - apiVersion: networkop.co.uk/v1beta1 + kind: Topology + metadata: + name: r1 + spec: + links: + - uid: 1 + peer_pod: r2 + local_intf: eth1 + local_ip: 12.12.12.1/24 + peer_intf: eth1 + peer_ip: 12.12.12.2/24 + - apiVersion: networkop.co.uk/v1beta1 + kind: Topology + metadata: + name: r2 + spec: + links: + - uid: 1 + peer_pod: r1 + local_intf: eth1 + local_ip: 12.12.12.2/24 + peer_intf: eth1 + peer_ip: 12.12.12.1/24 + - uid: 2 + peer_pod: localhost + local_intf: eth2 + local_ip: 172.17.0.199/16 + peer_intf: eth0 + - apiVersion: v1 + kind: Pod + metadata: + name: r1 + spec: + containers: + - image: alpine + name: pod + command: ["/bin/sh", "-c", "sleep 2000000000000"] + - apiVersion: v1 + kind: Pod + metadata: + name: r2 + spec: + containers: + - image: alpine + name: pod + command: ["/bin/sh", "-c", "sleep 2000000000000"] + - apiVersion: v1 + kind: Pod + metadata: + name: r4 + spec: + containers: + - image: alpine + name: pod + command: ["/bin/sh", "-c", "sleep 2000000000000"] diff --git a/third_party/meshnet/utils/wireutil/wire-util.go b/third_party/meshnet/utils/wireutil/wire-util.go new file mode 100644 index 000000000..cfd11144b --- /dev/null +++ b/third_party/meshnet/utils/wireutil/wire-util.go @@ -0,0 +1,51 @@ +package wireutil + +import ( + "fmt" + + "github.com/containernetworking/plugins/pkg/ns" + "github.com/safchain/ethtool" +) + +const ( + GRPCDefaultPort = 51111 + INTER_NODE_LINK_VXLAN = "VXLAN" + INTER_NODE_LINK_GRPC = "GRPC" +) + +func SetTxChecksumOff(intfName, nsName string) error { + var vethNs ns.NetNS + var err error + + if vethNs, err = ns.GetNS(nsName); err != nil { + return fmt.Errorf("could not get required ns %s: %v", nsName, err) + } + defer vethNs.Close() + + err = vethNs.Do(func(_ ns.NetNS) error { + etlHndl, err := ethtool.NewEthtool() + if err != nil { + return fmt.Errorf("could not open ethtool handle: %v", err) + } + defer etlHndl.Close() + + etlConf := map[string]bool{ + "tx-checksum-ipv4": false, + "tx-checksum-ipv6": false, + "tx-checksum-ip-generic": false, + "tx-tcp-segmentation": false, + "tx-tcp6-segmentation": false, + "tx-checksum-fcoe-crc": false, + "tx-checksum-sctp": false, + "tx-tcp-ecn-segmentation": false, + "tx-tcp-mangleid-segmentation": false, + } + + err = etlHndl.Change(intfName, etlConf) + if err != nil { + return fmt.Errorf("could not set tx checksum on interface %s, ns %s: %v", intfName, nsName, err) + } + return nil + }) + return err +} diff --git a/topo/node/alpine/alpine.go b/topo/node/alpine/alpine.go index 43ee15b23..56f452062 100644 --- a/topo/node/alpine/alpine.go +++ b/topo/node/alpine/alpine.go @@ -22,7 +22,7 @@ import ( "github.com/openconfig/kne/topo/node" "google.golang.org/protobuf/proto" - "k8s.io/utils/pointer" + "k8s.io/utils/ptr" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -123,7 +123,7 @@ func (n *Node) CreatePod(ctx context.Context) error { Resources: node.ToResourceRequirements(pb.Constraints), ImagePullPolicy: "IfNotPresent", SecurityContext: &corev1.SecurityContext{ - Privileged: pointer.Bool(true), + Privileged: ptr.To(true), }, }} @@ -193,14 +193,14 @@ func (n *Node) CreatePod(ctx context.Context) error { Resources: node.ToResourceRequirements(pb.Constraints), ImagePullPolicy: "IfNotPresent", SecurityContext: &corev1.SecurityContext{ - Privileged: pointer.Bool(true), + Privileged: ptr.To(true), }, VolumeMounts: extraMounts, } alpineContainers = append(alpineContainers, containerSpec) default: // Only Dataplane container is supported as the custom container - return fmt.Errorf("Alpine supports only 1 custom container, %d provided.", numContainers) + return fmt.Errorf("alpine supports only 1 custom container, %d provided", numContainers) } } @@ -223,11 +223,11 @@ func (n *Node) CreatePod(ctx context.Context) error { }, ImagePullPolicy: "IfNotPresent", SecurityContext: &corev1.SecurityContext{ - Privileged: pointer.Bool(true), + Privileged: ptr.To(true), }, }}, Containers: alpineContainers, - TerminationGracePeriodSeconds: pointer.Int64(0), + TerminationGracePeriodSeconds: ptr.To(int64(0)), NodeSelector: map[string]string{}, Affinity: &corev1.Affinity{ PodAntiAffinity: &corev1.PodAntiAffinity{ @@ -261,7 +261,7 @@ func (n *Node) CreatePod(ctx context.Context) error { MountPath: pb.Config.ConfigPath + "/" + pb.Config.ConfigFile, ReadOnly: true, } - if vol.VolumeSource.ConfigMap != nil { + if vol.ConfigMap != nil { vm.SubPath = pb.Config.ConfigFile } for i, c := range pod.Spec.Containers { diff --git a/topo/node/alpine/alpine_test.go b/topo/node/alpine/alpine_test.go index 56a447816..9356a1adc 100644 --- a/topo/node/alpine/alpine_test.go +++ b/topo/node/alpine/alpine_test.go @@ -29,7 +29,7 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" kfake "k8s.io/client-go/kubernetes/fake" - "k8s.io/utils/pointer" + "k8s.io/utils/ptr" ) func TestNew(t *testing.T) { @@ -200,7 +200,7 @@ func TestCreatePod(t *testing.T) { Requests: corev1.ResourceList{}}, ImagePullPolicy: "IfNotPresent", SecurityContext: &corev1.SecurityContext{ - Privileged: pointer.Bool(true), + Privileged: ptr.To(true), }, }, wantDpCtr: corev1.Container{ @@ -212,7 +212,7 @@ func TestCreatePod(t *testing.T) { Requests: corev1.ResourceList{}}, ImagePullPolicy: "IfNotPresent", SecurityContext: &corev1.SecurityContext{ - Privileged: pointer.Bool(true), + Privileged: ptr.To(true), }, VolumeMounts: []corev1.VolumeMount{{Name: "files", MountPath: "/files"}}, }, @@ -246,7 +246,7 @@ func TestCreatePod(t *testing.T) { Requests: corev1.ResourceList{}}, ImagePullPolicy: "IfNotPresent", SecurityContext: &corev1.SecurityContext{ - Privileged: pointer.Bool(true), + Privileged: ptr.To(true), }, VolumeMounts: []corev1.VolumeMount{{ Name: "startup-config-volume", @@ -264,7 +264,7 @@ func TestCreatePod(t *testing.T) { Requests: corev1.ResourceList{}}, ImagePullPolicy: "IfNotPresent", SecurityContext: &corev1.SecurityContext{ - Privileged: pointer.Bool(true), + Privileged: ptr.To(true), }, VolumeMounts: []corev1.VolumeMount{{ Name: "files", @@ -297,7 +297,7 @@ func TestCreatePod(t *testing.T) { Requests: corev1.ResourceList{}}, ImagePullPolicy: "IfNotPresent", SecurityContext: &corev1.SecurityContext{ - Privileged: pointer.Bool(true), + Privileged: ptr.To(true), }, }, }} diff --git a/topo/node/arista/arista.go b/topo/node/arista/arista.go index e3c70a551..6eb5a9146 100644 --- a/topo/node/arista/arista.go +++ b/topo/node/arista/arista.go @@ -259,7 +259,7 @@ func (n *Node) CreateCRD(ctx context.Context) error { }, } for label, v := range proto.GetLabels() { - device.ObjectMeta.Labels[label] = v + device.Labels[label] = v } for _, service := range proto.GetServices() { insidePort := service.Inside @@ -396,7 +396,7 @@ func (n *Node) ConfigPush(ctx context.Context, r io.Reader) error { } if resp.Failed == nil { - log.Infof("%s - finished config push", n.Impl.Proto.Name) + log.Infof("%s - finished config push", n.Proto.Name) } return resp.Failed @@ -422,7 +422,7 @@ func (n *Node) ResetCfg(ctx context.Context) error { } if resp.Failed == nil { - log.Infof("%s - finshed resetting config", n.Name()) + log.Infof("%s - finished resetting config", n.Name()) } return resp.Failed @@ -504,7 +504,7 @@ func (n *Node) FixInterfaces() error { for k, v := range n.Proto.Interfaces { switch { default: - return fmt.Errorf("Unrecognized interface name: %s", v.Name) + return fmt.Errorf("unrecognized interface name: %s", v.Name) case !strings.HasPrefix(k, "eth"), ethIntfRe.MatchString(v.Name), mgmtIntfRe.MatchString(v.Name): case v.Name == "": n.Proto.Interfaces[k].Name = fmt.Sprintf("Ethernet%s", strings.TrimPrefix(k, "eth")) diff --git a/topo/node/arista/arista_test.go b/topo/node/arista/arista_test.go index 9a2ede06e..754d1bd98 100644 --- a/topo/node/arista/arista_test.go +++ b/topo/node/arista/arista_test.go @@ -87,7 +87,7 @@ func TestNew(t *testing.T) { }, }, }, - wantErr: "Unrecognized interface name: Ethernet1/2/3/4", + wantErr: "unrecognized interface name: Ethernet1/2/3/4", }, { desc: "invalid eth intfs 2", nImpl: &node.Impl{ @@ -97,7 +97,7 @@ func TestNew(t *testing.T) { }, }, }, - wantErr: "Unrecognized interface name: Ethernet", + wantErr: "unrecognized interface name: Ethernet", }, { desc: "invalid management intfs 1", nImpl: &node.Impl{ @@ -109,7 +109,7 @@ func TestNew(t *testing.T) { }, }, }, - wantErr: "Unrecognized interface name: Management1/2/3", + wantErr: "unrecognized interface name: Management1/2/3", }, { desc: "invalid management intfs 2", nImpl: &node.Impl{ @@ -119,7 +119,7 @@ func TestNew(t *testing.T) { }, }, }, - wantErr: "Unrecognized interface name: Management", + wantErr: "unrecognized interface name: Management", }, { desc: "default check with empty topo proto", nImpl: &node.Impl{ @@ -457,7 +457,7 @@ func TestCRD(t *testing.T) { Proto: tt.proto, }, } - node.Impl.Proto.Name = name + node.Proto.Name = name err := node.CreateCRD(ctx) if s := errdiff.Check(err, tt.wantErr); s != "" { t.Errorf("New() unexpected err: %s", s) @@ -623,7 +623,7 @@ func TestStatus(t *testing.T) { Proto: &topopb.Node{}, }, } - node.Impl.Proto.Name = name + node.Proto.Name = name status, err := node.Status(ctx) if s := errdiff.Check(err, tt.cantWatch); s != "" { t.Errorf("Status() unexpected err: %s", s) diff --git a/topo/node/cisco/cisco.go b/topo/node/cisco/cisco.go index 88bd38a5b..76298deca 100644 --- a/topo/node/cisco/cisco.go +++ b/topo/node/cisco/cisco.go @@ -37,7 +37,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/rest" log "k8s.io/klog/v2" - "k8s.io/utils/pointer" + "k8s.io/utils/ptr" ) const ( @@ -161,7 +161,7 @@ func (n *Node) Create(ctx context.Context) error { initContainerImage = node.DefaultInitContainerImage } secContext := &corev1.SecurityContext{ - Privileged: pointer.Bool(true), + Privileged: ptr.To(true), } tty := false stdin := false @@ -169,8 +169,8 @@ func (n *Node) Create(ctx context.Context) error { // terminal. This is not required for 8000e nodes. if pb.Model == ModelXRD { secContext = &corev1.SecurityContext{ - Privileged: pointer.Bool(true), - RunAsUser: pointer.Int64(0), + Privileged: ptr.To(true), + RunAsUser: ptr.To(int64(0)), Capabilities: &corev1.Capabilities{ Add: []corev1.Capability{"SYS_ADMIN"}, }, @@ -221,7 +221,7 @@ func (n *Node) Create(ctx context.Context) error { }, }, }}, - TerminationGracePeriodSeconds: pointer.Int64(0), + TerminationGracePeriodSeconds: ptr.To(int64(0)), NodeSelector: map[string]string{}, Affinity: &corev1.Affinity{ PodAntiAffinity: &corev1.PodAntiAffinity{ @@ -243,7 +243,7 @@ func (n *Node) Create(ctx context.Context) error { }, } for label, v := range n.GetProto().GetLabels() { - pod.ObjectMeta.Labels[label] = v + pod.Labels[label] = v } if pb.Config.ConfigData != nil { vol, err := n.CreateConfig(ctx) @@ -256,7 +256,7 @@ func (n *Node) Create(ctx context.Context) error { MountPath: pb.Config.ConfigPath + "/" + pb.Config.ConfigFile, ReadOnly: true, } - if vol.VolumeSource.ConfigMap != nil { + if vol.ConfigMap != nil { vm.SubPath = pb.Config.ConfigFile } for i, c := range pod.Spec.Containers { @@ -277,10 +277,10 @@ func (n *Node) Create(ctx context.Context) error { } // DefaultNodeConstraints returns default node constraints for CISCO. -// If the model for 8000e is specificied correctly it returns defaults for 8000e. +// If the model for 8000e is specified correctly it returns defaults for 8000e. // Otherwise, it returns defaults for XRD by default. func (n *Node) DefaultNodeConstraints() node.Constraints { - if n.Impl == nil || n.Impl.Proto == nil { + if n.Impl == nil || n.Proto == nil { return defaultXRDConstraints } switch n.GetProto().Model { @@ -292,7 +292,7 @@ func (n *Node) DefaultNodeConstraints() node.Constraints { return defaultXRDConstraints } -// validateHostConstraints - Validates host contraints through the default node's implementation. It skips the validation optionally +// validateHostConstraints - Validates host constraints through the default node's implementation. It skips the validation optionally // based on skipValidation flag which is useful for unit tests func validateHostConstraints(n *Node, skipValidation bool) error { if skipValidation { @@ -409,7 +409,10 @@ func getCiscoInterfaceID(pb *tpb.Node, eth string) (string, error) { return pb.Interfaces[eth].Name, nil } // ethWithIDRegx.MatchString(eth) was successful, so no need to do extra check here - ethID, _ := strconv.Atoi(ethRegx.Split(eth, -1)[1]) + ethID, err := strconv.Atoi(ethRegx.Split(eth, -1)[1]) + if err != nil { + return "", fmt.Errorf("failed to parse interface ID from %q: %w", eth, err) + } eid := ethID - 1 switch pb.Model { case "8201": @@ -657,8 +660,8 @@ func endTelnet(d *scraplinetwork.Driver) error { // sending ctrl + ] (^]) to end telnet session gracefully. Otherwise, the next connection can be blocked. endTelnet := string(byte(29)) + " quit\n" log.Infof("Closing the connection by sending ctrl+] quit \n") - d.SendCommand(endTelnet) - return nil + _, err := d.SendCommand(endTelnet) + return err } func (n *Node) ResetCfg(ctx context.Context) error { @@ -671,9 +674,9 @@ func (n *Node) ResetCfg(ctx context.Context) error { var cmd string if n.Proto.Model == ModelXRD { - // Copy the snooped management interface config from a know location and the startup config from + // Copy the snooped management interface config from a known location and the startup config from // the mounted location so it can be applied. This is required to preserve the snooped management - // IP addres and since the "copy" xr_cli command can only access files on disk 0/1. + // IP address and since the "copy" xr_cli command can only access files on disk 0/1. startup_config := n.Proto.Config.Env["XR_EVERY_BOOT_CONFIG"] if startup_config == "" { return status.Errorf(codes.InvalidArgument, "XR_EVERY_BOOT_CONFIG is not set") @@ -748,7 +751,7 @@ func (n *Node) ConfigPush(ctx context.Context, r io.Reader) error { return err } if resp.Failed == nil { - log.Infof("%s - finished config push", n.Impl.Proto.Name) + log.Infof("%s - finished config push", n.Proto.Name) } return resp.Failed @@ -757,7 +760,7 @@ func (n *Node) ConfigPush(ctx context.Context, r io.Reader) error { func (n *Node) GenerateSelfSigned(context.Context) error { // IOS XR automatically generates a self-signed certificate when gRPC is first enabled. // If the startup configuration contains a gRPC configuration, or if the user configures - // gRPC after bootup, the self-signed cert will automatically be created and used. + // gRPC after boot up, the self-signed cert will automatically be created and used. return status.Errorf(codes.Unimplemented, "certificate generation is not supported") } diff --git a/topo/node/cisco/cisco_test.go b/topo/node/cisco/cisco_test.go index 7950127b0..c0d1eec61 100644 --- a/topo/node/cisco/cisco_test.go +++ b/topo/node/cisco/cisco_test.go @@ -41,7 +41,10 @@ func init() { } func defaultNode(pb *tpb.Node) *tpb.Node { - node, _ := defaults(pb) + node, err := defaults(pb) + if err != nil { + panic(err) + } return node } @@ -945,8 +948,14 @@ func TestNodeStatus(t *testing.T) { }() podIsUpRegex = regexp.MustCompile("fake log") // this is the expected log from a fake pod } - nImpl, _ := New(tt.ni) - n, _ := nImpl.(*Node) + nImpl, err := New(tt.ni) + if err != nil { + t.Fatalf("New() failed: %v", err) + } + n, ok := nImpl.(*Node) + if !ok { + t.Fatalf("nImpl is not a *Node") + } status, err := n.Status(ctx) if err != nil { t.Errorf("Error is not expected for Node Status") diff --git a/topo/node/drivenets/drivenets.go b/topo/node/drivenets/drivenets.go index 56fb895a4..85be438f9 100644 --- a/topo/node/drivenets/drivenets.go +++ b/topo/node/drivenets/drivenets.go @@ -121,7 +121,7 @@ var clientFn = func(c *rest.Config) (clientset.Interface, error) { } func (n *Node) Create(ctx context.Context) error { - if n.Impl.Proto.Model != modelCdnos { + if n.Proto.Model != modelCdnos { return fmt.Errorf("cannot create an instance of an unknown model") } return n.cdnosCreate(ctx) @@ -200,7 +200,7 @@ func (n *Node) cdnosCreate(ctx context.Context) error { } func (n *Node) Status(ctx context.Context) (node.Status, error) { - if n.Impl.Proto.Model != modelCdnos { + if n.Proto.Model != modelCdnos { return node.StatusUnknown, fmt.Errorf("invalid model specified") } return n.cdnosStatus(ctx) @@ -228,7 +228,7 @@ func (n *Node) cdnosStatus(ctx context.Context) (node.Status, error) { } func (n *Node) Delete(ctx context.Context) error { - if n.Impl.Proto.Model != modelCdnos { + if n.Proto.Model != modelCdnos { return fmt.Errorf("unknown model") } return n.cdnosDelete(ctx) diff --git a/topo/node/forward/forward.go b/topo/node/forward/forward.go index 5d4a63870..cb1c12f2b 100644 --- a/topo/node/forward/forward.go +++ b/topo/node/forward/forward.go @@ -26,7 +26,7 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" log "k8s.io/klog/v2" - "k8s.io/utils/pointer" + "k8s.io/utils/ptr" ) const ( @@ -168,10 +168,10 @@ func (n *Node) CreatePod(ctx context.Context) error { Resources: node.ToResourceRequirements(pb.Constraints), ImagePullPolicy: "IfNotPresent", SecurityContext: &corev1.SecurityContext{ - Privileged: pointer.Bool(true), + Privileged: ptr.To(true), }, }}, - TerminationGracePeriodSeconds: pointer.Int64(0), + TerminationGracePeriodSeconds: ptr.To(int64(0)), NodeSelector: map[string]string{}, Affinity: &corev1.Affinity{ PodAntiAffinity: &corev1.PodAntiAffinity{ @@ -193,7 +193,7 @@ func (n *Node) CreatePod(ctx context.Context) error { }, } for label, v := range n.GetProto().GetLabels() { - pod.ObjectMeta.Labels[label] = v + pod.Labels[label] = v } if pb.Config.ConfigData != nil { vol, err := n.CreateConfig(ctx) @@ -206,7 +206,7 @@ func (n *Node) CreatePod(ctx context.Context) error { MountPath: pb.Config.ConfigPath + "/" + pb.Config.ConfigFile, ReadOnly: true, } - if vol.VolumeSource.ConfigMap != nil { + if vol.ConfigMap != nil { vm.SubPath = pb.Config.ConfigFile } for i, c := range pod.Spec.Containers { diff --git a/topo/node/inclusterproxy/README.md b/topo/node/inclusterproxy/README.md index 4e030f12c..5862bf60d 100644 --- a/topo/node/inclusterproxy/README.md +++ b/topo/node/inclusterproxy/README.md @@ -1,41 +1,53 @@ # IN_CLUSTER_PROXY Node Type -The `IN_CLUSTER_PROXY` node type is a specialized KNE node designed to act as a back-to-back proxy inside the cluster. It binds to a specific target node (e.g., a Device Under Test / DUT) over a single dedicated link and forwards configured ports to it using `socat`. +The `IN_CLUSTER_PROXY` node type is a specialized KNE node designed to act as a +back-to-back proxy inside the cluster. It binds to a specific target node (e.g., +a Device Under Test / DUT) over a single dedicated link and forwards configured +ports to it using `socat`. ## Features -- **Automatic Command Generation**: Automatically computes static IP address sizing for sidecar point-to-point subnets and generates `socat` listener scriptlets directly without writing bash boilerplate. -- **Cross-Stack Support**: Transparently supports both point-to-point **IPv4 (`/31`)** and **IPv6 (`/127`)** addressing setups. -- **Static Topology Integrity Verification**: Proactively verifies that `eth1` connects directly to the declared target node backplane before attempting to load or deploy. +- **Automatic Command Generation**: Automatically computes static IP address + sizing for sidecar point-to-point subnets and generates `socat` listener + scriptlets directly without writing bash boilerplate. +- **Cross-Stack Support**: Transparently supports both point-to-point **IPv4 + (`/31`)** and **IPv6 (`/127`)** addressing setups. +- **Static Topology Integrity Verification**: Proactively verifies that `eth1` + connects directly to the declared target node backplane before attempting to + load or deploy. ## Node Constraints To pass static validation, the node **must** meet the following conditions: -| Parameter | Constraint | -| :--- | :--- | -| **Interfaces** | Exactly one interface named `eth1`. | -| **Links** | `eth1` **must** link directly to the node specified in the `proxy-pool-for` label. | -| **Services** | Exactly one Service mapping must be provided inside `Services` map. | -| **Labels** | The node must have the `proxy-pool-for` label correctly populated. | +| Parameter | Constraint | +| :------------- | :--------------------------------------------------------------------------------- | +| **Interfaces** | Exactly one interface named `eth1`. | +| **Links** | `eth1` **must** link directly to the node specified in the `proxy-pool-for` label. | +| **Services** | Exactly one Service mapping must be provided inside `Services` map. | +| **Labels** | The node must have the `proxy-pool-for` label correctly populated. | ## Configuration Labels -| Label Key | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| `proxy-pool-for` | String | **Yes** | Name of the target node this proxy is mediating. | -| `peer-ip` | IP | No (Opt-in) | IP address of the peer (DUT) connected over `eth1` (e.g. `192.168.100.1` or `2001:db8::1`). | -| `peer-prefix` | String | No (Opt-in) | Prefix length of the peer IP (e.g. `31` or `127`). | -| `target-port` | Integer | No (Opt-in) | Port on the peer node to forward proxy streams to. | +| Label Key | Type | Required | Description | +| :--------------- | :------ | :---------- | :------------------------------------------------------------------------------------------ | +| `proxy-pool-for` | String | **Yes** | Name of the target node this proxy is mediating. | +| `peer-ip` | IP | No (Opt-in) | IP address of the peer (DUT) connected over `eth1` (e.g. `192.168.100.1` or `2001:db8::1`). | +| `peer-prefix` | String | No (Opt-in) | Prefix length of the peer IP (e.g. `31` or `127`). | +| `target-port` | Integer | No (Opt-in) | Port on the peer node to forward proxy streams to. | -> **Note on Opt-in Automatic Setup**: -> If **all three of** `peer-ip`, `peer-prefix`, and `target-port` are provided, the controller will automatically calculate the inverse IP (your side of the `/31` or `/127` link) and generate full commands addressing `socat`. If omitted, users must configure `command` and `args` in `.Config` structures manually. +> **Note on Opt-in Automatic Setup**: If **all three of** `peer-ip`, +> `peer-prefix`, and `target-port` are provided, the controller will +> automatically calculate the inverse IP (your side of the `/31` or `/127` link) +> and generate full commands addressing `socat`. If omitted, users must +> configure `command` and `args` in `.Config` structures manually. --- ## Example (Protobuf text format) -Below is an example of an `IN_CLUSTER_PROXY` node mediating a BGP lookup connection to node `cx1`. +Below is an example of an `IN_CLUSTER_PROXY` node mediating a BGP lookup +connection to node `cx1`. ```protobuf nodes: { @@ -59,9 +71,9 @@ nodes: { } services: { key: 1790 - value: { - name: "bgp-proxy" - inside: 1790 + value: { + name: "bgp-proxy" + inside: 1790 } } } @@ -77,4 +89,5 @@ links: { In this example, the proxy container will automatically spin up to: 1. Assign `192.168.100.0/31` to its `eth1` interface. -2. Run `socat` forwarding any stream sent to port `1790` out to `192.168.100.1:179` across the wire. +2. Run `socat` forwarding any stream sent to port `1790` out to + `192.168.100.1:179` across the wire. diff --git a/topo/node/juniper/juniper.go b/topo/node/juniper/juniper.go index a1697a610..dfe19c2f3 100644 --- a/topo/node/juniper/juniper.go +++ b/topo/node/juniper/juniper.go @@ -23,7 +23,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/fields" log "k8s.io/klog/v2" - "k8s.io/utils/pointer" + "k8s.io/utils/ptr" ) // ErrIncompatibleCliConn raised when an invalid scrapligo cli transport type is found. @@ -171,10 +171,10 @@ func (n *Node) SpawnCLIConn() error { } // DefaultNodeConstraints returns default node constraints for Juniper. -// If the model for cptx is specificied correctly it returns defaults for cptx. +// If the model for cptx is specified correctly it returns defaults for cptx. // Otherwise, it returns defaults for ncptx by default. func (n *Node) DefaultNodeConstraints() node.Constraints { - if n.Impl == nil || n.Impl.Proto == nil { + if n.Impl == nil || n.Proto == nil { return defaultNCPTXConstraints } switch n.GetProto().Model { @@ -496,8 +496,8 @@ func (n *Node) Create(ctx context.Context) error { Resources: node.ToResourceRequirements(pb.Constraints), ImagePullPolicy: "IfNotPresent", SecurityContext: &corev1.SecurityContext{ - Privileged: pointer.Bool(true), - RunAsUser: pointer.Int64(0), + Privileged: ptr.To(true), + RunAsUser: ptr.To(int64(0)), Capabilities: &corev1.Capabilities{ Add: []corev1.Capability{"SYS_ADMIN", "NET_ADMIN"}, }, @@ -564,7 +564,7 @@ func (n *Node) Create(ctx context.Context) error { }, }, }, - TerminationGracePeriodSeconds: pointer.Int64(0), + TerminationGracePeriodSeconds: ptr.To(int64(0)), NodeSelector: map[string]string{}, Affinity: &corev1.Affinity{ PodAntiAffinity: &corev1.PodAntiAffinity{ @@ -586,7 +586,7 @@ func (n *Node) Create(ctx context.Context) error { }, } for label, v := range n.GetProto().GetLabels() { - pod.ObjectMeta.Labels[label] = v + pod.Labels[label] = v } if pb.Config.ConfigData != nil { vol, err := n.CreateConfig(ctx) @@ -599,7 +599,7 @@ func (n *Node) Create(ctx context.Context) error { MountPath: pb.Config.ConfigPath + "/" + pb.Config.ConfigFile, ReadOnly: true, } - if vol.VolumeSource.ConfigMap != nil { + if vol.ConfigMap != nil { vm.SubPath = pb.Config.ConfigFile } for i, c := range pod.Spec.Containers { diff --git a/topo/node/node.go b/topo/node/node.go index 3184d9051..a216ee9ac 100644 --- a/topo/node/node.go +++ b/topo/node/node.go @@ -29,7 +29,7 @@ import ( "k8s.io/client-go/rest" "k8s.io/client-go/tools/remotecommand" log "k8s.io/klog/v2" - "k8s.io/utils/pointer" + "k8s.io/utils/ptr" ) type Interface interface { @@ -286,7 +286,7 @@ func validateBoundedInteger(nodeConstraint *tpb.BoundedInteger, hostCons int) er if nodeConstraint.MinValue > nodeConstraint.MaxValue { return fmt.Errorf("invalid bounds. Max value %d is less than min value %d", nodeConstraint.MaxValue, nodeConstraint.MinValue) } - if !(nodeConstraint.MinValue <= int64(hostCons) && int64(hostCons) <= nodeConstraint.MaxValue) { + if int64(hostCons) < nodeConstraint.MinValue || int64(hostCons) > nodeConstraint.MaxValue { return fmt.Errorf("invalid bounded integer constraint. min: %d max %d constraint data %d", nodeConstraint.MinValue, nodeConstraint.MaxValue, hostCons) } @@ -429,10 +429,10 @@ func (n *Impl) CreatePod(ctx context.Context) error { Resources: ToResourceRequirements(pb.Constraints), ImagePullPolicy: "IfNotPresent", SecurityContext: &corev1.SecurityContext{ - Privileged: pointer.Bool(true), + Privileged: ptr.To(true), }, }}, - TerminationGracePeriodSeconds: pointer.Int64(0), + TerminationGracePeriodSeconds: ptr.To(int64(0)), NodeSelector: map[string]string{}, Affinity: &corev1.Affinity{ PodAntiAffinity: &corev1.PodAntiAffinity{ @@ -454,7 +454,7 @@ func (n *Impl) CreatePod(ctx context.Context) error { }, } for label, v := range n.GetProto().GetLabels() { - pod.ObjectMeta.Labels[label] = v + pod.Labels[label] = v } if pb.Config.ConfigData != nil { vol, err := n.CreateConfig(ctx) @@ -467,7 +467,7 @@ func (n *Impl) CreatePod(ctx context.Context) error { MountPath: pb.Config.ConfigPath + "/" + pb.Config.ConfigFile, ReadOnly: true, } - if vol.VolumeSource.ConfigMap != nil { + if vol.ConfigMap != nil { vm.SubPath = pb.Config.ConfigFile } for i, c := range pod.Spec.Containers { @@ -531,7 +531,7 @@ func (n *Impl) CreateService(ctx context.Context) error { // Large topologies may try to allocate more NodePorts than are // supported in default clusters. // https://kubernetes.io/docs/concepts/services-networking/service/#load-balancer-nodeport-allocation - AllocateLoadBalancerNodePorts: pointer.Bool(false), + AllocateLoadBalancerNodePorts: ptr.To(false), }, } sS, err := n.KubeClient.CoreV1().Services(n.Namespace).Create(ctx, s, metav1.CreateOptions{}) @@ -573,7 +573,7 @@ func (n *Impl) DeleteConfig(ctx context.Context) error { } log.V(1).Infof("Deleted config file %s", path) case vs.ConfigMap != nil: - name := vs.ConfigMap.LocalObjectReference.Name + name := vs.ConfigMap.Name if err := n.KubeClient.CoreV1().ConfigMaps(n.Namespace).Delete(ctx, name, metav1.DeleteOptions{}); err != nil { return err } @@ -589,7 +589,7 @@ func (n *Impl) DeleteService(ctx context.Context) error { TypeMeta: metav1.TypeMeta{ APIVersion: "v1", }, - GracePeriodSeconds: pointer.Int64(0), + GracePeriodSeconds: ptr.To(int64(0)), }) } @@ -627,6 +627,7 @@ func (n *Impl) Exec(ctx context.Context, cmd []string, stdin io.Reader, stdout i return err } log.Infof("Execing %s on %s", cmd, n.Name()) + //nolint:staticcheck return exec.Stream(remotecommand.StreamOptions{ Stdin: stdin, Stdout: stdout, @@ -725,9 +726,13 @@ func (n *Impl) PatchCLIConnOpen(bin string, cliCmd []string, opts []scrapliutil. // for a given platform. Retries indefinitely till success and returns a scrapligo network driver instance. func (n *Impl) GetCLIConn(platform string, opts []scrapliutil.Option) (*scraplinetwork.Driver, error) { if log.V(1).Enabled() { - li, _ := scraplilogging.NewInstance(scraplilogging.WithLevel("debug"), + li, err := scraplilogging.NewInstance(scraplilogging.WithLevel("debug"), scraplilogging.WithLogger(log.Info)) - opts = append(opts, scrapliopts.WithLogger(li)) + if err != nil { + log.Warningf("Failed to create scrapli logging instance: %v", err) + } else { + opts = append(opts, scrapliopts.WithLogger(li)) + } } for { @@ -773,10 +778,10 @@ func GetNodeLinks(n *tpb.Node) ([]topologyv1.Link, error) { continue } if ifc.PeerIntName == "" { - return nil, fmt.Errorf("interface %q PeerIntName canot be empty", ifcName) + return nil, fmt.Errorf("interface %q PeerIntName cannot be empty", ifcName) } if ifc.PeerName == "" { - return nil, fmt.Errorf("interface %q PeerName canot be empty", ifcName) + return nil, fmt.Errorf("interface %q PeerName cannot be empty", ifcName) } links = append(links, topologyv1.Link{ UID: int(ifc.Uid), diff --git a/topo/node/node_test.go b/topo/node/node_test.go index 3119861c7..0f08a30f6 100644 --- a/topo/node/node_test.go +++ b/topo/node/node_test.go @@ -15,7 +15,7 @@ import ( "k8s.io/apimachinery/pkg/util/intstr" kfake "k8s.io/client-go/kubernetes/fake" "k8s.io/client-go/rest" - "k8s.io/utils/pointer" + "k8s.io/utils/ptr" ) func NewNR(impl *Impl) (Node, error) { @@ -160,18 +160,18 @@ func TestCreateConfig(t *testing.T) { }, }, }, { - desc: "config file dne", + desc: "config file does not exist", node: &topopb.Node{ Name: "dev1", Vendor: topopb.Vendor(1001), Config: &topopb.Config{ ConfigFile: "test.cfg", ConfigData: &topopb.Config_File{ - File: "testdata/dne.cfg", + File: "testdata/nonexistent.cfg", }, }, }, - wantErr: "open testdata/dne.cfg: no such file", + wantErr: "open testdata/nonexistent.cfg: no such file", }} for _, tt := range tests { t.Run(tt.desc, func(t *testing.T) { @@ -199,7 +199,7 @@ func TestCreateConfig(t *testing.T) { t.Errorf("CreateConfig() did not create the expected file: %v", err) } case vs.ConfigMap != nil: - gotCM, err := n.KubeClient.CoreV1().ConfigMaps(n.Namespace).Get(ctx, vs.ConfigMap.LocalObjectReference.Name, metav1.GetOptions{}) + gotCM, err := n.KubeClient.CoreV1().ConfigMaps(n.Namespace).Get(ctx, vs.ConfigMap.Name, metav1.GetOptions{}) if err != nil { t.Errorf("CreateConfig() did not create the expected configmap: %v", err) } @@ -255,7 +255,7 @@ func TestService(t *testing.T) { }}, Selector: map[string]string{"app": "dev1"}, Type: "LoadBalancer", - AllocateLoadBalancerNodePorts: pointer.Bool(false), + AllocateLoadBalancerNodePorts: ptr.To(false), }, }}, }, { @@ -301,7 +301,7 @@ func TestService(t *testing.T) { }}, Selector: map[string]string{"app": "dev2"}, Type: "LoadBalancer", - AllocateLoadBalancerNodePorts: pointer.Bool(false), + AllocateLoadBalancerNodePorts: ptr.To(false), }, }}, }, { @@ -365,7 +365,7 @@ func TestValidateConstraints(t *testing.T) { constraintValues map[string]int }{ { - desc: "Invalid case - contraint value is greater than upper bound", + desc: "Invalid case - constraint value is greater than upper bound", node: &topopb.Node{ Name: "node1", HostConstraints: []*topopb.HostConstraint{ diff --git a/topo/node/nokia/nokia.go b/topo/node/nokia/nokia.go index 6769f3c21..0c122961f 100644 --- a/topo/node/nokia/nokia.go +++ b/topo/node/nokia/nokia.go @@ -211,7 +211,7 @@ func (n *Node) ConfigPush(ctx context.Context, r io.Reader) error { } if resp.Failed != nil { - log.Infof("%s - failed saving config to file", n.Impl.Proto.Name) + log.Infof("%s - failed saving config to file", n.Proto.Name) return resp.Failed } @@ -228,7 +228,7 @@ func (n *Node) ConfigPush(ctx context.Context, r io.Reader) error { } if mresp.Failed != nil { - log.Infof("%s - failed config push", n.Impl.Proto.Name) + log.Infof("%s - failed config push", n.Proto.Name) return resp.Failed } diff --git a/topo/node/openconfig/openconfig.go b/topo/node/openconfig/openconfig.go index 2f9cf5d84..7c6326453 100644 --- a/topo/node/openconfig/openconfig.go +++ b/topo/node/openconfig/openconfig.go @@ -160,7 +160,7 @@ var clientFn = func(c *rest.Config) (clientset.Interface, error) { } func (n *Node) Create(ctx context.Context) error { - switch n.Impl.Proto.Model { + switch n.Proto.Model { case modelLemming: return n.lemmingCreate(ctx) case modelMagna: @@ -236,19 +236,19 @@ func (n *Node) lemmingCreate(ctx context.Context) error { } func (n *Node) Status(ctx context.Context) (node.Status, error) { - switch n.Impl.Proto.Model { + switch n.Proto.Model { case modelMagna: // magna's status uses the standard underlying node implementation. return n.Impl.Status(ctx) case modelLemming: return n.lemmingStatus(ctx) default: - return node.StatusUnknown, fmt.Errorf("invalid model specified.") + return node.StatusUnknown, fmt.Errorf("invalid model specified") } } func (n *Node) DefaultNodeConstraints() node.Constraints { - switch n.Impl.Proto.Model { + switch n.Proto.Model { case modelLemming: return defaultLemmingConstraints default: @@ -278,7 +278,7 @@ func (n *Node) lemmingStatus(ctx context.Context) (node.Status, error) { } func (n *Node) Delete(ctx context.Context) error { - switch n.Impl.Proto.Model { + switch n.Proto.Model { case modelMagna: // magna's implementation uses the standard underlying node implementation. return n.Impl.Delete(ctx) diff --git a/topo/node/sonic/sonic.go b/topo/node/sonic/sonic.go index 4841c100b..86f0bd5da 100644 --- a/topo/node/sonic/sonic.go +++ b/topo/node/sonic/sonic.go @@ -11,6 +11,8 @@ // 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 sonic implements a SONIC node in the topology. package sonic import ( @@ -20,7 +22,7 @@ import ( "github.com/openconfig/kne/topo/node" "google.golang.org/protobuf/proto" - "k8s.io/utils/pointer" + "k8s.io/utils/ptr" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -52,6 +54,7 @@ var ( } ) +// New returns a new SONIC node. func New(nodeImpl *node.Impl) (node.Node, error) { if nodeImpl == nil { return nil, fmt.Errorf("nodeImpl cannot be nil") @@ -83,10 +86,12 @@ func renameInterfaces(in map[string]*tpb.Interface) map[string]*tpb.Interface { return intf } +// Node is a SONIC node. type Node struct { *node.Impl } +// Create creates the SONIC node. func (n *Node) Create(ctx context.Context) error { if err := n.ValidateConstraints(); err != nil { return fmt.Errorf("node %s failed to validate node with errors: %s", n.Name(), err) @@ -120,7 +125,7 @@ func (n *Node) CreatePod(ctx context.Context) error { Resources: node.ToResourceRequirements(pb.Constraints), ImagePullPolicy: "IfNotPresent", SecurityContext: &corev1.SecurityContext{ - Privileged: pointer.Bool(true), + Privileged: ptr.To(true), }, }} @@ -143,11 +148,11 @@ func (n *Node) CreatePod(ctx context.Context) error { }, ImagePullPolicy: "IfNotPresent", SecurityContext: &corev1.SecurityContext{ - Privileged: pointer.Bool(true), + Privileged: ptr.To(true), }, }}, Containers: sonicContainers, - TerminationGracePeriodSeconds: pointer.Int64(0), + TerminationGracePeriodSeconds: ptr.To(int64(0)), NodeSelector: map[string]string{}, Affinity: &corev1.Affinity{ PodAntiAffinity: &corev1.PodAntiAffinity{ @@ -180,7 +185,7 @@ func (n *Node) CreatePod(ctx context.Context) error { MountPath: pb.Config.ConfigPath + "/" + pb.Config.ConfigFile, ReadOnly: true, } - if vol.VolumeSource.ConfigMap != nil { + if vol.ConfigMap != nil { vm.SubPath = pb.Config.ConfigFile } for i, c := range pod.Spec.Containers { @@ -221,6 +226,7 @@ func defaults(pb *tpb.Node) *tpb.Node { return pb } +// DefaultNodeConstraints returns the default node constraints. func (n *Node) DefaultNodeConstraints() node.Constraints { return defaultConstraints } diff --git a/topo/node/sonic/sonic_test.go b/topo/node/sonic/sonic_test.go index db6dd1e90..3ddfa056d 100644 --- a/topo/node/sonic/sonic_test.go +++ b/topo/node/sonic/sonic_test.go @@ -27,7 +27,7 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" kfake "k8s.io/client-go/kubernetes/fake" - "k8s.io/utils/pointer" + "k8s.io/utils/ptr" ) func TestNew(t *testing.T) { @@ -152,11 +152,11 @@ func TestNew(t *testing.T) { func TestCreatePod(t *testing.T) { tests := []struct { - desc string - nImpl *node.Impl - wantInitCtr corev1.Container - wantSonicCtr corev1.Container - wantErr string + desc string + nImpl *node.Impl + wantInitContainer corev1.Container + wantSonicContainer corev1.Container + wantErr string }{{ desc: "simple sonic container", nImpl: &node.Impl{ @@ -170,16 +170,16 @@ func TestCreatePod(t *testing.T) { }, }, }, - wantInitCtr: corev1.Container{ - Name: "init-sonic-node", - Image: node.DefaultInitContainerImage, - Args: []string{"1", "10", "1"}, + wantInitContainer: corev1.Container{ + Name: "init-sonic-node", + Image: node.DefaultInitContainerImage, + Args: []string{"1", "10", "1"}, ImagePullPolicy: "IfNotPresent", SecurityContext: &corev1.SecurityContext{ - Privileged: pointer.Bool(true), + Privileged: ptr.To(true), }, }, - wantSonicCtr: corev1.Container{ + wantSonicContainer: corev1.Container{ Name: "sonic-node", Image: "sonicImage", Command: []string{"sonicCommand"}, @@ -189,7 +189,7 @@ func TestCreatePod(t *testing.T) { }, ImagePullPolicy: "IfNotPresent", SecurityContext: &corev1.SecurityContext{ - Privileged: pointer.Bool(true), + Privileged: ptr.To(true), }, }, }, { @@ -214,16 +214,16 @@ func TestCreatePod(t *testing.T) { }, }, }, - wantInitCtr: corev1.Container{ - Name: "init-sonic-node", - Image: "customInitImage", - Args: []string{"3", "5", "1"}, + wantInitContainer: corev1.Container{ + Name: "init-sonic-node", + Image: "customInitImage", + Args: []string{"3", "5", "1"}, ImagePullPolicy: "IfNotPresent", SecurityContext: &corev1.SecurityContext{ - Privileged: pointer.Bool(true), + Privileged: ptr.To(true), }, }, - wantSonicCtr: corev1.Container{ + wantSonicContainer: corev1.Container{ Name: "sonic-node", Image: "sonicImage", Command: []string{"sonicCommand"}, @@ -233,7 +233,7 @@ func TestCreatePod(t *testing.T) { }, ImagePullPolicy: "IfNotPresent", SecurityContext: &corev1.SecurityContext{ - Privileged: pointer.Bool(true), + Privileged: ptr.To(true), }, }, }, { @@ -252,16 +252,16 @@ func TestCreatePod(t *testing.T) { }, }, }, - wantInitCtr: corev1.Container{ - Name: "init-sonic-node", - Image: node.DefaultInitContainerImage, - Args: []string{"1", "10", "1"}, + wantInitContainer: corev1.Container{ + Name: "init-sonic-node", + Image: node.DefaultInitContainerImage, + Args: []string{"1", "10", "1"}, ImagePullPolicy: "IfNotPresent", SecurityContext: &corev1.SecurityContext{ - Privileged: pointer.Bool(true), + Privileged: ptr.To(true), }, }, - wantSonicCtr: corev1.Container{ + wantSonicContainer: corev1.Container{ Name: "sonic-node", Image: "sonicImage", Command: []string{"sonicCommand"}, @@ -271,7 +271,7 @@ func TestCreatePod(t *testing.T) { }, ImagePullPolicy: "IfNotPresent", SecurityContext: &corev1.SecurityContext{ - Privileged: pointer.Bool(true), + Privileged: ptr.To(true), }, VolumeMounts: []corev1.VolumeMount{{ Name: "startup-config-volume", @@ -305,16 +305,16 @@ func TestCreatePod(t *testing.T) { if len(initContainers) != 1 { t.Fatalf("Num init containers mismatch: want: 1 got: %v", len(initContainers)) } - if s := cmp.Diff(tt.wantInitCtr, initContainers[0]); s != "" { - t.Fatalf("Init Container mismatch: %s,\n got:\n%v \n want:\n%v\n", s, initContainers[0], tt.wantInitCtr) + if s := cmp.Diff(tt.wantInitContainer, initContainers[0]); s != "" { + t.Fatalf("Init Container mismatch: %s,\n got:\n%v \n want:\n%v\n", s, initContainers[0], tt.wantInitContainer) } containers := pod.Spec.Containers if len(containers) != 1 { t.Fatalf("Num containers mismatch: want: 1 got: %v", len(containers)) } - if s := cmp.Diff(tt.wantSonicCtr, containers[0]); s != "" { - t.Fatalf("Sonic Container mismatch: %s,\n got:\n%v \n want:\n%v\n", s, containers[0], tt.wantSonicCtr) + if s := cmp.Diff(tt.wantSonicContainer, containers[0]); s != "" { + t.Fatalf("Sonic Container mismatch: %s,\n got:\n%v \n want:\n%v\n", s, containers[0], tt.wantSonicContainer) } }) } diff --git a/topo/testdata/invalid_topo.yaml b/topo/testdata/invalid_topo.yaml index e55c0b4dd..06c9ca4c6 100644 --- a/topo/testdata/invalid_topo.yaml +++ b/topo/testdata/invalid_topo.yaml @@ -1,3 +1,4 @@ +--- name: "test-data-topology" nodes: - name: "r1" diff --git a/topo/testdata/valid_topo.yaml b/topo/testdata/valid_topo.yaml index 8eb0573c1..6a1b58ba3 100644 --- a/topo/testdata/valid_topo.yaml +++ b/topo/testdata/valid_topo.yaml @@ -1,3 +1,4 @@ +--- name: "test-data-topology" nodes: - name: "r1" diff --git a/topo/topo.go b/topo/topo.go index 2e131194b..2ea4f8c1d 100644 --- a/topo/topo.go +++ b/topo/topo.go @@ -267,7 +267,7 @@ func (m *Manager) Create(ctx context.Context, timeout time.Duration) (rerr error } } ctx, cancel := context.WithCancel(ctx) - // Watch the containter status of the pods so we can fail if a container fails to start running. + // Watch the container status of the pods so we can fail if a container fails to start running. if w, err := pods.NewWatcher(ctx, m.kClient, cancel); err != nil { log.Warningf("Failed to start pod watcher: %v", err) } else { @@ -409,7 +409,10 @@ func (m *Manager) Show(ctx context.Context) (*cpb.ShowTopologyResponse, error) { } stateMap := &stateMap{} for _, n := range m.nodes { - phase, _ := n.Status(ctx) + phase, err := n.Status(ctx) + if err != nil { + return nil, err + } stateMap.setNodeState(n.Name(), phase) } return &cpb.ShowTopologyResponse{ @@ -530,8 +533,8 @@ func setLinkPeer(nodeName string, podName string, link *topologyv1.Link, peerSpe for _, peerSpec := range peerSpecs { for _, peerLink := range peerSpec.Spec.Links { // make sure self ifc and peer ifc belong to same link (and hence UID) but are not the same interfaces - if peerLink.UID == link.UID && !(nodeName == link.PeerPod && peerLink.LocalIntf == link.LocalIntf) { - link.PeerPod = peerSpec.ObjectMeta.Name + if peerLink.UID == link.UID && (nodeName != link.PeerPod || peerLink.LocalIntf != link.LocalIntf) { + link.PeerPod = peerSpec.Name link.PeerIntf = peerLink.LocalIntf return nil } @@ -568,7 +571,7 @@ func (m *Manager) topologySpecs(ctx context.Context) ([]*topologyv1.Topology, er return nil, fmt.Errorf("specs do not exist for node %s", link.PeerPod) } - if err := setLinkPeer(nodeName, spec.ObjectMeta.Name, link, peerSpecs); err != nil { + if err := setLinkPeer(nodeName, spec.Name, link, peerSpecs); err != nil { return nil, err } } @@ -677,10 +680,10 @@ func (m *Manager) createMeshnetTopologies(ctx context.Context) error { } log.V(2).Infof("Got topology specs for namespace %s: %+v", m.topo.Name, topologies) for _, t := range topologies { - log.Infof("Creating topology for meshnet node %s", t.ObjectMeta.Name) + log.Infof("Creating topology for meshnet node %s", t.Name) sT, err := m.tClient.Topology(m.topo.Name).Create(ctx, t, metav1.CreateOptions{}) if err != nil { - return fmt.Errorf("could not create topology for meshnet node %s: %v", t.ObjectMeta.Name, err) + return fmt.Errorf("could not create topology for meshnet node %s: %v", t.Name, err) } log.V(1).Infof("Meshnet Node:\n%+v\n", sT) } @@ -695,8 +698,8 @@ func (m *Manager) deleteMeshnetTopologies(ctx context.Context) error { } var errs errlist.List for _, n := range nodes { - if err := m.tClient.Topology(m.topo.Name).Delete(ctx, n.ObjectMeta.Name, metav1.DeleteOptions{}); err != nil { - errs.Add(fmt.Errorf("failed to delete meshnet node %q: %w", n.ObjectMeta.Name, err)) + if err := m.tClient.Topology(m.topo.Name).Delete(ctx, n.Name, metav1.DeleteOptions{}); err != nil { + errs.Add(fmt.Errorf("failed to delete meshnet node %q: %w", n.Name, err)) } } return errs.Err() @@ -718,7 +721,7 @@ func (m *Manager) checkNodeStatus(ctx context.Context, timeout time.Duration) er phase, err := n.Status(ctx) if err != nil || phase == node.StatusFailed { - return fmt.Errorf("Node %s: Status %s Reason %v", n, phase, err) + return fmt.Errorf("node %s: status %s reason %v", n, phase, err) } if phase == node.StatusRunning { log.Infof("Node %s: Status %s", n, phase) diff --git a/topo/topo_test.go b/topo/topo_test.go index a958a7211..e871ad60a 100644 --- a/topo/topo_test.go +++ b/topo/topo_test.go @@ -555,7 +555,7 @@ func TestCreate(t *testing.T) { }, }, }, - wantErr: `Node "bad" (vendor: "1002", model: ""): Status FAILED`, + wantErr: `node "bad" (vendor: "1002", model: ""): status FAILED`, }, { desc: "failed to report metrics, create still passes", opts: []Option{WithUsageReporting(true, "", "")}, @@ -678,7 +678,7 @@ func TestDelete(t *testing.T) { Type: watch.Deleted, Object: &corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{ - Name: "dne", + Name: "nonexistent", }, }, }, @@ -1796,7 +1796,7 @@ func TestConfigPush(t *testing.T) { wantErr: "does not implement ConfigPusher interface", }, { desc: "node not found", - name: "dne", + name: "nonexistent", wantErr: "not found", }} for _, tt := range tests { @@ -1834,7 +1834,7 @@ func TestResetCfg(t *testing.T) { wantErr: "does not implement Resetter interface", }, { desc: "node not found", - name: "dne", + name: "nonexistent", wantErr: "not found", }} for _, tt := range tests { @@ -1901,7 +1901,7 @@ func TestGenerateSelfSigned(t *testing.T) { name: "no_info", }, { desc: "node not found", - name: "dne", + name: "nonexistent", wantErr: "not found", }} for _, tt := range tests { diff --git a/x/webhook/README.md b/x/webhook/README.md index 2b82d2bd2..39b891828 100644 --- a/x/webhook/README.md +++ b/x/webhook/README.md @@ -1,12 +1,11 @@ # KNE Mutating Webhook This directory contains the code and configurations (in the form of manifests) -for the mutating webhook. The webhook should be deployed onto a KNE -cluster. +for the mutating webhook. The webhook should be deployed onto a KNE cluster. -This webhook can be used to mutate any K8 resources. This directory contains -the generic webhook along with an example mutator that simply adds an alpine -linux container to created pods. +This webhook can be used to mutate any K8 resources. This directory contains the +generic webhook along with an example mutator that simply adds an alpine linux +container to created pods. To develop custom a custom mutation simply change the mutate function in the examples subdirectory. @@ -79,9 +78,9 @@ default kne-assembly-webhook-f5b8cf987-lpxjt We can now create the KNE topology. -*Note* The KNE topology must have the label `webhook:enabled` for each node, as in -[this example](examples/topology.textproto), -otherwise the webhook will ignore the pod upon create. +_Note_ The KNE topology must have the label `webhook:enabled` for each node, as +in [this example](examples/topology.textproto), otherwise the webhook will +ignore the pod upon create. ```bash labels { @@ -96,8 +95,8 @@ Use the normal KNE command to create the topology. kne create examples/topology.textproto ``` -You should now see r1 with 2 containers instead of the one, this is -because the webhook has injected the alpine linux container. +You should now see r1 with 2 containers instead of the one, this is because the +webhook has injected the alpine linux container. ```bash $ kubectl get pods -n webhook-example @@ -175,8 +174,8 @@ I0402 23:24:36.394188 1 mutate.go:45] Mutating &TypeMeta{Kind:Pod,APIVersi I0402 23:24:36.394227 1 addcontainer.go:34] Ignoring pod "r2", mutation not requested ``` -This output shows that it mutated the pod r1 but not r2 since -the label was not added to that KNE node. +This output shows that it mutated the pod r1 but not r2 since the label was not +added to that KNE node. ### TLS @@ -197,7 +196,6 @@ Edit `main.go` to specify any mutation functions as desired. The example uses the mutation function found in `examples/addcontainer/addcontainer.go` but any mutation function is supported. This includes mutating services and other resources besides just pods. However you may also have to change -`manifests/mutating.config.yaml` to select other resources types than just -pods. +`manifests/mutating.config.yaml` to select other resources types than just pods. After customization is done, rebuild the container and reapply the manifests. diff --git a/x/webhook/examples/addcontainer/addcontainer_test.go b/x/webhook/examples/addcontainer/addcontainer_test.go index e119fb1e8..892451ad8 100644 --- a/x/webhook/examples/addcontainer/addcontainer_test.go +++ b/x/webhook/examples/addcontainer/addcontainer_test.go @@ -69,5 +69,4 @@ func TestAddContainer(t *testing.T) { } }) } - } diff --git a/x/webhook/main.go b/x/webhook/main.go index 0df6bf624..9d50dab95 100644 --- a/x/webhook/main.go +++ b/x/webhook/main.go @@ -90,7 +90,9 @@ func parseRequest(r http.Request) (*admissionv1.AdmissionReview, error) { } bodybuf := new(bytes.Buffer) - bodybuf.ReadFrom(r.Body) + if _, err := bodybuf.ReadFrom(r.Body); err != nil { + return nil, fmt.Errorf("failed to read request body: %w", err) + } body := bodybuf.Bytes() if len(body) == 0 { diff --git a/x/webhook/manifests/deploy.yaml b/x/webhook/manifests/deploy.yaml index fddb16a57..3f1214291 100644 --- a/x/webhook/manifests/deploy.yaml +++ b/x/webhook/manifests/deploy.yaml @@ -1,3 +1,4 @@ +--- apiVersion: apps/v1 kind: Deployment metadata: @@ -14,7 +15,7 @@ spec: metadata: labels: app: kne-assembly-webhook - spec: + spec: containers: - image: webhook:latest imagePullPolicy: IfNotPresent diff --git a/x/webhook/manifests/mutating.config.yaml b/x/webhook/manifests/mutating.config.yaml index 8c0bdbd84..cde9f29df 100644 --- a/x/webhook/manifests/mutating.config.yaml +++ b/x/webhook/manifests/mutating.config.yaml @@ -1,3 +1,4 @@ +--- apiVersion: admissionregistration.k8s.io/v1 kind: MutatingWebhookConfiguration metadata: diff --git a/x/webhook/manifests/namespace.yaml b/x/webhook/manifests/namespace.yaml index 2adf86a8a..5724d8e5d 100644 --- a/x/webhook/manifests/namespace.yaml +++ b/x/webhook/manifests/namespace.yaml @@ -1,3 +1,4 @@ +--- apiVersion: v1 kind: Namespace metadata: diff --git a/x/webhook/manifests/tls.secret.yaml b/x/webhook/manifests/tls.secret.yaml index 4e5bd8bbf..4946ca214 100644 --- a/x/webhook/manifests/tls.secret.yaml +++ b/x/webhook/manifests/tls.secret.yaml @@ -1,3 +1,4 @@ +--- apiVersion: v1 data: tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURWVENDQWoyZ0F3SUJBZ0lVVEJRMWJtWFlOZFNCRC9iUllkakpsajJtNVlVd0RRWUpLb1pJaHZjTkFRRUwKQlFBd0xERUxNQWtHQTFVRUJoTUNRVlV4SFRBYkJnTlZCQU1NRkd0dVpTMWhjM05sYldKc2VTMTNaV0pvYjI5cgpNQjRYRFRJME1EUXdNakl4TVRnME1Wb1hEVEkxTURRd01qSXhNVGcwTVZvd0xERUxNQWtHQTFVRUJoTUNRVlV4CkhUQWJCZ05WQkFNTUZHdHVaUzFoYzNObGJXSnNlUzEzWldKb2IyOXJNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUYKQUFPQ0FROEFNSUlCQ2dLQ0FRRUE0M0F2L2lLRzRLNFE0N1luZlc1bGVtTUFLcWRTWjQ3YXhDRDBzM0hoSVI2dgpiTlRldmVmamEvRTN5S08vZ282ZTNVT3d6T01qanVEVHRveVJWZ05kQnBkUklXc2pKSXd2S1ZBR0tUV0ZRVmppCmoyNzIyTG9nNkJ2OVBOVmJSRFRiSUErNnRWbW9IUWFIei9KY3lOR0ZtQmxwZWxhWWY4SXZwQjNkWkY5T3lNSWoKWDVydXlxeTFyUi9uNUliZytZK0M1KzZDWW1OVDVNN2Nqajd3MzNwQ2dWVkRXaGk3NkU3QjJ0TVVTM01aczlMNwpvaHI0T3phK04wczl6ZlgreFY3bnVCTlQ5d1hmNGRET0g3REdUUVhvYjVKYVF0Uk45NlZuRFhtd3Z0VGlSZTZvClExMW1IRkNyQWprbGYyeXZwOXplM2tQekZ1djJkTHg5NzhjSk9vZWlzUUlEQVFBQm8yOHdiVEFyQmdOVkhSRUUKSkRBaWdpQnJibVV0WVhOelpXMWliSGt0ZDJWaWFHOXZheTVrWldaaGRXeDBMbk4yWXpBZEJnTlZIUTRFRmdRVQpkS2lmUkNadHc0MnVieENBVzlOSmd4ZWhubkl3SHdZRFZSMGpCQmd3Rm9BVWpMOUxtZHp0bzNhcEQ5Tm41UFMxCkVLNk04S1l3RFFZSktvWklodmNOQVFFTEJRQURnZ0VCQUIyOTg1VGUzZzA2c2dMRzdKYTNpZmF1SXFEbDBEVk4KMHlqbXFQQnNNL2syU3JDSE53UW53Rk01MVRxVlhoek8yM2o3bUhhR1FVdlNpTUZzRG1kK1JCS09MVUNFSWwzLwphTlkrNVpaS2thTUZzM1d0bjIzVWtzaEpKR3VJR1N1dXkreTRqdTJ1WXJ4RTVuZWpvRzBvRTk4TXo5ZmZ4bDVFClh4TnRiejZPR1JFNXloQXNBcE1jbnVsY0ZPektFaS9hNHN3MW4vam5KbEwwV0ptRHN1T2FOOWNZMndFS0pCa0cKOEF4d0ZYMUxlOHFhRkl2RGxqWVlnc2tnTjFVZ1hzZkdxdm05eGYzejQ4bDJURGF5T1NxWUhTRHN1T3d1WXRHNwoxV3JWazVjb09FbzljZVBTQlpzYnpKQlpGeFZnbERvdzFVTVUyVWp4TEt4MDQza0hvMEpzSjNzPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== diff --git a/x/wire/file/client/main.go b/x/wire/file/client/main.go index 4f9d56f35..4a5cc0a9b 100644 --- a/x/wire/file/client/main.go +++ b/x/wire/file/client/main.go @@ -48,7 +48,7 @@ func main() { opts := []grpc.DialOption{ grpc.WithTransportCredentials(insecure.NewCredentials()), } - conn, err := grpc.DialContext(ctx, *addr, opts...) + conn, err := grpc.NewClient(*addr, opts...) if err != nil { log.Fatalf("Failed to dial %q: %v", *addr, err) } @@ -64,7 +64,9 @@ func main() { return err } defer func() { - stream.CloseSend() + if err := stream.CloseSend(); err != nil { + log.Warningf("Failed to close send stream: %v", err) + } }() log.Infof("Transmitting endpoint %v over wire...", e) return w.Transmit(ctx, stream) diff --git a/x/wire/forward/main.go b/x/wire/forward/main.go index 15d6eb7e8..5d5bdf8ce 100644 --- a/x/wire/forward/main.go +++ b/x/wire/forward/main.go @@ -122,7 +122,7 @@ func main() { opts := []grpc.DialOption{ grpc.WithTransportCredentials(insecure.NewCredentials()), } - conn, err := grpc.DialContext(ctx, a, opts...) + conn, err := grpc.NewClient(a, opts...) if err != nil { log.Fatalf("Failed to dial %q: %v", a, err) } @@ -138,7 +138,9 @@ func main() { return err } defer func() { - stream.CloseSend() + if err := stream.CloseSend(); err != nil { + log.Warningf("Failed to close send stream: %v", err) + } }() log.Infof("Transmitting endpoint %v over wire...", e) return w.Transmit(ctx, stream) diff --git a/x/wire/intf/client/main.go b/x/wire/intf/client/main.go index 1ce877551..68341c882 100644 --- a/x/wire/intf/client/main.go +++ b/x/wire/intf/client/main.go @@ -48,7 +48,7 @@ func main() { opts := []grpc.DialOption{ grpc.WithTransportCredentials(insecure.NewCredentials()), } - conn, err := grpc.DialContext(ctx, *addr, opts...) + conn, err := grpc.NewClient(*addr, opts...) if err != nil { log.Fatalf("Failed to dial %q: %v", *addr, err) } @@ -64,7 +64,9 @@ func main() { return err } defer func() { - stream.CloseSend() + if err := stream.CloseSend(); err != nil { + log.Warningf("Failed to close send stream: %v", err) + } }() log.Infof("Transmitting endpoint %v over wire...", e) return w.Transmit(ctx, stream) diff --git a/x/wire/wire.go b/x/wire/wire.go index 219e62659..30dacb1e7 100644 --- a/x/wire/wire.go +++ b/x/wire/wire.go @@ -151,7 +151,11 @@ func (w *Wire) Transmit(ctx context.Context, stream Stream) error { }) g.Go(func() error { if cs, ok := stream.(grpc.ClientStream); ok { - defer cs.CloseSend() + defer func() { + if err := cs.CloseSend(); err != nil { + log.Warningf("Failed to close send stream: %v", err) + } + }() } for { data, err := w.src.Read()