diff --git a/.github/workflows/image-build.yml b/.github/workflows/image-build.yml index 51633a794..b024c38af 100644 --- a/.github/workflows/image-build.yml +++ b/.github/workflows/image-build.yml @@ -1,8 +1,26 @@ name: Image build on: [pull_request] jobs: + ep-build-amd64: + name: Image build/amd64 LEGACY entrypoint + runs-on: ubuntu-latest + steps: + - name: Check out code into the Go module directory + uses: actions/checkout@v2 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + + - name: Build container image + uses: docker/build-push-action@v2 + with: + context: . + push: false + tags: ghcr.io/${{ github.repository }}:ep-latest-amd64 + file: images/Dockerfile + build-amd64: - name: Image build/amd64 + name: Image build/amd64 daemonized alternative runs-on: ubuntu-latest steps: - name: Check out code into the Go module directory @@ -17,7 +35,7 @@ jobs: context: . push: false tags: ghcr.io/${{ github.repository }}:latest-amd64 - file: images/Dockerfile + file: images/Dockerfile.thick build-arm64: name: Image build/arm64 diff --git a/.github/workflows/image-push-master.yml b/.github/workflows/image-push-master.yml index dd964a771..48da13c5c 100644 --- a/.github/workflows/image-push-master.yml +++ b/.github/workflows/image-push-master.yml @@ -33,6 +33,16 @@ jobs: ghcr.io/${{ github.repository }}:snapshot-amd64 file: images/Dockerfile + - name: Push container image for daemon based deployment + if: github.repository_owner == 'k8snetworkplumbingwg' + uses: docker/build-push-action@v2 + with: + context: . + push: true + tags: | + ghcr.io/${{ github.repository }}:thick-amd64 + file: images/Dockerfile.thick + push-arm64: name: Image push/arm64 runs-on: ubuntu-latest @@ -223,3 +233,8 @@ jobs: docker manifest annotate ${{ env.REPOSITORY }}:latest ${{ env.REPOSITORY }}:latest-ppc64le --arch ppc64le docker manifest annotate ${{ env.REPOSITORY }}:latest ${{ env.REPOSITORY }}:latest-s390x --arch s390x docker manifest push ${{ env.REPOSITORY }}:latest + + docker pull ${{ env.REPOSITORY }}:thick-amd64 + docker manifest create ${{ env.REPOSITORY }}:thick ${{ env.REPOSITORY }}:thick-amd64 + docker manifest annotate ${{ env.REPOSITORY }}:thick ${{ env.REPOSITORY }}:thick-amd64 --arch amd64 + docker manifest push ${{ env.REPOSITORY }}:thick diff --git a/.github/workflows/image-push-release.yml b/.github/workflows/image-push-release.yml index f97c061f6..5c569171e 100644 --- a/.github/workflows/image-push-release.yml +++ b/.github/workflows/image-push-release.yml @@ -40,6 +40,17 @@ jobs: ${{ steps.docker_meta.outputs.tags }}-amd64 file: images/Dockerfile + - name: Push container image for daemon based deployment + if: github.repository_owner == 'k8snetworkplumbingwg' + uses: docker/build-push-action@v2 + with: + context: . + push: true + tags: | + ghcr.io/${{ github.repository }}:thick-amd64 + ${{ steps.docker_meta.outputs.tags }}-thick-amd64 + file: images/Dockerfile.thick + push-arm64: name: Image push/arm64 runs-on: ubuntu-latest diff --git a/.github/workflows/kind-e2e.yml b/.github/workflows/kind-e2e.yml index de639a034..ac004b706 100644 --- a/.github/workflows/kind-e2e.yml +++ b/.github/workflows/kind-e2e.yml @@ -14,7 +14,7 @@ jobs: run: docker run -d --restart=always -p "5000:5000" --name "kind-registry" registry:2 - name: Build latest-amd64 - run: docker build -t localhost:5000/multus:e2e -f images/Dockerfile . + run: docker build -t localhost:5000/multus:e2e -f images/Dockerfile.thick . - name: Push to local registry run: docker push localhost:5000/multus:e2e diff --git a/.github/workflows/legacy-kind-e2e.yml b/.github/workflows/legacy-kind-e2e.yml new file mode 100644 index 000000000..419915f94 --- /dev/null +++ b/.github/workflows/legacy-kind-e2e.yml @@ -0,0 +1,46 @@ +name: e2e-kind legacy installation with entrypoint script +on: [push, pull_request] +jobs: + e2e-kind: + runs-on: ubuntu-latest + if: > + (( github.event.pull_request.head.repo.owner.login != github.event.pull_request.base.repo.owner.login ) && + github.event_name == 'pull_request' ) || (github.event_name == 'push' && github.event.commits != '[]' ) + steps: + - name: Check out code into the Go module directory + uses: actions/checkout@v2 + + - name: Setup registry + run: docker run -d --restart=always -p "5000:5000" --name "kind-registry" registry:2 + + - name: Build latest-amd64 + run: docker build -t localhost:5000/multus:e2e -f images/Dockerfile . + + - name: Push to local registry + run: docker push localhost:5000/multus:e2e + + - name: Get kind/kubectl/koko + working-directory: ./e2e + run: ./get_tools.sh + + - name: Setup cluster + working-directory: ./e2e + run: ./setup_cluster.sh + + - name: Test simple pod + working-directory: ./e2e + run: ./test-simple-pod.sh + + - name: Test macvlan1 + working-directory: ./e2e + run: ./test-simple-macvlan1.sh + + - name: Test default route1 + working-directory: ./e2e + run: ./test-default-route1.sh + + - name: cleanup cluster and registry + run: | + kind delete cluster + docker kill kind-registry + docker rm kind-registry diff --git a/README.md b/README.md index 9f82a207f..eee7391d7 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ The quickstart installation method for Multus requires that you have first insta Clone this GitHub repository, we'll apply a daemonset which installs Multus using to `kubectl` from this repo. From the root directory of the clone, apply the daemonset YAML file: ``` -$ cat ./deployments/multus-daemonset.yml | kubectl apply -f - +$ cat ./deployments/multus-daemonset-thick-plugin.yml | kubectl apply -f - ``` This will configure your systems to be ready to use Multus CNI, but, to get started with adding additional interfaces to your pods, refer to our complete [quick-start guide](docs/quickstart.md) diff --git a/cmd/config-generation/kubeconfig.go b/cmd/config-generation/kubeconfig.go new file mode 100644 index 000000000..f1199097e --- /dev/null +++ b/cmd/config-generation/kubeconfig.go @@ -0,0 +1,147 @@ +// Copyright (c) 2021 Multus Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package main + +import ( + "encoding/base64" + "flag" + "fmt" + "io/ioutil" + "log" + "os" + "strings" +) + +const userRWPermission = 0600 + +const ( + cniConfigDirVarName = "cni-config-dir" + k8sCAFilePathVarName = "kube-ca-file" + k8sServiceHostVarName = "k8s-service-host" + k8sServicePortVarName = "k8s-service-port" + serviceAccountPath = "/var/run/secrets/kubernetes.io/serviceaccount" + skipTLSVerifyVarName = "skip-tls-verify" +) + +const ( + defaultCniConfigDir = "/host/etc/cni/net.d" + defaultK8sCAFilePath = "" + defaultK8sServiceHost = "" + defaultK8sServicePort = 0 + defaultSkipTLSValue = false +) + +func main() { + k8sServiceHost := flag.String(k8sServiceHostVarName, defaultK8sServiceHost, "Cluster IP of the kubernetes service") + k8sServicePort := flag.Int(k8sServicePortVarName, defaultK8sServicePort, "Port of the kubernetes service") + skipTLSVerify := flag.Bool(skipTLSVerifyVarName, defaultSkipTLSValue, "Should TLS verification be skipped") + kubeCAFilePath := flag.String(k8sCAFilePathVarName, defaultK8sCAFilePath, "Override the default kubernetes CA file path") + cniConfigDir := flag.String(cniConfigDirVarName, defaultCniConfigDir, "CNI config dir") + flag.Parse() + + if *k8sServiceHost == defaultK8sServiceHost { + logInvalidArg("must provide the k8s service cluster port") + } + if *k8sServicePort == defaultK8sServicePort { + logInvalidArg("must provide the k8s service cluster port") + } + if *kubeCAFilePath == defaultK8sServiceHost { + *kubeCAFilePath = serviceAccountPath + "/ca.crt" + } + + tlsCfg := "insecure-skip-tls-verify: true" + if !*skipTLSVerify { + kubeCAFileContents, err := k8sCAFileContentsBase64(*kubeCAFilePath) + if err != nil { + logError("failed grabbing CA file: %w", err) + } + tlsCfg = "certificate-authority-data: " + kubeCAFileContents + } + + multusConfigDir := *cniConfigDir + "/multus.d/" + if err := prepareCNIConfigDir(multusConfigDir); err != nil { + logError("failed to create CNI config dir: %w", err) + } + kubeConfigFilePath := *cniConfigDir + "/multus.d/multus.kubeconfig" + serviceAccountToken, err := k8sKubeConfigToken(serviceAccountPath + "/token") + if err != nil { + logError("failed grabbing k8s token: %w", err) + } + if err := writeKubeConfig(kubeConfigFilePath, "https", *k8sServiceHost, *k8sServicePort, tlsCfg, serviceAccountToken); err != nil { + logError("failed generating kubeconfig: %w", err) + } +} + +func k8sCAFileContentsBase64(pathCAFile string) (string, error) { + data, err := ioutil.ReadFile(pathCAFile) + if err != nil { + return "", fmt.Errorf("failed reading file %s: %w", pathCAFile, err) + } + return strings.Trim(base64.StdEncoding.EncodeToString(data), "\n"), nil +} + +func k8sKubeConfigToken(tokenPath string) (string, error) { + data, err := ioutil.ReadFile(tokenPath) + if err != nil { + return "", fmt.Errorf("failed reading file %s: %w", tokenPath, err) + } + return string(data), nil +} + +func writeKubeConfig(outputPath string, protocol string, k8sServiceIP string, k8sServicePort int, tlsConfig string, serviceAccountToken string) error { + kubeConfigTemplate := ` +# Kubeconfig file for Multus CNI plugin. +apiVersion: v1 +kind: Config +clusters: +- name: local + cluster: + server: %s://[%s]:%d + %s +users: +- name: multus + user: + token: "%s" +contexts: +- name: multus-context + context: + cluster: local + user: multus +current-context: multus-context +` + kubeconfig := fmt.Sprintf(kubeConfigTemplate, protocol, k8sServiceIP, k8sServicePort, tlsConfig, serviceAccountToken) + logInfo("Generated KubeConfig: \n%s", kubeconfig) + return ioutil.WriteFile(outputPath, []byte(kubeconfig), userRWPermission) +} + +func prepareCNIConfigDir(cniConfigDirPath string) error { + return os.MkdirAll(cniConfigDirPath, userRWPermission) +} + +func logInvalidArg(format string, values ...interface{}) { + log.Printf("ERROR: %s", fmt.Errorf(format, values...).Error()) + flag.PrintDefaults() + os.Exit(1) +} + +func logError(format string, values ...interface{}) { + log.Printf("ERROR: %s", fmt.Errorf(format, values...).Error()) + os.Exit(1) +} + +func logInfo(format string, values ...interface{}) { + log.Printf("INFO: %s", fmt.Sprintf(format, values...)) +} diff --git a/cmd/controller/main.go b/cmd/controller/main.go new file mode 100644 index 000000000..8a50cce58 --- /dev/null +++ b/cmd/controller/main.go @@ -0,0 +1,213 @@ +// Copyright (c) 2021 Multus Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package main + +import ( + "flag" + "fmt" + "io" + "os" + "path/filepath" + + "gopkg.in/k8snetworkplumbingwg/multus-cni.v3/pkg/config" + "gopkg.in/k8snetworkplumbingwg/multus-cni.v3/pkg/logging" + "gopkg.in/k8snetworkplumbingwg/multus-cni.v3/pkg/multus" +) + +const ( + multusPluginName = "multus" + multusConfigFileName = "00-multus.conf" +) + +const ( + defaultCniConfigDir = "/etc/cni/net.d" + defaultMultusAdditionalBinDir = "" + defaultMultusCNIVersion = "" + defaultMultusConfigFile = "auto" + defaultMultusGlobalNamespaces = "" + defaultMultusKubeconfigPath = "/etc/cni/net.d/multus.d/multus.kubeconfig" + defaultMultusLogFile = "" + defaultMultusLogLevel = "" + defaultMultusLogToStdErr = false + defaultMultusMasterCNIFile = "" + defaultMultusNamespaceIsolation = false + defaultMultusReadinessIndicatorFile = "" +) + +const ( + cniConfigDirVarName = "cni-config-dir" + multusAdditionalBinDirVarName = "additional-bin-dir" + multusAutoconfigDirVarName = "multus-autoconfig-dir" + multusCNIVersion = "cni-version" + multusConfigFileVarName = "multus-conf-file" + multusGlobalNamespaces = "global-namespaces" + multusLogFile = "multus-log-file" + multusLogLevel = "multus-log-level" + multusLogToStdErr = "multus-log-to-stderr" + multusKubeconfigPath = "multus-kubeconfig-file-host" + multusMasterCNIFileVarName = "multus-master-cni-file" + multusNamespaceIsolation = "namespace-isolation" + multusReadinessIndicatorFile = "readiness-indicator-file" +) + +func main() { + versionOpt := false + flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ExitOnError) + + cniConfigDir := flag.String(cniConfigDirVarName, defaultCniConfigDir, "CNI config dir") + multusConfigFile := flag.String(multusConfigFileVarName, defaultMultusConfigFile, "The multus configuration file to use. By default, a new configuration is generated.") + multusMasterCni := flag.String(multusMasterCNIFileVarName, defaultMultusMasterCNIFile, "The relative name of the configuration file of the cluster primary CNI.") + multusAutoconfigDir := flag.String(multusAutoconfigDirVarName, *cniConfigDir, "The directory path for the generated multus configuration.") + namespaceIsolation := flag.Bool(multusNamespaceIsolation, defaultMultusNamespaceIsolation, "If the network resources are only available within their defined namespaces.") + globalNamespaces := flag.String(multusGlobalNamespaces, defaultMultusGlobalNamespaces, "Comma-separated list of namespaces which can be referred to globally when namespace isolation is enabled.") + logToStdErr := flag.Bool(multusLogToStdErr, defaultMultusLogToStdErr, "If the multus logs are also to be echoed to stderr.") + logLevel := flag.String(multusLogLevel, defaultMultusLogLevel, "One of: debug/verbose/error/panic. Used only with --multus-conf-file=auto.") + logFile := flag.String(multusLogFile, defaultMultusLogFile, "Path where to multus will log. Used only with --multus-conf-file=auto.") + cniVersion := flag.String(multusCNIVersion, defaultMultusCNIVersion, "Allows you to specify CNI spec version. Used only with --multus-conf-file=auto.") + additionalBinDir := flag.String(multusAdditionalBinDirVarName, defaultMultusAdditionalBinDir, "Additional binary directory to specify in the configurations. Used only with --multus-conf-file=auto.") + readinessIndicator := flag.String(multusReadinessIndicatorFile, defaultMultusReadinessIndicatorFile, "Which file should be used as the readiness indicator. Used only with --multus-conf-file=auto.") + multusKubeconfig := flag.String(multusKubeconfigPath, defaultMultusKubeconfigPath, "The path to the kubeconfig") + overrideNetworkName := flag.Bool("override-network-name", false, "Used when ") + flag.BoolVar(&versionOpt, "version", false, "Show application version") + flag.BoolVar(&versionOpt, "v", false, "Show application version") + flag.Parse() + if versionOpt == true { + fmt.Printf("%s\n", multus.PrintVersionString()) + return + } + + if *logToStdErr { + logging.SetLogStderr(*logToStdErr) + } + if *logFile != defaultMultusLogFile { + logging.SetLogFile(*logFile) + } + if *logLevel != defaultMultusLogLevel { + logging.SetLogLevel(*logLevel) + } + + if *multusConfigFile == defaultMultusConfigFile { + if *cniVersion == defaultMultusCNIVersion { + _ = logging.Errorf("the CNI version is a mandatory parameter when the '-multus-config-file=auto' option is used") + } + + var configurationOptions []config.Option + if *namespaceIsolation { + configurationOptions = append( + configurationOptions, config.WithNamespaceIsolation()) + } + + if *globalNamespaces != defaultMultusGlobalNamespaces { + configurationOptions = append( + configurationOptions, config.WithGlobalNamespaces(*globalNamespaces)) + } + + if *logToStdErr != defaultMultusLogToStdErr { + configurationOptions = append( + configurationOptions, config.WithLogToStdErr()) + } + + if *logLevel != defaultMultusLogLevel { + configurationOptions = append( + configurationOptions, config.WithLogLevel(*logLevel)) + } + + if *logFile != defaultMultusLogFile { + configurationOptions = append( + configurationOptions, config.WithLogFile(*logFile)) + } + + if *additionalBinDir != defaultMultusAdditionalBinDir { + configurationOptions = append( + configurationOptions, config.WithAdditionalBinaryFileDir(*additionalBinDir)) + } + + if *readinessIndicator != defaultMultusReadinessIndicatorFile { + configurationOptions = append( + configurationOptions, config.WithReadinessFileIndicator(*readinessIndicator)) + } + multusConfig := config.NewMultusConfig(multusPluginName, *cniVersion, *multusKubeconfig, configurationOptions...) + + var configManager *config.Manager + var err error + if *multusMasterCni == "" { + configManager, err = config.NewManager(*multusConfig, *multusAutoconfigDir) + } else { + configManager, err = config.NewManagerWithExplicitPrimaryCNIPlugin( + *multusConfig, *multusAutoconfigDir, *multusMasterCni) + } + if err != nil { + _ = logging.Errorf("failed to create the configuration manager for the primary CNI plugin: %v", err) + os.Exit(2) + } + + if *overrideNetworkName { + if err := configManager.OverrideNetworkName(); err != nil { + _ = logging.Errorf("could not override the network name: %v", err) + } + } + + generatedMultusConfig, err := configManager.GenerateConfig() + if err != nil { + _ = logging.Errorf("failed to generated the multus configuration: %v", err) + } + logging.Verbosef("Generated MultusCNI config: %s", generatedMultusConfig) + + if err := configManager.PersistMultusConfig(generatedMultusConfig); err != nil { + _ = logging.Errorf("failed to persist the multus configuration: %v", err) + } + + configWatcherDoneChannel := make(chan struct{}) + go func(stopChannel chan struct{}, doneChannel chan struct{}) { + defer func() { + stopChannel <- struct{}{} + }() + if err := configManager.MonitorDelegatedPluginConfiguration(stopChannel, configWatcherDoneChannel); err != nil { + _ = logging.Errorf("error watching file: %v", err) + } + }(make(chan struct{}), configWatcherDoneChannel) + + <-configWatcherDoneChannel + } else { + if err := copyUserProvidedConfig(*multusConfigFile, *cniConfigDir); err != nil { + logging.Errorf("failed to copy the user provided configuration %s: %v", *multusConfigFile, err) + } + } +} + +func copyUserProvidedConfig(multusConfigPath string, cniConfigDir string) error { + srcFile, err := os.Open(multusConfigPath) + if err != nil { + return fmt.Errorf("failed to open (READ only) file %s: %w", multusConfigPath, err) + } + + dstFileName := cniConfigDir + "/" + filepath.Base(multusConfigPath) + dstFile, err := os.Create(dstFileName) + if err != nil { + return fmt.Errorf("creating copying file %s: %w", dstFileName, err) + } + nBytes, err := io.Copy(srcFile, dstFile) + if err != nil { + return fmt.Errorf("error copying file: %w", err) + } + srcFileInfo, err := srcFile.Stat() + if err != nil { + return fmt.Errorf("failed to stat the file: %w", err) + } else if nBytes != srcFileInfo.Size() { + return fmt.Errorf("error copying file - copied only %d bytes out of %d", nBytes, srcFileInfo.Size()) + } + return nil +} diff --git a/deployments/multus-daemonset-thick-plugin.yml b/deployments/multus-daemonset-thick-plugin.yml new file mode 100644 index 000000000..05b9f76a4 --- /dev/null +++ b/deployments/multus-daemonset-thick-plugin.yml @@ -0,0 +1,189 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: network-attachment-definitions.k8s.cni.cncf.io +spec: + group: k8s.cni.cncf.io + scope: Namespaced + names: + plural: network-attachment-definitions + singular: network-attachment-definition + kind: NetworkAttachmentDefinition + shortNames: + - net-attach-def + versions: + - name: v1 + served: true + storage: true + schema: + openAPIV3Schema: + description: 'NetworkAttachmentDefinition is a CRD schema specified by the Network Plumbing + Working Group to express the intent for attaching pods to one or more logical or physical + networks. More information available at: https://github.com/k8snetworkplumbingwg/multi-net-spec' + type: object + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this represen + tation 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: 'NetworkAttachmentDefinition spec defines the desired state of a network attachment' + type: object + properties: + config: + description: 'NetworkAttachmentDefinition config is a JSON-formatted CNI configuration' + type: string +--- +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: multus +rules: + - apiGroups: ["k8s.cni.cncf.io"] + resources: + - '*' + verbs: + - '*' + - apiGroups: + - "" + resources: + - pods + - pods/status + verbs: + - get + - update + - apiGroups: + - "" + - events.k8s.io + resources: + - events + verbs: + - create + - patch + - update +--- +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: multus +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: multus +subjects: + - kind: ServiceAccount + name: multus + namespace: kube-system +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: multus + namespace: kube-system +--- +apiVersion: apps/v1 +kind: DaemonSet +metadata: + name: kube-multus-ds + namespace: kube-system + labels: + tier: node + app: multus + name: multus +spec: + selector: + matchLabels: + name: multus + updateStrategy: + type: RollingUpdate + template: + metadata: + labels: + tier: node + app: multus + name: multus + spec: + hostNetwork: true + tolerations: + - operator: Exists + effect: NoSchedule + serviceAccountName: multus + containers: + - name: kube-multus + image: ghcr.io/k8snetworkplumbingwg/multus-cni:thick + command: [ "/usr/src/multus-cni/bin/multus-daemon" ] + args: + - "-cni-version=0.3.1" + - "-cni-config-dir=/host/etc/cni/net.d" + - "-multus-autoconfig-dir=/host/etc/cni/net.d" + - "-multus-log-to-stderr=true" + - "-multus-log-level=verbose" + resources: + requests: + cpu: "100m" + memory: "50Mi" + limits: + cpu: "100m" + memory: "50Mi" + securityContext: + privileged: true + volumeMounts: + - name: cni + mountPath: /host/etc/cni/net.d + - name: cnibin + mountPath: /host/opt/cni/bin + - name: multus-cfg + mountPath: /tmp/multus-conf + initContainers: + - name: install-multus-binary + image: ghcr.io/k8snetworkplumbingwg/multus-cni:stable + command: + - "cp" + - "/usr/src/multus-cni/bin/multus" + - "/host/opt/cni/bin/multus" + resources: + requests: + cpu: "10m" + memory: "15Mi" + securityContext: + privileged: true + volumeMounts: + - name: cnibin + mountPath: /host/opt/cni/bin + mountPropagation: Bidirectional + - name: generate-kubeconfig + image: docker.io/maiqueb/multus + command: + - "/usr/src/multus-cni/bin/generate-kubeconfig" + args: + - "-k8s-service-host=$(KUBERNETES_SERVICE_HOST)" + - "-k8s-service-port=$(KUBERNETES_SERVICE_PORT)" + resources: + requests: + cpu: "10m" + memory: "15Mi" + securityContext: + privileged: true + volumeMounts: + - name: cni + mountPath: /host/etc/cni/net.d + mountPropagation: Bidirectional + terminationGracePeriodSeconds: 10 + volumes: + - name: cni + hostPath: + path: /etc/cni/net.d + - name: cnibin + hostPath: + path: /opt/cni/bin + diff --git a/deployments/multus-daemonset.yml b/deployments/multus-daemonset.yml index 4b6b950d8..74c513cd0 100644 --- a/deployments/multus-daemonset.yml +++ b/deployments/multus-daemonset.yml @@ -190,6 +190,23 @@ spec: mountPath: /host/opt/cni/bin - name: multus-cfg mountPath: /tmp/multus-conf + initContainers: + - name: install-multus-binary + image: ghcr.io/k8snetworkplumbingwg/multus-cni:stable + command: + - "cp" + - "/usr/src/multus-cni/bin/multus" + - "/host/opt/cni/bin/multus" + resources: + requests: + cpu: "10m" + memory: "15Mi" + securityContext: + privileged: true + volumeMounts: + - name: cnibin + mountPath: /host/opt/cni/bin + mountPropagation: Bidirectional terminationGracePeriodSeconds: 10 volumes: - name: cni diff --git a/docs/quickstart.md b/docs/quickstart.md index 1d4ebfcc9..d56bc93f2 100644 --- a/docs/quickstart.md +++ b/docs/quickstart.md @@ -51,7 +51,7 @@ git clone https://github.com/k8snetworkplumbingwg/multus-cni.git && cd multus-cn We'll apply a YAML file with `kubectl` from this repo. ``` -$ cat ./deployments/multus-daemonset.yml | kubectl apply -f - +$ cat ./deployments/multus-daemonset-thick-plugin.yml | kubectl apply -f - ``` ### What the Multus daemonset does diff --git a/e2e/legacy-multus-daemonset.yml b/e2e/legacy-multus-daemonset.yml new file mode 100644 index 000000000..f61e16102 --- /dev/null +++ b/e2e/legacy-multus-daemonset.yml @@ -0,0 +1,299 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: network-attachment-definitions.k8s.cni.cncf.io +spec: + group: k8s.cni.cncf.io + scope: Namespaced + names: + plural: network-attachment-definitions + singular: network-attachment-definition + kind: NetworkAttachmentDefinition + shortNames: + - net-attach-def + versions: + - name: v1 + served: true + storage: true + schema: + openAPIV3Schema: + type: object + properties: + spec: + type: object + properties: + config: + type: string +--- +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: multus +rules: + - apiGroups: ["k8s.cni.cncf.io"] + resources: + - '*' + verbs: + - '*' + - apiGroups: + - "" + resources: + - pods + - pods/status + verbs: + - get + - update +--- +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: multus +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: multus +subjects: +- kind: ServiceAccount + name: multus + namespace: kube-system +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: multus + namespace: kube-system +--- +kind: ConfigMap +apiVersion: v1 +metadata: + name: multus-cni-config + namespace: kube-system + labels: + tier: node + app: multus +data: + # NOTE: If you'd prefer to manually apply a configuration file, you may create one here. + # In the case you'd like to customize the Multus installation, you should change the arguments to the Multus pod + # change the "args" line below from + # - "--multus-conf-file=auto" + # to: + # "--multus-conf-file=/tmp/multus-conf/70-multus.conf" + # Additionally -- you should ensure that the name "70-multus.conf" is the alphabetically first name in the + # /etc/cni/net.d/ directory on each node, otherwise, it will not be used by the Kubelet. + cni-conf.json: | + { + "name": "multus-cni-network", + "type": "multus", + "capabilities": { + "portMappings": true + }, + "delegates": [ + { + "cniVersion": "0.3.1", + "name": "default-cni-network", + "plugins": [ + { + "type": "flannel", + "name": "flannel.1", + "delegate": { + "isDefaultGateway": true, + "hairpinMode": true + } + }, + { + "type": "portmap", + "capabilities": { + "portMappings": true + } + } + ] + } + ], + "kubeconfig": "/etc/cni/net.d/multus.d/multus.kubeconfig" + } +--- +apiVersion: apps/v1 +kind: DaemonSet +metadata: + name: kube-multus-ds-amd64 + namespace: kube-system + labels: + tier: node + app: multus + name: multus +spec: + selector: + matchLabels: + name: multus + updateStrategy: + type: RollingUpdate + template: + metadata: + labels: + tier: node + app: multus + name: multus + spec: + hostNetwork: true + nodeSelector: + kubernetes.io/arch: amd64 + tolerations: + - operator: Exists + effect: NoSchedule + serviceAccountName: multus + containers: + - name: kube-multus + image: localhost:5000/multus:e2e + command: ["/entrypoint.sh"] + args: + - "--multus-conf-file=auto" + - "--cni-version=0.3.1" + resources: + requests: + cpu: "100m" + memory: "50Mi" + limits: + cpu: "100m" + memory: "50Mi" + securityContext: + privileged: true + volumeMounts: + - name: cni + mountPath: /host/etc/cni/net.d + - name: cnibin + mountPath: /host/opt/cni/bin + - name: multus-cfg + mountPath: /tmp/multus-conf + initContainers: + - name: install-multus-binary + image: localhost:5000/multus:e2e + command: + - "cp" + - "/usr/src/multus-cni/bin/multus" + - "/host/opt/cni/bin/multus" + resources: + requests: + cpu: "10m" + memory: "15Mi" + securityContext: + privileged: true + volumeMounts: + - name: cnibin + mountPath: /host/opt/cni/bin + mountPropagation: Bidirectional + - name: generate-kubeconfig + image: localhost:5000/multus:e2e + command: + - "/usr/src/multus-cni/bin/generate-kubeconfig" + args: + - "-k8s-service-host=$(KUBERNETES_SERVICE_HOST)" + - "-k8s-service-port=$(KUBERNETES_SERVICE_PORT)" + resources: + requests: + cpu: "10m" + memory: "15Mi" + securityContext: + privileged: true + volumeMounts: + - name: cni + mountPath: /host/etc/cni/net.d + mountPropagation: Bidirectional + - name: generate-multus-config + image: localhost:5000/multus:e2e + command: + - "/usr/src/multus-cni/bin/generate-multus-cni-config" + args: + - "-cni-version=0.3.1" + - "-cni-config-dir=/host/etc/cni/net.d" + - "-multus-autoconfig-dir=/host/etc/cni/net.d" + resources: + requests: + cpu: "10m" + memory: "15Mi" + securityContext: + privileged: true + volumeMounts: + - name: cni + mountPath: /host/etc/cni/net.d + mountPropagation: Bidirectional + volumes: + - name: cni + hostPath: + path: /etc/cni/net.d + - name: cnibin + hostPath: + path: /opt/cni/bin + - name: multus-cfg + configMap: + name: multus-cni-config + items: + - key: cni-conf.json + path: 70-multus.conf +--- +apiVersion: apps/v1 +kind: DaemonSet +metadata: + name: kube-multus-ds-ppc64le + namespace: kube-system + labels: + tier: node + app: multus + name: multus +spec: + selector: + matchLabels: + name: multus + updateStrategy: + type: RollingUpdate + template: + metadata: + labels: + tier: node + app: multus + name: multus + spec: + hostNetwork: true + nodeSelector: + kubernetes.io/arch: ppc64le + tolerations: + - operator: Exists + effect: NoSchedule + serviceAccountName: multus + containers: + - name: kube-multus + # ppc64le support requires multus:latest for now. support 3.3 or later. + image: nfvpe/multus:latest-ppc64le + command: ["/entrypoint.sh"] + args: + - "--multus-conf-file=auto" + - "--cni-version=0.3.1" + resources: + requests: + cpu: "100m" + memory: "90Mi" + limits: + cpu: "100m" + memory: "90Mi" + securityContext: + privileged: true + volumeMounts: + - name: cni + mountPath: /host/etc/cni/net.d + - name: cnibin + mountPath: /host/opt/cni/bin + - name: multus-cfg + mountPath: /tmp/multus-conf + volumes: + - name: cni + hostPath: + path: /etc/cni/net.d + - name: cnibin + hostPath: + path: /opt/cni/bin + - name: multus-cfg + configMap: + name: multus-cni-config + items: + - key: cni-conf.json + path: 70-multus.conf diff --git a/e2e/multus-daemonset.yml b/e2e/multus-daemonset.yml index ff3050c9c..3802e35eb 100644 --- a/e2e/multus-daemonset.yml +++ b/e2e/multus-daemonset.yml @@ -145,10 +145,15 @@ spec: containers: - name: kube-multus image: localhost:5000/multus:e2e - command: ["/entrypoint.sh"] + command: [ "/usr/src/multus-cni/bin/multus-daemon" ] args: - - "--multus-conf-file=auto" - - "--cni-version=0.3.1" + - "-multus-conf-file=auto" + - "-cni-version=0.3.1" + - "-cni-config-dir=/host/etc/cni/net.d" + - "-multus-autoconfig-dir=/host/etc/cni/net.d" + - "-multus-log-to-stderr=true" + - "-multus-log-level=debug" + - "-multus-log-file=/tmp/multus.log" resources: requests: cpu: "100m" @@ -165,6 +170,40 @@ spec: mountPath: /host/opt/cni/bin - name: multus-cfg mountPath: /tmp/multus-conf + initContainers: + - name: install-multus-binary + image: localhost:5000/multus:e2e + command: + - "cp" + - "/usr/src/multus-cni/bin/multus" + - "/host/opt/cni/bin/multus" + resources: + requests: + cpu: "10m" + memory: "15Mi" + securityContext: + privileged: true + volumeMounts: + - name: cnibin + mountPath: /host/opt/cni/bin + mountPropagation: Bidirectional + - name: generate-kubeconfig + image: localhost:5000/multus:e2e + command: + - "/usr/src/multus-cni/bin/generate-kubeconfig" + args: + - "-k8s-service-host=$(KUBERNETES_SERVICE_HOST)" + - "-k8s-service-port=$(KUBERNETES_SERVICE_PORT)" + resources: + requests: + cpu: "10m" + memory: "15Mi" + securityContext: + privileged: true + volumeMounts: + - name: cni + mountPath: /host/etc/cni/net.d + mountPropagation: Bidirectional volumes: - name: cni hostPath: diff --git a/go.mod b/go.mod index 4db189fd4..c69ea7dac 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,9 @@ go 1.16 require ( github.com/containernetworking/cni v0.8.1 github.com/containernetworking/plugins v0.9.1 + github.com/fsnotify/fsnotify v1.4.9 github.com/k8snetworkplumbingwg/network-attachment-definition-client v1.1.1-0.20210510153419-66a699ae3b05 + github.com/koron/go-dproxy v1.3.0 github.com/onsi/ginkgo v1.12.1 github.com/onsi/gomega v1.10.3 github.com/pkg/errors v0.9.1 diff --git a/go.sum b/go.sum index 3d0085adc..b1fee06f6 100644 --- a/go.sum +++ b/go.sum @@ -24,12 +24,10 @@ cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiy cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20201218220906-28db891af037/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/Azure/azure-sdk-for-go v43.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw= -github.com/Azure/go-autorest/autorest v0.11.12/go.mod h1:eipySxLmqSyC5s5k1CLupqet0PSENBEDP93LQ9a8QYw= github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg= github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= @@ -52,7 +50,6 @@ github.com/Microsoft/go-winio v0.4.15/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59 github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= github.com/Microsoft/hcsshim v0.8.10-0.20200715222032-5eafd1556990/go.mod h1:ay/0dTb7NsG8QMDfsRfLHgZo/6xAJShLe1+ePPflihk= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= -github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= @@ -102,20 +99,16 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmEg9bt0VpxxWqJlO4iwu3FBdHUzV7wQVg= github.com/cilium/ebpf v0.0.0-20200702112145-1c8d4c9ef775/go.mod h1:7cR51M8ViRLIdUjrmSXlK9pkrsDlLHbO8jiB8X8JnOc= -github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/clusterhq/flocker-go v0.0.0-20160920122132-2b8b7259d313/go.mod h1:P1wt9Z3DP8O6W3rvwCt0REIlshg1InHImaLW0t3ObY0= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/codegangsta/negroni v1.0.0/go.mod h1:v0y3T5G7Y1UlFfyxFn/QLRU4a2EuNau2iZY63YTKWo0= github.com/container-storage-interface/spec v1.2.0/go.mod h1:6URME8mwIBbpVyZV93Ce5St17xBiQJQY67NDsuohiy4= -github.com/container-storage-interface/spec v1.3.0/go.mod h1:6URME8mwIBbpVyZV93Ce5St17xBiQJQY67NDsuohiy4= github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59/go.mod h1:pA0z1pT8KYB3TCXK/ocprsh7MAkoW8bZVzPdih9snmM= github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= github.com/containerd/console v1.0.0/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE= -github.com/containerd/console v1.0.1/go.mod h1:XUsP6YE/mKtz6bxc+I8UiKKTP04qjQL4qcS3XoQ5xkw= github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.4.4/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= @@ -130,7 +123,6 @@ github.com/containernetworking/cni v0.8.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ github.com/containernetworking/plugins v0.9.1 h1:FD1tADPls2EEi3flPc2OegIY1M9pUa9r2Quag7HMLV8= github.com/containernetworking/plugins v0.9.1/go.mod h1:xP/idU2ldlzN6m4p5LmGiwRDjeJr6FLK6vuiUwoH7P8= github.com/coredns/corefile-migration v1.0.10/go.mod h1:RMy/mXdeDlYwzt0vdMEJvT2hGJ2I86/eO0UdXmH9XNI= -github.com/coredns/corefile-migration v1.0.11/go.mod h1:RMy/mXdeDlYwzt0vdMEJvT2hGJ2I86/eO0UdXmH9XNI= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= @@ -147,8 +139,6 @@ github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfc github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= 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= @@ -162,10 +152,8 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug= github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v17.12.0-ce-rc1.0.20200916142827-bd33bbf0497b+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker v20.10.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= @@ -173,7 +161,6 @@ github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZ github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/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 v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= @@ -181,7 +168,6 @@ github.com/emicklei/go-restful v2.10.0+incompatible/go.mod h1:otzb+WCGbkyDHkqmQm github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/euank/go-kmsg-parser v2.0.0+incompatible/go.mod h1:MhmAMZ8V4CYH4ybgdRwPr2TU5ThnS43puaKEMpja1uw= -github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.9.0+incompatible h1:kLcOMZeuLAJvL2BPWLMIj5oaZQobrkAqrL+WFZwQses= github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= @@ -190,7 +176,6 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= -github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= @@ -201,7 +186,6 @@ github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0 github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= github.com/go-acme/lego v2.5.0+incompatible/go.mod h1:yzMNe9CasVUhkquNvti5nAtPmG94USbYxYrZfTkIn0M= github.com/go-bindata/go-bindata v3.1.1+incompatible/go.mod h1:xK8Dsgwmeed+BBsSy2XTopBn/8uK2HWuGSnA11C3Joo= -github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -212,8 +196,6 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-logr/logr v0.2.0 h1:QvGt2nLcHH0WK9orKa+ppBPAxREcH364nPUedEpK0TY= github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= -github.com/go-logr/logr v0.4.0 h1:K7/B1jt6fIBQVd4Owv2MqGQClcgf0R266+7C/QjRcLc= -github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= @@ -245,12 +227,10 @@ github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsd github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY= github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= -github.com/go-openapi/spec v0.19.5/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk= github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY= github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU= -github.com/go-openapi/strfmt v0.19.5/go.mod h1:eftuHTlB/dI8Uq8JJOyRlieZf+WkkxUuk0dgdHXr2Qk= github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= @@ -259,10 +239,8 @@ github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA= github.com/go-openapi/validate v0.19.5/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4= -github.com/go-openapi/validate v0.19.8/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4= github.com/go-ozzo/ozzo-validation v3.5.0+incompatible/go.mod h1:gsEKFIVnabGBt6mXmxK0MoFy+cZoTJY6mu5Ll3LVLBU= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/gobuffalo/here v0.6.0/go.mod h1:wAG085dHOYqUpf+Ap+WOdrPTp5IYcDAs/x7PLa8Y5fM= github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= @@ -272,7 +250,6 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekf 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/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY= @@ -282,12 +259,9 @@ github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfb github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= 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 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= 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.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= @@ -307,10 +281,7 @@ github.com/golangplus/testing v0.0.0-20180327235837-af21d9c3145e/go.mod h1:0AA// github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/cadvisor v0.38.8/go.mod h1:1OFB9sOOMkBdUBGCO/1SArawTnDscgMzTodacVDe8mA= -github.com/google/cadvisor v0.39.0 h1:jai6dmBP9QAYluNGqU18yVUTw6uuyAW0AqtZIjvl8Qg= -github.com/google/cadvisor v0.39.0/go.mod h1:rjQFmK4jPCpxeUdLq9bYhNFFsjgGOtpnDmDeap0+nsw= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= 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= @@ -327,15 +298,12 @@ github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= -github.com/googleapis/gnostic v0.2.0 h1:l6N3VoaVzTncYYW+9yOz2LJJammFZGBO13sqgEhpy9g= github.com/googleapis/gnostic v0.2.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/googleapis/gnostic v0.4.1 h1:DLJCy1n/vrD4HPjOvYcT8aYQXpPIzoRZONaYwyycI+I= github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= @@ -367,7 +335,6 @@ github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.0.0-20180201235237-0fb14efe8c47/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= @@ -375,9 +342,7 @@ github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0m github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/heketi/heketi v9.0.1-0.20190917153846-c2e2a4ab7ab9+incompatible/go.mod h1:bB9ly3RchcQqsQ9CpyaQwvva7RS5ytVoSoholZQON6o= -github.com/heketi/heketi v10.2.0+incompatible/go.mod h1:bB9ly3RchcQqsQ9CpyaQwvva7RS5ytVoSoholZQON6o= github.com/heketi/tests v0.0.0-20151005000721-f3775cbcefd6/go.mod h1:xGMAM8JLi7UkZt1i4FQeQy0R2T8GLUwQhOP5M1gBhy4= -github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.5 h1:JboBksRwiiAJWvIYJVo46AfV+IAIKZpfrSzVKj42R4Q= @@ -407,16 +372,16 @@ github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgo github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/koron/go-dproxy v1.3.0 h1:wE0gxsw1NJnbkk5czp3/xUtwgTeLP8p/YaSjdUOmI7k= +github.com/koron/go-dproxy v1.3.0/go.mod h1:M+lZRjGA7zf1CdgBWoL8HH1lKb6jlgR4qnX3hxRdQHs= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= 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/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 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/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= github.com/libopenstorage/openstorage v1.0.0/go.mod h1:Sp1sIObHjat1BeXhfMqLZ14wnOzEhNx2YQedreMcUyc= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= @@ -434,13 +399,11 @@ github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN 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.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= -github.com/markbates/pkger v0.17.1/go.mod h1:0JoVlrol20BSywW79rN3kdFFsE5xYM+rSCQDXbLhiuI= github.com/marten-seemann/qtls v0.2.3/go.mod h1:xzjG7avBwGGbdZ8dTGxlBnLArsVKLvwmjgmPuiQEcYk= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= @@ -449,7 +412,6 @@ github.com/mholt/certmagic v0.6.2-0.20190624175158-6a42ef9fe8c2/go.mod h1:g4cOPx github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.3/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.4/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/miekg/dns v1.1.35/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= github.com/mindprince/gonvml v0.0.0-20190828220739-9ebdce4bb989/go.mod h1:2eu9pRWp8mo84xCg6KswZ+USQHjwgRhNp06sozOdsTY= github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= @@ -462,12 +424,8 @@ github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0Qu github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/moby/ipvs v1.0.1/go.mod h1:2pngiyseZbIKXNv7hsKj3O9UEz30c53MT9005gt2hxQ= -github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= -github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= github.com/moby/sys/mountinfo v0.1.3/go.mod h1:w2t2Avltqx8vE7gX5l+QiBKxODu2TX0+Syr3h52Tw4o= -github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo= -github.com/moby/term v0.0.0-20201216013528-df9cb8a40635/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc= 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= @@ -475,10 +433,8 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mohae/deepcopy v0.0.0-20170603005431-491d3605edfb/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= -github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/mrunalp/fileutils v0.0.0-20200520151820-abd8a0e76976/go.mod h1:x8F1gnqOkIEiO4rqoeEEEqQbo7HjGMTvyoq3gej4iT0= -github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mvdan/xurls v1.1.0/go.mod h1:tQlNn3BED8bE/15hnSL2HLkDeLWpNPAwtw7wkEq44oU= @@ -486,46 +442,35 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0= github.com/naoina/toml v0.1.1/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E= -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/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= -github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA= 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.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.11.0 h1:JAKSXpt1YjtLA7YpPiqO9ss6sNXEsPfSGdwN0UHqzrw= github.com/onsi/ginkgo v1.11.0/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/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME= 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 h1:gph6h/qe9GSUw1NhH1gp+qb+h8rXD8Cy60Z32Qw3ELA= github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= -github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v1.0.0-rc92/go.mod h1:X1zlU4p7wOlX4+WRCz+hvlRv8phdL7UqbYD+vQwNMmE= -github.com/opencontainers/runc v1.0.0-rc93/go.mod h1:3NOsor4w32B2tC0Zbl8Knk4Wg84SM2ImC1fxBuqJ/H0= github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.3-0.20200728170252-4d89ac9fbff6/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE= -github.com/opencontainers/selinux v1.8.0 h1:+77ba4ar4jsCbL1GLbFL8fFM57w6suPfSS9PDLDY7KM= -github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= 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= @@ -572,20 +517,17 @@ github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdh github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= -github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= -github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= @@ -607,13 +549,11 @@ github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= 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 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= -github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/thecodeteam/goscaleio v0.1.0/go.mod h1:68sdkZAsK8bvEwBlbQnlLS+xU+hvLYM/iQ8KXej1AwM= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= @@ -632,10 +572,7 @@ github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae h1:4hwBBUfQCFe3C github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= github.com/vmware/govmomi v0.20.3/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU= github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= -github.com/willf/bitset v1.1.11 h1:N7Z7E9UvjW+sGsEl7k/SJrvY2reP1A07MrGuCjIOjRE= -github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -650,7 +587,6 @@ go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= @@ -667,11 +603,9 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0 h1:hb9wdF1z5waM+dSIICn1l0DkLVDT3hqhhQsDNUmHPRE= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -679,7 +613,6 @@ golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4= golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= @@ -687,7 +620,6 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20210220032938-85be41e4509f/go.mod h1:I6l2HNBLBZEcrOoCpyKLdY2lHoRZ8lI4x60KMCQDft4= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= @@ -703,15 +635,12 @@ golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPI golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mobile v0.0.0-20201217150744-e6ae53a27f4f/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 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/mod v0.3.1-0.20200828183125-ce943fd02449/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= @@ -737,7 +666,6 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190923162816-aa69164e4478/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-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -747,15 +675,12 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210224082022-3d97a244fca7 h1:OgUuv8lsRpBibGNbSizVwKWlysjaNzmC9gYMhPVfqFM= golang.org/x/net v0.0.0-20210224082022-3d97a244fca7/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw= @@ -793,12 +718,9 @@ golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7w 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-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -817,41 +739,27 @@ golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201110211018-35f3e6cf4a65/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201117170446-d9b008d0a637/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073 h1:8qxJSnu+7dRq6upnbntrmriWByIakBuct5OM/MdQC1M= -golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE= -golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/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 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e h1:EHBhcS0mlXEAVwNyO2dLfjToGsyY4j24pTs2ScHnX7s= golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba h1:O8mE0/t419eoIwhTFpKVkHiTs/Igowgfkj25AcZrtiE= -golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 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= @@ -882,10 +790,8 @@ golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= @@ -897,7 +803,6 @@ golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjs golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 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/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= 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= @@ -922,14 +827,12 @@ google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/ google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 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/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7 h1:ZUjXAXmrAyrmmCPHgCA/vChHcpsX27MZ3yBonD/z1KE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -954,7 +857,6 @@ google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZi google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.23.0 h1:AzbTB6ux+okLTzP8Ru1Xs41C303zdcfEht7MQnYJt5A= 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.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= @@ -978,11 +880,8 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gcfg.v1 v1.2.0/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= @@ -1002,18 +901,13 @@ 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.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= 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 h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= -gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -1022,49 +916,26 @@ honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= k8s.io/api v0.20.10 h1:kAdgi1zcyenV88/uVEzS9B/fn1m4KRbmdKB0Lxl6z/M= k8s.io/api v0.20.10/go.mod h1:0kei3F6biGjtRQBo5dUeujq6Ji3UCh9aOSfp/THYd7I= -k8s.io/api v0.21.1 h1:94bbZ5NTjdINJEdzOkpS4vdPhkb1VFpTYC9zh43f75c= -k8s.io/api v0.21.1/go.mod h1:FstGROTmsSHBarKc8bylzXih8BLNYTiS3TZcsoEDg2s= k8s.io/apiextensions-apiserver v0.20.10/go.mod h1:am9XHHsM/FJBgPtl586TGSDAouRTLZC6wu25rb2VqCQ= -k8s.io/apiextensions-apiserver v0.21.1/go.mod h1:KESQFCGjqVcVsZ9g0xX5bacMjyX5emuWcS2arzdEouA= k8s.io/apimachinery v0.20.10 h1:GcFwz5hsGgKLohcNgv8GrInk60vUdFgBXW7uOY1i1YM= k8s.io/apimachinery v0.20.10/go.mod h1:kQa//VOAwyVwJ2+L9kOREbsnryfsGSkSM1przND4+mw= -k8s.io/apimachinery v0.21.1 h1:Q6XuHGlj2xc+hlMCvqyYfbv3H7SRGn2c8NycxJquDVs= -k8s.io/apimachinery v0.21.1/go.mod h1:jbreFvJo3ov9rj7eWT7+sYiRx+qZuCYXwWT1bcDswPY= k8s.io/apiserver v0.20.10 h1:9Th11BLOMY5HHHp2AciNNgp/y71XKO+FLHO0pultyrQ= k8s.io/apiserver v0.20.10/go.mod h1:bBm1wyFuID+0CsEYIdyiamOw9wEniEOq3HrCU40ky2M= -k8s.io/apiserver v0.21.1 h1:wTRcid53IhxhbFt4KTrFSw8tAncfr01EP91lzfcygVg= -k8s.io/apiserver v0.21.1/go.mod h1:nLLYZvMWn35glJ4/FZRhzLG/3MPxAaZTgV4FJZdr+tY= k8s.io/cli-runtime v0.20.10/go.mod h1:O8xLwPPEJsTyiCB/ePt4JgEPbStQUzSD8rt7GJJj6Kw= -k8s.io/cli-runtime v0.21.1/go.mod h1:TI9Bvl8lQWZB2KqE91QLCp9AZE4l29zNFnj/x4IX4Fw= k8s.io/client-go v0.20.10 h1:TgAL2pqcNWMH4eZoS9Uw0BLh2lu5a2A4pmegjp5pmsk= k8s.io/client-go v0.20.10/go.mod h1:fFg+aLoasv/R+xiVaWjxeqGFYltzgQcOQzkFaSRfnJ0= -k8s.io/client-go v0.21.1 h1:bhblWYLZKUu+pm50plvQF8WpY6TXdRRtcS/K9WauOj4= -k8s.io/client-go v0.21.1/go.mod h1:/kEw4RgW+3xnBGzvp9IWxKSNA+lXn3A7AuH3gdOAzLs= k8s.io/cloud-provider v0.20.10/go.mod h1:8HQ0NgW661PiH+QK1BPYD0QorpaxOXQs1ZsMfOe3uc0= -k8s.io/cloud-provider v0.21.1 h1:V7ro0ZuxMBNYVH4lJKxCdI+h2bQ7EApC5f7sQYrQLVE= -k8s.io/cloud-provider v0.21.1/go.mod h1:GgiRu7hOsZh3+VqMMbfLJJS9ZZM9A8k/YiZG8zkWpX4= k8s.io/cluster-bootstrap v0.20.10/go.mod h1:D+cqd8iJYeajaIUBUca4J3f/87L4qiFvMPzM5qs8x1o= -k8s.io/cluster-bootstrap v0.21.1/go.mod h1:izdXmPTfqI9gkjQLf9PQ1Y9/DSqG54zU2UByEzgdajs= k8s.io/code-generator v0.20.10/go.mod h1:i6FmG+QxaLxvJsezvZp0q/gAEzzOz3U53KFibghWToU= -k8s.io/code-generator v0.21.1/go.mod h1:hUlps5+9QaTrKx+jiM4rmq7YmH8wPOIko64uZCHDh6Q= k8s.io/component-base v0.20.10 h1:QNlekT6M2zBb4feHHmZ+YHZHcDbhbrYS7xHHY+v+kOE= k8s.io/component-base v0.20.10/go.mod h1:ZKOEin1xu68aJzxgzl5DZSp5J1IrjAOPlPN90/t6OI8= -k8s.io/component-base v0.21.1 h1:iLpj2btXbR326s/xNQWmPNGu0gaYSjzn7IN/5i28nQw= -k8s.io/component-base v0.21.1/go.mod h1:NgzFZ2qu4m1juby4TnrmpR8adRk6ka62YdH5DkIIyKA= k8s.io/component-helpers v0.20.10/go.mod h1:9SuOCO69yzUr8t9oajyO40NPAYK3JCYXwwyLS3YINR4= -k8s.io/component-helpers v0.21.1 h1:jhi4lHGHOV6mbPqNfITVUoLC3kNFkBQQO1rDDpnThAw= -k8s.io/component-helpers v0.21.1/go.mod h1:FtC1flbiQlosHQrLrRUulnKxE4ajgWCGy/67fT2GRlQ= k8s.io/controller-manager v0.20.10/go.mod h1:NwFcdJR5ZK0pjKNUZuZbus/tO8I0zSkGpp0ifQi2DK0= -k8s.io/controller-manager v0.21.1/go.mod h1:8ugs8DCcHqybiwdVERhnnyGoS5Ksq/ea1p2B0CosHyc= k8s.io/cri-api v0.20.10/go.mod h1:ew44AjNXwyn1s0U4xCKGodU7J1HzBeZ1MpGrpa5r8Yc= -k8s.io/cri-api v0.21.1 h1:dLQx77FHendlecUB9SE9GPls6clStVQ3eRsAzLIsr54= -k8s.io/cri-api v0.21.1/go.mod h1:nJbXlTpXwYCYuGMR7v3PQb1Du4WOGj2I9085xMVjr3I= k8s.io/csi-translation-lib v0.20.10/go.mod h1:dVQvr/Y/74jFZU955V/KqgZJ4E4hRF4IcsxUq0WbUrc= -k8s.io/csi-translation-lib v0.21.1/go.mod h1:y6NwcsxV1IsoqOm07G4hqANvCLXNDMC1t00lf+CqKRA= k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20201113003025-83324d819ded/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= -k8s.io/gengo v0.0.0-20201214224949-b6c5ce23f027/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= k8s.io/heapster v1.2.0-beta.1/go.mod h1:h1uhptVXMwC8xtZBYsPXKVi8fpdlYkTs6k949KozGrM= k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= @@ -1073,44 +944,23 @@ k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= k8s.io/klog/v2 v2.4.0 h1:7+X0fUguPyrKEC4WjH8iGDg3laWgMo5tMnRTIGTTxGQ= k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/klog/v2 v2.8.0 h1:Q3gmuM9hKEjefWFFYF0Mat+YyFJvsUyYuwyNNJ5C9Ts= -k8s.io/klog/v2 v2.8.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= k8s.io/kube-aggregator v0.20.10/go.mod h1:Y8T4ttD/sJIhaL84giy6aP9Q7aF9B23N/2u5KqEa/gQ= -k8s.io/kube-aggregator v0.21.1/go.mod h1:cAZ0n02IiSl57sQSHz4vvrz3upQRMbytOiZnpPJaQzQ= k8s.io/kube-controller-manager v0.20.10/go.mod h1:6XCS/QLajausNkLu/fPe0Pj6TxbpuNd51BQTqSwBIbg= -k8s.io/kube-controller-manager v0.21.1/go.mod h1:zEzQfcDGMQFFFpeWXv5GdJKIDR00LB4wp+hKYeRw7yc= -k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6 h1:Oh3Mzx5pJ+yIumsAD0MOECPVeXsVot0UkiaCGVyfGQY= k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E= k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd h1:sOHNzJIkytDF6qadMNKhhDRpc6ODik8lVC6nOur7B2c= k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM= -k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7 h1:vEx13qjvaZ4yfObSSXW7BrMc/KQBBT/Jyee8XtLf4x0= -k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7/go.mod h1:wXW5VT87nVfh/iLV8FpR2uDvrFyomxbtb1KivDbvPTE= k8s.io/kube-proxy v0.20.10/go.mod h1:hNgnO70MFOwZHQb87EYukEuy2uUvhrg/XZG2OTnyMy4= -k8s.io/kube-proxy v0.21.1/go.mod h1:5/Vg0HZHf2+475YhaeXWPvrw7PcQF0hW1Kx3Ug6kyBI= k8s.io/kube-scheduler v0.20.10/go.mod h1:GYb7hAdtx2DZ3l8SLhtcS9Qm+U5lgjCJIgF/QYKr1s0= -k8s.io/kube-scheduler v0.21.1 h1:knkUyAYE8i3v4hMdsLd7s2cHfhc+GjKOj3vzcBdInEM= -k8s.io/kube-scheduler v0.21.1/go.mod h1:AFLD3tRlC0lbhB+WClraGOV/e5+NQyi5PeeL2CMKx/Y= k8s.io/kubectl v0.20.10/go.mod h1:nM2vIk+39DHuXDmdCL3AehkkhbGUgRAlyPgkfycHoXY= -k8s.io/kubectl v0.21.1/go.mod h1:PMYR88MqESuysBM/MX+Vu4JbX/50nY4d4kny+SPEI2U= k8s.io/kubelet v0.20.10 h1:zyYFKuvCNVKVx1LWpAOwuYuZ7zdb9i/BI53Vy5rRdSw= k8s.io/kubelet v0.20.10/go.mod h1:OYBcBg22jbmycu0V6IMtq0Xpjoxj2iJ+LtH6XWsuesA= -k8s.io/kubelet v0.21.1 h1:JeZsCr3GN2Kjg3gn21jLU10RFu0APUK/vdpFWa8P8Nw= -k8s.io/kubelet v0.21.1/go.mod h1:poOR6Iaa5WqytFOp0egXFV8c2XTLFxaXTdj5njUlnVY= k8s.io/kubernetes v1.20.10 h1:4ng4jMVHQfLtUwD1Bv+xjzVPP2Zp41GeFVWK8x1Wwl0= k8s.io/kubernetes v1.20.10/go.mod h1:iE/QvEbLD6Ne9U03MX/BTBVSbj3pnj/HewBr8XDzLxw= -k8s.io/kubernetes v1.21.1 h1:U7cVOSdG+sMNOfL9XlenBV7avSBDHyWPE66gWnnYIIc= -k8s.io/kubernetes v1.21.1/go.mod h1:ef++isEL1PW0taH6z7DXrSztPglrZ7jQhyvcMEtm0gQ= k8s.io/legacy-cloud-providers v0.20.10/go.mod h1:5xDo9brr2bkusQr9KLWnUKY2bNZ52+9CJiIaqyGn7CE= -k8s.io/legacy-cloud-providers v0.21.1/go.mod h1:rpyWrAsPV1YNn7MmkZydMqleJijhay1Eqx+32K06Rd0= k8s.io/metrics v0.20.10/go.mod h1:M0IJySt6DSTjZQZamJ70b+F7I12pXqaeQmv/VpexC9I= -k8s.io/metrics v0.21.1/go.mod h1:pyDVLsLe++FIGDBFU80NcW4xMFsuiVTWL8Zfi7+PpNo= k8s.io/mount-utils v0.20.10/go.mod h1:Jv9NRZ5L2LF87A17GaGlArD+r3JAJdZFvo4XD1cG4Kc= -k8s.io/mount-utils v0.21.1 h1:uYf6zlKaaoUcPhWn6MElLkWf/f7UQgtkPZteumgwDbA= -k8s.io/mount-utils v0.21.1/go.mod h1:dwXbIPxKtTjrBEaX1aK/CMEf1KZ8GzMHpe3NEBfdFXI= k8s.io/sample-apiserver v0.20.10/go.mod h1:TSEZsVS5JKpV6r1aSVlNlMY17b1Ra2wkuT4LMCRealY= -k8s.io/sample-apiserver v0.21.1/go.mod h1:haiFU1SSB0+EaaqTpg+CxqOLVwsmjBOJ/CeOk6BTabs= k8s.io/system-validators v1.2.0/go.mod h1:bPldcLgkIUK22ALflnsXk8pvkTEndYdNuaHH6gRrl0Q= -k8s.io/system-validators v1.4.0/go.mod h1:bPldcLgkIUK22ALflnsXk8pvkTEndYdNuaHH6gRrl0Q= k8s.io/utils v0.0.0-20201110183641-67b214c5f920 h1:CbnUZsM497iRC5QMVkHwyl8s2tB3g7yaSHkYPkpgelw= k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw= @@ -1122,17 +972,10 @@ rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8 rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.22/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU= -sigs.k8s.io/kustomize/api v0.8.8/go.mod h1:He1zoK0nk43Pc6NlV085xDXDXTNprtcyKZVm3swsdNY= -sigs.k8s.io/kustomize/cmd/config v0.9.10/go.mod h1:Mrby0WnRH7hA6OwOYnYpfpiY0WJIMgYrEDfwOeFdMK0= -sigs.k8s.io/kustomize/kustomize/v4 v4.1.2/go.mod h1:PxBvo4WGYlCLeRPL+ziT64wBXqbgfcalOS/SXa/tcyo= -sigs.k8s.io/kustomize/kyaml v0.10.17/go.mod h1:mlQFagmkm1P+W4lZJbJ/yaxMd8PqMRSC4cPcfUVt5Hg= sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.1.0 h1:C4r9BgJ98vrKnnVCjwCSXcWjWe0NKcUQkmzDXZXGwH8= -sigs.k8s.io/structured-merge-diff/v4 v4.1.0/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.1.2 h1:Hr/htKFmJEbtMgS/UD0N+gtgctAqz81t3nu+sPzynno= sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= diff --git a/hack/build-go.sh b/hack/build-go.sh index 120f140c8..8ebbf6f39 100755 --- a/hack/build-go.sh +++ b/hack/build-go.sh @@ -41,6 +41,8 @@ if [ "$GO111MODULE" == "off" ]; then export GOBIN=${PWD}/bin export GOPATH=${PWD}/gopath go build -o ${PWD}/bin/multus -tags no_openssl -ldflags "${LDFLAGS}" "$@" ${REPO_PATH}/cmd + go build -o ${PWD}/bin/generate-kubeconfig -tags no_openssl -ldflags "${LDFLAGS}" ${REPO_PATH}/cmd/config-generation + go build -o ${PWD}/bin/multus-daemon -tags no_openssl -ldflags "${LDFLAGS}" "$@" ${REPO_PATH}/cmd/controller/ else # build with go modules export GO111MODULE=on @@ -51,4 +53,8 @@ else echo "Building plugins" go build ${BUILD_ARGS[*]} -ldflags "${LDFLAGS}" "$@" ./cmd + echo "Building spec generators" + go build -o "${DEST_DIR}"/generate-kubeconfig -ldflags "${LDFLAGS}" ./cmd/config-generation + echo "Building multus controller" + go build -o "${DEST_DIR}"/multus-daemon -ldflags "${LDFLAGS}" ./cmd/controller/ fi diff --git a/images/Dockerfile.thick b/images/Dockerfile.thick new file mode 100644 index 000000000..252232a97 --- /dev/null +++ b/images/Dockerfile.thick @@ -0,0 +1,15 @@ +# This Dockerfile is used to build the image available on DockerHub +FROM golang:1.17.1 as build + +# Add everything +ADD . /usr/src/multus-cni + +RUN cd /usr/src/multus-cni && \ + ./hack/build-go.sh + +FROM registry.access.redhat.com/ubi8/ubi-minimal +LABEL org.opencontainers.image.source https://github.com/k8snetworkplumbingwg/multus-cni +COPY --from=build /usr/src/multus-cni /usr/src/multus-cni +WORKDIR / + +ENTRYPOINT [ "/usr/src/multus-cni/bin/multus-daemon" ] diff --git a/images/README.md b/images/README.md index 2925050ab..947574153 100644 --- a/images/README.md +++ b/images/README.md @@ -41,9 +41,7 @@ in lexicographical order in cni-conf-dir). ./entrypoint.sh -h --help --cni-conf-dir=/host/etc/cni/net.d - --cni-bin-dir=/host/opt/cni/bin --multus-conf-file=/usr/src/multus-cni/images/70-multus.conf - --multus-bin-file=/usr/src/multus-cni/bin/multus --multus-kubeconfig-file-host=/etc/cni/net.d/multus.d/multus.kubeconfig ``` diff --git a/images/entrypoint.sh b/images/entrypoint.sh index 96db48dc0..f44050cbd 100755 --- a/images/entrypoint.sh +++ b/images/entrypoint.sh @@ -12,11 +12,9 @@ trap exitonsigterm SIGTERM # Set our known directories. CNI_CONF_DIR="/host/etc/cni/net.d" -CNI_BIN_DIR="/host/opt/cni/bin" ADDITIONAL_BIN_DIR="" MULTUS_CONF_FILE="/usr/src/multus-cni/images/70-multus.conf" MULTUS_AUTOCONF_DIR="/host/etc/cni/net.d" -MULTUS_BIN_FILE="/usr/src/multus-cni/bin/multus" MULTUS_KUBECONFIG_FILE_HOST="/etc/cni/net.d/multus.d/multus.kubeconfig" MULTUS_TEMP_KUBECONFIG="/tmp/multus.kubeconfig" MULTUS_MASTER_CNI_FILE_NAME="" @@ -36,22 +34,19 @@ SKIP_BINARY_COPY=false # Give help text for parameters. function usage() { - echo -e "This is an entrypoint script for Multus CNI to overlay its binary and " - echo -e "configuration into locations in a filesystem. The configuration & binary file " - echo -e "will be copied to the corresponding configuration directory. When " - echo -e "'--multus-conf-file=auto' is used, 00-multus.conf will be automatically " - echo -e "generated from the CNI configuration file of the master plugin (the first file " - echo -e "in lexicographical order in cni-conf-dir). When '--multus-master-cni-file-name'" - echo -e "is used, 00-multus.conf will only be automatically generated from the specific" - echo -e "file rather than the first file." + echo -e "This is an entrypoint script for Multus CNI to overlay its configuration into" + echo -e "locations in a filesystem. The configuration file will be copied to the" + echo -e "corresponding configuration directory. When '--multus-conf-file=auto' is used," + echo -e "00-multus.conf will be automatically generated from the CNI configuration file" + echo -e "of the master plugin (the first file in lexicographical order in cni-conf-dir)." + echo -e "When '--multus-master-cni-file-name' is used, 00-multus.conf will be" + echo -e "automatically generated from the specific file rather than the first file." echo -e "" echo -e "./entrypoint.sh" echo -e "\t-h --help" echo -e "\t--cni-conf-dir=$CNI_CONF_DIR" - echo -e "\t--cni-bin-dir=$CNI_BIN_DIR" echo -e "\t--cni-version=" echo -e "\t--multus-conf-file=$MULTUS_CONF_FILE" - echo -e "\t--multus-bin-file=$MULTUS_BIN_FILE" echo -e "\t--skip-multus-binary-copy=$SKIP_BINARY_COPY" echo -e "\t--multus-kubeconfig-file-host=$MULTUS_KUBECONFIG_FILE_HOST" echo -e "\t--multus-master-cni-file-name=$MULTUS_MASTER_CNI_FILE_NAME (empty by default, example: 10-calico.conflist)" @@ -103,15 +98,9 @@ while [ "$1" != "" ]; do --cni-conf-dir) CNI_CONF_DIR=$VALUE ;; - --cni-bin-dir) - CNI_BIN_DIR=$VALUE - ;; --multus-conf-file) MULTUS_CONF_FILE=$VALUE ;; - --multus-bin-file) - MULTUS_BIN_FILE=$VALUE - ;; --multus-kubeconfig-file-host) MULTUS_KUBECONFIG_FILE_HOST=$VALUE ;; @@ -166,7 +155,7 @@ done # Create array of known locations -declare -a arr=($CNI_CONF_DIR $CNI_BIN_DIR $MULTUS_BIN_FILE) +declare -a arr=($CNI_CONF_DIR) if [ "$MULTUS_CONF_FILE" != "auto" ]; then arr+=($MULTUS_CONF_FILE) fi @@ -181,14 +170,6 @@ do fi done -# Copy files into place and atomically move into final binary name -if [ "$SKIP_BINARY_COPY" = false ]; then - cp -f $MULTUS_BIN_FILE $CNI_BIN_DIR/_multus - mv -f $CNI_BIN_DIR/_multus $CNI_BIN_DIR/multus -else - log "Entrypoint skipped copying Multus binary." -fi - if [ "$MULTUS_CONF_FILE" != "auto" ]; then cp -f $MULTUS_CONF_FILE $CNI_CONF_DIR fi diff --git a/pkg/config/generator.go b/pkg/config/generator.go new file mode 100644 index 000000000..0a14e49e5 --- /dev/null +++ b/pkg/config/generator.go @@ -0,0 +1,223 @@ +// Copyright (c) 2021 Multus Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package config + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "path/filepath" + "sort" + "strings" + "time" + + "github.com/koron/go-dproxy" +) + +const ( + configListCapabilityKey = "plugins" + singleConfigCapabilityKey = "capabilities" +) + +// Option mutates the `conf` object +type Option func(conf *MultusConf) + +// MultusConf holds the multus configuration, and persists it to disk +type MultusConf struct { + BinDir string `json:"binDir,omitempty"` + Capabilities map[string]bool `json:"capabilities,omitempty"` + CNIVersion string `json:"cniVersion"` + Delegates []interface{} `json:"delegates"` + LogFile string `json:"logFile,omitempty"` + LogLevel string `json:"logLevel,omitempty"` + LogToStderr bool `json:"logToStderr,omitempty"` + Kubeconfig string `json:"kubeconfig"` + Name string `json:"name"` + NamespaceIsolation bool `json:"namespaceIsolation,omitempty"` + RawNonIsolatedNamespaces string `json:"globalNamespaces,omitempty"` + ReadinessIndicatorFile string `json:"readinessindicatorfile,omitempty"` + Type string `json:"type"` +} + +// NewMultusConfig creates a basic configuration generator. It can be mutated +// via the `With...` methods. +func NewMultusConfig(pluginName string, cniVersion string, kubeconfig string, configurationOptions ...Option) *MultusConf { + multusConfig := &MultusConf{ + Name: MultusDefaultNetworkName, + CNIVersion: cniVersion, + Type: pluginName, + Capabilities: map[string]bool{}, + Kubeconfig: kubeconfig, + Delegates: []interface{}{}, + } + for _, configOption := range configurationOptions { + configOption(multusConfig) + } + return multusConfig +} + +// Generate generates the multus configuration from whatever state is currently +// held +func (mc *MultusConf) Generate() (string, error) { + data, err := json.Marshal(mc) + return string(data), err +} + +// Mutate updates the MultusConf attributes according to the provided +// configuration `Option`s +func (mc *MultusConf) Mutate(configurationOptions ...Option) { + for _, configOption := range configurationOptions { + configOption(mc) + } +} + +// WithNamespaceIsolation mutates the inner state to enable the +// NamespaceIsolation attribute +func WithNamespaceIsolation() Option { + return func(conf *MultusConf) { + conf.NamespaceIsolation = true + } +} + +// WithGlobalNamespaces mutates the inner state to set the +// RawNonIsolatedNamespaces attribute +func WithGlobalNamespaces(globalNamespaces string) Option { + return func(conf *MultusConf) { + conf.RawNonIsolatedNamespaces = globalNamespaces + } +} + +// WithLogToStdErr mutates the inner state to enable the +// WithLogToStdErr attribute +func WithLogToStdErr() Option { + return func(conf *MultusConf) { + conf.LogToStderr = true + } +} + +// WithLogLevel mutates the inner state to set the +// LogLevel attribute +func WithLogLevel(logLevel string) Option { + return func(conf *MultusConf) { + conf.LogLevel = logLevel + } +} + +// WithLogFile mutates the inner state to set the +// logFile attribute +func WithLogFile(logFile string) Option { + return func(conf *MultusConf) { + conf.LogFile = logFile + } +} + +// WithReadinessFileIndicator mutates the inner state to set the +// ReadinessIndicatorFile attribute +func WithReadinessFileIndicator(path string) Option { + return func(conf *MultusConf) { + conf.ReadinessIndicatorFile = path + } +} + +// WithAdditionalBinaryFileDir mutates the inner state to set the +// BinDir attribute +func WithAdditionalBinaryFileDir(directoryPath string) Option { + return func(conf *MultusConf) { + conf.BinDir = directoryPath + } +} + +// WithOverriddenName mutates the inner state to set the +// Name attribute +func WithOverriddenName(networkName string) Option { + return func(conf *MultusConf) { + conf.Name = networkName + } +} + +func withCapabilities(cniData dproxy.Proxy) Option { + var enabledCapabilities []string + pluginsList, err := cniData.M(configListCapabilityKey).Array() + if err != nil { + pluginsList = nil + } + if len(pluginsList) > 0 { + for _, pluginData := range pluginsList { + enabledCapabilities = append( + enabledCapabilities, + extractCapabilities(dproxy.New(pluginData))...) + } + } else { + enabledCapabilities = extractCapabilities(cniData) + } + + return func(conf *MultusConf) { + for _, capability := range enabledCapabilities { + conf.Capabilities[capability] = true + } + } +} + +func withDelegates(primaryCNIConfigData interface{}) Option { + return func(conf *MultusConf) { + conf.Delegates = []interface{}{primaryCNIConfigData} + } +} + +func extractCapabilities(capabilitiesProxy dproxy.Proxy) []string { + capabilities, err := capabilitiesProxy.M(singleConfigCapabilityKey).Map() + if err != nil { + return nil + } + + var enabledCapabilities []string + if len(capabilities) > 0 { + for capName, isCapabilityEnabled := range capabilities { + if isCapabilityEnabled.(bool) { + enabledCapabilities = append(enabledCapabilities, capName) + } + } + } + return enabledCapabilities +} + +func findMasterPlugin(cniConfigDirPath string, remainingTries int) (string, error) { + if remainingTries == 0 { + return "", fmt.Errorf("could not find a plugin configuration in %s", cniConfigDirPath) + } + var cniPluginConfigs []string + files, err := ioutil.ReadDir(cniConfigDirPath) + if err != nil { + return "", fmt.Errorf("error when listing the CNI plugin configurations: %w", err) + } + + for _, file := range files { + if strings.HasPrefix(file.Name(), "00-multus") { + continue + } + fileExtension := filepath.Ext(file.Name()) + if fileExtension == ".conf" || fileExtension == ".conflist" { + cniPluginConfigs = append(cniPluginConfigs, file.Name()) + } + } + + if len(cniPluginConfigs) == 0 { + time.Sleep(time.Second) + return findMasterPlugin(cniConfigDirPath, remainingTries-1) + } + sort.Strings(cniPluginConfigs) + return cniPluginConfigs[0], nil +} diff --git a/pkg/config/generator_test.go b/pkg/config/generator_test.go new file mode 100644 index 000000000..d821e0644 --- /dev/null +++ b/pkg/config/generator_test.go @@ -0,0 +1,241 @@ +// Copyright (c) 2021 Multus Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package config + +import ( + "testing" + + "github.com/koron/go-dproxy" +) + +const ( + primaryCNIName = "myCNI" + cniVersion = "0.4.0" + kubeconfig = "/a/b/c/kubeconfig.kubeconfig" +) + +type testCase struct { + t *testing.T + configGenerationFunction func() (string, error) +} + +var primaryCNIConfig = map[string]interface{}{ + "cniVersion": "1.0.0", + "name": "ovn-kubernetes", + "type": "ovn-k8s-cni-overlay", + "ipam": "{}", + "dns": "{}", + "logFile": "/var/log/ovn-kubernetes/ovn-k8s-cni-overlay.log", + "logLevel": "5", + "logfile-maxsize": 100, + "logfile-maxbackups": 5, + "logfile-maxage": 5, +} + +func newMultusConfigWithDelegates(pluginName string, cniVersion string, kubeconfig string, primaryCNIPluginConfig interface{}, configOptions ...Option) *MultusConf { + multusConfig := NewMultusConfig(pluginName, cniVersion, kubeconfig, configOptions...) + multusConfig.Delegates = []interface{}{primaryCNIPluginConfig} + return multusConfig +} + +func TestBasicMultusConfig(t *testing.T) { + multusConfig := newMultusConfigWithDelegates( + primaryCNIName, + cniVersion, + kubeconfig, + primaryCNIConfig) + expectedResult := "{\"cniVersion\":\"0.4.0\",\"delegates\":[{\"cniVersion\":\"1.0.0\",\"dns\":\"{}\",\"ipam\":\"{}\",\"logFile\":\"/var/log/ovn-kubernetes/ovn-k8s-cni-overlay.log\",\"logLevel\":\"5\",\"logfile-maxage\":5,\"logfile-maxbackups\":5,\"logfile-maxsize\":100,\"name\":\"ovn-kubernetes\",\"type\":\"ovn-k8s-cni-overlay\"}],\"kubeconfig\":\"/a/b/c/kubeconfig.kubeconfig\",\"name\":\"multus-cni-network\",\"type\":\"myCNI\"}" + newTestCase(t, multusConfig.Generate).assertResult(expectedResult) +} + +func TestMultusConfigWithNamespaceIsolation(t *testing.T) { + multusConfig := newMultusConfigWithDelegates( + primaryCNIName, + cniVersion, + kubeconfig, + primaryCNIConfig, + WithNamespaceIsolation()) + expectedResult := "{\"cniVersion\":\"0.4.0\",\"delegates\":[{\"cniVersion\":\"1.0.0\",\"dns\":\"{}\",\"ipam\":\"{}\",\"logFile\":\"/var/log/ovn-kubernetes/ovn-k8s-cni-overlay.log\",\"logLevel\":\"5\",\"logfile-maxage\":5,\"logfile-maxbackups\":5,\"logfile-maxsize\":100,\"name\":\"ovn-kubernetes\",\"type\":\"ovn-k8s-cni-overlay\"}],\"kubeconfig\":\"/a/b/c/kubeconfig.kubeconfig\",\"name\":\"multus-cni-network\",\"namespaceIsolation\":true,\"type\":\"myCNI\"}" + newTestCase(t, multusConfig.Generate).assertResult(expectedResult) +} + +func TestMultusConfigWithReadinessIndicator(t *testing.T) { + multusConfig := newMultusConfigWithDelegates( + primaryCNIName, + cniVersion, + kubeconfig, + primaryCNIConfig, + WithReadinessFileIndicator("/a/b/u/it-lives")) + expectedResult := "{\"cniVersion\":\"0.4.0\",\"delegates\":[{\"cniVersion\":\"1.0.0\",\"dns\":\"{}\",\"ipam\":\"{}\",\"logFile\":\"/var/log/ovn-kubernetes/ovn-k8s-cni-overlay.log\",\"logLevel\":\"5\",\"logfile-maxage\":5,\"logfile-maxbackups\":5,\"logfile-maxsize\":100,\"name\":\"ovn-kubernetes\",\"type\":\"ovn-k8s-cni-overlay\"}],\"kubeconfig\":\"/a/b/c/kubeconfig.kubeconfig\",\"name\":\"multus-cni-network\",\"readinessindicatorfile\":\"/a/b/u/it-lives\",\"type\":\"myCNI\"}" + newTestCase(t, multusConfig.Generate).assertResult(expectedResult) +} + +func TestMultusConfigWithLoggingConfiguration(t *testing.T) { + multusConfig := newMultusConfigWithDelegates( + primaryCNIName, + cniVersion, + kubeconfig, + primaryCNIConfig, + WithLogLevel("notice"), + WithLogToStdErr(), + WithLogFile("/u/y/w/log.1")) + expectedResult := "{\"cniVersion\":\"0.4.0\",\"delegates\":[{\"cniVersion\":\"1.0.0\",\"dns\":\"{}\",\"ipam\":\"{}\",\"logFile\":\"/var/log/ovn-kubernetes/ovn-k8s-cni-overlay.log\",\"logLevel\":\"5\",\"logfile-maxage\":5,\"logfile-maxbackups\":5,\"logfile-maxsize\":100,\"name\":\"ovn-kubernetes\",\"type\":\"ovn-k8s-cni-overlay\"}],\"logFile\":\"/u/y/w/log.1\",\"logLevel\":\"notice\",\"logToStderr\":true,\"kubeconfig\":\"/a/b/c/kubeconfig.kubeconfig\",\"name\":\"multus-cni-network\",\"type\":\"myCNI\"}" + newTestCase(t, multusConfig.Generate).assertResult(expectedResult) +} + +func TestMultusConfigWithGlobalNamespace(t *testing.T) { + const globalNamespace = "come-along-ns" + multusConfig := newMultusConfigWithDelegates( + primaryCNIName, + cniVersion, + kubeconfig, + primaryCNIConfig, + WithGlobalNamespaces(globalNamespace)) + expectedResult := "{\"cniVersion\":\"0.4.0\",\"delegates\":[{\"cniVersion\":\"1.0.0\",\"dns\":\"{}\",\"ipam\":\"{}\",\"logFile\":\"/var/log/ovn-kubernetes/ovn-k8s-cni-overlay.log\",\"logLevel\":\"5\",\"logfile-maxage\":5,\"logfile-maxbackups\":5,\"logfile-maxsize\":100,\"name\":\"ovn-kubernetes\",\"type\":\"ovn-k8s-cni-overlay\"}],\"kubeconfig\":\"/a/b/c/kubeconfig.kubeconfig\",\"name\":\"multus-cni-network\",\"globalNamespaces\":\"come-along-ns\",\"type\":\"myCNI\"}" + newTestCase(t, multusConfig.Generate).assertResult(expectedResult) +} + +func TestMultusConfigWithAdditionalBinDir(t *testing.T) { + const anotherCNIBinDir = "a-dir-somewhere" + multusConfig := newMultusConfigWithDelegates( + primaryCNIName, + cniVersion, + kubeconfig, + primaryCNIConfig, + WithAdditionalBinaryFileDir(anotherCNIBinDir)) + expectedResult := "{\"binDir\":\"a-dir-somewhere\",\"cniVersion\":\"0.4.0\",\"delegates\":[{\"cniVersion\":\"1.0.0\",\"dns\":\"{}\",\"ipam\":\"{}\",\"logFile\":\"/var/log/ovn-kubernetes/ovn-k8s-cni-overlay.log\",\"logLevel\":\"5\",\"logfile-maxage\":5,\"logfile-maxbackups\":5,\"logfile-maxsize\":100,\"name\":\"ovn-kubernetes\",\"type\":\"ovn-k8s-cni-overlay\"}],\"kubeconfig\":\"/a/b/c/kubeconfig.kubeconfig\",\"name\":\"multus-cni-network\",\"type\":\"myCNI\"}" + newTestCase(t, multusConfig.Generate).assertResult(expectedResult) +} + +func TestMultusConfigWithCapabilities(t *testing.T) { + multusConfig := newMultusConfigWithDelegates( + primaryCNIName, + cniVersion, + kubeconfig, + primaryCNIConfig, + withCapabilities( + documentProxyHelper(`{"capabilities": {"portMappings": true}}`))) + expectedResult := "{\"capabilities\":{\"portMappings\":true},\"cniVersion\":\"0.4.0\",\"delegates\":[{\"cniVersion\":\"1.0.0\",\"dns\":\"{}\",\"ipam\":\"{}\",\"logFile\":\"/var/log/ovn-kubernetes/ovn-k8s-cni-overlay.log\",\"logLevel\":\"5\",\"logfile-maxage\":5,\"logfile-maxbackups\":5,\"logfile-maxsize\":100,\"name\":\"ovn-kubernetes\",\"type\":\"ovn-k8s-cni-overlay\"}],\"kubeconfig\":\"/a/b/c/kubeconfig.kubeconfig\",\"name\":\"multus-cni-network\",\"type\":\"myCNI\"}" + newTestCase(t, multusConfig.Generate).assertResult(expectedResult) +} + +func TestMultusConfigWithMultipleCapabilities(t *testing.T) { + multusConfig := newMultusConfigWithDelegates( + primaryCNIName, + cniVersion, + kubeconfig, + primaryCNIConfig, + withCapabilities( + documentProxyHelper(`{"capabilities": {"portMappings": true, "tuning": true}}`))) + expectedResult := "{\"capabilities\":{\"portMappings\":true,\"tuning\":true},\"cniVersion\":\"0.4.0\",\"delegates\":[{\"cniVersion\":\"1.0.0\",\"dns\":\"{}\",\"ipam\":\"{}\",\"logFile\":\"/var/log/ovn-kubernetes/ovn-k8s-cni-overlay.log\",\"logLevel\":\"5\",\"logfile-maxage\":5,\"logfile-maxbackups\":5,\"logfile-maxsize\":100,\"name\":\"ovn-kubernetes\",\"type\":\"ovn-k8s-cni-overlay\"}],\"kubeconfig\":\"/a/b/c/kubeconfig.kubeconfig\",\"name\":\"multus-cni-network\",\"type\":\"myCNI\"}" + newTestCase(t, multusConfig.Generate).assertResult(expectedResult) +} + +func TestMultusConfigWithMultipleCapabilitiesFilterOnlyEnabled(t *testing.T) { + multusConfig := newMultusConfigWithDelegates( + primaryCNIName, + cniVersion, + kubeconfig, + primaryCNIConfig, + withCapabilities( + documentProxyHelper(`{"capabilities": {"portMappings": true, "tuning": false}}`))) + expectedResult := "{\"capabilities\":{\"portMappings\":true},\"cniVersion\":\"0.4.0\",\"delegates\":[{\"cniVersion\":\"1.0.0\",\"dns\":\"{}\",\"ipam\":\"{}\",\"logFile\":\"/var/log/ovn-kubernetes/ovn-k8s-cni-overlay.log\",\"logLevel\":\"5\",\"logfile-maxage\":5,\"logfile-maxbackups\":5,\"logfile-maxsize\":100,\"name\":\"ovn-kubernetes\",\"type\":\"ovn-k8s-cni-overlay\"}],\"kubeconfig\":\"/a/b/c/kubeconfig.kubeconfig\",\"name\":\"multus-cni-network\",\"type\":\"myCNI\"}" + newTestCase(t, multusConfig.Generate).assertResult(expectedResult) +} + +func TestMultusConfigWithMultipleCapabilitiesDefinedOnAPlugin(t *testing.T) { + multusConfig := newMultusConfigWithDelegates( + primaryCNIName, + cniVersion, + kubeconfig, + primaryCNIConfig, + withCapabilities( + documentProxyHelper(`{"plugins": [ {"capabilities": {"portMappings": true, "tuning": true}} ] }`))) + expectedResult := "{\"capabilities\":{\"portMappings\":true,\"tuning\":true},\"cniVersion\":\"0.4.0\",\"delegates\":[{\"cniVersion\":\"1.0.0\",\"dns\":\"{}\",\"ipam\":\"{}\",\"logFile\":\"/var/log/ovn-kubernetes/ovn-k8s-cni-overlay.log\",\"logLevel\":\"5\",\"logfile-maxage\":5,\"logfile-maxbackups\":5,\"logfile-maxsize\":100,\"name\":\"ovn-kubernetes\",\"type\":\"ovn-k8s-cni-overlay\"}],\"kubeconfig\":\"/a/b/c/kubeconfig.kubeconfig\",\"name\":\"multus-cni-network\",\"type\":\"myCNI\"}" + newTestCase(t, multusConfig.Generate).assertResult(expectedResult) +} + +func TestMultusConfigWithCapabilitiesDefinedOnMultiplePlugins(t *testing.T) { + multusConfig := newMultusConfigWithDelegates( + primaryCNIName, + cniVersion, + kubeconfig, + primaryCNIConfig, + withCapabilities( + documentProxyHelper(`{"plugins": [ {"capabilities": { "portMappings": true }}, {"capabilities": { "tuning": true }} ]}`))) + expectedResult := "{\"capabilities\":{\"portMappings\":true,\"tuning\":true},\"cniVersion\":\"0.4.0\",\"delegates\":[{\"cniVersion\":\"1.0.0\",\"dns\":\"{}\",\"ipam\":\"{}\",\"logFile\":\"/var/log/ovn-kubernetes/ovn-k8s-cni-overlay.log\",\"logLevel\":\"5\",\"logfile-maxage\":5,\"logfile-maxbackups\":5,\"logfile-maxsize\":100,\"name\":\"ovn-kubernetes\",\"type\":\"ovn-k8s-cni-overlay\"}],\"kubeconfig\":\"/a/b/c/kubeconfig.kubeconfig\",\"name\":\"multus-cni-network\",\"type\":\"myCNI\"}" + newTestCase(t, multusConfig.Generate).assertResult(expectedResult) +} + +func TestMultusConfigWithCapabilitiesDefinedOnMultiplePluginsFilterOnlyEnabled(t *testing.T) { + multusConfig := newMultusConfigWithDelegates( + primaryCNIName, + cniVersion, + kubeconfig, + primaryCNIConfig, + withCapabilities( + documentProxyHelper(` +{ + "plugins": [ + { + "capabilities": { + "portMappings": true + } + }, + { + "capabilities": { + "tuning": false + } + } + ] +}`))) + expectedResult := "{\"capabilities\":{\"portMappings\":true},\"cniVersion\":\"0.4.0\",\"delegates\":[{\"cniVersion\":\"1.0.0\",\"dns\":\"{}\",\"ipam\":\"{}\",\"logFile\":\"/var/log/ovn-kubernetes/ovn-k8s-cni-overlay.log\",\"logLevel\":\"5\",\"logfile-maxage\":5,\"logfile-maxbackups\":5,\"logfile-maxsize\":100,\"name\":\"ovn-kubernetes\",\"type\":\"ovn-k8s-cni-overlay\"}],\"kubeconfig\":\"/a/b/c/kubeconfig.kubeconfig\",\"name\":\"multus-cni-network\",\"type\":\"myCNI\"}" + newTestCase(t, multusConfig.Generate).assertResult(expectedResult) +} + +func TestMultusConfigWithOverriddenName(t *testing.T) { + newNetworkName := "mega-net-2000" + multusConfig := newMultusConfigWithDelegates( + primaryCNIName, + cniVersion, + kubeconfig, + primaryCNIConfig, + WithOverriddenName(newNetworkName)) + expectedResult := "{\"cniVersion\":\"0.4.0\",\"delegates\":[{\"cniVersion\":\"1.0.0\",\"dns\":\"{}\",\"ipam\":\"{}\",\"logFile\":\"/var/log/ovn-kubernetes/ovn-k8s-cni-overlay.log\",\"logLevel\":\"5\",\"logfile-maxage\":5,\"logfile-maxbackups\":5,\"logfile-maxsize\":100,\"name\":\"ovn-kubernetes\",\"type\":\"ovn-k8s-cni-overlay\"}],\"kubeconfig\":\"/a/b/c/kubeconfig.kubeconfig\",\"name\":\"mega-net-2000\",\"type\":\"myCNI\"}" + newTestCase(t, multusConfig.Generate).assertResult(expectedResult) +} + +func newTestCase(t *testing.T, configGenerationFunc func() (string, error)) *testCase { + return &testCase{ + t: t, + configGenerationFunction: configGenerationFunc, + } +} + +func (tc testCase) assertResult(expectedResult string) { + multusCNIConfig, err := tc.configGenerationFunction() + if err != nil { + tc.t.Fatalf("error generating multus configuration: %v", err) + } + if multusCNIConfig != expectedResult { + tc.t.Fatalf("multus config generation failed.\nExpected:\n%s\nbut GOT:\n%s", expectedResult, multusCNIConfig) + } +} + +func documentProxyHelper(pluginInfo string) dproxy.Proxy { + dp, _ := documentProxy([]byte(pluginInfo)) + return dp +} diff --git a/pkg/config/manager.go b/pkg/config/manager.go new file mode 100644 index 000000000..abb02a44c --- /dev/null +++ b/pkg/config/manager.go @@ -0,0 +1,233 @@ +// Copyright (c) 2021 Multus Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package config + +import ( + "encoding/json" + "fmt" + "io/ioutil" + + "github.com/fsnotify/fsnotify" + "github.com/koron/go-dproxy" + + "gopkg.in/k8snetworkplumbingwg/multus-cni.v3/pkg/logging" +) + +// MultusDefaultNetworkName holds the default name of the multus network +const ( + multusConfigFileName = "00-multus.conf" + MultusDefaultNetworkName = "multus-cni-network" + userRWPermission = 0600 +) + +// Manager monitors the configuration of the primary CNI plugin, and +// regenerates multus configuration whenever it gets updated. +type Manager struct { + cniConfigData dproxy.Proxy + configWatcher *fsnotify.Watcher + multusConfig *MultusConf + multusConfigDir string + multusConfigFilePath string + primaryCNIConfigPath string +} + +// NewManager returns a config manager object, configured to persist the +// configuration to `multusAutoconfigDir`. This constructor will auto-discover +// the primary CNI for which it will delegate. +func NewManager(config MultusConf, multusAutoconfigDir string) (*Manager, error) { + defaultCNIPluginName, err := primaryCNIPluginName(multusAutoconfigDir) + if err != nil { + _ = logging.Errorf("failed to find the primary CNI plugin: %v", err) + return nil, err + } + return newManager(config, multusAutoconfigDir, defaultCNIPluginName) +} + +// NewManagerWithExplicitPrimaryCNIPlugin returns a config manager object, +// configured to persist the configuration to `multusAutoconfigDir`. This +// constructor will use the primary CNI plugin indicated by the user, via the +// primaryCNIPluginName variable. +func NewManagerWithExplicitPrimaryCNIPlugin(config MultusConf, multusAutoconfigDir string, primaryCNIPluginName string) (*Manager, error) { + return newManager(config, multusAutoconfigDir, primaryCNIPluginName) +} + +func newManager(config MultusConf, multusConfigDir string, defaultCNIPluginName string) (*Manager, error) { + watcher, err := newWatcher(multusConfigDir) + if err != nil { + return nil, err + } + + configManager := &Manager{ + configWatcher: watcher, + multusConfig: &config, + multusConfigDir: multusConfigDir, + multusConfigFilePath: cniPluginConfigFilePath(multusConfigDir, multusConfigFileName), + primaryCNIConfigPath: cniPluginConfigFilePath(multusConfigDir, defaultCNIPluginName), + } + + if err := configManager.loadPrimaryCNIConfigFromFile(); err != nil { + return nil, fmt.Errorf("failed to load the primary CNI configuration as a multus delegate") + } + return configManager, nil +} + +func (m *Manager) loadPrimaryCNIConfigFromFile() error { + primaryCNIConfigData, err := primaryCNIData(m.primaryCNIConfigPath) + if err != nil { + return logging.Errorf("failed to access the primary CNI configuration from %s: %v", m.primaryCNIConfigPath, err) + } + m.loadPrimaryCNIConfigurationData(primaryCNIConfigData) + return nil +} + +// OverrideNetworkName overrides the name of the multus configuration with the +// name of the delegated primary CNI. +func (m *Manager) OverrideNetworkName() error { + networkName, err := m.cniConfigData.M("name").String() + if err != nil { + return fmt.Errorf("failed to access delegate CNI plugin name") + } + if networkName == "" { + return fmt.Errorf("the primary CNI Configuration does not feature the network name: %v", m.cniConfigData) + } + m.multusConfig.Mutate(WithOverriddenName(networkName)) + return nil +} + +func (m *Manager) loadPrimaryCNIConfigurationData(primaryCNIConfigData interface{}) { + cniConfig := dproxy.New(primaryCNIConfigData) + m.cniConfigData = cniConfig + m.multusConfig.Mutate( + withDelegates(primaryCNIConfigData), + withCapabilities(cniConfig)) +} + +// GenerateConfig generates a multus configuration from its current state +func (m Manager) GenerateConfig() (string, error) { + if err := m.loadPrimaryCNIConfigFromFile(); err != nil { + _ = logging.Errorf("failed to read the primary CNI plugin config from %s", m.primaryCNIConfigPath) + return "", nil + } + return m.multusConfig.Generate() +} + +// MonitorDelegatedPluginConfiguration monitors the configuration file pointed +// to by the primaryCNIPluginName attribute, and re-generates the multus +// configuration whenever the primary CNI config is updated. +func (m Manager) MonitorDelegatedPluginConfiguration(shutDown chan struct{}, done chan struct{}) error { + logging.Verbosef("started to watch file %s", m.primaryCNIConfigPath) + + for { + select { + case event := <-m.configWatcher.Events: + // we're watching the DIR where the config sits, and the event + // does not concern the primary CNI config. Skip it. + if event.Name != m.primaryCNIConfigPath { + logging.Debugf("skipping un-related event %v", event) + continue + } + + if !shouldRegenerateConfig(event) { + continue + } + + updatedConfig, err := m.GenerateConfig() + if err != nil { + _ = logging.Errorf("failed to regenerate the multus configuration: %v", err) + } + + logging.Debugf("Re-generated MultusCNI config: %s", updatedConfig) + if err := m.PersistMultusConfig(updatedConfig); err != nil { + _ = logging.Errorf("failed to persist the multus configuration: %v", err) + } + + case err := <-m.configWatcher.Errors: + if err == nil { + continue + } + logging.Errorf("CNI monitoring error %v", err) + + case <-shutDown: + logging.Verbosef("Stopped monitoring, closing channel ...") + _ = m.configWatcher.Close() + done <- struct{}{} + return nil + } + } +} + +// PersistMultusConfig persists the provided configuration to the disc, with +// Read / Write permissions. The output file path is `/00-multus.conf` +func (m Manager) PersistMultusConfig(config string) error { + return ioutil.WriteFile(m.multusConfigFilePath, []byte(config), userRWPermission) +} + +func primaryCNIPluginName(multusAutoconfigDir string) (string, error) { + masterCniConfigFileName, err := findMasterPlugin(multusAutoconfigDir, 120) + if err != nil { + return "", fmt.Errorf("failed to find the cluster master CNI plugin: %w", err) + } + return masterCniConfigFileName, nil +} + +func cniPluginConfigFilePath(cniConfigDir string, cniConfigFileName string) string { + return cniConfigDir + fmt.Sprintf("/%s", cniConfigFileName) +} + +func newWatcher(cniConfigDir string) (*fsnotify.Watcher, error) { + watcher, err := fsnotify.NewWatcher() + if err != nil { + return nil, fmt.Errorf("failed to create new watcher for %q: %v", cniConfigDir, err) + } + defer func() { + // Close watcher on error + if err != nil { + watcher.Close() + } + }() + + if err = watcher.Add(cniConfigDir); err != nil { + return nil, fmt.Errorf("failed to add watch on %q: %v", cniConfigDir, err) + } + + return watcher, nil +} + +func shouldRegenerateConfig(event fsnotify.Event) bool { + return event.Op&fsnotify.Write == fsnotify.Write || + event.Op&fsnotify.Create == fsnotify.Create +} + +func primaryCNIData(masterCNIPluginPath string) (interface{}, error) { + masterCNIConfigData, err := ioutil.ReadFile(masterCNIPluginPath) + if err != nil { + return nil, fmt.Errorf("failed to read the cluster primary CNI config %s: %w", masterCNIPluginPath, err) + } + + var cniData interface{} + if err := json.Unmarshal(masterCNIConfigData, &cniData); err != nil { + return nil, fmt.Errorf("failed to unmarshall primary CNI config: %w", err) + } + return cniData, nil +} + +func documentProxy(masterCNIConfigData []byte) (dproxy.Proxy, error) { + var cniData interface{} + if err := json.Unmarshal(masterCNIConfigData, &cniData); err != nil { + return nil, fmt.Errorf("failed to unmarshall the delegate CNI configuration: %w", err) + } + return dproxy.New(cniData), nil +} diff --git a/pkg/config/manager_test.go b/pkg/config/manager_test.go new file mode 100644 index 000000000..c2797c9d5 --- /dev/null +++ b/pkg/config/manager_test.go @@ -0,0 +1,121 @@ +// Copyright (c) 2021 Multus Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package config + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "os" + "testing" + "time" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "github.com/onsi/gomega/format" + + "github.com/fsnotify/fsnotify" +) + +const suiteName = "Configuration Manager" + +func TestMultusConfigurationManager(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, suiteName) +} + +var _ = Describe(suiteName, func() { + const ( + primaryCNIPluginName = "00-mycni.conf" + primaryCNIPluginTemplate = ` +{ + "cniVersion": "0.4.0", + "name": "mycni-name", + "type": "mycni", + "ipam": {}, + "dns": {} +} +` + tmpDir = "/tmp" + ) + + var configManager *Manager + var multusConfigFilePath string + + BeforeEach(func() { + format.TruncatedDiff = false + multusConfigFilePath = fmt.Sprintf("%s/%s", tmpDir, primaryCNIPluginName) + Expect(ioutil.WriteFile(multusConfigFilePath, []byte(primaryCNIPluginTemplate), userRWPermission)).To(Succeed()) + + multusConf := NewMultusConfig( + primaryCNIName, + cniVersion, + kubeconfig) + var err error + configManager, err = NewManagerWithExplicitPrimaryCNIPlugin(*multusConf, tmpDir, primaryCNIPluginName) + Expect(err).NotTo(HaveOccurred()) + }) + + AfterEach(func() { + Expect(os.Remove(multusConfigFilePath)).To(Succeed()) + }) + + It("Generates a configuration, based on the contents of the delegated CNI config file", func() { + expectedResult := "{\"cniVersion\":\"0.4.0\",\"delegates\":[{\"cniVersion\":\"0.4.0\",\"dns\":{},\"ipam\":{},\"name\":\"mycni-name\",\"type\":\"mycni\"}],\"kubeconfig\":\"/a/b/c/kubeconfig.kubeconfig\",\"name\":\"multus-cni-network\",\"type\":\"myCNI\"}" + config, err := configManager.GenerateConfig() + Expect(err).NotTo(HaveOccurred()) + Expect(config).To(Equal(expectedResult)) + }) + + It("Reacts to updates of the delegated CNI configuration", func() { + stopChan := make(chan struct{}) + doneChan := make(chan struct{}) + go func(stopChannel, doneChan chan struct{}) { + Expect(configManager.MonitorDelegatedPluginConfiguration(stopChannel, doneChan)).To(Succeed()) + }(stopChan, doneChan) + + newCNIConfig := "{\"cniVersion\":\"0.4.0\",\"dns\":{},\"ipam\":{},\"name\":\"mycni-name\",\"type\":\"mycni\"}" + Expect(ioutil.WriteFile(multusConfigFilePath, []byte(newCNIConfig), userRWPermission)).To(Succeed()) + Eventually(<-configManager.configWatcher.Events, time.Second).Should(Equal( + fsnotify.Event{ + Name: multusConfigFilePath, + Op: fsnotify.Write, + })) + + updatedConfig, err := configManager.cniConfigData.Map() + Expect(err).NotTo(HaveOccurred()) + bytes, err := json.Marshal(updatedConfig) + Expect(err).NotTo(HaveOccurred()) + Expect(string(bytes)).To(Equal(newCNIConfig)) + + // cleanup the watcher + go func() { stopChan <- struct{}{} }() + Eventually(<-doneChan, time.Second).Should(Equal(struct{}{})) + }) + + When("the user requests the name of the multus configuration to be overridden", func() { + BeforeEach(func() { + Expect(configManager.OverrideNetworkName()).To(Succeed()) + }) + + It("Overrides the name of the multus configuration when requested", func() { + expectedResult := "{\"cniVersion\":\"0.4.0\",\"delegates\":[{\"cniVersion\":\"0.4.0\",\"dns\":{},\"ipam\":{},\"name\":\"mycni-name\",\"type\":\"mycni\"}],\"kubeconfig\":\"/a/b/c/kubeconfig.kubeconfig\",\"name\":\"mycni-name\",\"type\":\"myCNI\"}" + config, err := configManager.GenerateConfig() + Expect(err).NotTo(HaveOccurred()) + Expect(config).To(Equal(expectedResult)) + }) + }) +}) diff --git a/pkg/kubeletclient/kubeletclient_test.go b/pkg/kubeletclient/kubeletclient_test.go index f2edbae56..2ac4f5603 100644 --- a/pkg/kubeletclient/kubeletclient_test.go +++ b/pkg/kubeletclient/kubeletclient_test.go @@ -71,6 +71,7 @@ func TestKubeletclient(t *testing.T) { } var testKubeletSocket string + func setUp() error { tempSocketDir, err := ioutil.TempDir("", "kubelet-resource-client") if err != nil { diff --git a/vendor/github.com/koron/go-dproxy/.gitignore b/vendor/github.com/koron/go-dproxy/.gitignore new file mode 100644 index 000000000..85ef71818 --- /dev/null +++ b/vendor/github.com/koron/go-dproxy/.gitignore @@ -0,0 +1,2 @@ +/tags +/tmp/ diff --git a/vendor/github.com/koron/go-dproxy/LICENSE b/vendor/github.com/koron/go-dproxy/LICENSE new file mode 100644 index 000000000..e96eb7757 --- /dev/null +++ b/vendor/github.com/koron/go-dproxy/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 MURAOKA Taro + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/github.com/koron/go-dproxy/Makefile b/vendor/github.com/koron/go-dproxy/Makefile new file mode 100644 index 000000000..d07c5ecf7 --- /dev/null +++ b/vendor/github.com/koron/go-dproxy/Makefile @@ -0,0 +1,29 @@ +default: test + +test: + go test ./... + +test-full: + go test -v -race ./... + +lint: + go vet ./... + @echo "" + golint ./... + +cyclo: + -gocyclo -top 10 -avg . + +report: + @echo "misspell" + @find . -name "*.go" | xargs misspell + @echo "" + -errcheck ./... + @echo "" + -gocyclo -over 14 -avg . + @echo "" + go vet ./... + @echo "" + golint ./... + +.PHONY: test test-full lint cyclo report diff --git a/vendor/github.com/koron/go-dproxy/README.md b/vendor/github.com/koron/go-dproxy/README.md new file mode 100644 index 000000000..81ee55fba --- /dev/null +++ b/vendor/github.com/koron/go-dproxy/README.md @@ -0,0 +1,155 @@ +# dProxy - document proxy + +[![GoDoc](https://godoc.org/github.com/koron/go-dproxy?status.svg)](https://godoc.org/github.com/koron/go-dproxy) +[![CircleCI](https://img.shields.io/circleci/project/github/koron/go-dproxy/master.svg)](https://circleci.com/gh/koron/go-dproxy/tree/master) +[![Go Report Card](https://goreportcard.com/badge/github.com/koron/go-dproxy)](https://goreportcard.com/report/github.com/koron/go-dproxy) + +dProxy is a proxy to access `interface{}` (document) by simple query. +It is intented to be used with `json.Unmarshal()` or `json.NewDecorder()`. + +See codes for overview. + +```go +import ( + "encoding/json" + + "github.com/koron/go-dproxy" +) + +var v interface{} +json.Unmarshal([]byte(`{ + "cities": [ "tokyo", 100, "osaka", 200, "hakata", 300 ], + "data": { + "custom": [ "male", 23, "female", 24 ] + } +}`), &v) + +// s == "tokyo", got a string. +s, _ := dproxy.New(v).M("cities").A(0).String() + +// err != nil, type not matched. +_, err := dproxy.New(v).M("cities").A(0).Float64() + +// n == 200, got a float64 +n, _ := dproxy.New(v).M("cities").A(3).Float64() + +// can be chained. +dproxy.New(v).M("data").M("custom").A(0).String() + +// err.Error() == "not found: data.kustom", wrong query can be verified. +_, err = dproxy.New(v).M("data").M("kustom").String() +``` + + +## Getting started + +### Proxy + +1. Wrap a value (`interface{}`) with `dproxy.New()` get `dproxy.Proxy`. + + ```go + p := dproxy.New(v) // v should be a value of interface{} + ``` + +2. Query as a map (`map[string]interface{}`)by `M()`, returns `dproxy.Proxy`. + + ```go + p.M("cities") + ``` + +3. Query as an array (`[]interface{}`) with `A()`, returns `dproxy.Proxy`. + + ```go + p.A(3) + ``` + +4. Therefore, can be chained queries. + + ```go + p.M("cities").A(3) + ``` + +5. Get a value finally. + + ```go + n, _ := p.M("cities").A(3).Int64() + ``` + +6. You'll get an error when getting a value, if there were some mistakes. + + ```go + // OOPS! "kustom" is typo, must be "custom" + _, err := p.M("data").M("kustom").A(3).Int64() + + // "not found: data.kustom" + fmt.Println(err) + ``` + +7. If you tried to get a value as different type, get an error. + + ```go + // OOPS! "cities[3]" (=200) should be float64 or int64. + _, err := p.M("cities").A(3).String() + + // "not matched types: expected=string actual=float64: cities[3]" + fmt.Println(err) + ``` + +8. You can verify queries easily. + +### Drain + +Getting value and error from Proxy/ProxySet multiple times, is very awful. +It must check error when every getting values. + +```go +p := dproxy.New(v) +v1, err := p.M("cities").A(3).Int64() +if err != nil { + return err +} +v2, err := p.M("data").M("kustom").A(3).Int64() +if err != nil { + return err +} +v3, err := p.M("cities").A(3).String() +if err != nil { + return err +} +``` + +It can be rewrite as simple like below with `dproxy.Drain` + +```go +var d Drain +p := dproxy.New(v) +v1 := d.Int64(p.M("cities").A(3)) +v2 := d.Int64(p.M("data").M("kustom").A(3)) +v3 := d.String(p.M("cities").A(3)) +if err := d.CombineErrors(); err != nil { + return err +} +``` + +### JSON Pointer + +JSON Pointer can be used to query `interface{}` + +```go +v1, err := dproxy.New(v).P("/cities/0").Int64() +``` + +or + +```go +v1, err := dproxy.Pointer(v, "/cities/0").Int64() +``` + +See [RFC6901][1] for details of JSON Pointer. + + +## LICENSE + +MIT license. See LICENSE. + +[1]: https://tools.ietf.org/html/rfc6901 diff --git a/vendor/github.com/koron/go-dproxy/drain.go b/vendor/github.com/koron/go-dproxy/drain.go new file mode 100644 index 000000000..0dfd80f7b --- /dev/null +++ b/vendor/github.com/koron/go-dproxy/drain.go @@ -0,0 +1,150 @@ +package dproxy + +import "bytes" + +// Drain stores errors from Proxy or ProxySet. +type Drain struct { + errors []error +} + +// Has returns true if the drain stored some of errors. +func (d *Drain) Has() bool { + return d != nil && len(d.errors) > 0 +} + +// First returns a stored error. Returns nil if there are no errors. +func (d *Drain) First() error { + if d == nil || len(d.errors) == 0 { + return nil + } + return d.errors[0] +} + +// All returns all errors which stored. Return nil if no errors stored. +func (d *Drain) All() []error { + if d == nil || len(d.errors) == 0 { + return nil + } + a := make([]error, 0, len(d.errors)) + return append(a, d.errors...) +} + +// CombineErrors returns an error which combined all stored errors. Return nil +// if not erros stored. +func (d *Drain) CombineErrors() error { + if d == nil || len(d.errors) == 0 { + return nil + } + return drainError(d.errors) +} + +func (d *Drain) put(err error) { + if err == nil { + return + } + d.errors = append(d.errors, err) +} + +// Bool returns bool value and stores an error. +func (d *Drain) Bool(p Proxy) bool { + v, err := p.Bool() + d.put(err) + return v +} + +// Int64 returns int64 value and stores an error. +func (d *Drain) Int64(p Proxy) int64 { + v, err := p.Int64() + d.put(err) + return v +} + +// Float64 returns float64 value and stores an error. +func (d *Drain) Float64(p Proxy) float64 { + v, err := p.Float64() + d.put(err) + return v +} + +// String returns string value and stores an error. +func (d *Drain) String(p Proxy) string { + v, err := p.String() + d.put(err) + return v +} + +// Array returns []interface{} value and stores an error. +func (d *Drain) Array(p Proxy) []interface{} { + v, err := p.Array() + d.put(err) + return v +} + +// Map returns map[string]interface{} value and stores an error. +func (d *Drain) Map(p Proxy) map[string]interface{} { + v, err := p.Map() + d.put(err) + return v +} + +// BoolArray returns []bool value and stores an error. +func (d *Drain) BoolArray(ps ProxySet) []bool { + v, err := ps.BoolArray() + d.put(err) + return v +} + +// Int64Array returns []int64 value and stores an error. +func (d *Drain) Int64Array(ps ProxySet) []int64 { + v, err := ps.Int64Array() + d.put(err) + return v +} + +// Float64Array returns []float64 value and stores an error. +func (d *Drain) Float64Array(ps ProxySet) []float64 { + v, err := ps.Float64Array() + d.put(err) + return v +} + +// StringArray returns []string value and stores an error. +func (d *Drain) StringArray(ps ProxySet) []string { + v, err := ps.StringArray() + d.put(err) + return v +} + +// ArrayArray returns [][]interface{} value and stores an error. +func (d *Drain) ArrayArray(ps ProxySet) [][]interface{} { + v, err := ps.ArrayArray() + d.put(err) + return v +} + +// MapArray returns []map[string]interface{} value and stores an error. +func (d *Drain) MapArray(ps ProxySet) []map[string]interface{} { + v, err := ps.MapArray() + d.put(err) + return v +} + +// ProxyArray returns []Proxy value and stores an error. +func (d *Drain) ProxyArray(ps ProxySet) []Proxy { + v, err := ps.ProxyArray() + d.put(err) + return v +} + +type drainError []error + +func (derr drainError) Error() string { + b := bytes.Buffer{} + for i, err := range derr { + if i > 0 { + _, _ = b.WriteString("; ") + } + _, _ = b.WriteString(err.Error()) + } + return b.String() +} diff --git a/vendor/github.com/koron/go-dproxy/error.go b/vendor/github.com/koron/go-dproxy/error.go new file mode 100644 index 000000000..45f89f123 --- /dev/null +++ b/vendor/github.com/koron/go-dproxy/error.go @@ -0,0 +1,253 @@ +package dproxy + +import ( + "fmt" + "strconv" +) + +// ErrorType is type of errors +type ErrorType int + +const ( + // Etype means expected type is not matched with actual. + Etype ErrorType = iota + 1 + + // Enotfound means key or index doesn't exist. + Enotfound + + // EmapNorArray means target is not a map nor an array (for JSON Pointer) + EmapNorArray + + // EconvertFailure means value conversion is failed. + EconvertFailure + + // EinvalidIndex means token is invalid as index (for JSON Pointer) + EinvalidIndex + + // EinvalidQuery means query is invalid as JSON Pointer. + EinvalidQuery + + // ErequiredType means the type mismatch against user required one. + // For example M() requires map, A() requires array. + ErequiredType +) + +func (et ErrorType) String() string { + switch et { + case Etype: + return "Etype" + case Enotfound: + return "Enotfound" + case EmapNorArray: + return "EmapNorArray" + case EconvertFailure: + return "EconvertFailure" + case EinvalidIndex: + return "EinvalidIndex" + case EinvalidQuery: + return "EinvalidQuery" + case ErequiredType: + return "ErequiredType" + default: + return "Eunknown" + } +} + +// Error get detail information of the errror. +type Error interface { + // ErrorType returns type of error. + ErrorType() ErrorType + + // FullAddress returns query string where cause first error. + FullAddress() string +} + +type errorProxy struct { + errorType ErrorType + parent frame + label string + + expected Type + actual Type + infoStr string +} + +// errorProxy implements error, Proxy and ProxySet. +var ( + _ error = (*errorProxy)(nil) + _ Proxy = (*errorProxy)(nil) + _ ProxySet = (*errorProxy)(nil) +) + +func (p *errorProxy) Nil() bool { + return false +} + +func (p *errorProxy) Value() (interface{}, error) { + return nil, p +} + +func (p *errorProxy) Bool() (bool, error) { + return false, p +} + +func (p *errorProxy) Int64() (int64, error) { + return 0, p +} + +func (p *errorProxy) Float64() (float64, error) { + return 0, p +} + +func (p *errorProxy) String() (string, error) { + return "", p +} + +func (p *errorProxy) Array() ([]interface{}, error) { + return nil, p +} + +func (p *errorProxy) Map() (map[string]interface{}, error) { + return nil, p +} + +func (p *errorProxy) A(n int) Proxy { + return p +} + +func (p *errorProxy) M(k string) Proxy { + return p +} + +func (p *errorProxy) P(q string) Proxy { + return p +} + +func (p *errorProxy) Empty() bool { + return true +} + +func (p *errorProxy) Len() int { + return 0 +} + +func (p *errorProxy) BoolArray() ([]bool, error) { + return nil, p +} + +func (p *errorProxy) Int64Array() ([]int64, error) { + return nil, p +} + +func (p *errorProxy) Float64Array() ([]float64, error) { + return nil, p +} + +func (p *errorProxy) StringArray() ([]string, error) { + return nil, p +} + +func (p *errorProxy) ArrayArray() ([][]interface{}, error) { + return nil, p +} + +func (p *errorProxy) MapArray() ([]map[string]interface{}, error) { + return nil, p +} + +func (p *errorProxy) ProxyArray() ([]Proxy, error) { + return nil, p +} + +func (p *errorProxy) ProxySet() ProxySet { + return p +} + +func (p *errorProxy) Q(k string) ProxySet { + return p +} + +func (p *errorProxy) Qc(k string) ProxySet { + return p +} + +func (p *errorProxy) findJPT(t string) Proxy { + return p +} + +func (p *errorProxy) parentFrame() frame { + return p.parent +} + +func (p *errorProxy) frameLabel() string { + return p.label +} + +func (p *errorProxy) Error() string { + switch p.errorType { + case Etype: + return fmt.Sprintf("not matched types: expected=%s actual=%s: %s", + p.expected.String(), p.actual.String(), p.FullAddress()) + case Enotfound: + return fmt.Sprintf("not found: %s", p.FullAddress()) + case EmapNorArray: + // FIXME: better error message. + return fmt.Sprintf("not map nor array: actual=%s: %s", + p.actual.String(), p.FullAddress()) + case EconvertFailure: + return fmt.Sprintf("convert error: %s: %s", p.infoStr, p.FullAddress()) + case EinvalidIndex: + // FIXME: better error message. + return fmt.Sprintf("invalid index: %s: %s", p.infoStr, p.FullAddress()) + case EinvalidQuery: + // FIXME: better error message. + return fmt.Sprintf("invalid query: %s: %s", p.infoStr, p.FullAddress()) + case ErequiredType: + return fmt.Sprintf("not required types: required=%s actual=%s: %s", + p.expected.String(), p.actual.String(), p.FullAddress()) + default: + return fmt.Sprintf("unexpected: %s", p.FullAddress()) + } +} + +func (p *errorProxy) ErrorType() ErrorType { + return p.errorType +} + +func (p *errorProxy) FullAddress() string { + return fullAddress(p) +} + +func typeError(p frame, expected Type, actual interface{}) *errorProxy { + return &errorProxy{ + errorType: Etype, + parent: p, + expected: expected, + actual: detectType(actual), + } +} + +func requiredTypeError(p frame, expected Type, actual interface{}) *errorProxy { + return &errorProxy{ + errorType: ErequiredType, + parent: p, + expected: expected, + actual: detectType(actual), + } +} + +func elementTypeError(p frame, index int, expected Type, actual interface{}) *errorProxy { + q := &simpleFrame{ + parent: p, + label: "[" + strconv.Itoa(index) + "]", + } + return typeError(q, expected, actual) +} + +func notfoundError(p frame, address string) *errorProxy { + return &errorProxy{ + errorType: Enotfound, + parent: p, + label: address, + } +} diff --git a/vendor/github.com/koron/go-dproxy/frame.go b/vendor/github.com/koron/go-dproxy/frame.go new file mode 100644 index 000000000..6e8fbe1b2 --- /dev/null +++ b/vendor/github.com/koron/go-dproxy/frame.go @@ -0,0 +1,42 @@ +package dproxy + +type frame interface { + // parentFrame returns parent frame. + parentFrame() frame + // frameLabel return label of frame. + frameLabel() string +} + +func fullAddress(f frame) string { + x := 0 + for g := f; g != nil; g = g.parentFrame() { + x += len(g.frameLabel()) + } + if x == 0 { + return "(root)" + } + b := make([]byte, x) + for g := f; g != nil; g = g.parentFrame() { + x -= len(g.frameLabel()) + copy(b[x:], g.frameLabel()) + } + if b[0] == '.' { + return string(b[1:]) + } + return string(b) +} + +type simpleFrame struct { + parent frame + label string +} + +var _ frame = (*simpleFrame)(nil) + +func (f *simpleFrame) parentFrame() frame { + return f.parent +} + +func (f *simpleFrame) frameLabel() string { + return f.label +} diff --git a/vendor/github.com/koron/go-dproxy/go.mod b/vendor/github.com/koron/go-dproxy/go.mod new file mode 100644 index 000000000..d34f7befb --- /dev/null +++ b/vendor/github.com/koron/go-dproxy/go.mod @@ -0,0 +1,3 @@ +module github.com/koron/go-dproxy + +go 1.13 diff --git a/vendor/github.com/koron/go-dproxy/go.sum b/vendor/github.com/koron/go-dproxy/go.sum new file mode 100644 index 000000000..e69de29bb diff --git a/vendor/github.com/koron/go-dproxy/pointer.go b/vendor/github.com/koron/go-dproxy/pointer.go new file mode 100644 index 000000000..e4933bc88 --- /dev/null +++ b/vendor/github.com/koron/go-dproxy/pointer.go @@ -0,0 +1,31 @@ +package dproxy + +import "strings" + +var jptR = strings.NewReplacer("~1", "/", "~0", "~") + +func unescapeJPT(t string) string { + return jptR.Replace(t) +} + +func pointer(p Proxy, q string) Proxy { + if len(q) == 0 { + return p + } + if q[0] != '/' { + return &errorProxy{ + errorType: EinvalidQuery, + parent: p, + infoStr: "not start with '/'", + } + } + for _, t := range strings.Split(q[1:], "/") { + p = p.findJPT(unescapeJPT(t)) + } + return p +} + +// Pointer returns a Proxy which pointed by JSON Pointer's query q +func Pointer(v interface{}, q string) Proxy { + return pointer(New(v), q) +} diff --git a/vendor/github.com/koron/go-dproxy/proxy.go b/vendor/github.com/koron/go-dproxy/proxy.go new file mode 100644 index 000000000..9c7b0c777 --- /dev/null +++ b/vendor/github.com/koron/go-dproxy/proxy.go @@ -0,0 +1,102 @@ +package dproxy + +// Proxy is a proxy to access a document (interface{}). +type Proxy interface { + // Nil returns true, if target value is nil. + Nil() bool + + // Value returns a proxied value. If there are no values, it returns + // error. + Value() (interface{}, error) + + // Bool returns its value. If value isn't the type, it returns error. + Bool() (bool, error) + + // Int64 returns its value. If value isn't the type, it returns error. + Int64() (int64, error) + + // Float64 returns its value. If value isn't the type, it returns error. + Float64() (float64, error) + + // String returns its value. If value isn't the type, it returns error. + String() (string, error) + + // Array returns its value. If value isn't the type, it returns error. + Array() ([]interface{}, error) + + // Map returns its value. If value isn't the type, it returns error. + Map() (map[string]interface{}, error) + + // A returns an item from value treated as the array. + A(n int) Proxy + + // M returns an item from value treated as the map. + M(k string) Proxy + + // P returns which pointed by JSON Pointer's query q. + P(q string) Proxy + + // ProxySet returns a set which converted from its array value. + ProxySet() ProxySet + + // Q returns set of all items which property matchs with k. + Q(k string) ProxySet + + // findJPT returns a match of JSON Pointer's Token t. + findJPT(t string) Proxy + + // Proxy implements frame. + frame +} + +// ProxySet proxies to access to set. +type ProxySet interface { + // Empty returns true when the set is empty. + Empty() bool + + // Len returns count of items in the set. + Len() int + + // BoolArray returns []bool which converterd from the set. + BoolArray() ([]bool, error) + + // Int64Array returns []int64 which converterd from the set. + Int64Array() ([]int64, error) + + // Float64Array returns []float64 which converterd from the set. + Float64Array() ([]float64, error) + + // StringArray returns []string which converterd from the set. + StringArray() ([]string, error) + + // ArrayArray returns [][]interface{} which converterd from the set. + ArrayArray() ([][]interface{}, error) + + // MapArray returns []map[string]interface{} which converterd from the set. + MapArray() ([]map[string]interface{}, error) + + // ProxyArray returns []Proxy which wrap each items. + ProxyArray() ([]Proxy, error) + + // A returns an proxy for index in the set. + A(n int) Proxy + + // Q returns set of all items which property matchs with k. + Q(k string) ProxySet + + // Qc returns set of property of all items. + Qc(k string) ProxySet + + // Proxy implements frame. + frame +} + +// New creates a new Proxy instance for v. +func New(v interface{}) Proxy { + return &valueProxy{value: v} +} + +// NewSet create a new ProxySet instance for v. +func NewSet(v []interface{}) ProxySet { + return &setProxy{values: v} +} diff --git a/vendor/github.com/koron/go-dproxy/set.go b/vendor/github.com/koron/go-dproxy/set.go new file mode 100644 index 000000000..15e8bb40e --- /dev/null +++ b/vendor/github.com/koron/go-dproxy/set.go @@ -0,0 +1,193 @@ +package dproxy + +import "strconv" + +type setProxy struct { + values []interface{} + parent frame + label string +} + +// setProxy implements ProxySet +var _ ProxySet = (*setProxy)(nil) + +func (p *setProxy) Empty() bool { + return p.Len() == 0 +} + +func (p *setProxy) Len() int { + return len(p.values) +} + +func (p *setProxy) BoolArray() ([]bool, error) { + r := make([]bool, len(p.values)) + for i, v := range p.values { + switch v := v.(type) { + case bool: + r[i] = v + default: + return nil, elementTypeError(p, i, Tbool, v) + } + } + return r, nil +} + +func (p *setProxy) Int64Array() ([]int64, error) { + r := make([]int64, len(p.values)) + for i, v := range p.values { + switch v := v.(type) { + case int: + r[i] = int64(v) + case int32: + r[i] = int64(v) + case int64: + r[i] = v + case float32: + r[i] = int64(v) + case float64: + r[i] = int64(v) + default: + return nil, elementTypeError(p, i, Tint64, v) + } + } + return r, nil +} + +func (p *setProxy) Float64Array() ([]float64, error) { + r := make([]float64, len(p.values)) + for i, v := range p.values { + switch v := v.(type) { + case int: + r[i] = float64(v) + case int32: + r[i] = float64(v) + case int64: + r[i] = float64(v) + case float32: + r[i] = float64(v) + case float64: + r[i] = v + default: + return nil, elementTypeError(p, i, Tfloat64, v) + } + } + return r, nil +} + +func (p *setProxy) StringArray() ([]string, error) { + r := make([]string, len(p.values)) + for i, v := range p.values { + switch v := v.(type) { + case string: + r[i] = v + default: + return nil, elementTypeError(p, i, Tstring, v) + } + } + return r, nil +} + +func (p *setProxy) ArrayArray() ([][]interface{}, error) { + r := make([][]interface{}, len(p.values)) + for i, v := range p.values { + switch v := v.(type) { + case []interface{}: + r[i] = v + default: + return nil, elementTypeError(p, i, Tarray, v) + } + } + return r, nil +} + +func (p *setProxy) MapArray() ([]map[string]interface{}, error) { + r := make([]map[string]interface{}, len(p.values)) + for i, v := range p.values { + switch v := v.(type) { + case map[string]interface{}: + r[i] = v + default: + return nil, elementTypeError(p, i, Tmap, v) + } + } + return r, nil +} + +func (p *setProxy) ProxyArray() ([]Proxy, error) { + r := make([]Proxy, 0, len(p.values)) + for i, v := range p.values { + r = append(r, &valueProxy{ + value: v, + parent: p, + label: "[" + strconv.Itoa(i) + "]", + }) + } + return r, nil +} + +func (p *setProxy) A(n int) Proxy { + a := "[" + strconv.Itoa(n) + "]" + if n < 0 || n >= len(p.values) { + return notfoundError(p, a) + } + return &valueProxy{ + value: p.values[n], + parent: p, + label: a, + } +} + +func (p *setProxy) Q(k string) ProxySet { + w := findAll(p.values, k) + return &setProxy{ + values: w, + parent: p, + label: ".." + k, + } +} + +func (p *setProxy) Qc(k string) ProxySet { + r := make([]interface{}, 0, len(p.values)) + for _, v := range p.values { + switch v := v.(type) { + case map[string]interface{}: + if w, ok := v[k]; ok { + r = append(r, w) + } + } + } + return &setProxy{ + values: r, + parent: p, + label: ".." + k, + } +} + +func (p *setProxy) parentFrame() frame { + return p.parent +} + +func (p *setProxy) frameLabel() string { + return p.label +} + +func findAll(v interface{}, k string) []interface{} { + return findAllImpl(v, k, make([]interface{}, 0, 10)) +} + +func findAllImpl(v interface{}, k string, r []interface{}) []interface{} { + switch v := v.(type) { + case map[string]interface{}: + for n, w := range v { + if n == k { + r = append(r, w) + } + r = findAllImpl(w, k, r) + } + case []interface{}: + for _, w := range v { + r = findAllImpl(w, k, r) + } + } + return r +} diff --git a/vendor/github.com/koron/go-dproxy/type.go b/vendor/github.com/koron/go-dproxy/type.go new file mode 100644 index 000000000..8ab499296 --- /dev/null +++ b/vendor/github.com/koron/go-dproxy/type.go @@ -0,0 +1,76 @@ +package dproxy + +// Type is type of value. +type Type int + +const ( + // Tunknown shows value is not supported. + Tunknown Type = iota + + // Tnil shows value is nil. + Tnil + + // Tbool shows value is bool. + Tbool + + // Tint64 shows value is int64. + Tint64 + + // Tfloat64 shows value is float64. + Tfloat64 + + // Tstring shows value is string. + Tstring + + // Tarray shows value is an array ([]interface{}) + Tarray + + // Tmap shows value is a map (map[string]interface{}) + Tmap +) + +// detectType returns type of a value. +func detectType(v interface{}) Type { + if v == nil { + return Tnil + } + switch v.(type) { + case bool: + return Tbool + case int, int32, int64: + return Tint64 + case float32, float64: + return Tfloat64 + case string: + return Tstring + case []interface{}: + return Tarray + case map[string]interface{}: + return Tmap + default: + return Tunknown + } +} + +func (t Type) String() string { + switch t { + case Tunknown: + return "unknown" + case Tnil: + return "nil" + case Tbool: + return "bool" + case Tint64: + return "int64" + case Tfloat64: + return "float64" + case Tstring: + return "string" + case Tarray: + return "array" + case Tmap: + return "map" + default: + return "unknown" + } +} diff --git a/vendor/github.com/koron/go-dproxy/value.go b/vendor/github.com/koron/go-dproxy/value.go new file mode 100644 index 000000000..97d9a2382 --- /dev/null +++ b/vendor/github.com/koron/go-dproxy/value.go @@ -0,0 +1,222 @@ +package dproxy + +import ( + "reflect" + "strconv" +) + +type valueProxy struct { + value interface{} + parent frame + label string +} + +// valueProxy implements Proxy. +var _ Proxy = (*valueProxy)(nil) + +func (p *valueProxy) Nil() bool { + return p.value == nil +} + +func (p *valueProxy) Value() (interface{}, error) { + return p.value, nil +} + +func (p *valueProxy) Bool() (bool, error) { + switch v := p.value.(type) { + case bool: + return v, nil + default: + return false, typeError(p, Tbool, v) + } +} + +type int64er interface { + Int64() (int64, error) +} + +func (p *valueProxy) Int64() (int64, error) { + switch v := p.value.(type) { + case int: + return int64(v), nil + case int32: + return int64(v), nil + case int64: + return v, nil + case float32: + return int64(v), nil + case float64: + return int64(v), nil + case int64er: + w, err := v.Int64() + if err != nil { + return 0, &errorProxy{ + errorType: EconvertFailure, + parent: p, + infoStr: err.Error(), + } + } + return w, nil + default: + return 0, typeError(p, Tint64, v) + } +} + +type float64er interface { + Float64() (float64, error) +} + +func (p *valueProxy) Float64() (float64, error) { + switch v := p.value.(type) { + case int: + return float64(v), nil + case int32: + return float64(v), nil + case int64: + return float64(v), nil + case float32: + return float64(v), nil + case float64: + return v, nil + case float64er: + w, err := v.Float64() + if err != nil { + return 0, &errorProxy{ + errorType: EconvertFailure, + parent: p, + infoStr: err.Error(), + } + } + return w, nil + default: + return 0, typeError(p, Tfloat64, v) + } +} + +func (p *valueProxy) String() (string, error) { + switch v := p.value.(type) { + case string: + return v, nil + default: + return "", typeError(p, Tstring, v) + } +} + +func (p *valueProxy) Array() ([]interface{}, error) { + switch v := p.value.(type) { + case []interface{}: + return v, nil + default: + return nil, typeError(p, Tarray, v) + } +} + +func (p *valueProxy) Map() (map[string]interface{}, error) { + switch v := p.value.(type) { + case map[string]interface{}: + return v, nil + default: + return nil, typeError(p, Tmap, v) + } +} + +func (p *valueProxy) A(n int) Proxy { + switch v := p.value.(type) { + case []interface{}: + a := "[" + strconv.Itoa(n) + "]" + if n < 0 || n >= len(v) { + return notfoundError(p, a) + } + return &valueProxy{ + value: v[n], + parent: p, + label: a, + } + default: + return requiredTypeError(p, Tarray, v) + } +} + +var mapType = reflect.TypeOf(map[string]interface{}(nil)) + +func (p *valueProxy) m(v map[string]interface{}, k string) Proxy { + a := "." + k + w, ok := v[k] + if !ok { + return notfoundError(p, a) + } + return &valueProxy{ + value: w, + parent: p, + label: a, + } +} + +func (p *valueProxy) M(k string) Proxy { + if v, ok := p.value.(map[string]interface{}); ok { + return p.m(v, k) + } + + if rv := reflect.ValueOf(p.value); rv.IsValid() && rv.Type().ConvertibleTo(mapType) { + v, _ := rv.Convert(mapType).Interface().(map[string]interface{}) + return p.m(v, k) + } + + return requiredTypeError(p, Tmap, p.value) +} + +func (p *valueProxy) P(q string) Proxy { + return pointer(p, q) +} + +func (p *valueProxy) ProxySet() ProxySet { + switch v := p.value.(type) { + case []interface{}: + return &setProxy{ + values: v, + parent: p, + } + default: + return typeError(p, Tarray, v) + } +} + +func (p *valueProxy) Q(k string) ProxySet { + w := findAll(p.value, k) + return &setProxy{ + values: w, + parent: p, + label: ".." + k, + } +} + +func (p *valueProxy) findJPT(t string) Proxy { + switch v := p.value.(type) { + case map[string]interface{}: + return p.M(t) + case []interface{}: + n, err := strconv.ParseUint(t, 10, 0) + if err != nil { + return &errorProxy{ + errorType: EinvalidIndex, + parent: p, + infoStr: err.Error(), + } + } + return p.A(int(n)) + default: + return &errorProxy{ + errorType: EmapNorArray, + parent: p, + actual: detectType(v), + } + } +} + +func (p *valueProxy) parentFrame() frame { + return p.parent +} + +func (p *valueProxy) frameLabel() string { + return p.label +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 133a8be47..f63646c74 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -26,6 +26,7 @@ github.com/davecgh/go-spew/spew # github.com/evanphx/json-patch v4.9.0+incompatible github.com/evanphx/json-patch # github.com/fsnotify/fsnotify v1.4.9 +## explicit github.com/fsnotify/fsnotify # github.com/go-logr/logr v0.2.0 github.com/go-logr/logr @@ -62,6 +63,9 @@ github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/client/ github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/client/clientset/versioned/typed/k8s.cni.cncf.io/v1 github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/client/clientset/versioned/typed/k8s.cni.cncf.io/v1/fake github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/utils +# github.com/koron/go-dproxy v1.3.0 +## explicit +github.com/koron/go-dproxy # github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 github.com/matttproud/golang_protobuf_extensions/pbutil # github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd