diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml index 959dbb0691..083a161365 100644 --- a/.github/workflows/e2e-tests.yml +++ b/.github/workflows/e2e-tests.yml @@ -9,26 +9,114 @@ on: workflow_dispatch: merge_group: jobs: + + # Build the OLM image and save it as an artifact + build: + runs-on: ubuntu-latest + outputs: + sha: ${{ steps.vars.outputs.sha }} + steps: + # checkout code and setup go + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version-file: "go.mod" + # build binaries and image for e2e test (includes experimental features) + - name: Build controller image + run: make e2e-build + - name: Save image + run: docker save quay.io/operator-framework/olm:local -o olm-image.tar + - name: Upload Docker image as artifact + uses: actions/upload-artifact@v4 + with: + name: olm-image.tar + path: olm-image.tar + + # Run e2e tests in parallel jobs + # Take olm image from the previous stage e2e: + needs: build strategy: fail-fast: false matrix: - parallel-id: [0, 1, 2, 3] + parallel-id: [0, 1, 2, 3, flakes] runs-on: ubuntu-latest + env: + # absolute path to test artifacts directory + ARTIFACT_DIR: ${{ github.workspace }}/artifacts + E2E_TEST_CHUNK: ${{ matrix.parallel-id }} + E2E_NODES: 2 + E2E_KUBECONFIG_ROOT: ${{ github.workspace }}/kubeconfigs steps: + # checkout code and setup go - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: go-version-file: "go.mod" - - run: mkdir -p artifacts - - run: make e2e-local E2E_TEST_CHUNK=${{ matrix.parallel-id }} E2E_TEST_NUM_CHUNKS=${{ strategy.job-total }} E2E_NODES=2 ARTIFACT_DIR=./artifacts/ SKIP='\[FLAKE\]' - - name: Archive Test Artifacts # test results, failed or not, are always uploaded. + + # load the olm image + - name: Load OLM Docker image + uses: actions/download-artifact@v4 + with: + name: olm-image.tar + path: . + - run: docker load < olm-image.tar + + # set e2e environment variables + # Set ginkgo output and parallelism + - run: echo "GINKGO_E2E_OPTS=-output-dir ${ARTIFACT_DIR} -junit-report junit_e2e.xml -nodes ${E2E_NODES}" >> $GITHUB_ENV + + # Setting -kubeconfig-root tells the e2e test suite to look for kubeconfigs + # in /kubeconfig- + # This is used to run tests in parallel on multiple clusters as the current e2e + # test suite does not support running tests in parallel on a single cluster + - run: echo "E2E_OPTS=-kubeconfig-root=${E2E_KUBECONFIG_ROOT}" >> $GITHUB_ENV + + # run e2e tests + # create artifacts directory + - run: mkdir -p ${ARTIFACT_DIR} + + # deploy test clusters + - name: Deploy test cluster(s) + # create kubeconfig root and store the kubeconfig for each cluster within it as you create the clusters + # Add kind and helm options to specify kubeconfig location + # Deploy the new cluster and helm install olm for testing + run: | + mkdir -p ${E2E_KUBECONFIG_ROOT} + for i in $(seq 1 ${E2E_NODES}); do + KIND_CLUSTER_NAME="kind-olmv0-${i}" \ + KIND_CREATE_OPTS="--kubeconfig=${E2E_KUBECONFIG_ROOT}/kubeconfig-${i}" \ + HELM_INSTALL_OPTS="--kubeconfig ${E2E_KUBECONFIG_ROOT}/kubeconfig-${i}" \ + make kind-create deploy; + done + + # run non-flakes if matrix-id is not 'flakes' + - name: Run e2e tests + if: ${{ matrix.parallel-id != 'flakes' }} + # calculate the number of chunks as the number of parallel jobs minus 1 (flakes job) + # use the split tool to split the test suite into chunks and run the chunk corresponding to the matrix-id + # focus on those tests and skip tests marked as FLAKE + run: | + E2E_TEST_NUM_CHUNKS=$(( ${{ strategy.job-total }} - 1 )) \ + GINKGO_E2E_OPTS="${GINKGO_E2E_OPTS} -focus '$(go run ./test/e2e/split/... -chunks $E2E_TEST_NUM_CHUNKS -print-chunk $E2E_TEST_CHUNK ./test/e2e)' -skip '\[FLAKE\]'" \ + make e2e; + + # run e2e tests for flakes if matrix-id is 'flakes' + - name: Run flaky e2e tests + if: ${{ matrix.parallel-id == 'flakes' }} + # focus on tests marked as FLAKE + run: | + GINKGO_E2E_OPTS="${GINKGO_E2E_OPTS} -focus '\[FLAKE\]'" make e2e + + # archive test results + - name: Archive Test Artifacts if: ${{ always() }} uses: actions/upload-artifact@v4 with: name: e2e-test-output-${{ (github.event.pull_request.head.sha || github.sha) }}-${{ github.run_id }}-${{ matrix.parallel-id }} - path: ${{ github.workspace }}/bin/artifacts/* + path: ${{ env.ARTIFACT_DIR }}/* # TODO: create job to combine test artifacts using code in https://github.com/operator-framework/operator-lifecycle-manager/pull/1476 + e2e-tests: if: ${{ always() }} runs-on: ubuntu-latest @@ -40,4 +128,4 @@ jobs: if: ${{ needs.e2e.result == 'failure' }} run: | echo 'Failure: at least one e2e matrix job has failed' - exit 1 + exit 1 \ No newline at end of file diff --git a/.github/workflows/flaky-e2e.yml b/.github/workflows/flaky-e2e.yml deleted file mode 100644 index b2078e6f51..0000000000 --- a/.github/workflows/flaky-e2e.yml +++ /dev/null @@ -1,25 +0,0 @@ -name: flaky-e2e-tests -on: - schedule: - - cron: '30 5,17 * * *' # run this every day at 5:30 and 17:30 UTC (00:30 and 12:30 ET) - push: - branches: - - master - pull_request: - workflow_dispatch: -jobs: - flaky-e2e-tests: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-go@v5 - with: - go-version-file: "go.mod" - - run: mkdir -p artifacts - - run: make e2e-local E2E_NODES=1 TEST='\[FLAKE\]' ARTIFACT_DIR=./artifacts/ - - name: Archive Test Artifacts # test results, failed or not, are always uploaded. - if: ${{ always() }} - uses: actions/upload-artifact@v4 - with: - name: e2e-test-output-${{(github.event.pull_request.head.sha||github.sha)}}-${{ github.run_id }} - path: ${{ github.workspace }}/bin/artifacts/* diff --git a/.gitignore b/.gitignore index 892ea66dfc..c95fe5f9fe 100644 --- a/.gitignore +++ b/.gitignore @@ -453,6 +453,5 @@ apiserver.crt apiserver.key !vendor/** -test/e2e-local.image.tar test/e2e/.kube dist/ diff --git a/Makefile b/Makefile index f4f5c888a4..83dfd42cf6 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,10 @@ ########################## # OLM - Build and Test # ########################## +# Setting SHELL to bash allows bash commands to be executed by recipes. +# Options are set to exit when a recipe line exits non-zero or a piped command fails. +SHELL := /usr/bin/env bash -o pipefail +.SHELLFLAGS := -ec # Undefine GOFLAGS environment variable. ifdef GOFLAGS @@ -22,6 +26,8 @@ SPECIFIC_UNIT_TEST := $(if $(TEST),-run $(TEST),) LOCAL_NAMESPACE := "olm" export GO111MODULE=on YQ_INTERNAL := go run $(MOD_FLAGS) ./vendor/github.com/mikefarah/yq/v3/ +HELM := go run $(MOD_FLAGS) ./vendor/helm.sh/helm/v3/cmd/helm +KIND := go run $(MOD_FLAGS) ./vendor/sigs.k8s.io/kind KUBEBUILDER_ASSETS := $(or $(or $(KUBEBUILDER_ASSETS),$(dir $(shell command -v kubebuilder))),/usr/local/kubebuilder/bin) export KUBEBUILDER_ASSETS GO := GO111MODULE=on GOFLAGS="$(MOD_FLAGS)" go @@ -33,6 +39,15 @@ ARCH := arm64 else ARCH := amd64 endif + +KIND_CLUSTER_NAME ?= kind-olmv0 +# Not guaranteed to have patch releases available and node image tags are full versions (i.e v1.28.0 - no v1.28, v1.29, etc.) +# The KIND_NODE_VERSION is set by getting the version of the k8s.io/client-go dependency from the go.mod +# and sets major version to "1" and the patch version to "0". For example, a client-go version of v0.28.5 +# will map to a KIND_NODE_VERSION of 1.28.0 +KIND_NODE_VERSION := $(shell go list -m k8s.io/client-go | cut -d" " -f2 | sed 's/^v0\.\([[:digit:]]\{1,\}\)\.[[:digit:]]\{1,\}$$/1.\1.0/') +KIND_CLUSTER_IMAGE := kindest/node:v$(KIND_NODE_VERSION) + # Phony prerequisite for targets that rely on the go build cache to determine staleness. .PHONY: build test clean vendor \ coverage coverage-html e2e \ @@ -118,49 +133,43 @@ deploy-local: e2e.namespace: @printf "e2e-tests-$(shell date +%s)-$$RANDOM" > e2e.namespace -E2E_NODES ?= 1 -E2E_FLAKE_ATTEMPTS ?= 1 -E2E_TIMEOUT ?= 90m -# Optionally run an individual chunk of e2e test specs. -# Do not use this from the CLI; this is intended to be used by CI only. -E2E_TEST_CHUNK ?= all -E2E_TEST_NUM_CHUNKS ?= 4 -ifneq (all,$(E2E_TEST_CHUNK)) -TEST := $(shell go run ./test/e2e/split/... -chunks $(E2E_TEST_NUM_CHUNKS) -print-chunk $(E2E_TEST_CHUNK) ./test/e2e) -endif -E2E_OPTS ?= $(if $(E2E_SEED),-seed '$(E2E_SEED)') $(if $(SKIP), -skip '$(SKIP)') $(if $(TEST),-focus '$(TEST)') $(if $(ARTIFACT_DIR), -output-dir $(ARTIFACT_DIR) -junit-report junit_e2e.xml) -flake-attempts $(E2E_FLAKE_ATTEMPTS) -nodes $(E2E_NODES) -timeout $(E2E_TIMEOUT) -v -randomize-suites -race -trace -progress -E2E_INSTALL_NS ?= operator-lifecycle-manager -E2E_CATALOG_NS ?= $(E2E_INSTALL_NS) -E2E_TEST_NS ?= operators - +.PHONY: e2e +GINKGO_E2E_OPTS += -timeout 90m -v -randomize-suites -race -trace --show-node-events +E2E_OPTS += -namespace=operators -olmNamespace=operator-lifecycle-manager -catalogNamespace=operator-lifecycle-manager -dummyImage=bitnami/nginx:latest e2e: - $(GINKGO) $(E2E_OPTS) $(or $(run), ./test/e2e) $< -- -namespace=$(E2E_TEST_NS) -olmNamespace=$(E2E_INSTALL_NS) -catalogNamespace=$(E2E_CATALOG_NS) -dummyImage=bitnami/nginx:latest $(or $(extra_args), -kubeconfig=${KUBECONFIG}) - -# See workflows/e2e-tests.yml See test/e2e/README.md for details. -.PHONY: e2e-local -e2e-local: BUILD_TAGS="json1 e2e experimental_metrics" -e2e-local: extra_args=-kind.images=../test/e2e-local.image.tar -test-data-dir=../test/e2e/testdata -gather-artifacts-script-path=../test/e2e/collect-ci-artifacts.sh -e2e-local: run=bin/e2e-local.test -e2e-local: bin/e2e-local.test test/e2e-local.image.tar -e2e-local: e2e - -# this target updates the zz_chart.go file with files found in deploy/chart -# this will always fire since it has been marked as phony -.PHONY: test/e2e/assets/chart/zz_chart.go -test/e2e/assets/chart/zz_chart.go: $(shell find deploy/chart -type f) - $(BINDATA) -o $@ -pkg chart -prefix deploy/chart/ $^ - -# execute kind and helm end to end tests -bin/e2e-local.test: FORCE test/e2e/assets/chart/zz_chart.go - $(GO) test -c -tags kind,helm -o $@ ./test/e2e - -# set go env and other vars, ensure that the dockerfile exists, and then build wait, cpb, and other command binaries and finally the kind image archive -test/e2e-local.image.tar: export GOOS=linux -test/e2e-local.image.tar: export GOARCH=amd64 -test/e2e-local.image.tar: build_cmd=build -test/e2e-local.image.tar: e2e.Dockerfile bin/wait bin/cpb $(CMDS) + $(GINKGO) $(GINKGO_E2E_OPTS) ./test/e2e -- $(E2E_OPTS) + +.PHONY: kind-clean +kind-clean: + $(KIND) delete cluster --name $(KIND_CLUSTER_NAME) || true + +.PHONY: kind-create +kind-create: kind-clean + $(KIND) create cluster --name $(KIND_CLUSTER_NAME) --image $(KIND_CLUSTER_IMAGE) $(KIND_CREATE_OPTS) + $(KIND) export kubeconfig --name $(KIND_CLUSTER_NAME) + +.PHONY: deploy +OLM_IMAGE := quay.io/operator-framework/olm:local +deploy: + $(KIND) load docker-image $(OLM_IMAGE) --name $(KIND_CLUSTER_NAME); \ + $(HELM) install olm deploy/chart \ + --set debug=true \ + --set olm.image.ref=$(OLM_IMAGE) \ + --set olm.image.pullPolicy=IfNotPresent \ + --set catalog.image.ref=$(OLM_IMAGE) \ + --set catalog.image.pullPolicy=IfNotPresent \ + --set package.image.ref=$(OLM_IMAGE) \ + --set package.image.pullPolicy=IfNotPresent \ + $(HELM_INSTALL_OPTS) \ + --wait; + +.PHONY: e2e-build +e2e-build: BUILD_TAGS="json1 e2e experimental_metrics" +e2e-build: export GOOS=linux +e2e-build: export GOARCH=amd64 +e2e-build: build_cmd=build +e2e-build: e2e.Dockerfile bin/wait bin/cpb $(CMDS) docker build -t quay.io/operator-framework/olm:local -f $< bin - docker save -o $@ quay.io/operator-framework/olm:local vendor: go mod tidy diff --git a/test/e2e/assets/chart/.gitignore b/test/e2e/assets/chart/.gitignore deleted file mode 100644 index f612a7a6b9..0000000000 --- a/test/e2e/assets/chart/.gitignore +++ /dev/null @@ -1 +0,0 @@ -zz_chart.go diff --git a/test/e2e/assets/chart/doc.go b/test/e2e/assets/chart/doc.go deleted file mode 100644 index 2bf398738d..0000000000 --- a/test/e2e/assets/chart/doc.go +++ /dev/null @@ -1,3 +0,0 @@ -package chart - -// This package exists to ignore certain build files to make sure upstream tests pass. diff --git a/test/e2e/ctx/installer_helm.go b/test/e2e/ctx/installer_helm.go deleted file mode 100644 index 505a7965ca..0000000000 --- a/test/e2e/ctx/installer_helm.go +++ /dev/null @@ -1,123 +0,0 @@ -//go:build helm -// +build helm - -package ctx - -import ( - "fmt" - "time" - - "helm.sh/helm/v3/pkg/action" - "helm.sh/helm/v3/pkg/chart/loader" - "helm.sh/helm/v3/pkg/cli/values" - "helm.sh/helm/v3/pkg/getter" - "k8s.io/apimachinery/pkg/api/meta" - "k8s.io/apimachinery/pkg/util/rand" - "k8s.io/client-go/discovery" - "k8s.io/client-go/discovery/cached/memory" - "k8s.io/client-go/rest" - "k8s.io/client-go/restmapper" - "k8s.io/client-go/tools/clientcmd" - clientcmdapi "k8s.io/client-go/tools/clientcmd/api" - - "github.com/operator-framework/operator-lifecycle-manager/test/e2e/assets/chart" -) - -// clientAdapter implements genericclioptions.RESTClientGetter and -// clientcmd.ClientConfig around *rest.Config, in order to satisfy -// Helm. -type clientAdapter struct { - *rest.Config -} - -func (a clientAdapter) ToRESTConfig() (*rest.Config, error) { - if a.Config == nil { - return nil, fmt.Errorf("REST config is nil") - } - return a.Config, nil -} - -func (a clientAdapter) ToDiscoveryClient() (discovery.CachedDiscoveryInterface, error) { - cfg, err := a.ToRESTConfig() - if err != nil { - return nil, err - } - dc, err := discovery.NewDiscoveryClientForConfig(cfg) - if err != nil { - return nil, err - } - return memory.NewMemCacheClient(dc), nil -} - -func (a clientAdapter) ToRESTMapper() (meta.RESTMapper, error) { - dc, err := a.ToDiscoveryClient() - if err != nil { - return nil, err - } - mapper := restmapper.NewDeferredDiscoveryRESTMapper(dc) - return restmapper.NewShortcutExpander(mapper, dc, nil), nil -} - -func (a clientAdapter) ToRawKubeConfigLoader() clientcmd.ClientConfig { - return clientcmd.ClientConfig(a) -} - -func (a clientAdapter) RawConfig() (clientcmdapi.Config, error) { - return clientcmdapi.Config{}, fmt.Errorf("not supported") -} - -func (a clientAdapter) ClientConfig() (*rest.Config, error) { - return a.ToRESTConfig() -} - -func (a clientAdapter) Namespace() (string, bool, error) { - return "default", false, nil -} - -func (a clientAdapter) ConfigAccess() clientcmd.ConfigAccess { - return clientcmd.NewDefaultClientConfigLoadingRules() -} - -func Install(ctx *TestContext) error { - cfg := action.Configuration{} - cfg.Init(clientAdapter{ctx.RESTConfig()}, "", "memory", ctx.Logf) - act := action.NewInstall(&cfg) - act.Timeout = 5 * time.Second - act.ReleaseName = fmt.Sprintf("release-%s", rand.String(8)) - - var files []*loader.BufferedFile - for _, name := range chart.AssetNames() { - data, err := chart.Asset(name) - if err != nil { - return err - } - files = append(files, &loader.BufferedFile{Name: name, Data: data}) - } - - valueOptions := values.Options{ - Values: []string{ - "debug=true", - "olm.image.ref=quay.io/operator-framework/olm:local", - "olm.image.pullPolicy=IfNotPresent", - "catalog.image.ref=quay.io/operator-framework/olm:local", - "catalog.image.pullPolicy=IfNotPresent", - "package.image.ref=quay.io/operator-framework/olm:local", - "package.image.pullPolicy=IfNotPresent", - }, - } - - chart, err := loader.LoadFiles(files) - if err != nil { - return err - } - - values, err := valueOptions.MergeValues(getter.Providers(nil)) - if err != nil { - return err - } - - if _, err := act.Run(chart, values); err != nil { - return err - } - return nil -} diff --git a/test/e2e/ctx/installer_none.go b/test/e2e/ctx/installer_none.go index 48375d6d1b..e037337389 100644 --- a/test/e2e/ctx/installer_none.go +++ b/test/e2e/ctx/installer_none.go @@ -1,8 +1,5 @@ -//go:build !helm -// +build !helm - package ctx -func Install(ctx *TestContext) error { +func Install(_ *TestContext) error { return nil } diff --git a/test/e2e/ctx/provisioner_kind.go b/test/e2e/ctx/provisioner_kind.go deleted file mode 100644 index 6ac0ab976c..0000000000 --- a/test/e2e/ctx/provisioner_kind.go +++ /dev/null @@ -1,158 +0,0 @@ -//go:build kind -// +build kind - -package ctx - -import ( - "encoding/csv" - "flag" - "fmt" - "os" - "path/filepath" - "strings" - "sync" - "time" - - "k8s.io/apimachinery/pkg/util/rand" - "k8s.io/client-go/tools/clientcmd" - "sigs.k8s.io/kind/pkg/cluster" - "sigs.k8s.io/kind/pkg/cluster/nodeutils" - "sigs.k8s.io/kind/pkg/log" -) - -var ( - images = flag.String("kind.images", "", "comma-separated list of image archives to load on cluster nodes, relative to the test binary or test package path") - logDir = "logs" - - verbosity int -) - -func init() { - // https://github.com/kubernetes-sigs/kind/blob/v0.10.0/pkg/log/types.go#L38-L45 - flag.IntVar(&verbosity, "kind.verbosity", 0, "log verbosity level") -} - -type kindLogAdapter struct { - *TestContext -} - -var ( - _ log.Logger = kindLogAdapter{} - _ log.InfoLogger = kindLogAdapter{} -) - -func (kl kindLogAdapter) Enabled() bool { - return true -} - -func (kl kindLogAdapter) Info(message string) { - kl.Infof("%s", message) -} - -func (kl kindLogAdapter) Infof(format string, args ...interface{}) { - kl.Logf(format, args...) -} - -func (kl kindLogAdapter) Warn(message string) { - kl.Warnf("%s", message) -} - -func (kl kindLogAdapter) Warnf(format string, args ...interface{}) { - kl.Logf(format, args...) -} - -func (kl kindLogAdapter) Error(message string) { - kl.Errorf("%s", message) -} - -func (kl kindLogAdapter) Errorf(format string, args ...interface{}) { - kl.Logf(format, args...) -} - -func (kl kindLogAdapter) V(level log.Level) log.InfoLogger { - if level > log.Level(verbosity) { - return log.NoopInfoLogger{} - } - return kl -} - -func Provision(ctx *TestContext) (func(), error) { - dir, err := os.MkdirTemp("", "kind.") - if err != nil { - return nil, fmt.Errorf("failed to create temporary directory: %s", err.Error()) - } - kubeconfigPath := filepath.Join(dir, "kubeconfig") - - ctx.Logf("e2e cluster kubeconfig: %s\n", kubeconfigPath) - provider := cluster.NewProvider( - cluster.ProviderWithLogger(kindLogAdapter{ctx}), - ) - name := fmt.Sprintf("kind-%s", rand.String(16)) - if err := provider.Create( - name, - cluster.CreateWithWaitForReady(5*time.Minute), - cluster.CreateWithKubeconfigPath(kubeconfigPath), - ); err != nil { - return nil, fmt.Errorf("failed to create kind cluster: %s", err.Error()) - } - - nodes, err := provider.ListNodes(name) - if err != nil { - return nil, fmt.Errorf("failed to list kind nodes: %s", err.Error()) - } - - var archives []string - if images != nil { - records, err := csv.NewReader(strings.NewReader(*images)).ReadAll() - if err != nil { - return nil, fmt.Errorf("error parsing image flag: %s", err.Error()) - } - for _, row := range records { - archives = append(archives, row...) - } - } - - for _, archive := range archives { - for _, node := range nodes { - fd, err := os.Open(archive) - if err != nil { - return nil, fmt.Errorf("error opening archive %q: %s", archive, err.Error()) - } - err = nodeutils.LoadImageArchive(node, fd) - fd.Close() - if err != nil { - return nil, fmt.Errorf("error loading image archive %q to node %q: %s", archive, node, err.Error()) - } - } - } - - kubeconfig, err := provider.KubeConfig(name, false) - if err != nil { - return nil, fmt.Errorf("failed to read kubeconfig: %s", err.Error()) - } - restConfig, err := clientcmd.RESTConfigFromKubeConfig([]byte(kubeconfig)) - if err != nil { - return nil, fmt.Errorf("error loading kubeconfig: %s", err.Error()) - } - ctx.restConfig = restConfig - ctx.kubeconfigPath = kubeconfigPath - - var once sync.Once - deprovision := func() { - once.Do(func() { - // remove the temporary kubeconfig directory - if err := os.RemoveAll(dir); err != nil { - ctx.Logf("failed to remove the %s kubeconfig directory: %v", dir, err) - } - if ctx.artifactsDir != "" { - ctx.Logf("collecting container logs for the %s cluster", name) - if err := provider.CollectLogs(name, filepath.Join(ctx.artifactsDir, logDir)); err != nil { - ctx.Logf("failed to collect logs: %v", err) - } - } - provider.Delete(name, kubeconfigPath) - }) - } - - return deprovision, setDerivedFields(ctx) -} diff --git a/test/e2e/ctx/provisioner_kubeconfig.go b/test/e2e/ctx/provisioner_kubeconfig.go index 1ce1980d71..b6c6a92ce4 100644 --- a/test/e2e/ctx/provisioner_kubeconfig.go +++ b/test/e2e/ctx/provisioner_kubeconfig.go @@ -1,6 +1,3 @@ -//go:build !kind -// +build !kind - package ctx import ( diff --git a/test/e2e/e2e_test.go b/test/e2e/e2e_test.go index 28fcc8f8d2..7deb176552 100644 --- a/test/e2e/e2e_test.go +++ b/test/e2e/e2e_test.go @@ -5,6 +5,8 @@ import ( "flag" "fmt" "os" + "path" + "strconv" "testing" "time" @@ -51,7 +53,7 @@ var ( collectArtifactsScriptPath = flag.String( "gather-artifacts-script-path", "./collect-ci-artifacts.sh", - "configures the relative/absolute path to the script resposible for collecting CI artifacts", + "configures the relative/absolute path to the script responsible for collecting CI artifacts", ) testdataPath = flag.String( @@ -60,12 +62,20 @@ var ( "configures where to find the testdata directory", ) + kubeconfigRootDir = flag.String( + "kubeconfig-root", + "", + "configures the root directory for kubeconfig files when running tests in parallel. "+ + "Each test worker will expect their kubeconfig file to be /kubeconfig-. "+ + "Where, is the number of the test worker (> 0). "+ + "Note that this flag will override the kubeconfig flag.", + ) + testdataDir = "" testNamespace = "" operatorNamespace = "" communityOperatorsImage = "" globalCatalogNamespace = "" - junitDir = "junit" ) func TestEndToEnd(t *testing.T) { @@ -82,10 +92,12 @@ var deprovision func() = func() {} // This function initializes a client which is used to create an operator group for a given namespace var _ = BeforeSuite(func() { - if kubeConfigPath != nil && *kubeConfigPath != "" { - // This flag can be deprecated in favor of the kubeconfig provisioner: + if kubeconfigRootDir != nil && *kubeconfigRootDir != "" { + os.Setenv("KUBECONFIG", path.Join(*kubeconfigRootDir, "kubeconfig-"+strconv.Itoa(GinkgoParallelProcess()))) + } else if kubeConfigPath != nil && *kubeConfigPath != "" { os.Setenv("KUBECONFIG", *kubeConfigPath) } + if collectArtifactsScriptPath != nil && *collectArtifactsScriptPath != "" { os.Setenv("E2E_ARTIFACT_SCRIPT", *collectArtifactsScriptPath) } diff --git a/tools.go b/tools.go index 3d1b0026f1..1125e67bf9 100644 --- a/tools.go +++ b/tools.go @@ -15,4 +15,5 @@ import ( _ "k8s.io/code-generator" _ "k8s.io/kube-openapi/cmd/openapi-gen" _ "sigs.k8s.io/controller-tools/cmd/controller-gen" + _ "sigs.k8s.io/kind" ) diff --git a/vendor/modules.txt b/vendor/modules.txt index 039c496af2..1743479067 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -2192,8 +2192,13 @@ sigs.k8s.io/json sigs.k8s.io/json/internal/golang/encoding/json # sigs.k8s.io/kind v0.22.0 ## explicit; go 1.16 +sigs.k8s.io/kind +sigs.k8s.io/kind/cmd/kind/app sigs.k8s.io/kind/pkg/apis/config/defaults sigs.k8s.io/kind/pkg/apis/config/v1alpha4 +sigs.k8s.io/kind/pkg/build/nodeimage +sigs.k8s.io/kind/pkg/build/nodeimage/internal/container/docker +sigs.k8s.io/kind/pkg/build/nodeimage/internal/kube sigs.k8s.io/kind/pkg/cluster sigs.k8s.io/kind/pkg/cluster/constants sigs.k8s.io/kind/pkg/cluster/internal/create @@ -2218,6 +2223,28 @@ sigs.k8s.io/kind/pkg/cluster/internal/providers/podman sigs.k8s.io/kind/pkg/cluster/nodes sigs.k8s.io/kind/pkg/cluster/nodeutils sigs.k8s.io/kind/pkg/cmd +sigs.k8s.io/kind/pkg/cmd/kind +sigs.k8s.io/kind/pkg/cmd/kind/build +sigs.k8s.io/kind/pkg/cmd/kind/build/nodeimage +sigs.k8s.io/kind/pkg/cmd/kind/completion +sigs.k8s.io/kind/pkg/cmd/kind/completion/bash +sigs.k8s.io/kind/pkg/cmd/kind/completion/fish +sigs.k8s.io/kind/pkg/cmd/kind/completion/zsh +sigs.k8s.io/kind/pkg/cmd/kind/create +sigs.k8s.io/kind/pkg/cmd/kind/create/cluster +sigs.k8s.io/kind/pkg/cmd/kind/delete +sigs.k8s.io/kind/pkg/cmd/kind/delete/cluster +sigs.k8s.io/kind/pkg/cmd/kind/delete/clusters +sigs.k8s.io/kind/pkg/cmd/kind/export +sigs.k8s.io/kind/pkg/cmd/kind/export/kubeconfig +sigs.k8s.io/kind/pkg/cmd/kind/export/logs +sigs.k8s.io/kind/pkg/cmd/kind/get +sigs.k8s.io/kind/pkg/cmd/kind/get/clusters +sigs.k8s.io/kind/pkg/cmd/kind/get/kubeconfig +sigs.k8s.io/kind/pkg/cmd/kind/get/nodes +sigs.k8s.io/kind/pkg/cmd/kind/load +sigs.k8s.io/kind/pkg/cmd/kind/load/docker-image +sigs.k8s.io/kind/pkg/cmd/kind/load/image-archive sigs.k8s.io/kind/pkg/cmd/kind/version sigs.k8s.io/kind/pkg/errors sigs.k8s.io/kind/pkg/exec @@ -2227,6 +2254,7 @@ sigs.k8s.io/kind/pkg/internal/apis/config/encoding sigs.k8s.io/kind/pkg/internal/cli sigs.k8s.io/kind/pkg/internal/env sigs.k8s.io/kind/pkg/internal/patch +sigs.k8s.io/kind/pkg/internal/runtime sigs.k8s.io/kind/pkg/internal/sets sigs.k8s.io/kind/pkg/internal/version sigs.k8s.io/kind/pkg/log diff --git a/vendor/sigs.k8s.io/kind/.gitignore b/vendor/sigs.k8s.io/kind/.gitignore new file mode 100644 index 0000000000..f285fcfbcf --- /dev/null +++ b/vendor/sigs.k8s.io/kind/.gitignore @@ -0,0 +1,22 @@ +# build and test outputs +/bin/ +/_output/ +/_artifacts/ + +# used for the code generators only +/vendor/ + +# macOS +.DS_Store + +# Vagrant +.vagrant + +# files generated by editors +.idea/ +*.iml +.vscode/ +*.swp +*.sublime-project +*.sublime-workspace +*~ diff --git a/vendor/sigs.k8s.io/kind/.go-version b/vendor/sigs.k8s.io/kind/.go-version new file mode 100644 index 0000000000..5009c3e672 --- /dev/null +++ b/vendor/sigs.k8s.io/kind/.go-version @@ -0,0 +1 @@ +1.20.13 diff --git a/vendor/sigs.k8s.io/kind/CONTRIBUTING.md b/vendor/sigs.k8s.io/kind/CONTRIBUTING.md new file mode 100644 index 0000000000..6dd6e155bf --- /dev/null +++ b/vendor/sigs.k8s.io/kind/CONTRIBUTING.md @@ -0,0 +1,24 @@ +# Contributing Guidelines + +Welcome to Kubernetes. We are excited about the prospect of you joining our [community](https://github.com/kubernetes/community)! The Kubernetes community abides by the CNCF [code of conduct](code-of-conduct.md). Here is an excerpt: + +_As contributors and maintainers of this project, and in the interest of fostering an open and welcoming community, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities._ + +## Getting Started + +We have full documentation on how to get started contributing here: https://kind.sigs.k8s.io/docs/contributing/getting-started/, _please_ read this! +A lot of work went into this guide 🙃 + +## Mentorship + +- [Mentoring Initiatives](https://git.k8s.io/community/mentoring) - Kubernetes has a diverse set of mentorship programs available that are always looking for volunteers! + + diff --git a/vendor/sigs.k8s.io/kind/Makefile b/vendor/sigs.k8s.io/kind/Makefile new file mode 100644 index 0000000000..566da0ea7a --- /dev/null +++ b/vendor/sigs.k8s.io/kind/Makefile @@ -0,0 +1,114 @@ +# Copyright 2019 The Kubernetes Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# 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. + +# Old-skool build tools. +# Simple makefile to build kind quickly and reproducibly +# +# Common uses: +# - installing kind: `make install INSTALL_DIR=$HOME/go/bin` +# - building: `make build` +# - cleaning up and starting over: `make clean` +# +################################################################################ +# ========================== Capture Environment =============================== +# get the repo root and output path +REPO_ROOT:=${CURDIR} +OUT_DIR=$(REPO_ROOT)/bin +# record the source commit in the binary, overridable +COMMIT?=$(shell git rev-parse HEAD 2>/dev/null) +# count the commits since the last release +COMMIT_COUNT?=$(shell git describe --tags | rev | cut -d- -f2 | rev) +################################################################################ +# ========================= Setup Go With Gimme ================================ +# go version to use for build etc. +# setup correct go version with gimme +PATH:=$(shell . hack/build/setup-go.sh && echo "$${PATH}") +# go1.9+ can autodetect GOROOT, but if some other tool sets it ... +GOROOT:= +# enable modules +GO111MODULE=on +# disable CGO by default for static binaries +CGO_ENABLED=0 +export PATH GOROOT GO111MODULE CGO_ENABLED +# work around broken PATH export +SPACE:=$(subst ,, ) +SHELL:=env PATH=$(subst $(SPACE),\$(SPACE),$(PATH)) $(SHELL) +################################################################################ +# ============================== OPTIONS ======================================= +# install tool +INSTALL?=install +# install will place binaries here, by default attempts to mimic go install +INSTALL_DIR?=$(shell hack/build/goinstalldir.sh) +# the output binary name, overridden when cross compiling +KIND_BINARY_NAME?=kind +# build flags for the kind binary +# - reproducible builds: -trimpath and -ldflags=-buildid= +# - smaller binaries: -w (trim debugger data, but not panics) +# - metadata: -X=... to bake in git commit +KIND_VERSION_PKG:=sigs.k8s.io/kind/pkg/cmd/kind/version +KIND_BUILD_LD_FLAGS:=-X=$(KIND_VERSION_PKG).gitCommit=$(COMMIT) -X=$(KIND_VERSION_PKG).gitCommitCount=$(COMMIT_COUNT) +KIND_BUILD_FLAGS?=-trimpath -ldflags="-buildid= -w $(KIND_BUILD_LD_FLAGS)" +################################################################################ +# ================================= Building =================================== +# standard "make" target -> builds +all: build +# builds kind in a container, outputs to $(OUT_DIR) +kind: + go build -v -o "$(OUT_DIR)/$(KIND_BINARY_NAME)" $(KIND_BUILD_FLAGS) +# alias for building kind +build: kind +# use: make install INSTALL_DIR=/usr/local/bin +install: build + $(INSTALL) -d $(INSTALL_DIR) + $(INSTALL) "$(OUT_DIR)/$(KIND_BINARY_NAME)" "$(INSTALL_DIR)/$(KIND_BINARY_NAME)" +################################################################################ +# ================================= Testing ==================================== +# unit tests (hermetic) +unit: + MODE=unit hack/make-rules/test.sh +# integration tests +integration: + MODE=integration hack/make-rules/test.sh +# all tests +test: + hack/make-rules/test.sh +################################################################################ +# ================================= Cleanup ==================================== +# standard cleanup target +clean: + rm -rf "$(OUT_DIR)/" +################################################################################ +# ============================== Auto-Update =================================== +# update generated code, gofmt, etc. +update: + hack/make-rules/update/all.sh +# update generated code +generate: + hack/make-rules/update/generated.sh +# gofmt +gofmt: + hack/make-rules/update/gofmt.sh +################################################################################ +# ================================== Linting =================================== +# run linters, ensure generated code, etc. +verify: + hack/make-rules/verify/all.sh +# code linters +lint: + hack/make-rules/verify/lint.sh +# shell linter +shellcheck: + hack/make-rules/verify/shellcheck.sh +################################################################################# +.PHONY: all kind build install unit clean update generate gofmt verify lint shellcheck diff --git a/vendor/sigs.k8s.io/kind/OWNERS b/vendor/sigs.k8s.io/kind/OWNERS new file mode 100644 index 0000000000..d527d3051a --- /dev/null +++ b/vendor/sigs.k8s.io/kind/OWNERS @@ -0,0 +1,12 @@ +# See the OWNERS docs at https://go.k8s.io/owners + +reviewers: + - aojea + - BenTheElder + - neolit123 +approvers: + - aojea + - BenTheElder +emeritus_approvers: + - amwat + - munnerz diff --git a/vendor/sigs.k8s.io/kind/README.md b/vendor/sigs.k8s.io/kind/README.md new file mode 100644 index 0000000000..6ddc411635 --- /dev/null +++ b/vendor/sigs.k8s.io/kind/README.md @@ -0,0 +1,176 @@ +

kind

+ +# Please see [Our Documentation](https://kind.sigs.k8s.io/docs/user/quick-start/) for more in-depth installation etc. + +kind is a tool for running local Kubernetes clusters using Docker container "nodes". +kind was primarily designed for testing Kubernetes itself, but may be used for local development or CI. + +If you have [go] 1.16+ and [docker] or [podman] installed `go install sigs.k8s.io/kind@v0.21.0 && kind create cluster` is all you need! + +![](site/static/images/kind-create-cluster.png) + +kind consists of: +- Go [packages][packages] implementing [cluster creation][cluster package], [image build][build package], etc. +- A command line interface ([`kind`][kind cli]) built on these packages. +- Docker [image(s)][images] written to run systemd, Kubernetes, etc. +- [`kubetest`][kubetest] integration also built on these packages (WIP) + +kind bootstraps each "node" with [kubeadm][kubeadm]. For more details see [the design documentation][design doc]. + +**NOTE**: kind is still a work in progress, see the [1.0 roadmap]. + +## Installation and usage + +For a complete [install guide] see [the documentation here][install guide]. + +You can install kind with `go install sigs.k8s.io/kind@v0.21.0`. + +**NOTE**: please use the latest go to do this. KIND is developed with the latest stable go, see [`.go-version`](./.go-version) for the exact version we're using. + +This will put `kind` in `$(go env GOPATH)/bin`. If you encounter the error +`kind: command not found` after installation then you may need to either add that directory to your `$PATH` as +shown [here](https://golang.org/doc/code.html#GOPATH) or do a manual installation by cloning the repo and run +`make build` from the repository. + +Without installing go, kind can be built reproducibly with docker using `make build`. + +Stable binaries are also available on the [releases] page. Stable releases are +generally recommended for CI usage in particular. +To install, download the binary for your platform from "Assets" and place this +into your `$PATH`: + +On Linux: + +```console +# For AMD64 / x86_64 +[ $(uname -m) = x86_64 ] && curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.21.0/kind-$(uname)-amd64 +# For ARM64 +[ $(uname -m) = aarch64 ] && curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.21.0/kind-$(uname)-arm64 +chmod +x ./kind +sudo mv ./kind /usr/local/bin/kind +``` + +On macOS via Homebrew: + +```console +brew install kind +``` + +On macOS via MacPorts: + +```console +sudo port selfupdate && sudo port install kind +``` + +On macOS via Bash: + +```console +# For Intel Macs +[ $(uname -m) = x86_64 ] && curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.21.0/kind-darwin-amd64 +# For M1 / ARM Macs +[ $(uname -m) = arm64 ] && curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.21.0/kind-darwin-arm64 +chmod +x ./kind +mv ./kind /some-dir-in-your-PATH/kind +``` + +On Windows: + +```powershell +curl.exe -Lo kind-windows-amd64.exe https://kind.sigs.k8s.io/dl/v0.21.0/kind-windows-amd64 +Move-Item .\kind-windows-amd64.exe c:\some-dir-in-your-PATH\kind.exe + +# OR via Chocolatey (https://chocolatey.org/packages/kind) +choco install kind +``` + +To use kind, you will need to [install docker]. +Once you have docker running you can create a cluster with: + +```console +kind create cluster +``` + +To delete your cluster use: + +```console +kind delete cluster +``` + + +To create a cluster from Kubernetes source: +- ensure that Kubernetes is cloned in `$(go env GOPATH)/src/k8s.io/kubernetes` +- build a node image and create a cluster with: +```console +kind build node-image +kind create cluster --image kindest/node:latest +``` + +Multi-node clusters and other advanced features may be configured with a config +file, for more usage see [the docs][user guide] or run `kind [command] --help` + +## Community + +Please reach out for bugs, feature requests, and other issues! +The maintainers of this project are reachable via: + +- [Kubernetes Slack] in the [#kind] channel +- [filing an issue] against this repo +- The Kubernetes [SIG-Testing Mailing List] + +Current maintainers are [@aojea] and [@BenTheElder] - feel free to +reach out if you have any questions! + +Pull Requests are very welcome! +If you're planning a new feature, please file an issue to discuss first. + +Check the [issue tracker] for `help wanted` issues if you're unsure where to +start, or feel free to reach out to discuss. 🙂 + +See also: our own [contributor guide] and the Kubernetes [community page]. + +## Why kind? + +- kind supports multi-node (including HA) clusters +- kind supports building Kubernetes release builds from source + - support for make / bash or docker, in addition to pre-published builds +- kind supports Linux, macOS and Windows +- kind is a [CNCF certified conformant Kubernetes installer](https://landscape.cncf.io/?selected=kind) + +### Code of conduct + +Participation in the Kubernetes community is governed by the [Kubernetes Code of Conduct]. + + +[go]: https://golang.org/ +[go-supported]: https://golang.org/doc/devel/release.html#policy +[docker]: https://www.docker.com/ +[podman]: https://podman.io/ +[community page]: https://kubernetes.io/community/ +[Kubernetes Code of Conduct]: code-of-conduct.md +[Go Report Card Badge]: https://goreportcard.com/badge/sigs.k8s.io/kind +[Go Report Card]: https://goreportcard.com/report/sigs.k8s.io/kind +[conformance tests]: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/conformance-tests.md +[packages]: ./pkg +[cluster package]: ./pkg/cluster +[build package]: ./pkg/build +[kind cli]: ./main.go +[images]: ./images +[kubetest]: https://github.com/kubernetes/test-infra/tree/master/kubetest +[kubeadm]: https://kubernetes.io/docs/reference/setup-tools/kubeadm/kubeadm/ +[design doc]: https://kind.sigs.k8s.io/docs/design/initial +[user guide]: https://kind.sigs.k8s.io/docs/user/quick-start +[SIG-Testing Mailing List]: https://groups.google.com/forum/#!forum/kubernetes-sig-testing +[issue tracker]: https://github.com/kubernetes-sigs/kind/issues +[filing an issue]: https://github.com/kubernetes-sigs/kind/issues/new +[Kubernetes Slack]: http://slack.k8s.io/ +[#kind]: https://kubernetes.slack.com/messages/CEKK1KTN2/ +[1.0 roadmap]: https://kind.sigs.k8s.io/docs/contributing/1.0-roadmap +[install docker]: https://docs.docker.com/install/ +[@BenTheElder]: https://github.com/BenTheElder +[@munnerz]: https://github.com/munnerz +[@aojea]: https://github.com/aojea +[@amwat]: https://github.com/amwat +[contributor guide]: https://kind.sigs.k8s.io/docs/contributing/getting-started +[releases]: https://github.com/kubernetes-sigs/kind/releases +[install guide]: https://kind.sigs.k8s.io/docs/user/quick-start/#installation +[modules]: https://github.com/golang/go/wiki/Modules diff --git a/vendor/sigs.k8s.io/kind/SECURITY_CONTACTS b/vendor/sigs.k8s.io/kind/SECURITY_CONTACTS new file mode 100644 index 0000000000..785b6e0c3b --- /dev/null +++ b/vendor/sigs.k8s.io/kind/SECURITY_CONTACTS @@ -0,0 +1,14 @@ +# Defined below are the security contacts for this repo. +# +# They are the contact point for the Product Security Team to reach out +# to for triaging and handling of incoming issues. +# +# The below names agree to abide by the +# [Embargo Policy](https://github.com/kubernetes/sig-release/blob/master/security-release-process-documentation/security-release-process.md#embargo-policy) +# and will be removed and replaced if they violate that agreement. +# +# DO NOT REPORT SECURITY VULNERABILITIES DIRECTLY TO THESE NAMES, FOLLOW THE +# INSTRUCTIONS AT https://kubernetes.io/security/ + +BenTheElder +munnerz diff --git a/vendor/sigs.k8s.io/kind/cmd/kind/app/main.go b/vendor/sigs.k8s.io/kind/cmd/kind/app/main.go new file mode 100644 index 0000000000..fa3ac9bce7 --- /dev/null +++ b/vendor/sigs.k8s.io/kind/cmd/kind/app/main.go @@ -0,0 +1,107 @@ +/* +Copyright 2019 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +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 app + +import ( + "io" + "os" + + "github.com/spf13/pflag" + + "sigs.k8s.io/kind/pkg/cmd" + "sigs.k8s.io/kind/pkg/cmd/kind" + "sigs.k8s.io/kind/pkg/errors" + "sigs.k8s.io/kind/pkg/exec" + "sigs.k8s.io/kind/pkg/log" +) + +// Main is the kind main(), it will invoke Run(), if an error is returned +// it will then call os.Exit +func Main() { + if err := Run(cmd.NewLogger(), cmd.StandardIOStreams(), os.Args[1:]); err != nil { + os.Exit(1) + } +} + +// Run invokes the kind root command, returning the error. +// See: sigs.k8s.io/kind/pkg/cmd/kind +func Run(logger log.Logger, streams cmd.IOStreams, args []string) error { + // NOTE: we handle the quiet flag here so we can fully silence cobra + if checkQuiet(args) { + // if we are in quiet mode, we want to suppress all status output + // only streams.Out should be written to (program output) + logger = log.NoopLogger{} + streams.ErrOut = io.Discard + } + // actually run the command + c := kind.NewCommand(logger, streams) + c.SetArgs(args) + if err := c.Execute(); err != nil { + logError(logger, err) + return err + } + return nil +} + +// checkQuiet returns true if -q / --quiet was set in args +func checkQuiet(args []string) bool { + flags := pflag.NewFlagSet("persistent-quiet", pflag.ContinueOnError) + flags.ParseErrorsWhitelist.UnknownFlags = true + quiet := false + flags.BoolVarP( + &quiet, + "quiet", + "q", + false, + "silence all stderr output", + ) + // NOTE: pflag will error if -h / --help is specified + // We don't care here. That will be handled downstream + // It will also call flags.Usage so we're making that no-op + flags.Usage = func() {} + _ = flags.Parse(args) + return quiet +} + +// logError logs the error and the root stacktrace if there is one +func logError(logger log.Logger, err error) { + colorEnabled := cmd.ColorEnabled(logger) + if colorEnabled { + logger.Errorf("\x1b[31mERROR\x1b[0m: %v", err) + } else { + logger.Errorf("ERROR: %v", err) + } + // Display Output if the error was from running a command ... + if err := exec.RunErrorForError(err); err != nil { + if colorEnabled { + logger.Errorf("\x1b[31mCommand Output\x1b[0m: %s", err.Output) + } else { + logger.Errorf("\nCommand Output: %s", err.Output) + } + } + // TODO: stacktrace should probably be guarded by a higher level ...? + if logger.V(1).Enabled() { + // Then display stack trace if any (there should be one...) + if trace := errors.StackTrace(err); trace != nil { + if colorEnabled { + logger.Errorf("\x1b[31mStack Trace\x1b[0m: %+v", trace) + } else { + logger.Errorf("\nStack Trace: %+v", trace) + } + } + } +} diff --git a/vendor/sigs.k8s.io/kind/code-of-conduct.md b/vendor/sigs.k8s.io/kind/code-of-conduct.md new file mode 100644 index 0000000000..0d15c00cf3 --- /dev/null +++ b/vendor/sigs.k8s.io/kind/code-of-conduct.md @@ -0,0 +1,3 @@ +# Kubernetes Community Code of Conduct + +Please refer to our [Kubernetes Community Code of Conduct](https://git.k8s.io/community/code-of-conduct.md) diff --git a/vendor/sigs.k8s.io/kind/main.go b/vendor/sigs.k8s.io/kind/main.go new file mode 100644 index 0000000000..402fdc873b --- /dev/null +++ b/vendor/sigs.k8s.io/kind/main.go @@ -0,0 +1,26 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// This package is a stub main wrapping cmd/kind.Main() +package main + +import ( + "sigs.k8s.io/kind/cmd/kind/app" +) + +func main() { + app.Main() +} diff --git a/vendor/sigs.k8s.io/kind/netlify.toml b/vendor/sigs.k8s.io/kind/netlify.toml new file mode 100644 index 0000000000..c6f0b0b113 --- /dev/null +++ b/vendor/sigs.k8s.io/kind/netlify.toml @@ -0,0 +1,19 @@ +# netlify configuration +[build] +base = "site/" +publish = "site/public/" +command = "hugo" + +[build.environment] +HUGO_VERSION = "0.111.3" + +[context.production.environment] +# this controls our robots.txt +HUGO_ENV = "production" +HUGO_BASEURL = "https://kind.sigs.k8s.io/" + +[context.deploy-preview] +command = "hugo --enableGitInfo --buildFuture -b $DEPLOY_PRIME_URL" + +[context.branch-deploy] +command = "hugo --enableGitInfo --buildFuture -b $DEPLOY_PRIME_URL" diff --git a/vendor/sigs.k8s.io/kind/pkg/build/nodeimage/build.go b/vendor/sigs.k8s.io/kind/pkg/build/nodeimage/build.go new file mode 100644 index 0000000000..34d68a81d4 --- /dev/null +++ b/vendor/sigs.k8s.io/kind/pkg/build/nodeimage/build.go @@ -0,0 +1,78 @@ +/* +Copyright 2020 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +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 nodeimage + +import ( + "runtime" + + "sigs.k8s.io/kind/pkg/build/nodeimage/internal/kube" + "sigs.k8s.io/kind/pkg/errors" + "sigs.k8s.io/kind/pkg/log" +) + +// Build builds a node image using the supplied options +func Build(options ...Option) error { + // default options + ctx := &buildContext{ + image: DefaultImage, + baseImage: DefaultBaseImage, + logger: log.NoopLogger{}, + arch: runtime.GOARCH, + } + + // apply user options + for _, option := range options { + if err := option.apply(ctx); err != nil { + return err + } + } + + // verify that we're using a supported arch + if !supportedArch(ctx.arch) { + ctx.logger.Warnf("unsupported architecture %q", ctx.arch) + } + + // locate sources if no kubernetes source was specified + if ctx.kubeRoot == "" { + kubeRoot, err := kube.FindSource() + if err != nil { + return errors.Wrap(err, "error finding kuberoot") + } + ctx.kubeRoot = kubeRoot + } + + // initialize bits + builder, err := kube.NewDockerBuilder(ctx.logger, ctx.kubeRoot, ctx.arch) + if err != nil { + return err + } + ctx.builder = builder + + // do the actual build + return ctx.Build() +} + +func supportedArch(arch string) bool { + switch arch { + default: + return false + // currently we nominally support building node images for these + case "amd64": + case "arm64": + } + return true +} diff --git a/vendor/sigs.k8s.io/kind/pkg/build/nodeimage/buildcontext.go b/vendor/sigs.k8s.io/kind/pkg/build/nodeimage/buildcontext.go new file mode 100644 index 0000000000..3a6f3a91d0 --- /dev/null +++ b/vendor/sigs.k8s.io/kind/pkg/build/nodeimage/buildcontext.go @@ -0,0 +1,365 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +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 nodeimage + +import ( + "fmt" + "math/rand" + "os" + "path" + "strings" + "time" + + "sigs.k8s.io/kind/pkg/errors" + "sigs.k8s.io/kind/pkg/exec" + "sigs.k8s.io/kind/pkg/log" + + "sigs.k8s.io/kind/pkg/build/nodeimage/internal/container/docker" + "sigs.k8s.io/kind/pkg/build/nodeimage/internal/kube" + "sigs.k8s.io/kind/pkg/internal/sets" + "sigs.k8s.io/kind/pkg/internal/version" +) + +const ( + // httpProxy is the HTTP_PROXY environment variable key + httpProxy = "HTTP_PROXY" + // httpsProxy is the HTTPS_PROXY environment variable key + httpsProxy = "HTTPS_PROXY" + // noProxy is the NO_PROXY environment variable key + noProxy = "NO_PROXY" +) + +// buildContext is used to build the kind node image, and contains +// build configuration +type buildContext struct { + // option fields + image string + baseImage string + logger log.Logger + arch string + kubeRoot string + // non-option fields + builder kube.Builder +} + +// Build builds the cluster node image, the source dir must be set on +// the buildContext +func (c *buildContext) Build() (err error) { + // ensure kubernetes build is up-to-date first + c.logger.V(0).Info("Starting to build Kubernetes") + bits, err := c.builder.Build() + if err != nil { + c.logger.Errorf("Failed to build Kubernetes: %v", err) + return errors.Wrap(err, "failed to build kubernetes") + } + c.logger.V(0).Info("Finished building Kubernetes") + + // then perform the actual docker image build + c.logger.V(0).Info("Building node image ...") + return c.buildImage(bits) +} + +func (c *buildContext) buildImage(bits kube.Bits) error { + // create build container + // NOTE: we are using docker run + docker commit, so we can install + // debian packages without permanently copying them into the image. + // if docker gets proper squash support, we can rm them instead + // This also allows the KubeBit implementations to programmatically + // install in the image + containerID, err := c.createBuildContainer() + cmder := docker.ContainerCmder(containerID) + + // ensure we will delete it + if containerID != "" { + defer func() { + _ = exec.Command("docker", "rm", "-f", "-v", containerID).Run() + }() + } + if err != nil { + c.logger.Errorf("Image build Failed! Failed to create build container: %v", err) + return err + } + + c.logger.V(0).Info("Building in container: " + containerID) + + // copy artifacts in + for _, binary := range bits.BinaryPaths() { + // TODO: probably should be /usr/local/bin, but the existing kubelet + // service file expects /usr/bin/kubelet + nodePath := "/usr/bin/" + path.Base(binary) + if err := exec.Command("docker", "cp", binary, containerID+":"+nodePath).Run(); err != nil { + return err + } + if err := cmder.Command("chmod", "+x", nodePath).Run(); err != nil { + return err + } + if err := cmder.Command("chown", "root:root", nodePath).Run(); err != nil { + return err + } + } + + // write version + // TODO: support grabbing version from a binary instead? + // This may or may not be a good idea ... + rawVersion := bits.Version() + parsedVersion, err := version.ParseSemantic(rawVersion) + if err != nil { + return errors.Wrap(err, "invalid Kubernetes version") + } + if err := createFile(cmder, "/kind/version", rawVersion); err != nil { + return err + } + + // pre-pull images that were not part of the build and write CNI / storage + // manifests + if _, err = c.prePullImagesAndWriteManifests(bits, parsedVersion, containerID); err != nil { + c.logger.Errorf("Image build Failed! Failed to pull Images: %v", err) + return err + } + + // Save the image changes to a new image + if err = exec.Command( + "docker", "commit", + // we need to put this back after changing it when running the image + "--change", `ENTRYPOINT [ "/usr/local/bin/entrypoint", "/sbin/init" ]`, + // remove proxy settings since they're for the building process + // and should not be carried with the built image + "--change", `ENV HTTP_PROXY="" HTTPS_PROXY="" NO_PROXY=""`, + containerID, c.image, + ).Run(); err != nil { + c.logger.Errorf("Image build Failed! Failed to save image: %v", err) + return err + } + + c.logger.V(0).Infof("Image %q build completed.", c.image) + return nil +} + +// returns a set of image tags that will be side-loaded +func (c *buildContext) getBuiltImages(bits kube.Bits) (sets.String, error) { + images := sets.NewString() + for _, path := range bits.ImagePaths() { + tags, err := docker.GetArchiveTags(path) + if err != nil { + return nil, err + } + images.Insert(tags...) + } + return images, nil +} + +// must be run after kubernetes has been installed on the node +func (c *buildContext) prePullImagesAndWriteManifests(bits kube.Bits, parsedVersion *version.Version, containerID string) ([]string, error) { + // first get the images we actually built + builtImages, err := c.getBuiltImages(bits) + if err != nil { + c.logger.Errorf("Image build Failed! Failed to get built images: %v", err) + return nil, err + } + + // helpers to run things in the build container + cmder := docker.ContainerCmder(containerID) + + // For kubernetes v1.15+ (actually 1.16 alpha versions) we may need to + // drop the arch suffix from images to get the expected image + archSuffix := "-" + c.arch + fixRepository := func(repository string) string { + if strings.HasSuffix(repository, archSuffix) { + fixed := strings.TrimSuffix(repository, archSuffix) + c.logger.V(1).Info("fixed: " + repository + " -> " + fixed) + repository = fixed + } + return repository + } + + // correct set of built tags using the same logic we will use to rewrite + // the tags as we load the archives + fixedImages := sets.NewString() + fixedImagesMap := make(map[string]string, builtImages.Len()) // key: original images, value: fixed images + for _, image := range builtImages.List() { + registry, tag, err := docker.SplitImage(image) + if err != nil { + return nil, err + } + registry = fixRepository(registry) + fixedImage := registry + ":" + tag + fixedImages.Insert(fixedImage) + fixedImagesMap[image] = fixedImage + } + builtImages = fixedImages + c.logger.V(1).Info("Detected built images: " + strings.Join(builtImages.List(), ", ")) + + // gets the list of images required by kubeadm + requiredImages, err := exec.OutputLines(cmder.Command( + "kubeadm", "config", "images", "list", "--kubernetes-version", bits.Version(), + )) + if err != nil { + return nil, err + } + + // replace pause image with our own + containerdConfig, err := exec.Output(cmder.Command("cat", containerdConfigPath)) + if err != nil { + return nil, err + } + pauseImage, err := findSandboxImage(string(containerdConfig)) + if err != nil { + return nil, err + } + n := 0 + for _, image := range requiredImages { + if !strings.Contains(image, "pause") { + requiredImages[n] = image + n++ + } + } + requiredImages = append(requiredImages[:n], pauseImage) + + if parsedVersion.LessThan(version.MustParseSemantic("v1.24.0")) { + if err := configureContainerdSystemdCgroupFalse(cmder, string(containerdConfig)); err != nil { + return nil, err + } + } + + // write the default CNI manifest + if err := createFile(cmder, defaultCNIManifestLocation, defaultCNIManifest); err != nil { + c.logger.Errorf("Image build Failed! Failed write default CNI Manifest: %v", err) + return nil, err + } + // all builds should install the default CNI images from the above manifest currently + requiredImages = append(requiredImages, defaultCNIImages...) + + // write the default Storage manifest + if err := createFile(cmder, defaultStorageManifestLocation, defaultStorageManifest); err != nil { + c.logger.Errorf("Image build Failed! Failed write default Storage Manifest: %v", err) + return nil, err + } + // all builds should install the default storage driver images currently + requiredImages = append(requiredImages, defaultStorageImages...) + + // setup image importer + importer := newContainerdImporter(cmder) + if err := importer.Prepare(); err != nil { + c.logger.Errorf("Image build Failed! Failed to prepare containerd to load images %v", err) + return nil, err + } + + // TODO: return this error? + defer func() { + if err := importer.End(); err != nil { + c.logger.Errorf("Image build Failed! Failed to tear down containerd after loading images %v", err) + } + }() + + fns := []func() error{} + for _, image := range requiredImages { + image := image // https://golang.org/doc/faq#closures_and_goroutines + fns = append(fns, func() error { + if !builtImages.Has(image) { + if err = importer.Pull(image, dockerBuildOsAndArch(c.arch)); err != nil { + c.logger.Warnf("Failed to pull %s with error: %v", image, err) + runE := exec.RunErrorForError(err) + c.logger.Warn(string(runE.Output)) + } + } + return nil + }) + } + if err := errors.AggregateConcurrent(fns); err != nil { + return nil, err + } + + // create a plan of image loading + loadFns := []func() error{} + for _, image := range bits.ImagePaths() { + image := image // capture loop var + loadFns = append(loadFns, func() error { + f, err := os.Open(image) + if err != nil { + return err + } + defer f.Close() + return importer.LoadCommand().SetStdout(os.Stdout).SetStderr(os.Stderr).SetStdin(f).Run() + // we will rewrite / correct the tags in tagFns below + }) + } + + // run all image loading concurrently until one fails or all succeed + if err := errors.UntilErrorConcurrent(loadFns); err != nil { + c.logger.Errorf("Image build Failed! Failed to load images %v", err) + return nil, err + } + + // create a plan of image re-tagging + tagFns := []func() error{} + for unfixed, fixed := range fixedImagesMap { + unfixed, fixed := unfixed, fixed // capture loop var + if unfixed != fixed { + tagFns = append(tagFns, func() error { + return importer.Tag(unfixed, fixed) + }) + } + } + + // run all image re-tragging concurrently until one fails or all succeed + if err := errors.UntilErrorConcurrent(tagFns); err != nil { + c.logger.Errorf("Image build Failed! Failed to re-tag images %v", err) + return nil, err + } + + return importer.ListImported() +} + +func (c *buildContext) createBuildContainer() (id string, err error) { + // attempt to explicitly pull the image if it doesn't exist locally + // we don't care if this returns error, we'll still try to run which also pulls + _ = docker.Pull(c.logger, c.baseImage, dockerBuildOsAndArch(c.arch), 4) + // this should be good enough: a specific prefix, the current unix time, + // and a little random bits in case we have multiple builds simultaneously + random := rand.New(rand.NewSource(time.Now().UnixNano())).Int31() + id = fmt.Sprintf("kind-build-%d-%d", time.Now().UTC().Unix(), random) + runArgs := []string{ + "-d", // make the client exit while the container continues to run + // the container should hang forever, so we can exec in it + "--entrypoint=sleep", + "--name=" + id, + "--platform=" + dockerBuildOsAndArch(c.arch), + "--security-opt", "seccomp=unconfined", // ignore seccomp + } + // pass proxy settings from environment variables to the building container + // to make them work during the building process + for _, name := range []string{httpProxy, httpsProxy, noProxy} { + val := os.Getenv(name) + if val == "" { + val = os.Getenv(strings.ToLower(name)) + } + if val != "" { + runArgs = append(runArgs, "--env", name+"="+val) + } + } + err = docker.Run( + c.baseImage, + runArgs, + []string{ + "infinity", // sleep infinitely to keep the container around + }, + ) + if err != nil { + return id, errors.Wrap(err, "failed to create build container") + } + return id, nil +} diff --git a/vendor/sigs.k8s.io/kind/pkg/build/nodeimage/const.go b/vendor/sigs.k8s.io/kind/pkg/build/nodeimage/const.go new file mode 100644 index 0000000000..fd9afe6366 --- /dev/null +++ b/vendor/sigs.k8s.io/kind/pkg/build/nodeimage/const.go @@ -0,0 +1,25 @@ +/* +Copyright 2019 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +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 nodeimage + +// these are well known paths within the node image +const ( + // TODO: refactor kubernetesVersionLocation to a common internal package + kubernetesVersionLocation = "/kind/version" + defaultCNIManifestLocation = "/kind/manifests/default-cni.yaml" + defaultStorageManifestLocation = "/kind/manifests/default-storage.yaml" +) diff --git a/vendor/sigs.k8s.io/kind/pkg/build/nodeimage/const_cni.go b/vendor/sigs.k8s.io/kind/pkg/build/nodeimage/const_cni.go new file mode 100644 index 0000000000..480dc6a1d4 --- /dev/null +++ b/vendor/sigs.k8s.io/kind/pkg/build/nodeimage/const_cni.go @@ -0,0 +1,145 @@ +/* +Copyright 2019 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +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 nodeimage + +/* +The default CNI manifest and images are our own tiny kindnet +*/ + +const kindnetdImage = "docker.io/kindest/kindnetd:v20240202-8f1494ea" + +var defaultCNIImages = []string{kindnetdImage} + +// TODO: migrate to fully patching and deprecate the template +const defaultCNIManifest = ` +# kindnetd networking manifest +# would you kindly template this file +# would you kindly patch this file +--- +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: kindnet +rules: + - apiGroups: + - policy + resources: + - podsecuritypolicies + verbs: + - use + resourceNames: + - kindnet + - apiGroups: + - "" + resources: + - nodes + verbs: + - list + - watch +--- +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: kindnet +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: kindnet +subjects: +- kind: ServiceAccount + name: kindnet + namespace: kube-system +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: kindnet + namespace: kube-system +--- +apiVersion: apps/v1 +kind: DaemonSet +metadata: + name: kindnet + namespace: kube-system + labels: + tier: node + app: kindnet + k8s-app: kindnet +spec: + selector: + matchLabels: + app: kindnet + template: + metadata: + labels: + tier: node + app: kindnet + k8s-app: kindnet + spec: + hostNetwork: true + nodeSelector: + kubernetes.io/os: linux + tolerations: + - operator: Exists + serviceAccountName: kindnet + containers: + - name: kindnet-cni + image: ` + kindnetdImage + ` + env: + - name: HOST_IP + valueFrom: + fieldRef: + fieldPath: status.hostIP + - name: POD_IP + valueFrom: + fieldRef: + fieldPath: status.podIP + - name: POD_SUBNET + value: {{ .PodSubnet }} + volumeMounts: + - name: cni-cfg + mountPath: /etc/cni/net.d + - name: xtables-lock + mountPath: /run/xtables.lock + readOnly: false + - name: lib-modules + mountPath: /lib/modules + readOnly: true + resources: + requests: + cpu: "100m" + memory: "50Mi" + limits: + cpu: "100m" + memory: "50Mi" + securityContext: + privileged: false + capabilities: + add: ["NET_RAW", "NET_ADMIN"] + volumes: + - name: cni-cfg + hostPath: + path: /etc/cni/net.d + - name: xtables-lock + hostPath: + path: /run/xtables.lock + type: FileOrCreate + - name: lib-modules + hostPath: + path: /lib/modules +--- +` diff --git a/vendor/sigs.k8s.io/kind/pkg/build/nodeimage/const_storage.go b/vendor/sigs.k8s.io/kind/pkg/build/nodeimage/const_storage.go new file mode 100644 index 0000000000..a45313b833 --- /dev/null +++ b/vendor/sigs.k8s.io/kind/pkg/build/nodeimage/const_storage.go @@ -0,0 +1,180 @@ +/* +Copyright 2019 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +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 nodeimage + +/* +The default PV driver manifest and images are provisionally rancher.io/local-path-provisioner +NOTE: we have customized it in the following ways: +- storage is under /var instead of /opt +- our own image and helper image +- schedule to linux nodes only +- install as the default storage class +*/ + +const storageProvisionerImage = "docker.io/kindest/local-path-provisioner:v20240202-8f1494ea" +const storageHelperImage = "docker.io/kindest/local-path-helper:v20230510-486859a6" + +// image we need to preload +var defaultStorageImages = []string{storageProvisionerImage, storageHelperImage} + +const defaultStorageManifest = ` +apiVersion: v1 +kind: Namespace +metadata: + name: local-path-storage + +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: local-path-provisioner-service-account + namespace: local-path-storage + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: local-path-provisioner-role +rules: + - apiGroups: [ "" ] + resources: [ "nodes", "persistentvolumeclaims", "configmaps" ] + verbs: [ "get", "list", "watch" ] + - apiGroups: [ "" ] + resources: [ "endpoints", "persistentvolumes", "pods" ] + verbs: [ "*" ] + - apiGroups: [ "" ] + resources: [ "events" ] + verbs: [ "create", "patch" ] + - apiGroups: [ "storage.k8s.io" ] + resources: [ "storageclasses" ] + verbs: [ "get", "list", "watch" ] + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: local-path-provisioner-bind +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: local-path-provisioner-role +subjects: + - kind: ServiceAccount + name: local-path-provisioner-service-account + namespace: local-path-storage + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: local-path-provisioner + namespace: local-path-storage +spec: + replicas: 1 + selector: + matchLabels: + app: local-path-provisioner + template: + metadata: + labels: + app: local-path-provisioner + spec: + nodeSelector: + kubernetes.io/os: linux + # TODO: Remove the "master" taint after kubeadm nodes no longer have it. + # This can be done once kind no longer supports kubeadm 1.24. + # https://github.com/kubernetes-sigs/kind/issues/1699 + tolerations: + - key: node-role.kubernetes.io/control-plane + operator: Equal + effect: NoSchedule + - key: node-role.kubernetes.io/master + operator: Equal + effect: NoSchedule + serviceAccountName: local-path-provisioner-service-account + containers: + - name: local-path-provisioner + image: ` + storageProvisionerImage + ` + imagePullPolicy: IfNotPresent + command: + - local-path-provisioner + - --debug + - start + - --helper-image + - ` + storageHelperImage + ` + - --config + - /etc/config/config.json + volumeMounts: + - name: config-volume + mountPath: /etc/config/ + env: + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + volumes: + - name: config-volume + configMap: + name: local-path-config + +--- +apiVersion: storage.k8s.io/v1 +kind: StorageClass +metadata: + name: standard + namespace: kube-system + annotations: + storageclass.kubernetes.io/is-default-class: "true" +provisioner: rancher.io/local-path +volumeBindingMode: WaitForFirstConsumer +reclaimPolicy: Delete + +--- +kind: ConfigMap +apiVersion: v1 +metadata: + name: local-path-config + namespace: local-path-storage +data: + config.json: |- + { + "nodePathMap":[ + { + "node":"DEFAULT_PATH_FOR_NON_LISTED_NODES", + "paths":["/var/local-path-provisioner"] + } + ] + } + setup: |- + #!/bin/sh + set -eu + mkdir -m 0777 -p "$VOL_DIR" + teardown: |- + #!/bin/sh + set -eu + rm -rf "$VOL_DIR" + helperPod.yaml: |- + apiVersion: v1 + kind: Pod + metadata: + name: helper-pod + spec: + containers: + - name: helper-pod + image: ` + storageHelperImage + ` + imagePullPolicy: IfNotPresent +` diff --git a/vendor/sigs.k8s.io/kind/pkg/build/nodeimage/containerd.go b/vendor/sigs.k8s.io/kind/pkg/build/nodeimage/containerd.go new file mode 100644 index 0000000000..820af82c95 --- /dev/null +++ b/vendor/sigs.k8s.io/kind/pkg/build/nodeimage/containerd.go @@ -0,0 +1,50 @@ +/* +Copyright 2022 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +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 nodeimage + +import ( + "strings" + + "sigs.k8s.io/kind/pkg/errors" + "sigs.k8s.io/kind/pkg/exec" + + "sigs.k8s.io/kind/pkg/internal/patch" +) + +const containerdConfigPath = "/etc/containerd/config.toml" + +const containerdConfigPatchSystemdCgroupFalse = ` +[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options] + SystemdCgroup = false + +[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.test-handler.options] + SystemdCgroup = false +` + +func configureContainerdSystemdCgroupFalse(containerCmdr exec.Cmder, config string) error { + patched, err := patch.TOML(config, []string{containerdConfigPatchSystemdCgroupFalse}, []string{}) + if err != nil { + return errors.Wrap(err, "failed to configure containerd SystemdCgroup=false") + } + err = containerCmdr.Command( + "cp", "/dev/stdin", containerdConfigPath, + ).SetStdin(strings.NewReader(patched)).Run() + if err != nil { + return errors.Wrap(err, "failed to configure containerd SystemdCgroup=false") + } + return nil +} diff --git a/vendor/sigs.k8s.io/kind/pkg/build/nodeimage/defaults.go b/vendor/sigs.k8s.io/kind/pkg/build/nodeimage/defaults.go new file mode 100644 index 0000000000..5e8d089607 --- /dev/null +++ b/vendor/sigs.k8s.io/kind/pkg/build/nodeimage/defaults.go @@ -0,0 +1,25 @@ +/* +Copyright 2020 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +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 nodeimage + +// DefaultImage is the default name:tag for the built image +const DefaultImage = "kindest/node:latest" + +// DefaultBaseImage is the default base image used +// TODO: come up with a reasonable solution to digest pinning +// https://github.com/moby/moby/issues/43188 +const DefaultBaseImage = "docker.io/kindest/base:v20240212-c4cadcab" diff --git a/vendor/sigs.k8s.io/kind/pkg/build/nodeimage/doc.go b/vendor/sigs.k8s.io/kind/pkg/build/nodeimage/doc.go new file mode 100644 index 0000000000..5ae7d8ed52 --- /dev/null +++ b/vendor/sigs.k8s.io/kind/pkg/build/nodeimage/doc.go @@ -0,0 +1,18 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +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 nodeimage implements functionality to build the kind node image +package nodeimage diff --git a/vendor/sigs.k8s.io/kind/pkg/build/nodeimage/helpers.go b/vendor/sigs.k8s.io/kind/pkg/build/nodeimage/helpers.go new file mode 100644 index 0000000000..4e7e3dbd71 --- /dev/null +++ b/vendor/sigs.k8s.io/kind/pkg/build/nodeimage/helpers.go @@ -0,0 +1,55 @@ +/* +Copyright 2020 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +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 nodeimage + +import ( + "path" + "regexp" + "strings" + + "sigs.k8s.io/kind/pkg/errors" + "sigs.k8s.io/kind/pkg/exec" +) + +// createFile creates the file at filePath in the container, +// ensuring the directory exists and writing contents to the file +func createFile(containerCmder exec.Cmder, filePath, contents string) error { + // NOTE: the paths inside the container should use the path package + // and not filepath (!), we want posixy paths in the linux container, NOT + // whatever path format the host uses. For paths on the host we use filepath + if err := containerCmder.Command("mkdir", "-p", path.Dir(filePath)).Run(); err != nil { + return err + } + + return containerCmder.Command( + "cp", "/dev/stdin", filePath, + ).SetStdin( + strings.NewReader(contents), + ).Run() +} + +func findSandboxImage(config string) (string, error) { + match := regexp.MustCompile(`sandbox_image\s+=\s+"([^\n]+)"`).FindStringSubmatch(config) + if len(match) < 2 { + return "", errors.New("failed to parse sandbox_image from config") + } + return match[1], nil +} + +func dockerBuildOsAndArch(arch string) string { + return "linux/" + arch +} diff --git a/vendor/sigs.k8s.io/kind/pkg/build/nodeimage/imageimporter.go b/vendor/sigs.k8s.io/kind/pkg/build/nodeimage/imageimporter.go new file mode 100644 index 0000000000..7839263fe7 --- /dev/null +++ b/vendor/sigs.k8s.io/kind/pkg/build/nodeimage/imageimporter.go @@ -0,0 +1,70 @@ +/* +Copyright 2019 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +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 nodeimage + +import ( + "io" + + "sigs.k8s.io/kind/pkg/exec" +) + +type containerdImporter struct { + containerCmder exec.Cmder +} + +func newContainerdImporter(containerCmder exec.Cmder) *containerdImporter { + return &containerdImporter{ + containerCmder: containerCmder, + } +} + +func (c *containerdImporter) Prepare() error { + if err := c.containerCmder.Command( + "bash", "-c", "nohup containerd > /dev/null 2>&1 &", + ).Run(); err != nil { + return err + } + // TODO(bentheelder): some healthcheck? + return nil +} + +func (c *containerdImporter) End() error { + return c.containerCmder.Command("pkill", "containerd").Run() +} + +func (c *containerdImporter) Pull(image, platform string) error { + return c.containerCmder.Command( + "ctr", "--namespace=k8s.io", "content", "fetch", "--platform="+platform, image, + ).SetStdout(io.Discard).SetStderr(io.Discard).Run() +} + +func (c *containerdImporter) LoadCommand() exec.Cmd { + return c.containerCmder.Command( + // TODO: ideally we do not need this in the future. we have fixed at least one image + "ctr", "--namespace=k8s.io", "images", "import", "--label=io.cri-containerd.pinned=pinned", "--all-platforms", "--no-unpack", "--digests", "-", + ) +} + +func (c *containerdImporter) Tag(src, target string) error { + return c.containerCmder.Command( + "ctr", "--namespace=k8s.io", "images", "tag", "--force", src, target, + ).Run() +} + +func (c *containerdImporter) ListImported() ([]string, error) { + return exec.OutputLines(c.containerCmder.Command("ctr", "--namespace=k8s.io", "images", "list", "-q")) +} diff --git a/vendor/sigs.k8s.io/kind/pkg/build/nodeimage/internal/container/docker/archive.go b/vendor/sigs.k8s.io/kind/pkg/build/nodeimage/internal/container/docker/archive.go new file mode 100644 index 0000000000..0006552050 --- /dev/null +++ b/vendor/sigs.k8s.io/kind/pkg/build/nodeimage/internal/container/docker/archive.go @@ -0,0 +1,117 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +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 docker contains helpers for working with docker +// This package has no stability guarantees whatsoever! +package docker + +import ( + "archive/tar" + "encoding/json" + "fmt" + "io" + "os" + + "sigs.k8s.io/kind/pkg/errors" +) + +// GetArchiveTags obtains a list of "repo:tag" docker image tags from a +// given docker image archive (tarball) path +// compatible with all known specs: +// https://github.com/moby/moby/blob/master/image/spec/v1.md +// https://github.com/moby/moby/blob/master/image/spec/v1.1.md +// https://github.com/moby/moby/blob/master/image/spec/v1.2.md +func GetArchiveTags(path string) ([]string, error) { + // open the archive and find the repositories entry + f, err := os.Open(path) + if err != nil { + return nil, err + } + defer f.Close() + tr := tar.NewReader(f) + var hdr *tar.Header + for { + hdr, err = tr.Next() + if err == io.EOF { + return nil, errors.New("could not find image metadata") + } + if err != nil { + return nil, err + } + if hdr.Name == "manifest.json" || hdr.Name == "repositories" { + break + } + } + // read and parse the tags + b, err := io.ReadAll(tr) + if err != nil { + return nil, err + } + res := []string{} + // parse + if hdr.Name == "repositories" { + repoTags, err := parseRepositories(b) + if err != nil { + return nil, err + } + // convert to tags in the docker CLI sense + for repo, tags := range repoTags { + for tag := range tags { + res = append(res, fmt.Sprintf("%s:%s", repo, tag)) + } + } + } else if hdr.Name == "manifest.json" { + manifest, err := parseDockerV1Manifest(b) + if err != nil { + return nil, err + } + res = append(res, manifest[0].RepoTags...) + } + return res, nil +} + +// archiveRepositories represents repository:tag:ref +// +// https://github.com/moby/moby/blob/master/image/spec/v1.md +// https://github.com/moby/moby/blob/master/image/spec/v1.1.md +// https://github.com/moby/moby/blob/master/image/spec/v1.2.md +type archiveRepositories map[string]map[string]string + +// https://github.com/moby/moby/blob/master/image/spec/v1.2.md#combined-image-json--filesystem-changeset-format +type metadataEntry struct { + Config string `json:"Config"` + RepoTags []string `json:"RepoTags"` + Layers []string `json:"Layers"` +} + +// returns repository:tag:ref +func parseRepositories(data []byte) (archiveRepositories, error) { + var repoTags archiveRepositories + if err := json.Unmarshal(data, &repoTags); err != nil { + return nil, err + } + return repoTags, nil +} + +// parseDockerV1Manifest parses Docker Image Spec v1 manifest (not OCI Image Spec manifest) +// https://github.com/moby/moby/blob/v20.10.22/image/spec/v1.2.md#combined-image-json--filesystem-changeset-format +func parseDockerV1Manifest(data []byte) ([]metadataEntry, error) { + var entries []metadataEntry + if err := json.Unmarshal(data, &entries); err != nil { + return nil, err + } + return entries, nil +} diff --git a/vendor/sigs.k8s.io/kind/pkg/build/nodeimage/internal/container/docker/doc.go b/vendor/sigs.k8s.io/kind/pkg/build/nodeimage/internal/container/docker/doc.go new file mode 100644 index 0000000000..a4a2b89325 --- /dev/null +++ b/vendor/sigs.k8s.io/kind/pkg/build/nodeimage/internal/container/docker/doc.go @@ -0,0 +1,19 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +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 docker contains helpers for working with docker +// This package has no stability guarantees whatsoever! +package docker diff --git a/vendor/sigs.k8s.io/kind/pkg/build/nodeimage/internal/container/docker/exec.go b/vendor/sigs.k8s.io/kind/pkg/build/nodeimage/internal/container/docker/exec.go new file mode 100644 index 0000000000..346e201234 --- /dev/null +++ b/vendor/sigs.k8s.io/kind/pkg/build/nodeimage/internal/container/docker/exec.go @@ -0,0 +1,132 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +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 docker + +import ( + "context" + "io" + + "sigs.k8s.io/kind/pkg/exec" +) + +// containerCmder implements exec.Cmder for docker containers +type containerCmder struct { + nameOrID string +} + +// ContainerCmder creates a new exec.Cmder against a docker container +func ContainerCmder(containerNameOrID string) exec.Cmder { + return &containerCmder{ + nameOrID: containerNameOrID, + } +} + +func (c *containerCmder) Command(command string, args ...string) exec.Cmd { + return &containerCmd{ + nameOrID: c.nameOrID, + command: command, + args: args, + } +} + +func (c *containerCmder) CommandContext(ctx context.Context, command string, args ...string) exec.Cmd { + return &containerCmd{ + nameOrID: c.nameOrID, + command: command, + args: args, + ctx: ctx, + } +} + +// containerCmd implements exec.Cmd for docker containers +type containerCmd struct { + nameOrID string // the container name or ID + command string + args []string + env []string + stdin io.Reader + stdout io.Writer + stderr io.Writer + ctx context.Context +} + +func (c *containerCmd) Run() error { + args := []string{ + "exec", + // run with privileges so we can remount etc.. + // this might not make sense in the most general sense, but it is + // important to many kind commands + "--privileged", + } + if c.stdin != nil { + args = append(args, + "-i", // interactive so we can supply input + ) + } + // set env + for _, env := range c.env { + args = append(args, "-e", env) + } + // specify the container and command, after this everything will be + // args the command in the container rather than to docker + args = append( + args, + c.nameOrID, // ... against the container + c.command, // with the command specified + ) + args = append( + args, + // finally, with the caller args + c.args..., + ) + var cmd exec.Cmd + if c.ctx != nil { + cmd = exec.CommandContext(c.ctx, "docker", args...) + } else { + cmd = exec.Command("docker", args...) + } + if c.stdin != nil { + cmd.SetStdin(c.stdin) + } + if c.stderr != nil { + cmd.SetStderr(c.stderr) + } + if c.stdout != nil { + cmd.SetStdout(c.stdout) + } + return cmd.Run() +} + +func (c *containerCmd) SetEnv(env ...string) exec.Cmd { + c.env = env + return c +} + +func (c *containerCmd) SetStdin(r io.Reader) exec.Cmd { + c.stdin = r + return c +} + +func (c *containerCmd) SetStdout(w io.Writer) exec.Cmd { + c.stdout = w + return c +} + +func (c *containerCmd) SetStderr(w io.Writer) exec.Cmd { + c.stderr = w + return c +} diff --git a/vendor/sigs.k8s.io/kind/pkg/build/nodeimage/internal/container/docker/image.go b/vendor/sigs.k8s.io/kind/pkg/build/nodeimage/internal/container/docker/image.go new file mode 100644 index 0000000000..767fb4dc24 --- /dev/null +++ b/vendor/sigs.k8s.io/kind/pkg/build/nodeimage/internal/container/docker/image.go @@ -0,0 +1,86 @@ +/* +Copyright 2019 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +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 docker + +import ( + "fmt" + "strings" + + "sigs.k8s.io/kind/pkg/errors" + "sigs.k8s.io/kind/pkg/exec" +) + +// SplitImage splits an image into (registry,tag) following these cases: +// +// alpine -> (alpine, latest) +// +// alpine:latest -> (alpine, latest) +// +// alpine@sha256:28ef97b8686a0b5399129e9b763d5b7e5ff03576aa5580d6f4182a49c5fe1913 -> (alpine, latest@sha256:28ef97b8686a0b5399129e9b763d5b7e5ff03576aa5580d6f4182a49c5fe1913) +// +// alpine:latest@sha256:28ef97b8686a0b5399129e9b763d5b7e5ff03576aa5580d6f4182a49c5fe1913 -> (alpine, latest@sha256:28ef97b8686a0b5399129e9b763d5b7e5ff03576aa5580d6f4182a49c5fe1913) +// +// NOTE: for our purposes we consider the sha to be part of the tag, and we +// resolve the implicit :latest +func SplitImage(image string) (registry, tag string, err error) { + // we are looking for ':' and '@' + firstColon := strings.IndexByte(image, 58) + firstAt := strings.IndexByte(image, 64) + + // there should be a registry before the tag, and @/: should not be the last + // character, these cases are assumed not to exist by the rest of the code + if firstColon == 0 || firstAt == 0 || firstColon+1 == len(image) || firstAt+1 == len(image) { + return "", "", fmt.Errorf("unexpected image: %q", image) + } + + // NOTE: The order of these cases matters + // case: alpine + if firstColon == -1 && firstAt == -1 { + return image, "latest", nil + } + + // case: alpine@sha256:28ef97b8686a0b5399129e9b763d5b7e5ff03576aa5580d6f4182a49c5fe1913 + if firstAt != -1 && firstAt < firstColon { + return image[:firstAt], "latest" + image[firstAt:], nil + } + + // case: alpine:latest + // case: alpine:latest@sha256:28ef97b8686a0b5399129e9b763d5b7e5ff03576aa5580d6f4182a49c5fe1913 + return image[:firstColon], image[firstColon+1:], nil +} + +// ImageInspect return low-level information on containers images +func ImageInspect(containerNameOrID, format string) ([]string, error) { + cmd := exec.Command("docker", "image", "inspect", + "-f", format, + containerNameOrID, // ... against the container + ) + + return exec.OutputLines(cmd) +} + +// ImageID return the Id of the container image +func ImageID(containerNameOrID string) (string, error) { + lines, err := ImageInspect(containerNameOrID, "{{ .Id }}") + if err != nil { + return "", err + } + if len(lines) != 1 { + return "", errors.Errorf("Docker image ID should only be one line, got %d lines", len(lines)) + } + return lines[0], nil +} diff --git a/vendor/sigs.k8s.io/kind/pkg/build/nodeimage/internal/container/docker/pull.go b/vendor/sigs.k8s.io/kind/pkg/build/nodeimage/internal/container/docker/pull.go new file mode 100644 index 0000000000..0ac49de6b4 --- /dev/null +++ b/vendor/sigs.k8s.io/kind/pkg/build/nodeimage/internal/container/docker/pull.go @@ -0,0 +1,43 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +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 docker + +import ( + "time" + + "sigs.k8s.io/kind/pkg/exec" + "sigs.k8s.io/kind/pkg/log" +) + +// Pull pulls an image, retrying up to retries times +func Pull(logger log.Logger, image string, platform string, retries int) error { + logger.V(1).Infof("Pulling image: %s for platform %s ...", image, platform) + err := exec.Command("docker", "pull", "--platform="+platform, image).Run() + // retry pulling up to retries times if necessary + if err != nil { + for i := 0; i < retries; i++ { + time.Sleep(time.Second * time.Duration(i+1)) + logger.V(1).Infof("Trying again to pull image: %q ... %v", image, err) + // TODO(bentheelder): add some backoff / sleep? + err = exec.Command("docker", "pull", "--platform="+platform, image).Run() + if err == nil { + break + } + } + } + return err +} diff --git a/vendor/sigs.k8s.io/kind/pkg/build/nodeimage/internal/container/docker/run.go b/vendor/sigs.k8s.io/kind/pkg/build/nodeimage/internal/container/docker/run.go new file mode 100644 index 0000000000..54e8a42b4e --- /dev/null +++ b/vendor/sigs.k8s.io/kind/pkg/build/nodeimage/internal/container/docker/run.go @@ -0,0 +1,32 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +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 docker + +import ( + "sigs.k8s.io/kind/pkg/exec" +) + +// Run creates a container with "docker run", with some error handling +func Run(image string, runArgs []string, containerArgs []string) error { + // construct the actual docker run argv + args := []string{"run"} + args = append(args, runArgs...) + args = append(args, image) + args = append(args, containerArgs...) + cmd := exec.Command("docker", args...) + return cmd.Run() +} diff --git a/vendor/sigs.k8s.io/kind/pkg/build/nodeimage/internal/container/docker/save.go b/vendor/sigs.k8s.io/kind/pkg/build/nodeimage/internal/container/docker/save.go new file mode 100644 index 0000000000..ded4737756 --- /dev/null +++ b/vendor/sigs.k8s.io/kind/pkg/build/nodeimage/internal/container/docker/save.go @@ -0,0 +1,26 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +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 docker + +import ( + "sigs.k8s.io/kind/pkg/exec" +) + +// Save saves image to dest, as in `docker save` +func Save(image, dest string) error { + return exec.Command("docker", "save", "-o", dest, image).Run() +} diff --git a/vendor/sigs.k8s.io/kind/pkg/build/nodeimage/internal/kube/bits.go b/vendor/sigs.k8s.io/kind/pkg/build/nodeimage/internal/kube/bits.go new file mode 100644 index 0000000000..427b0740e9 --- /dev/null +++ b/vendor/sigs.k8s.io/kind/pkg/build/nodeimage/internal/kube/bits.go @@ -0,0 +1,54 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +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 kube + +// Bits provides the locations of Kubernetes Binaries / Images +// needed on the cluster nodes +// Implementations should be registered with RegisterNamedBits +type Bits interface { + // BinaryPaths returns a list of paths to binaries on the host machine that + // should be added to PATH in the Node image + BinaryPaths() []string + // ImagePaths returns a list of paths to image archives to be loaded into + // the Node + ImagePaths() []string + // Version + Version() string +} + +// shared real bits implementation for now + +type bits struct { + // computed at build time + binaryPaths []string + imagePaths []string + version string +} + +var _ Bits = &bits{} + +func (b *bits) BinaryPaths() []string { + return b.binaryPaths +} + +func (b *bits) ImagePaths() []string { + return b.imagePaths +} + +func (b *bits) Version() string { + return b.version +} diff --git a/vendor/sigs.k8s.io/kind/pkg/build/nodeimage/internal/kube/builder.go b/vendor/sigs.k8s.io/kind/pkg/build/nodeimage/internal/kube/builder.go new file mode 100644 index 0000000000..ffe7549191 --- /dev/null +++ b/vendor/sigs.k8s.io/kind/pkg/build/nodeimage/internal/kube/builder.go @@ -0,0 +1,26 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +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 kube + +// Builder represents and implementation of building Kubernetes +// building may constitute downloading a release +type Builder interface { + // Build returns a Bits and any errors encountered while building Kubernetes. + // Some implementations (upstream binaries) may use this step to obtain + // an existing build instead + Build() (Bits, error) +} diff --git a/vendor/sigs.k8s.io/kind/pkg/build/nodeimage/internal/kube/builder_docker.go b/vendor/sigs.k8s.io/kind/pkg/build/nodeimage/internal/kube/builder_docker.go new file mode 100644 index 0000000000..7ff9348dca --- /dev/null +++ b/vendor/sigs.k8s.io/kind/pkg/build/nodeimage/internal/kube/builder_docker.go @@ -0,0 +1,162 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +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 kube + +import ( + "os" + "path/filepath" + "strings" + + "sigs.k8s.io/kind/pkg/errors" + "sigs.k8s.io/kind/pkg/exec" + "sigs.k8s.io/kind/pkg/log" + + "sigs.k8s.io/kind/pkg/internal/version" +) + +// TODO(bentheelder): plumb through arch + +// dockerBuilder implements Bits for a local docker-ized make / bash build +type dockerBuilder struct { + kubeRoot string + arch string + logger log.Logger +} + +var _ Builder = &dockerBuilder{} + +// NewDockerBuilder returns a new Bits backed by the docker-ized build, +// given kubeRoot, the path to the kubernetes source directory +func NewDockerBuilder(logger log.Logger, kubeRoot, arch string) (Builder, error) { + return &dockerBuilder{ + kubeRoot: kubeRoot, + arch: arch, + logger: logger, + }, nil +} + +// Build implements Bits.Build +func (b *dockerBuilder) Build() (Bits, error) { + // cd to k8s source + cwd, err := os.Getwd() + if err != nil { + return nil, err + } + // make sure we cd back when done + defer func() { + // TODO(bentheelder): set return error? + _ = os.Chdir(cwd) + }() + if err := os.Chdir(b.kubeRoot); err != nil { + return nil, err + } + + // capture version info + sourceVersionRaw, err := sourceVersion(b.kubeRoot) + if err != nil { + return nil, err + } + + kubeVersion, err := version.ParseSemantic(sourceVersionRaw) + if err != nil { + return nil, errors.Wrap(err, "failed to parse source version") + } + + makeVars := []string{ + // ensure the build isn't especially noisy.. + "KUBE_VERBOSE=0", + // we don't want to build these images as we don't use them ... + "KUBE_BUILD_HYPERKUBE=n", + "KUBE_BUILD_CONFORMANCE=n", + // build for the host platform + "KUBE_BUILD_PLATFORMS=" + dockerBuildOsAndArch(b.arch), + } + + // we will pass through the environment variables, prepending defaults + // NOTE: if env are specified multiple times the last one wins + // NOTE: currently there are no defaults so this is essentially a deep copy + env := append([]string{}, os.Environ()...) + // binaries we want to build + what := []string{ + // binaries we use directly + "cmd/kubeadm", + "cmd/kubectl", + "cmd/kubelet", + } + + // build images + binaries (binaries only on 1.21+) + cmd := exec.Command("make", + append( + []string{ + "quick-release-images", + "KUBE_EXTRA_WHAT=" + strings.Join(what, " "), + }, + makeVars..., + )..., + ).SetEnv(env...) + exec.InheritOutput(cmd) + if err := cmd.Run(); err != nil { + return nil, errors.Wrap(err, "failed to build images") + } + + // KUBE_EXTRA_WHAT added in this commit + // https://github.com/kubernetes/kubernetes/commit/35061acc28a666569fdd4d1c8a7693e3c01e14be + if kubeVersion.LessThan(version.MustParseSemantic("v1.21.0-beta.1.153+35061acc28a666")) { + // on older versions we still need to build binaries separately + cmd = exec.Command( + "build/run.sh", + append( + []string{ + "make", + "all", + "WHAT=" + strings.Join(what, " "), + }, + makeVars..., + )..., + ).SetEnv(env...) + exec.InheritOutput(cmd) + if err := cmd.Run(); err != nil { + return nil, errors.Wrap(err, "failed to build binaries") + } + } + + binDir := filepath.Join(b.kubeRoot, + "_output", "dockerized", "bin", "linux", b.arch, + ) + imageDir := filepath.Join(b.kubeRoot, + "_output", "release-images", b.arch, + ) + + return &bits{ + binaryPaths: []string{ + filepath.Join(binDir, "kubeadm"), + filepath.Join(binDir, "kubelet"), + filepath.Join(binDir, "kubectl"), + }, + imagePaths: []string{ + filepath.Join(imageDir, "kube-apiserver.tar"), + filepath.Join(imageDir, "kube-controller-manager.tar"), + filepath.Join(imageDir, "kube-scheduler.tar"), + filepath.Join(imageDir, "kube-proxy.tar"), + }, + version: sourceVersionRaw, + }, nil +} + +func dockerBuildOsAndArch(arch string) string { + return "linux/" + arch +} diff --git a/vendor/sigs.k8s.io/kind/pkg/build/nodeimage/internal/kube/doc.go b/vendor/sigs.k8s.io/kind/pkg/build/nodeimage/internal/kube/doc.go new file mode 100644 index 0000000000..507301cada --- /dev/null +++ b/vendor/sigs.k8s.io/kind/pkg/build/nodeimage/internal/kube/doc.go @@ -0,0 +1,19 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +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 kube implements functionality to build Kubernetes for the purposes +// of installing into the kind node image +package kube diff --git a/vendor/sigs.k8s.io/kind/pkg/build/nodeimage/internal/kube/source.go b/vendor/sigs.k8s.io/kind/pkg/build/nodeimage/internal/kube/source.go new file mode 100644 index 0000000000..fb0d9c7d8b --- /dev/null +++ b/vendor/sigs.k8s.io/kind/pkg/build/nodeimage/internal/kube/source.go @@ -0,0 +1,104 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +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 kube + +import ( + "fmt" + "go/build" + "os" + "path/filepath" + "strings" + + "github.com/alessio/shellescape" + + "sigs.k8s.io/kind/pkg/errors" + "sigs.k8s.io/kind/pkg/exec" +) + +// FindSource attempts to locate a kubernetes checkout using go's build package +func FindSource() (root string, err error) { + // check current working directory first + wd, err := os.Getwd() + if err != nil { + return "", fmt.Errorf("failed to locate kubernetes source, could not current working directory: %w", err) + } + if probablyKubeDir(wd) { + return wd, nil + } + // then look under GOPATH + gopath := build.Default.GOPATH + if gopath == "" { + return "", errors.New("could not find Kubernetes source under current working directory and GOPATH is not set") + } + // try k8s.io/kubernetes first (old canonical GOPATH locaation) + if dir := filepath.Join(gopath, "src", "k8s.io", "kubernetes"); probablyKubeDir(dir) { + return dir, nil + } + // then try github.com/kubernetes/kubernetes (CI without path_alias set) + if dir := filepath.Join(gopath, "src", "github.com", "kubernetes", "kubernetes"); probablyKubeDir(dir) { + return dir, nil + } + return "", fmt.Errorf("could not find Kubernetes source under current working directory or GOPATH=%s", build.Default.GOPATH) +} + +// probablyKubeDir returns true if the dir looks plausibly like a kubernetes +// source directory +func probablyKubeDir(dir string) bool { + // TODO: should we do more checks? + // NOTE: go.mod with this line has existed since Kubernetes 1.15 + const sentinelLine = "module k8s.io/kubernetes" + contents, err := os.ReadFile(filepath.Join(dir, "go.mod")) + if err != nil { + return false + } + return strings.Contains(string(contents), sentinelLine) +} + +// sourceVersion the kubernetes git version based on hack/print-workspace-status.sh +// the raw version is also returned +func sourceVersion(kubeRoot string) (string, error) { + // get the version output + cmd := exec.Command( + "sh", "-c", + fmt.Sprintf( + "cd %s && hack/print-workspace-status.sh", + shellescape.Quote(kubeRoot), + ), + ) + output, err := exec.OutputLines(cmd) + if err != nil { + return "", err + } + + // parse it, and populate it into _output/git_version + version := "" + for _, line := range output { + parts := strings.SplitN(line, " ", 2) + if len(parts) != 2 { + return "", errors.Errorf("could not parse kubernetes version: %q", strings.Join(output, "\n")) + } + if parts[0] == "gitVersion" { + version = parts[1] + return version, nil + } + } + if version == "" { + return "", errors.Errorf("could not obtain kubernetes version: %q", strings.Join(output, "\n")) + + } + return "", errors.Errorf("could not find kubernetes version in output: %q", strings.Join(output, "\n")) +} diff --git a/vendor/sigs.k8s.io/kind/pkg/build/nodeimage/options.go b/vendor/sigs.k8s.io/kind/pkg/build/nodeimage/options.go new file mode 100644 index 0000000000..88906e25a5 --- /dev/null +++ b/vendor/sigs.k8s.io/kind/pkg/build/nodeimage/options.go @@ -0,0 +1,74 @@ +/* +Copyright 2020 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +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 nodeimage + +import ( + "sigs.k8s.io/kind/pkg/log" +) + +// Option is a configuration option supplied to Build +type Option interface { + apply(*buildContext) error +} + +type optionAdapter func(*buildContext) error + +func (c optionAdapter) apply(o *buildContext) error { + return c(o) +} + +// WithImage configures a build to tag the built image with `image` +func WithImage(image string) Option { + return optionAdapter(func(b *buildContext) error { + b.image = image + return nil + }) +} + +// WithBaseImage configures a build to use `image` as the base image +func WithBaseImage(image string) Option { + return optionAdapter(func(b *buildContext) error { + b.baseImage = image + return nil + }) +} + +// WithKuberoot sets the path to the Kubernetes source directory (if empty, the path will be autodetected) +func WithKuberoot(root string) Option { + return optionAdapter(func(b *buildContext) error { + b.kubeRoot = root + return nil + }) +} + +// WithLogger sets the logger +func WithLogger(logger log.Logger) Option { + return optionAdapter(func(b *buildContext) error { + b.logger = logger + return nil + }) +} + +// WithArch sets the architecture to build for +func WithArch(arch string) Option { + return optionAdapter(func(b *buildContext) error { + if arch != "" { + b.arch = arch + } + return nil + }) +} diff --git a/vendor/sigs.k8s.io/kind/pkg/cmd/kind/build/build.go b/vendor/sigs.k8s.io/kind/pkg/cmd/kind/build/build.go new file mode 100644 index 0000000000..1c62df609c --- /dev/null +++ b/vendor/sigs.k8s.io/kind/pkg/cmd/kind/build/build.go @@ -0,0 +1,49 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +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 build implements the `build` command +package build + +import ( + "errors" + + "github.com/spf13/cobra" + + "sigs.k8s.io/kind/pkg/cmd" + "sigs.k8s.io/kind/pkg/cmd/kind/build/nodeimage" + "sigs.k8s.io/kind/pkg/log" +) + +// NewCommand returns a new cobra.Command for building +func NewCommand(logger log.Logger, streams cmd.IOStreams) *cobra.Command { + cmd := &cobra.Command{ + Args: cobra.NoArgs, + // TODO(bentheelder): more detailed usage + Use: "build", + Short: "Build one of [node-image]", + Long: "Build one of [node-image]", + RunE: func(cmd *cobra.Command, args []string) error { + err := cmd.Help() + if err != nil { + return err + } + return errors.New("Subcommand is required") + }, + } + // add subcommands + cmd.AddCommand(nodeimage.NewCommand(logger, streams)) + return cmd +} diff --git a/vendor/sigs.k8s.io/kind/pkg/cmd/kind/build/nodeimage/nodeimage.go b/vendor/sigs.k8s.io/kind/pkg/cmd/kind/build/nodeimage/nodeimage.go new file mode 100644 index 0000000000..91211adf34 --- /dev/null +++ b/vendor/sigs.k8s.io/kind/pkg/cmd/kind/build/nodeimage/nodeimage.go @@ -0,0 +1,107 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +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 nodeimage + +import ( + "github.com/spf13/cobra" + + "sigs.k8s.io/kind/pkg/build/nodeimage" + "sigs.k8s.io/kind/pkg/cmd" + "sigs.k8s.io/kind/pkg/errors" + "sigs.k8s.io/kind/pkg/log" +) + +type flagpole struct { + Source string + BuildType string + Image string + BaseImage string + KubeRoot string + Arch string +} + +// NewCommand returns a new cobra.Command for building the node image +func NewCommand(logger log.Logger, streams cmd.IOStreams) *cobra.Command { + flags := &flagpole{} + cmd := &cobra.Command{ + Args: cobra.MaximumNArgs(1), + // TODO(bentheelder): more detailed usage + Use: "node-image [kubernetes-source]", + Short: "Build the node image", + Long: "Build the node image which contains Kubernetes build artifacts and other kind requirements", + RunE: func(cmd *cobra.Command, args []string) error { + if cmd.Flags().Lookup("kube-root").Changed { + if len(args) != 0 { + return errors.New("passing an argument and deprecated --kube-root is not supported, please switch to just the argument") + } + logger.Warn("--kube-root is deprecated, please switch to passing this as an argument") + } + if cmd.Flags().Lookup("type").Changed { + return errors.New("--type is no longer supported, please remove this flag") + } + return runE(logger, flags, args) + }, + } + cmd.Flags().StringVar( + &flags.BuildType, + "type", + "docker", + "build type, default is docker", + ) + cmd.Flags().StringVar( + &flags.Image, + "image", + nodeimage.DefaultImage, + "name:tag of the resulting image to be built", + ) + cmd.Flags().StringVar( + &flags.KubeRoot, + "kube-root", + "", + "DEPRECATED: please switch to just the argument. Path to the Kubernetes source directory (if empty, the path is autodetected)", + ) + cmd.Flags().StringVar( + &flags.BaseImage, + "base-image", + nodeimage.DefaultBaseImage, + "name:tag of the base image to use for the build", + ) + cmd.Flags().StringVar( + &flags.Arch, + "arch", + "", + "architecture to build for, defaults to the host architecture", + ) + return cmd +} + +func runE(logger log.Logger, flags *flagpole, args []string) error { + kubeRoot := flags.KubeRoot + if len(args) > 0 { + kubeRoot = args[0] + } + if err := nodeimage.Build( + nodeimage.WithImage(flags.Image), + nodeimage.WithBaseImage(flags.BaseImage), + nodeimage.WithKuberoot(kubeRoot), + nodeimage.WithLogger(logger), + nodeimage.WithArch(flags.Arch), + ); err != nil { + return errors.Wrap(err, "error building node image") + } + return nil +} diff --git a/vendor/sigs.k8s.io/kind/pkg/cmd/kind/completion/bash/bash.go b/vendor/sigs.k8s.io/kind/pkg/cmd/kind/completion/bash/bash.go new file mode 100644 index 0000000000..bdb909e5e1 --- /dev/null +++ b/vendor/sigs.k8s.io/kind/pkg/cmd/kind/completion/bash/bash.go @@ -0,0 +1,38 @@ +/* +Copyright 2019 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +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 bash implements the `bash` command +package bash + +import ( + "github.com/spf13/cobra" + + "sigs.k8s.io/kind/pkg/cmd" + "sigs.k8s.io/kind/pkg/log" +) + +// NewCommand returns a new cobra.Command for cluster creation +func NewCommand(logger log.Logger, streams cmd.IOStreams) *cobra.Command { + cmd := &cobra.Command{ + Args: cobra.NoArgs, + Use: "bash", + Short: "Output shell completions for bash", + RunE: func(cmd *cobra.Command, args []string) error { + return cmd.Parent().Parent().GenBashCompletion(streams.Out) + }, + } + return cmd +} diff --git a/vendor/sigs.k8s.io/kind/pkg/cmd/kind/completion/completion.go b/vendor/sigs.k8s.io/kind/pkg/cmd/kind/completion/completion.go new file mode 100644 index 0000000000..d588d3b45d --- /dev/null +++ b/vendor/sigs.k8s.io/kind/pkg/cmd/kind/completion/completion.go @@ -0,0 +1,74 @@ +/* +Copyright 2019 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +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 completion implements the `completion` command +package completion + +import ( + "errors" + + "github.com/spf13/cobra" + + "sigs.k8s.io/kind/pkg/cmd" + "sigs.k8s.io/kind/pkg/cmd/kind/completion/bash" + "sigs.k8s.io/kind/pkg/cmd/kind/completion/fish" + "sigs.k8s.io/kind/pkg/cmd/kind/completion/zsh" + "sigs.k8s.io/kind/pkg/log" +) + +// NewCommand returns a new cobra.Command for cluster creation +func NewCommand(logger log.Logger, streams cmd.IOStreams) *cobra.Command { + cmd := &cobra.Command{ + Args: cobra.NoArgs, + Use: "completion", + Short: "Output shell completion code for the specified shell (bash, zsh or fish)", + Long: longDescription, + RunE: func(cmd *cobra.Command, args []string) error { + err := cmd.Help() + if err != nil { + return err + } + return errors.New("Subcommand is required") + }, + } + cmd.AddCommand(zsh.NewCommand(logger, streams)) + cmd.AddCommand(bash.NewCommand(logger, streams)) + cmd.AddCommand(fish.NewCommand(logger, streams)) + return cmd +} + +const longDescription = ` +Outputs kind shell completion for the given shell (bash or zsh) +This depends on the bash-completion binary. Example installation instructions: +# for bash users + $ kind completion bash > ~/.kind-completion + $ source ~/.kind-completion + +# for zsh users + % kind completion zsh > /usr/local/share/zsh/site-functions/_kind + % autoload -U compinit && compinit +# or if zsh-completion is installed via homebrew + % kind completion zsh > "${fpath[1]}/_kind" +# or if you use oh-my-zsh (needs zsh-completions plugin) + % mkdir $ZSH/completions/ + % kind completion zsh > $ZSH/completions/_kind + +# for fish users + % kind completion fish > ~/.config/fish/completions/kind.fish + +Additionally, you may want to output the completion to a file and source in your .bashrc +Note for zsh users: [1] zsh completions are only supported in versions of zsh >= 5.2 +` diff --git a/vendor/sigs.k8s.io/kind/pkg/cmd/kind/completion/fish/fish.go b/vendor/sigs.k8s.io/kind/pkg/cmd/kind/completion/fish/fish.go new file mode 100644 index 0000000000..a48343cc38 --- /dev/null +++ b/vendor/sigs.k8s.io/kind/pkg/cmd/kind/completion/fish/fish.go @@ -0,0 +1,37 @@ +/* +Copyright 2019 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +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 fish implements the `fish` command +package fish + +import ( + "github.com/spf13/cobra" + + "sigs.k8s.io/kind/pkg/cmd" + "sigs.k8s.io/kind/pkg/log" +) + +// NewCommand returns a new cobra.Command for cluster creation +func NewCommand(logger log.Logger, streams cmd.IOStreams) *cobra.Command { + cmd := &cobra.Command{ + Use: "fish", + Short: "Output shell completions for fish", + RunE: func(cmd *cobra.Command, args []string) error { + return cmd.Parent().Parent().GenFishCompletion(streams.Out, true) + }, + } + return cmd +} diff --git a/vendor/sigs.k8s.io/kind/pkg/cmd/kind/completion/zsh/zsh.go b/vendor/sigs.k8s.io/kind/pkg/cmd/kind/completion/zsh/zsh.go new file mode 100644 index 0000000000..04c3f90fab --- /dev/null +++ b/vendor/sigs.k8s.io/kind/pkg/cmd/kind/completion/zsh/zsh.go @@ -0,0 +1,38 @@ +/* +Copyright 2019 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +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 zsh implements the `zsh` command +package zsh + +import ( + "github.com/spf13/cobra" + + "sigs.k8s.io/kind/pkg/cmd" + "sigs.k8s.io/kind/pkg/log" +) + +// NewCommand returns a new cobra.Command for cluster creation +func NewCommand(logger log.Logger, streams cmd.IOStreams) *cobra.Command { + cmd := &cobra.Command{ + Args: cobra.NoArgs, + Use: "zsh", + Short: "Output shell completions for zsh", + RunE: func(cmd *cobra.Command, args []string) error { + return cmd.Parent().Parent().GenZshCompletion(streams.Out) + }, + } + return cmd +} diff --git a/vendor/sigs.k8s.io/kind/pkg/cmd/kind/create/cluster/createcluster.go b/vendor/sigs.k8s.io/kind/pkg/cmd/kind/create/cluster/createcluster.go new file mode 100644 index 0000000000..6bb66dcfc3 --- /dev/null +++ b/vendor/sigs.k8s.io/kind/pkg/cmd/kind/create/cluster/createcluster.go @@ -0,0 +1,139 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +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 cluster implements the `create cluster` command +package cluster + +import ( + "io" + "time" + + "github.com/spf13/cobra" + + "sigs.k8s.io/kind/pkg/cluster" + "sigs.k8s.io/kind/pkg/cmd" + "sigs.k8s.io/kind/pkg/errors" + "sigs.k8s.io/kind/pkg/log" + + "sigs.k8s.io/kind/pkg/internal/cli" + "sigs.k8s.io/kind/pkg/internal/runtime" +) + +type flagpole struct { + Name string + Config string + ImageName string + Retain bool + Wait time.Duration + Kubeconfig string +} + +// NewCommand returns a new cobra.Command for cluster creation +func NewCommand(logger log.Logger, streams cmd.IOStreams) *cobra.Command { + flags := &flagpole{} + cmd := &cobra.Command{ + Args: cobra.NoArgs, + Use: "cluster", + Short: "Creates a local Kubernetes cluster", + Long: "Creates a local Kubernetes cluster using Docker container 'nodes'", + RunE: func(cmd *cobra.Command, args []string) error { + cli.OverrideDefaultName(cmd.Flags()) + return runE(logger, streams, flags) + }, + } + cmd.Flags().StringVarP( + &flags.Name, + "name", + "n", + "", + "cluster name, overrides KIND_CLUSTER_NAME, config (default kind)", + ) + cmd.Flags().StringVar( + &flags.Config, + "config", + "", + "path to a kind config file", + ) + cmd.Flags().StringVar( + &flags.ImageName, + "image", + "", + "node docker image to use for booting the cluster", + ) + cmd.Flags().BoolVar( + &flags.Retain, + "retain", + false, + "retain nodes for debugging when cluster creation fails", + ) + cmd.Flags().DurationVar( + &flags.Wait, + "wait", + time.Duration(0), + "wait for control plane node to be ready (default 0s)", + ) + cmd.Flags().StringVar( + &flags.Kubeconfig, + "kubeconfig", + "", + "sets kubeconfig path instead of $KUBECONFIG or $HOME/.kube/config", + ) + return cmd +} + +func runE(logger log.Logger, streams cmd.IOStreams, flags *flagpole) error { + provider := cluster.NewProvider( + cluster.ProviderWithLogger(logger), + runtime.GetDefault(logger), + ) + + // handle config flag, we might need to read from stdin + withConfig, err := configOption(flags.Config, streams.In) + if err != nil { + return err + } + + // create the cluster + if err = provider.Create( + flags.Name, + withConfig, + cluster.CreateWithNodeImage(flags.ImageName), + cluster.CreateWithRetain(flags.Retain), + cluster.CreateWithWaitForReady(flags.Wait), + cluster.CreateWithKubeconfigPath(flags.Kubeconfig), + cluster.CreateWithDisplayUsage(true), + cluster.CreateWithDisplaySalutation(true), + ); err != nil { + return errors.Wrap(err, "failed to create cluster") + } + + return nil +} + +// configOption converts the raw --config flag value to a cluster creation +// option matching it. it will read from stdin if the flag value is `-` +func configOption(rawConfigFlag string, stdin io.Reader) (cluster.CreateOption, error) { + // if not - then we are using a real file + if rawConfigFlag != "-" { + return cluster.CreateWithConfigFile(rawConfigFlag), nil + } + // otherwise read from stdin + raw, err := io.ReadAll(stdin) + if err != nil { + return nil, errors.Wrap(err, "error reading config from stdin") + } + return cluster.CreateWithRawConfig(raw), nil +} diff --git a/vendor/sigs.k8s.io/kind/pkg/cmd/kind/create/create.go b/vendor/sigs.k8s.io/kind/pkg/cmd/kind/create/create.go new file mode 100644 index 0000000000..6087477974 --- /dev/null +++ b/vendor/sigs.k8s.io/kind/pkg/cmd/kind/create/create.go @@ -0,0 +1,47 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package create implements the `create` command +package create + +import ( + "errors" + + "github.com/spf13/cobra" + + "sigs.k8s.io/kind/pkg/cmd" + createcluster "sigs.k8s.io/kind/pkg/cmd/kind/create/cluster" + "sigs.k8s.io/kind/pkg/log" +) + +// NewCommand returns a new cobra.Command for cluster creation +func NewCommand(logger log.Logger, streams cmd.IOStreams) *cobra.Command { + cmd := &cobra.Command{ + Args: cobra.NoArgs, + Use: "create", + Short: "Creates one of [cluster]", + Long: "Creates one of local Kubernetes cluster (cluster)", + RunE: func(cmd *cobra.Command, args []string) error { + err := cmd.Help() + if err != nil { + return err + } + return errors.New("Subcommand is required") + }, + } + cmd.AddCommand(createcluster.NewCommand(logger, streams)) + return cmd +} diff --git a/vendor/sigs.k8s.io/kind/pkg/cmd/kind/delete/cluster/deletecluster.go b/vendor/sigs.k8s.io/kind/pkg/cmd/kind/delete/cluster/deletecluster.go new file mode 100644 index 0000000000..3fa98c5be0 --- /dev/null +++ b/vendor/sigs.k8s.io/kind/pkg/cmd/kind/delete/cluster/deletecluster.go @@ -0,0 +1,78 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +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 cluster implements the `delete` command +package cluster + +import ( + "github.com/spf13/cobra" + + "sigs.k8s.io/kind/pkg/cluster" + "sigs.k8s.io/kind/pkg/cmd" + "sigs.k8s.io/kind/pkg/errors" + "sigs.k8s.io/kind/pkg/log" + + "sigs.k8s.io/kind/pkg/internal/cli" + "sigs.k8s.io/kind/pkg/internal/runtime" +) + +type flagpole struct { + Name string + Kubeconfig string +} + +// NewCommand returns a new cobra.Command for cluster deletion +func NewCommand(logger log.Logger, streams cmd.IOStreams) *cobra.Command { + flags := &flagpole{} + cmd := &cobra.Command{ + Args: cobra.NoArgs, + // TODO(bentheelder): more detailed usage + Use: "cluster", + Short: "Deletes a cluster", + Long: "Deletes a resource", + RunE: func(cmd *cobra.Command, args []string) error { + cli.OverrideDefaultName(cmd.Flags()) + return deleteCluster(logger, flags) + }, + } + cmd.Flags().StringVarP( + &flags.Name, + "name", + "n", + cluster.DefaultName, + "the cluster name", + ) + cmd.Flags().StringVar( + &flags.Kubeconfig, + "kubeconfig", + "", + "sets kubeconfig path instead of $KUBECONFIG or $HOME/.kube/config", + ) + return cmd +} + +func deleteCluster(logger log.Logger, flags *flagpole) error { + provider := cluster.NewProvider( + cluster.ProviderWithLogger(logger), + runtime.GetDefault(logger), + ) + // Delete individual cluster + logger.V(0).Infof("Deleting cluster %q ...", flags.Name) + if err := provider.Delete(flags.Name, flags.Kubeconfig); err != nil { + return errors.Wrapf(err, "failed to delete cluster %q", flags.Name) + } + return nil +} diff --git a/vendor/sigs.k8s.io/kind/pkg/cmd/kind/delete/clusters/deleteclusters.go b/vendor/sigs.k8s.io/kind/pkg/cmd/kind/delete/clusters/deleteclusters.go new file mode 100644 index 0000000000..fbf7c4f8c6 --- /dev/null +++ b/vendor/sigs.k8s.io/kind/pkg/cmd/kind/delete/clusters/deleteclusters.go @@ -0,0 +1,91 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +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 clusters implements the `delete` command for multiple clusters +package clusters + +import ( + "github.com/spf13/cobra" + + "sigs.k8s.io/kind/pkg/cluster" + "sigs.k8s.io/kind/pkg/cmd" + "sigs.k8s.io/kind/pkg/errors" + "sigs.k8s.io/kind/pkg/log" + + "sigs.k8s.io/kind/pkg/internal/runtime" +) + +type flagpole struct { + Kubeconfig string + All bool +} + +// NewCommand returns a new cobra.Command for cluster deletion +func NewCommand(logger log.Logger, streams cmd.IOStreams) *cobra.Command { + flags := &flagpole{} + cmd := &cobra.Command{ + Args: cobra.MinimumNArgs(0), + // TODO(bentheelder): more detailed usage + Use: "clusters", + Short: "Deletes one or more clusters", + Long: "Deletes a resource", + RunE: func(cmd *cobra.Command, args []string) error { + if !flags.All && len(args) == 0 { + return errors.New("no cluster names provided") + } + + return deleteClusters(logger, flags, args) + }, + } + cmd.Flags().StringVar( + &flags.Kubeconfig, + "kubeconfig", + "", + "sets kubeconfig path instead of $KUBECONFIG or $HOME/.kube/config", + ) + cmd.Flags().BoolVarP( + &flags.All, + "all", + "A", + false, + "delete all clusters", + ) + return cmd +} + +func deleteClusters(logger log.Logger, flags *flagpole, clusters []string) error { + provider := cluster.NewProvider( + cluster.ProviderWithLogger(logger), + runtime.GetDefault(logger), + ) + var err error + if flags.All { + //Delete all clusters + if clusters, err = provider.List(); err != nil { + return errors.Wrap(err, "failed listing clusters for delete") + } + } + var success []string + for _, cluster := range clusters { + if err = provider.Delete(cluster, flags.Kubeconfig); err != nil { + logger.V(0).Infof("%s\n", errors.Wrapf(err, "failed to delete cluster %q", cluster)) + continue + } + success = append(success, cluster) + } + logger.V(0).Infof("Deleted clusters: %q", success) + return nil +} diff --git a/vendor/sigs.k8s.io/kind/pkg/cmd/kind/delete/delete.go b/vendor/sigs.k8s.io/kind/pkg/cmd/kind/delete/delete.go new file mode 100644 index 0000000000..f2f540ce2f --- /dev/null +++ b/vendor/sigs.k8s.io/kind/pkg/cmd/kind/delete/delete.go @@ -0,0 +1,50 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +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 delete implements the `delete` command +package delete + +import ( + "errors" + + "github.com/spf13/cobra" + + "sigs.k8s.io/kind/pkg/cmd" + deletecluster "sigs.k8s.io/kind/pkg/cmd/kind/delete/cluster" + deleteclusters "sigs.k8s.io/kind/pkg/cmd/kind/delete/clusters" + "sigs.k8s.io/kind/pkg/log" +) + +// NewCommand returns a new cobra.Command for cluster deletion +func NewCommand(logger log.Logger, streams cmd.IOStreams) *cobra.Command { + cmd := &cobra.Command{ + Args: cobra.NoArgs, + // TODO(bentheelder): more detailed usage + Use: "delete", + Short: "Deletes one of [cluster]", + Long: "Deletes one of [cluster]", + RunE: func(cmd *cobra.Command, args []string) error { + err := cmd.Help() + if err != nil { + return err + } + return errors.New("Subcommand is required") + }, + } + cmd.AddCommand(deletecluster.NewCommand(logger, streams)) + cmd.AddCommand(deleteclusters.NewCommand(logger, streams)) + return cmd +} diff --git a/vendor/sigs.k8s.io/kind/pkg/cmd/kind/export/export.go b/vendor/sigs.k8s.io/kind/pkg/cmd/kind/export/export.go new file mode 100644 index 0000000000..73fb49f979 --- /dev/null +++ b/vendor/sigs.k8s.io/kind/pkg/cmd/kind/export/export.go @@ -0,0 +1,51 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +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 export implements the `export` command +package export + +import ( + "errors" + + "github.com/spf13/cobra" + + "sigs.k8s.io/kind/pkg/cmd" + "sigs.k8s.io/kind/pkg/cmd/kind/export/kubeconfig" + "sigs.k8s.io/kind/pkg/cmd/kind/export/logs" + "sigs.k8s.io/kind/pkg/log" +) + +// NewCommand returns a new cobra.Command for export +func NewCommand(logger log.Logger, streams cmd.IOStreams) *cobra.Command { + cmd := &cobra.Command{ + Args: cobra.NoArgs, + // TODO(bentheelder): more detailed usage + Use: "export", + Short: "Exports one of [kubeconfig, logs]", + Long: "Exports one of [kubeconfig, logs]", + RunE: func(cmd *cobra.Command, args []string) error { + err := cmd.Help() + if err != nil { + return err + } + return errors.New("Subcommand is required") + }, + } + // add subcommands + cmd.AddCommand(logs.NewCommand(logger, streams)) + cmd.AddCommand(kubeconfig.NewCommand(logger, streams)) + return cmd +} diff --git a/vendor/sigs.k8s.io/kind/pkg/cmd/kind/export/kubeconfig/kubeconfig.go b/vendor/sigs.k8s.io/kind/pkg/cmd/kind/export/kubeconfig/kubeconfig.go new file mode 100644 index 0000000000..298cbd1b45 --- /dev/null +++ b/vendor/sigs.k8s.io/kind/pkg/cmd/kind/export/kubeconfig/kubeconfig.go @@ -0,0 +1,84 @@ +/* +Copyright 2019 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +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 kubeconfig implements the `kubeconfig` command +package kubeconfig + +import ( + "github.com/spf13/cobra" + + "sigs.k8s.io/kind/pkg/cluster" + "sigs.k8s.io/kind/pkg/cmd" + "sigs.k8s.io/kind/pkg/log" + + "sigs.k8s.io/kind/pkg/internal/cli" + "sigs.k8s.io/kind/pkg/internal/runtime" +) + +type flagpole struct { + Name string + Kubeconfig string + Internal bool +} + +// NewCommand returns a new cobra.Command for exporting the kubeconfig +func NewCommand(logger log.Logger, streams cmd.IOStreams) *cobra.Command { + flags := &flagpole{} + cmd := &cobra.Command{ + Args: cobra.NoArgs, + Use: "kubeconfig", + Short: "Exports cluster kubeconfig", + Long: "Exports cluster kubeconfig", + RunE: func(cmd *cobra.Command, args []string) error { + cli.OverrideDefaultName(cmd.Flags()) + return runE(logger, flags) + }, + } + cmd.Flags().StringVarP( + &flags.Name, + "name", + "n", + cluster.DefaultName, + "the cluster context name", + ) + cmd.Flags().StringVar( + &flags.Kubeconfig, + "kubeconfig", + "", + "sets kubeconfig path instead of $KUBECONFIG or $HOME/.kube/config", + ) + cmd.Flags().BoolVar( + &flags.Internal, + "internal", + false, + "use internal address instead of external", + ) + return cmd +} + +func runE(logger log.Logger, flags *flagpole) error { + provider := cluster.NewProvider( + cluster.ProviderWithLogger(logger), + runtime.GetDefault(logger), + ) + if err := provider.ExportKubeConfig(flags.Name, flags.Kubeconfig, flags.Internal); err != nil { + return err + } + // TODO: get kind-name from a method? OTOH we probably want to keep this + // naming scheme stable anyhow... + logger.V(0).Infof(`Set kubectl context to "kind-%s"`, flags.Name) + return nil +} diff --git a/vendor/sigs.k8s.io/kind/pkg/cmd/kind/export/logs/logs.go b/vendor/sigs.k8s.io/kind/pkg/cmd/kind/export/logs/logs.go new file mode 100644 index 0000000000..adf6847998 --- /dev/null +++ b/vendor/sigs.k8s.io/kind/pkg/cmd/kind/export/logs/logs.go @@ -0,0 +1,96 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +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 logs implements the `logs` command +package logs + +import ( + "fmt" + + "github.com/spf13/cobra" + + "sigs.k8s.io/kind/pkg/cluster" + "sigs.k8s.io/kind/pkg/cmd" + "sigs.k8s.io/kind/pkg/fs" + "sigs.k8s.io/kind/pkg/log" + + "sigs.k8s.io/kind/pkg/internal/cli" + "sigs.k8s.io/kind/pkg/internal/runtime" +) + +type flagpole struct { + Name string +} + +// NewCommand returns a new cobra.Command for getting the cluster logs +func NewCommand(logger log.Logger, streams cmd.IOStreams) *cobra.Command { + flags := &flagpole{} + cmd := &cobra.Command{ + Args: cobra.MaximumNArgs(1), + // TODO(bentheelder): more detailed usage + Use: "logs [output-dir]", + Short: "Exports logs to a tempdir or [output-dir] if specified", + Long: "Exports logs to a tempdir or [output-dir] if specified", + RunE: func(cmd *cobra.Command, args []string) error { + cli.OverrideDefaultName(cmd.Flags()) + return runE(logger, streams, flags, args) + }, + } + cmd.Flags().StringVarP( + &flags.Name, + "name", + "n", + cluster.DefaultName, + "the cluster context name", + ) + return cmd +} + +func runE(logger log.Logger, streams cmd.IOStreams, flags *flagpole, args []string) error { + provider := cluster.NewProvider( + cluster.ProviderWithLogger(logger), + runtime.GetDefault(logger), + ) + + // Check if the cluster has any running nodes + nodes, err := provider.ListNodes(flags.Name) + if err != nil { + return err + } + if len(nodes) == 0 { + return fmt.Errorf("unknown cluster %q", flags.Name) + } + + // get the optional directory argument, or create a tempdir + var dir string + if len(args) == 0 { + t, err := fs.TempDir("", "") + if err != nil { + return err + } + dir = t + } else { + dir = args[0] + } + + // NOTE: the path is the output of this command to be captured by calling tools + // whereas "Exporting logs..." is info / debug (stderr) + logger.V(0).Infof("Exporting logs for cluster %q to:", flags.Name) + fmt.Fprintln(streams.Out, dir) + + // collect the logs + return provider.CollectLogs(flags.Name, dir) +} diff --git a/vendor/sigs.k8s.io/kind/pkg/cmd/kind/get/clusters/clusters.go b/vendor/sigs.k8s.io/kind/pkg/cmd/kind/get/clusters/clusters.go new file mode 100644 index 0000000000..77b5d38c39 --- /dev/null +++ b/vendor/sigs.k8s.io/kind/pkg/cmd/kind/get/clusters/clusters.go @@ -0,0 +1,64 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +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 clusters implements the `clusters` command +package clusters + +import ( + "fmt" + + "github.com/spf13/cobra" + + "sigs.k8s.io/kind/pkg/cluster" + "sigs.k8s.io/kind/pkg/cmd" + "sigs.k8s.io/kind/pkg/log" + + "sigs.k8s.io/kind/pkg/internal/runtime" +) + +// NewCommand returns a new cobra.Command for getting the list of clusters +func NewCommand(logger log.Logger, streams cmd.IOStreams) *cobra.Command { + cmd := &cobra.Command{ + Args: cobra.NoArgs, + // TODO(bentheelder): more detailed usage + Use: "clusters", + Short: "Lists existing kind clusters by their name", + Long: "Lists existing kind clusters by their name", + RunE: func(cmd *cobra.Command, args []string) error { + return runE(logger, streams) + }, + } + return cmd +} + +func runE(logger log.Logger, streams cmd.IOStreams) error { + provider := cluster.NewProvider( + cluster.ProviderWithLogger(logger), + runtime.GetDefault(logger), + ) + clusters, err := provider.List() + if err != nil { + return err + } + if len(clusters) == 0 { + logger.V(0).Info("No kind clusters found.") + return nil + } + for _, cluster := range clusters { + fmt.Fprintln(streams.Out, cluster) + } + return nil +} diff --git a/vendor/sigs.k8s.io/kind/pkg/cmd/kind/get/get.go b/vendor/sigs.k8s.io/kind/pkg/cmd/kind/get/get.go new file mode 100644 index 0000000000..84ed382e7c --- /dev/null +++ b/vendor/sigs.k8s.io/kind/pkg/cmd/kind/get/get.go @@ -0,0 +1,53 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +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 get implements the `get` command +package get + +import ( + "errors" + + "github.com/spf13/cobra" + + "sigs.k8s.io/kind/pkg/cmd" + "sigs.k8s.io/kind/pkg/cmd/kind/get/clusters" + "sigs.k8s.io/kind/pkg/cmd/kind/get/kubeconfig" + "sigs.k8s.io/kind/pkg/cmd/kind/get/nodes" + "sigs.k8s.io/kind/pkg/log" +) + +// NewCommand returns a new cobra.Command for get +func NewCommand(logger log.Logger, streams cmd.IOStreams) *cobra.Command { + cmd := &cobra.Command{ + Args: cobra.NoArgs, + // TODO(bentheelder): more detailed usage + Use: "get", + Short: "Gets one of [clusters, nodes, kubeconfig]", + Long: "Gets one of [clusters, nodes, kubeconfig]", + RunE: func(cmd *cobra.Command, args []string) error { + err := cmd.Help() + if err != nil { + return err + } + return errors.New("Subcommand is required") + }, + } + // add subcommands + cmd.AddCommand(clusters.NewCommand(logger, streams)) + cmd.AddCommand(nodes.NewCommand(logger, streams)) + cmd.AddCommand(kubeconfig.NewCommand(logger, streams)) + return cmd +} diff --git a/vendor/sigs.k8s.io/kind/pkg/cmd/kind/get/kubeconfig/kubeconfig.go b/vendor/sigs.k8s.io/kind/pkg/cmd/kind/get/kubeconfig/kubeconfig.go new file mode 100644 index 0000000000..fdc889c4f4 --- /dev/null +++ b/vendor/sigs.k8s.io/kind/pkg/cmd/kind/get/kubeconfig/kubeconfig.go @@ -0,0 +1,78 @@ +/* +Copyright 2019 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +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 kubeconfig implements the `kubeconfig` command +package kubeconfig + +import ( + "fmt" + + "github.com/spf13/cobra" + + "sigs.k8s.io/kind/pkg/cluster" + "sigs.k8s.io/kind/pkg/cmd" + "sigs.k8s.io/kind/pkg/log" + + "sigs.k8s.io/kind/pkg/internal/cli" + "sigs.k8s.io/kind/pkg/internal/runtime" +) + +type flagpole struct { + Name string + Internal bool +} + +// NewCommand returns a new cobra.Command for getting the kubeconfig +func NewCommand(logger log.Logger, streams cmd.IOStreams) *cobra.Command { + flags := &flagpole{} + cmd := &cobra.Command{ + Args: cobra.NoArgs, + Use: "kubeconfig", + Short: "Prints cluster kubeconfig", + Long: "Prints cluster kubeconfig", + RunE: func(cmd *cobra.Command, args []string) error { + cli.OverrideDefaultName(cmd.Flags()) + return runE(logger, streams, flags) + }, + } + cmd.Flags().StringVarP( + &flags.Name, + "name", + "n", + cluster.DefaultName, + "the cluster context name", + ) + cmd.Flags().BoolVar( + &flags.Internal, + "internal", + false, + "use internal address instead of external", + ) + return cmd +} + +func runE(logger log.Logger, streams cmd.IOStreams, flags *flagpole) error { + provider := cluster.NewProvider( + cluster.ProviderWithLogger(logger), + runtime.GetDefault(logger), + ) + cfg, err := provider.KubeConfig(flags.Name, flags.Internal) + if err != nil { + return err + } + fmt.Fprintln(streams.Out, cfg) + return nil +} diff --git a/vendor/sigs.k8s.io/kind/pkg/cmd/kind/get/nodes/nodes.go b/vendor/sigs.k8s.io/kind/pkg/cmd/kind/get/nodes/nodes.go new file mode 100644 index 0000000000..1858d64ba2 --- /dev/null +++ b/vendor/sigs.k8s.io/kind/pkg/cmd/kind/get/nodes/nodes.go @@ -0,0 +1,109 @@ +/* +Copyright 2019 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +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 nodes implements the `nodes` command +package nodes + +import ( + "fmt" + + "github.com/spf13/cobra" + + "sigs.k8s.io/kind/pkg/cluster" + "sigs.k8s.io/kind/pkg/cluster/nodes" + "sigs.k8s.io/kind/pkg/cmd" + "sigs.k8s.io/kind/pkg/log" + + "sigs.k8s.io/kind/pkg/internal/cli" + "sigs.k8s.io/kind/pkg/internal/runtime" +) + +type flagpole struct { + Name string + AllClusters bool +} + +// NewCommand returns a new cobra.Command for getting the list of nodes for a given cluster +func NewCommand(logger log.Logger, streams cmd.IOStreams) *cobra.Command { + flags := &flagpole{} + cmd := &cobra.Command{ + Args: cobra.NoArgs, + Use: "nodes", + Short: "Lists existing kind nodes by their name", + Long: "Lists existing kind nodes by their name", + RunE: func(cmd *cobra.Command, args []string) error { + cli.OverrideDefaultName(cmd.Flags()) + return runE(logger, streams, flags) + }, + } + cmd.Flags().StringVarP( + &flags.Name, + "name", + "n", + cluster.DefaultName, + "the cluster context name", + ) + cmd.Flags().BoolVarP( + &flags.AllClusters, + "all-clusters", + "A", + false, + "If present, list all the available nodes across all cluster contexts. Current context is ignored even if specified with --name.", + ) + return cmd +} + +func runE(logger log.Logger, streams cmd.IOStreams, flags *flagpole) error { + // List nodes by cluster context name + provider := cluster.NewProvider( + cluster.ProviderWithLogger(logger), + runtime.GetDefault(logger), + ) + + var nodes []nodes.Node + var err error + if flags.AllClusters { + clusters, err := provider.List() + if err != nil { + return err + } + for _, clusterName := range clusters { + clusterNodes, err := provider.ListNodes(clusterName) + if err != nil { + return err + } + nodes = append(nodes, clusterNodes...) + } + if len(nodes) == 0 { + logger.V(0).Infof("No kind nodes for any cluster.") + return nil + } + } else { + nodes, err = provider.ListNodes(flags.Name) + if err != nil { + return err + } + if len(nodes) == 0 { + logger.V(0).Infof("No kind nodes found for cluster %q.", flags.Name) + return nil + } + } + + for _, node := range nodes { + fmt.Fprintln(streams.Out, node.String()) + } + return nil +} diff --git a/vendor/sigs.k8s.io/kind/pkg/cmd/kind/load/docker-image/docker-image.go b/vendor/sigs.k8s.io/kind/pkg/cmd/kind/load/docker-image/docker-image.go new file mode 100644 index 0000000000..39fa431462 --- /dev/null +++ b/vendor/sigs.k8s.io/kind/pkg/cmd/kind/load/docker-image/docker-image.go @@ -0,0 +1,284 @@ +/* +Copyright 2019 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +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 load implements the `load` command +package load + +import ( + "fmt" + "os" + "path/filepath" + "strings" + + "github.com/spf13/cobra" + + "sigs.k8s.io/kind/pkg/cluster" + "sigs.k8s.io/kind/pkg/cluster/nodes" + "sigs.k8s.io/kind/pkg/cluster/nodeutils" + "sigs.k8s.io/kind/pkg/cmd" + "sigs.k8s.io/kind/pkg/errors" + "sigs.k8s.io/kind/pkg/exec" + "sigs.k8s.io/kind/pkg/fs" + "sigs.k8s.io/kind/pkg/log" + + "sigs.k8s.io/kind/pkg/internal/cli" + "sigs.k8s.io/kind/pkg/internal/runtime" +) + +type ( + imageTagFetcher func(nodes.Node, string) (map[string]bool, error) +) + +type flagpole struct { + Name string + Nodes []string +} + +// NewCommand returns a new cobra.Command for loading an image into a cluster +func NewCommand(logger log.Logger, streams cmd.IOStreams) *cobra.Command { + flags := &flagpole{} + cmd := &cobra.Command{ + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("a list of image names is required") + } + return nil + }, + Use: "docker-image [IMAGE...]", + Short: "Loads docker images from host into nodes", + Long: "Loads docker images from host into all or specified nodes by name", + RunE: func(cmd *cobra.Command, args []string) error { + cli.OverrideDefaultName(cmd.Flags()) + return runE(logger, flags, args) + }, + } + cmd.Flags().StringVarP( + &flags.Name, + "name", + "n", + cluster.DefaultName, + "the cluster context name", + ) + cmd.Flags().StringSliceVar( + &flags.Nodes, + "nodes", + nil, + "comma separated list of nodes to load images into", + ) + return cmd +} + +func runE(logger log.Logger, flags *flagpole, args []string) error { + provider := cluster.NewProvider( + cluster.ProviderWithLogger(logger), + runtime.GetDefault(logger), + ) + + // Check that the image exists locally and gets its ID, if not return error + imageNames := removeDuplicates(args) + var imageIDs []string + for _, imageName := range imageNames { + imageID, err := imageID(imageName) + if err != nil { + return fmt.Errorf("image: %q not present locally", imageName) + } + imageIDs = append(imageIDs, imageID) + } + + // Check if the cluster nodes exist + nodeList, err := provider.ListInternalNodes(flags.Name) + if err != nil { + return err + } + if len(nodeList) == 0 { + return fmt.Errorf("no nodes found for cluster %q", flags.Name) + } + + // map cluster nodes by their name + nodesByName := map[string]nodes.Node{} + for _, node := range nodeList { + // TODO(bentheelder): this depends on the fact that ListByCluster() + // will have name for nameOrId. + nodesByName[node.String()] = node + } + + // pick only the user selected nodes and ensure they exist + // the default is all nodes unless flags.Nodes is set + candidateNodes := nodeList + if len(flags.Nodes) > 0 { + candidateNodes = []nodes.Node{} + for _, name := range flags.Nodes { + node, ok := nodesByName[name] + if !ok { + return fmt.Errorf("unknown node: %q", name) + } + candidateNodes = append(candidateNodes, node) + } + } + + // pick only the nodes that don't have the image + selectedNodes := map[string]nodes.Node{} + fns := []func() error{} + for i, imageName := range imageNames { + imageID := imageIDs[i] + processed := false + for _, node := range candidateNodes { + exists, reTagRequired, sanitizedImageName := checkIfImageReTagRequired(node, imageID, imageName, nodeutils.ImageTags) + if exists && !reTagRequired { + continue + } + + if reTagRequired { + // We will try to re-tag the image. If the re-tag fails, we will fall back to the default behavior of loading + // the images into the nodes again + logger.V(0).Infof("Image with ID: %s already present on the node %s but is missing the tag %s. re-tagging...", imageID, node.String(), sanitizedImageName) + if err := nodeutils.ReTagImage(node, imageID, sanitizedImageName); err != nil { + logger.Errorf("failed to re-tag image on the node %s due to an error %s. Will load it instead...", node.String(), err) + selectedNodes[node.String()] = node + } else { + processed = true + } + continue + } + id, err := nodeutils.ImageID(node, imageName) + if err != nil || id != imageID { + selectedNodes[node.String()] = node + logger.V(0).Infof("Image: %q with ID %q not yet present on node %q, loading...", imageName, imageID, node.String()) + } + continue + } + if len(selectedNodes) == 0 && !processed { + logger.V(0).Infof("Image: %q with ID %q found to be already present on all nodes.", imageName, imageID) + } + } + + // return early if no node needs the image + if len(selectedNodes) == 0 { + return nil + } + + // Setup the tar path where the images will be saved + dir, err := fs.TempDir("", "images-tar") + if err != nil { + return errors.Wrap(err, "failed to create tempdir") + } + defer os.RemoveAll(dir) + imagesTarPath := filepath.Join(dir, "images.tar") + // Save the images into a tar + err = save(imageNames, imagesTarPath) + if err != nil { + return err + } + + // Load the images on the selected nodes + for _, selectedNode := range selectedNodes { + selectedNode := selectedNode // capture loop variable + fns = append(fns, func() error { + return loadImage(imagesTarPath, selectedNode) + }) + } + return errors.UntilErrorConcurrent(fns) +} + +// TODO: we should consider having a cluster method to load images + +// loads an image tarball onto a node +func loadImage(imageTarName string, node nodes.Node) error { + f, err := os.Open(imageTarName) + if err != nil { + return errors.Wrap(err, "failed to open image") + } + defer f.Close() + return nodeutils.LoadImageArchive(node, f) +} + +// save saves images to dest, as in `docker save` +func save(images []string, dest string) error { + commandArgs := append([]string{"save", "-o", dest}, images...) + return exec.Command("docker", commandArgs...).Run() +} + +// imageID return the Id of the container image +func imageID(containerNameOrID string) (string, error) { + cmd := exec.Command("docker", "image", "inspect", + "-f", "{{ .Id }}", + containerNameOrID, // ... against the container + ) + lines, err := exec.OutputLines(cmd) + if err != nil { + return "", err + } + if len(lines) != 1 { + return "", errors.Errorf("Docker image ID should only be one line, got %d lines", len(lines)) + } + return lines[0], nil +} + +// removeDuplicates removes duplicates from a string slice +func removeDuplicates(slice []string) []string { + result := []string{} + seenKeys := make(map[string]struct{}) + for _, k := range slice { + if _, seen := seenKeys[k]; !seen { + result = append(result, k) + seenKeys[k] = struct{}{} + } + } + return result +} + +// checkIfImageExists makes sure we only perform the reverse lookup of the ImageID to tag map +func checkIfImageReTagRequired(node nodes.Node, imageID, imageName string, tagFetcher imageTagFetcher) (exists, reTagRequired bool, sanitizedImage string) { + tags, err := tagFetcher(node, imageID) + if len(tags) == 0 || err != nil { + exists = false + return + } + exists = true + sanitizedImage = sanitizeImage(imageName) + if ok := tags[sanitizedImage]; ok { + reTagRequired = false + return + } + reTagRequired = true + return +} + +// sanitizeImage is a helper to return human readable image name +// This is a modified version of the same function found under providers/podman/images.go +func sanitizeImage(image string) (sanitizedName string) { + const ( + defaultDomain = "docker.io/" + officialRepoName = "library" + ) + sanitizedName = image + + if !strings.ContainsRune(image, '/') { + sanitizedName = officialRepoName + "/" + image + } + + i := strings.IndexRune(sanitizedName, '/') + if i == -1 || (!strings.ContainsAny(sanitizedName[:i], ".:") && sanitizedName[:i] != "localhost") { + sanitizedName = defaultDomain + sanitizedName + } + + i = strings.IndexRune(sanitizedName, ':') + if i == -1 { + sanitizedName += ":latest" + } + + return +} diff --git a/vendor/sigs.k8s.io/kind/pkg/cmd/kind/load/image-archive/image-archive.go b/vendor/sigs.k8s.io/kind/pkg/cmd/kind/load/image-archive/image-archive.go new file mode 100644 index 0000000000..9bd1d679de --- /dev/null +++ b/vendor/sigs.k8s.io/kind/pkg/cmd/kind/load/image-archive/image-archive.go @@ -0,0 +1,138 @@ +/* +Copyright 2019 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +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 load implements the `load` command +package load + +import ( + "fmt" + "os" + + "github.com/spf13/cobra" + "sigs.k8s.io/kind/pkg/errors" + + "sigs.k8s.io/kind/pkg/cluster" + "sigs.k8s.io/kind/pkg/cluster/nodes" + "sigs.k8s.io/kind/pkg/cluster/nodeutils" + "sigs.k8s.io/kind/pkg/cmd" + "sigs.k8s.io/kind/pkg/log" + + "sigs.k8s.io/kind/pkg/internal/cli" + "sigs.k8s.io/kind/pkg/internal/runtime" +) + +type flagpole struct { + Name string + Nodes []string +} + +// NewCommand returns a new cobra.Command for loading an image into a cluster +func NewCommand(logger log.Logger, streams cmd.IOStreams) *cobra.Command { + flags := &flagpole{} + cmd := &cobra.Command{ + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return fmt.Errorf("name of image archive is required") + } + return nil + }, + Use: "image-archive ", + Short: "Loads docker image from archive into nodes", + Long: "Loads docker image from archive into all or specified nodes by name", + RunE: func(cmd *cobra.Command, args []string) error { + cli.OverrideDefaultName(cmd.Flags()) + return runE(logger, flags, args) + }, + } + cmd.Flags().StringVarP( + &flags.Name, + "name", + "n", + cluster.DefaultName, + "the cluster context name", + ) + cmd.Flags().StringSliceVar( + &flags.Nodes, + "nodes", + nil, + "comma separated list of nodes to load images into", + ) + return cmd +} + +func runE(logger log.Logger, flags *flagpole, args []string) error { + provider := cluster.NewProvider( + cluster.ProviderWithLogger(logger), + runtime.GetDefault(logger), + ) + + // Check if file exists + imageTarPath := args[0] + if _, err := os.Stat(imageTarPath); err != nil { + return err + } + + // Check if the cluster nodes exist + nodeList, err := provider.ListInternalNodes(flags.Name) + if err != nil { + return err + } + if len(nodeList) == 0 { + return fmt.Errorf("no nodes found for cluster %q", flags.Name) + } + + // map cluster nodes by their name + nodesByName := map[string]nodes.Node{} + for _, node := range nodeList { + // TODO(bentheelder): this depends on the fact that ListByCluster() + // will have name for nameOrId. + nodesByName[node.String()] = node + } + + // pick only the user selected nodes and ensure they exist + // the default is all nodes unless flags.Nodes is set + selectedNodes := nodeList + if len(flags.Nodes) > 0 { + selectedNodes = []nodes.Node{} + for _, name := range flags.Nodes { + node, ok := nodesByName[name] + if !ok { + return fmt.Errorf("unknown node: %s", name) + } + selectedNodes = append(selectedNodes, node) + } + } + + // Load the image on the selected nodes + fns := []func() error{} + for _, selectedNode := range selectedNodes { + selectedNode := selectedNode // capture loop variable + fns = append(fns, func() error { + return loadImage(imageTarPath, selectedNode) + }) + } + return errors.UntilErrorConcurrent(fns) +} + +// loads an image tarball onto a node +func loadImage(imageTarName string, node nodes.Node) error { + f, err := os.Open(imageTarName) + if err != nil { + return errors.Wrap(err, "failed to open image") + } + defer f.Close() + return nodeutils.LoadImageArchive(node, f) +} diff --git a/vendor/sigs.k8s.io/kind/pkg/cmd/kind/load/load.go b/vendor/sigs.k8s.io/kind/pkg/cmd/kind/load/load.go new file mode 100644 index 0000000000..36436dbe9a --- /dev/null +++ b/vendor/sigs.k8s.io/kind/pkg/cmd/kind/load/load.go @@ -0,0 +1,50 @@ +/* +Copyright 2019 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +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 load implements the `load` command +package load + +import ( + "errors" + + "github.com/spf13/cobra" + + "sigs.k8s.io/kind/pkg/cmd" + dockerimage "sigs.k8s.io/kind/pkg/cmd/kind/load/docker-image" + imagearchive "sigs.k8s.io/kind/pkg/cmd/kind/load/image-archive" + "sigs.k8s.io/kind/pkg/log" +) + +// NewCommand returns a new cobra.Command for get +func NewCommand(logger log.Logger, streams cmd.IOStreams) *cobra.Command { + cmd := &cobra.Command{ + Args: cobra.NoArgs, + Use: "load", + Short: "Loads images into nodes", + Long: "Loads images into node from an archive or image on host", + RunE: func(cmd *cobra.Command, args []string) error { + err := cmd.Help() + if err != nil { + return err + } + return errors.New("Subcommand is required") + }, + } + // add subcommands + cmd.AddCommand(dockerimage.NewCommand(logger, streams)) + cmd.AddCommand(imagearchive.NewCommand(logger, streams)) + return cmd +} diff --git a/vendor/sigs.k8s.io/kind/pkg/cmd/kind/root.go b/vendor/sigs.k8s.io/kind/pkg/cmd/kind/root.go new file mode 100644 index 0000000000..09589b6197 --- /dev/null +++ b/vendor/sigs.k8s.io/kind/pkg/cmd/kind/root.go @@ -0,0 +1,143 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +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 kind implements the root kind cobra command, and the cli Main() +package kind + +import ( + "io" + + "github.com/spf13/cobra" + + "sigs.k8s.io/kind/pkg/cmd" + "sigs.k8s.io/kind/pkg/cmd/kind/build" + "sigs.k8s.io/kind/pkg/cmd/kind/completion" + "sigs.k8s.io/kind/pkg/cmd/kind/create" + "sigs.k8s.io/kind/pkg/cmd/kind/delete" + "sigs.k8s.io/kind/pkg/cmd/kind/export" + "sigs.k8s.io/kind/pkg/cmd/kind/get" + "sigs.k8s.io/kind/pkg/cmd/kind/load" + "sigs.k8s.io/kind/pkg/cmd/kind/version" + "sigs.k8s.io/kind/pkg/log" +) + +type flagpole struct { + LogLevel string + Verbosity int32 + Quiet bool +} + +// NewCommand returns a new cobra.Command implementing the root command for kind +func NewCommand(logger log.Logger, streams cmd.IOStreams) *cobra.Command { + flags := &flagpole{} + cmd := &cobra.Command{ + Args: cobra.NoArgs, + Use: "kind", + Short: "kind is a tool for managing local Kubernetes clusters", + Long: "kind creates and manages local Kubernetes clusters using Docker container 'nodes'", + PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + return runE(logger, flags, cmd) + }, + SilenceUsage: true, + SilenceErrors: true, + Version: version.Version(), + } + cmd.SetOut(streams.Out) + cmd.SetErr(streams.ErrOut) + cmd.PersistentFlags().StringVar( + &flags.LogLevel, + "loglevel", + "", + "DEPRECATED: see -v instead", + ) + cmd.PersistentFlags().Int32VarP( + &flags.Verbosity, + "verbosity", + "v", + 0, + "info log verbosity, higher value produces more output", + ) + cmd.PersistentFlags().BoolVarP( + &flags.Quiet, + "quiet", + "q", + false, + "silence all stderr output", + ) + // add all top level subcommands + cmd.AddCommand(build.NewCommand(logger, streams)) + cmd.AddCommand(completion.NewCommand(logger, streams)) + cmd.AddCommand(create.NewCommand(logger, streams)) + cmd.AddCommand(delete.NewCommand(logger, streams)) + cmd.AddCommand(export.NewCommand(logger, streams)) + cmd.AddCommand(get.NewCommand(logger, streams)) + cmd.AddCommand(version.NewCommand(logger, streams)) + cmd.AddCommand(load.NewCommand(logger, streams)) + return cmd +} + +func runE(logger log.Logger, flags *flagpole, command *cobra.Command) error { + // handle limited migration for --loglevel + setLogLevel := command.Flag("loglevel").Changed + setVerbosity := command.Flag("verbosity").Changed + if setLogLevel && !setVerbosity { + switch flags.LogLevel { + case "debug": + flags.Verbosity = 3 + case "trace": + flags.Verbosity = 2147483647 + } + } + // normal logger setup + if flags.Quiet { + // NOTE: if we are coming from app.Run handling this flag is + // redundant, however it doesn't hurt, and this may be called directly. + maybeSetWriter(logger, io.Discard) + } + maybeSetVerbosity(logger, log.Level(flags.Verbosity)) + // warn about deprecated flag if used + if setLogLevel { + if cmd.ColorEnabled(logger) { + logger.Warn("\x1b[93mWARNING\x1b[0m: --loglevel is deprecated, please switch to -v and -q!") + } else { + logger.Warn("WARNING: --loglevel is deprecated, please switch to -v and -q!") + } + } + return nil +} + +// maybeSetWriter will call logger.SetWriter(w) if logger has a SetWriter method +func maybeSetWriter(logger log.Logger, w io.Writer) { + type writerSetter interface { + SetWriter(io.Writer) + } + v, ok := logger.(writerSetter) + if ok { + v.SetWriter(w) + } +} + +// maybeSetVerbosity will call logger.SetVerbosity(verbosity) if logger +// has a SetVerbosity method +func maybeSetVerbosity(logger log.Logger, verbosity log.Level) { + type verboser interface { + SetVerbosity(log.Level) + } + v, ok := logger.(verboser) + if ok { + v.SetVerbosity(verbosity) + } +} diff --git a/vendor/sigs.k8s.io/kind/pkg/internal/runtime/runtime.go b/vendor/sigs.k8s.io/kind/pkg/internal/runtime/runtime.go new file mode 100644 index 0000000000..c673374666 --- /dev/null +++ b/vendor/sigs.k8s.io/kind/pkg/internal/runtime/runtime.go @@ -0,0 +1,25 @@ +package runtime + +import ( + "os" + + "sigs.k8s.io/kind/pkg/cluster" + "sigs.k8s.io/kind/pkg/log" +) + +// GetDefault selected the default runtime from the environment override +func GetDefault(logger log.Logger) cluster.ProviderOption { + switch p := os.Getenv("KIND_EXPERIMENTAL_PROVIDER"); p { + case "": + return nil + case "podman": + logger.Warn("using podman due to KIND_EXPERIMENTAL_PROVIDER") + return cluster.ProviderWithPodman() + case "docker": + logger.Warn("using docker due to KIND_EXPERIMENTAL_PROVIDER") + return cluster.ProviderWithDocker() + default: + logger.Warnf("ignoring unknown value %q for KIND_EXPERIMENTAL_PROVIDER", p) + return nil + } +}