diff --git a/bootstrap/Makefile b/bootstrap/Makefile index 7dac1e4f4ad..3c2197832db 100755 --- a/bootstrap/Makefile +++ b/bootstrap/Makefile @@ -13,18 +13,36 @@ # limitations under the License. # GCLOUD_PROJECT ?= kubeflow-images-public -GOLANG_VERSION ?= 1.11.5 +GOLANG_VERSION ?= 1.12 GOPATH ?= $(HOME)/go GOOGLE_APPLICATION_CREDENTIALS ?= $(HOME)/auth.json # To build without the cache set the environment variable # export DOCKER_BUILD_OPTS=--no-cache IMG ?= gcr.io/$(GCLOUD_PROJECT)/bootstrapper TAG ?= $(shell git describe --tags --always --dirty) -PORT ?= 2345 +# set to -V +VERBOSE ?= PLUGINS_ENVIRONMENT ?= $(GOPATH)/src/github.com/kubeflow/kubeflow/bootstrap/bin export GO111MODULE = on export GO = go +%.so: + cd cmd/plugins/$* && \ + $(GO) build -i -gcflags 'all=-N -l' -o ../../../bin/$*.so -buildmode=plugin $*.go + +%.init: + @echo kfctl init test/$* $(VERBOSE) --platform $* --project $(GCLOUD_PROJECT) --version pull/2673 && \ + GOOGLE_APPLICATION_CREDENTIALS=$(GOOGLE_APPLICATION_CREDENTIALS) PLUGINS_ENVIRONMENT=$(PLUGINS_ENVIRONMENT) kfctl init $(PWD)/test/$* $(VERBOSE) --platform $* --project $(GCLOUD_PROJECT) --version pull/2673 + +%.init-no-platform: + @echo kfctl init test/$* $(VERBOSE) --version pull/2673 && \ + kfctl init $(PWD)/test/$* $(VERBOSE) --version pull/2673 + +%.generate: + @echo kfctl generate all $(VERBOSE) '(--platform '$*')' && \ + cd test/$* && \ + GOOGLE_APPLICATION_CREDENTIALS=$(GOOGLE_APPLICATION_CREDENTIALS) PLUGINS_ENVIRONMENT=$(PLUGINS_ENVIRONMENT) kfctl generate all $(VERBOSE) --mount-local --email gcp-deploy@$(GCLOUD_PROJECT).iam.gserviceaccount.com + all: build auth: @@ -32,27 +50,44 @@ auth: # Run go fmt against code fmt: - $(GO) fmt ./pkg/... ./cmd/... ./config/... + @$(GO) fmt ./config/... ./pkg/apis/apps/client/... ./pkg/apis/apps/gcp/... ./pkg/utils/... ./pkg/client/minikube ./pkg/client/gcp/... ./cmd/kfctl/... # Run go vet against code vet: - $(GO) vet ./pkg/... ./cmd/... ./config/... + @$(GO) vet ./config/... ./pkg/apis/apps/client/... ./pkg/apis/apps/gcp/... ./pkg/utils/... ./pkg/client/minikube ./pkg/client/gcp/... ./cmd/kfctl/... generate: - $(GO) generate ./pkg/... ./cmd/... ./config/... - -deepcopy: - @deepcopy-gen -i github.com/kubeflow/kubeflow/bootstrap/pkg/apis/... -O zz_generated.deepcopy + @$(GO) generate ./config/... ./pkg/apis/apps/client/... ./pkg/apis/apps/gcp/... ./pkg/utils/... ./pkg/client/minikube ./pkg/client/gcp/... ./cmd/kfctl/... + +/tmp/v2: + @[ ! -d /tmp/v2 ] && unzip -q -d /tmp hack/v2.zip + +$(GOPATH)/bin/deepcopy-gen: + GO111MODULE=off $(GO) get k8s.io/code-generator/cmd/deepcopy-gen + +config/zz_generated.deepcopy.go: config/types.go + $(GOPATH)/bin/deepcopy-gen -i github.com/kubeflow/kubeflow/bootstrap/config/... -O zz_generated.deepcopy + +pkg/apis/apps/gcp/v1alpha1/zz_generated.deepcopy.go: pkg/apis/apps/gcp/v1alpha1/application_types.go + $(GOPATH)/bin/deepcopy-gen -i github.com/kubeflow/kubeflow/bootstrap/pkg/apis/apps/gcp/... -O zz_generated.deepcopy + +pkg/apis/apps/ksonnet/v1alpha1/zz_generated.deepcopy.go: pkg/apis/apps/ksonnet/v1alpha1/application_types.go + $(GOPATH)/bin/deepcopy-gen -i github.com/kubeflow/kubeflow/bootstrap/pkg/apis/apps/ksonnet/... -O zz_generated.deepcopy + +pkg/apis/apps/client/v1alpha1/zz_generated.deepcopy.go: pkg/apis/apps/client/v1alpha1/application_types.go + $(GOPATH)/bin/deepcopy-gen -i github.com/kubeflow/kubeflow/bootstrap/pkg/apis/apps/client/... -O zz_generated.deepcopy + +v2/pkg/apis/apps/client/v1alpha1/zz_generated.deepcopy.go: v2/pkg/apis/apps/client/v1alpha1/application_types.go + $(GOPATH)/bin/deepcopy-gen -i github.com/kubeflow/kubeflow/bootstrap/v2/pkg/apis/apps/client/... -O zz_generated.deepcopy + +deepcopy: $(GOPATH)/bin/deepcopy-gen config/zz_generated.deepcopy.go pkg/apis/apps/ksonnet/v1alpha1/zz_generated.deepcopy.go pkg/apis/apps/gcp/v1alpha1/zz_generated.deepcopy.go pkg/apis/apps/client/v1alpha1/zz_generated.deepcopy.go build-bootstrap: generate fmt vet $(GO) build -gcflags 'all=-N -l' -o bin/bootstrapper cmd/bootstrap/main.go -build-kfctl: generate fmt vet +build-kfctl: /tmp/v2 deepcopy generate fmt vet $(GO) build -i -gcflags 'all=-N -l' -o bin/kfctl cmd/kfctl/main.go -build-dockerfordesktop-plugin: generate fmt vet - $(GO) build -i -gcflags 'all=-N -l' -o bin/dockerfordesktop.so -buildmode=plugin cmd/plugins/dockerfordesktop/dockerfordesktop.go - build-local: build-bootstrap build-kfctl # To edit which registries to add to bootstrapper, edit config (eg. config/default.yaml) @@ -79,7 +114,7 @@ push-latest: push gcloud container images add-tag --quiet $(IMG):$(TAG) $(IMG):latest --verbosity=info echo created $(IMG):latest -install: build-kfctl +install: build-kfctl dockerfordesktop.so @echo copying bin/kfctl to /usr/local/bin @cp bin/kfctl /usr/local/bin @@ -88,47 +123,9 @@ run-local-docker: --mount type=bind,source=${HOME}/kf_app,target=/home/kubeflow \ --entrypoint /bin/bash $(IMG):$(TAG) -# static and plugins toogle whether we try and load a platform as a .so. -# There is an example plugin 'dockerfordesktop' that can be loaded as a .so. -# By default we disable loading plugins and link dockerfordesktop into the kfctl binary (static) -static: - @ex pkg/apis/apps/group.go - version Prints the version of kfctl. +kfctl can dynamically load platforms and package managers that are not statically linked, as +described below in [Extending kfctl](#extending-kfctl). -Flags: - -h, --help help for kfctl +## Usage -Use "kfctl [command] --help" for more information about a command. +```kubeflow client tool + + Usage: + kfctl [command] + + Available Commands: + apply Deploy a generated kubeflow application. + delete Delete a kubeflow application. + generate Generate a kubeflow application where resources is one of 'platform|k8s|all'. + help Help about any command + init Create a kubeflow application under <[path/]name> + show Deploy a generated kubeflow application. + version Prints the version of kfctl. + + Flags: + -h, --help help for kfctl + + Use "kfctl [command] --help" for more information about a command. ``` Typical use-case, non-platform specific. ```sh -kfctl init ~/myapp -cd ~/myapp -kfctl generate -kfctl apply +kfctl init ~/myapp && \ +cd ~/myapp && \ +kfctl generate all && \ +kfctl apply all ``` ## Subcommands @@ -152,7 +144,7 @@ Flags: --ipName string ipName if '--platform gcp' --mount-local mount-local if '--platform minikube' -V, --verbose verbose output default is false - --zone string zone if '--platform gcp' (default "us-east1-d") + --zone string zone if '--platform gcp' (default "us-east1-d")``` ``` ### **apply** @@ -166,9 +158,10 @@ Usage: kfctl apply [all(=default)|k8s|platform] [flags] Flags: - -h, --help help for apply - -V, --verbose verbose output default is false -``` + -h, --help help for apply + --oauth_id string OAuth Client ID, GCP only. Required if ENV CLIENT_ID is not set. Value passed will take precedence to ENV. + --oauth_secret string OAuth Client ID, GCP only. Required if ENV CLIENT_SECRET is not set. Value passed will take precedence to ENV. + -V, --verbose verbose output default is false``` ### **delete** @@ -182,14 +175,13 @@ Usage: Flags: -h, --help help for delete - -V, --verbose verbose output default is false -``` + -V, --verbose verbose output default is false``` --- ## Extending kfctl -`kfctl` can be extended to work with new platforms without requiring recompilation. +`kfctl` can be extended to work with new platforms or package managers without requiring recompilation. An example is under bootstrap/cmd/plugins/dockerfordesktop/dockerfordesktop.go. A particular platform provides a shared library (.so) under the env var `PLUGINS_ENVIRONMENT` that kfctl would load and execute. The shared library needs to define @@ -226,91 +218,134 @@ make test-kfctl ### Testing `kfctl init` for all platforms ``` -make test-known-platforms-init +make test-init ``` ### Testing `kfctl generate` for all platforms ``` -make test-known-platforms-generate +make test-generate ``` -## Debugging +#### app.yaml example for --platform gcp -In order to debug in goland, the plugin code must be disabled. -See https://github.com/golang/go/issues/23733. -This is expected to be resolved with golang 1.12.X -To disable the plugin code (which will cause dockerfordesktop.go to be linked statically in kfctl) -and allow debugging in goland run: - -``` -make static -``` - -otherwise run - -``` -make plugins ``` +apiVersion: client.apps.kubeflow.org/v1alpha1 +kind: Client +metadata: + creationTimestamp: null + name: gcp + namespace: kubeflow +spec: + appdir: /Users/kdkasrav/go/src/github.com/kubeflow/kubeflow/bootstrap/test/gcp + componentParams: + application: + - name: components + value: + cert-manager: + - initRequired: true + name: acmeEmail + value: gcp-deploy@constant-cubist-173123.iam.gserviceaccount.com + cloud-endpoints: + - name: secretName + value: admin-gcp-sa + iap-ingress: + - initRequired: true + name: ipName + value: gcp-ip + - initRequired: true + name: hostname + value: gcp.endpoints.constant-cubist-173123.cloud.goog + jupyter: + - name: jupyterHubAuthenticator + value: iap + - name: platform + value: gke + pipeline: + - name: mysqlPd + value: gcp-storage-metadata-store + - name: minioPd + value: gcp-storage-artifact-store + components: + - ambassador + - application + - argo + - centraldashboard + - cert-manager + - cloud-endpoints + - iap-ingress + - jupyter + - jupyter-web-app + - katib + - metacontroller + - notebook-controller + - pipeline + - pytorch-operator + - tensorboard + - tf-job-operator + email: gcp-deploy@constant-cubist-173123.iam.gserviceaccount.com + hostname: gcp.endpoints.constant-cubist-173123.cloud.goog + ipName: gcp-ip + packages: + - application + - argo + - common + - examples + - gcp + - jupyter + - katib + - metacontroller + - modeldb + - mpi-job + - pipeline + - pytorch-job + - seldon + - tensorboard + - tf-serving + - tf-training + platform: gcp + project: XXXXXX + repo: /Users/kdkasrav/go/src/github.com/kubeflow/kubeflow/bootstrap/test/gcp/.cache/2673/kubeflow + useBasicAuth: false + version: "2673" + zone: us-east1-d + status: {} + ``` -Note: the default is `make static`. Do not checkin code after doing `make plugins`. - -Note: static and plugins make targets result in 2 files being changed: -- pkg/apis/apps/group.go -- cmd/kfctl/cmd/root.go - -These files have comments that are toggled (effectively a golang macro hack). -This will go away when the fix noted above is available and we've moved to -this version of go. - - -## KfApp Types used in app.yaml - -### ksonnet related types (originally under bootstrap/cmd/bootstrap, moved to pkg/apis/apps/ksonnet/v1alpha1) +## gcp-click-to-deploy (no changes) -```golang -type Ksonnet struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` +Ksonnet types have been moved to `github.com/kubeflow/kubeflow/bootstrap/pkg/apis/apps/ksonnet/v1alpha1` - Spec KsonnetSpec `json:"spec,omitempty"` - Status KsonnetStatus `json:"status,omitempty"` -} +## golang modules and versioned packages -type NameValue struct { - Name string `json:"name,omitempty"` - Value string `json:"value,omitempty"` -} +Both ksonnet and kustomize package managers are loaded as .so's. +(They will be statically linked soon see [#2635](https://github.com/kubeflow/kubeflow/issues/2635)) +The complication is that ksonnet and kustomize do not have an overlap of kubernetes versions as well +as client-go, and controller-runtime. -// KsonnetSpec defines the desired state of Ksonnet -type KsonnetSpec struct { - Platform string `json:"platform,omitempty"` - Version string `json:"version,omitempty"` - Repo string `json:"repo,omitempty"` - Components []string `json:"components,omitempty"` - Packages []string `json:"packages,omitempty"` - Parameters map[string][]NameValue `json:"parameters,omitempty"` -} -``` +- **ksonnet** (highest version) + - k8s.io/api kubernetes-1.10.4 + - k8s.io/apimachinery kubernetes-1.10.4 + - k8s.io/client-go v7.0.0 + - sigs.k8s.io/controller-runtime v0.1.1 -#### app.yaml example for --platform minikube +- **kustomize** (cannot downgrade to kubernetes-1.10.4, client-go v7.0.0) + - k8s.io/api kubernetes-1.13.4 + - k8s.io/apimachinery kubernetes-1.13.4 + - k8s.io/client-go v10.0.0 + - sigs.k8s.io/controller-runtime v0.1.10 -``` -apiVersion: ksonnet.apps.kubeflow.org/v1alpha1 -kind: Ksonnet -metadata: - creationTimestamp: null - name: minikube - namespace: kubeflow -spec: - appdir: /Users/kdkasrav/go/src/github.com/kubeflow/kubeflow/bootstrap/ksonnet - platform: minikube - repo: /Users/kdkasrav/go/src/github.com/kubeflow/kubeflow/bootstrap/ksonnet/.cache/master/kubeflow - version: master -status: {} -``` +kustomize leverages golang modules by using 'v2' versions of +the above libraries. -## gcp-click-to-deploy (no changes) +We insert golang package versioning for these libraries despite the fact that kubernetes has yet to move to golang modules. Updating these libraries to use golang modules is straight-forward and can be done using local git clones. Background information on how this is done can be found [here](https://github.com/golang/go/wiki/Modules#releasing-modules-v2-or-higher). -Ksonnet types have been moved to `github.com/kubeflow/kubeflow/bootstrap/pkg/apis/apps/ksonnet/v1alpha1` +Currently a build artifact was hand built and checked into PR #2548 as bootstrap/hack/v2.zip. +This build artifact holds versioned packages for the above libraries. This hand-built artifact needs to be removed and replaced with a golang tool such as [mod](https://github.com/marwan-at-work/mod) that can do this algorithmically and as part of building kfctl. +The [mod](https://github.com/marwan-at-work/mod) tool needs to be modified in the following ways: +1. Update literal references to versioned packages as found [here](https://github.com/kubernetes/api/blob/kubernetes-1.13.4/apps/v1/generated.pb.go#L62) and [here](https://github.com/kubernetes/api/blob/kubernetes-1.13.4/apps/v1/generated.pb.go#L63). +2. Move a subset of libraries tagged as [+incompatible](https://groups.google.com/forum/#!topic/golang-codereviews/t-xcPhCn3FI) within the go.mod file, instead of all of them. +3. Create a local archive of these versioned packages +4. Update go.mod files that reference the versioned packages with a replace directive pointing to the the local archive location. +5. Do not update the git repos or push changes back to git diff --git a/bootstrap/cmd/kfctl/cmd/generate.go b/bootstrap/cmd/kfctl/cmd/generate.go index 5264ad47aad..3449adfd9a0 100644 --- a/bootstrap/cmd/kfctl/cmd/generate.go +++ b/bootstrap/cmd/kfctl/cmd/generate.go @@ -114,7 +114,7 @@ func init() { return } - // platforms minikube, docker-for-desktop + // platforms minikube generateCmd.Flags().Bool(string(kftypes.MOUNT_LOCAL), false, string(kftypes.MOUNT_LOCAL)+" if '--platform minikube'") bindErr = generateCfg.BindPFlag(string(kftypes.MOUNT_LOCAL), generateCmd.Flags().Lookup(string(kftypes.MOUNT_LOCAL))) diff --git a/bootstrap/cmd/kfctl/cmd/init.go b/bootstrap/cmd/kfctl/cmd/init.go index f3eae03d4ed..14d71970e75 100644 --- a/bootstrap/cmd/kfctl/cmd/init.go +++ b/bootstrap/cmd/kfctl/cmd/init.go @@ -78,7 +78,7 @@ func init() { initCfg.SetConfigName("app") initCfg.SetConfigType("yaml") - initCmd.Flags().StringP(string(kftypes.PLATFORM), "p", kftypes.DefaultPlatform, + initCmd.Flags().StringP(string(kftypes.PLATFORM), "p", "", "one of 'gcp|minikube'") bindErr := initCfg.BindPFlag(string(kftypes.PLATFORM), initCmd.Flags().Lookup(string(kftypes.PLATFORM))) if bindErr != nil { @@ -95,7 +95,8 @@ func init() { } initCmd.Flags().StringP(string(kftypes.VERSION), "v", kftypes.DefaultVersion, - "desired "+string(kftypes.VERSION)+" Kubeflow or latest tag if not provided by user ") + "desired "+string(kftypes.VERSION)+" of Kubeflow or master if not specified. Version can be "+ + "master (eg --version master) or a git tag (eg --version=v0.5.0), or a PR (eg --version pull/).") bindErr = initCfg.BindPFlag(string(kftypes.VERSION), initCmd.Flags().Lookup(string(kftypes.VERSION))) if bindErr != nil { log.Errorf("couldn't set flag --%v: %v", string(kftypes.VERSION), bindErr) diff --git a/bootstrap/cmd/kfctl/cmd/show.go b/bootstrap/cmd/kfctl/cmd/show.go new file mode 100644 index 00000000000..c44748f9f91 --- /dev/null +++ b/bootstrap/cmd/kfctl/cmd/show.go @@ -0,0 +1,76 @@ +// Copyright 2018 The Kubeflow 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 cmd + +import ( + kftypes "github.com/kubeflow/kubeflow/bootstrap/pkg/apis/apps" + "github.com/kubeflow/kubeflow/bootstrap/pkg/client/coordinator" + log "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +var showCfg = viper.New() + +// showCmd represents the show command +var showCmd = &cobra.Command{ + Use: "show [all(=default)|k8s|platform]", + Short: "Deploy a generated kubeflow application.", + Long: `Deploy a generated kubeflow application.`, + Run: func(cmd *cobra.Command, args []string) { + log.SetLevel(log.InfoLevel) + log.Info("show kubeflow application") + if showCfg.GetBool(string(kftypes.VERBOSE)) == true { + log.SetLevel(log.InfoLevel) + } else { + log.SetLevel(log.WarnLevel) + } + resource, resourceErr := processResourceArg(args) + if resourceErr != nil { + log.Errorf("invalid resource: %v", resourceErr) + return + } + options := map[string]interface{}{} + kfApp, kfAppErr := coordinator.LoadKfApp(options) + if kfAppErr != nil { + log.Errorf("couldn't load KfApp: %v", kfAppErr) + return + } + show, ok := kfApp.(kftypes.KfShow) + if ok && show != nil { + showErr := show.Show(resource, options) + if showErr != nil { + log.Errorf("couldn't show KfApp: %v", showErr) + return + } + } + }, +} + +func init() { + rootCmd.AddCommand(showCmd) + + showCfg.SetConfigName("app") + showCfg.SetConfigType("yaml") + + // verbose output + showCmd.Flags().BoolP(string(kftypes.VERBOSE), "V", false, + string(kftypes.VERBOSE)+" output default is false") + bindErr := showCfg.BindPFlag(string(kftypes.VERBOSE), showCmd.Flags().Lookup(string(kftypes.VERBOSE))) + if bindErr != nil { + log.Errorf("couldn't set flag --%v: %v", string(kftypes.VERBOSE), bindErr) + return + } +} diff --git a/bootstrap/config/doc.go b/bootstrap/config/doc.go new file mode 100644 index 00000000000..ab9018f4890 --- /dev/null +++ b/bootstrap/config/doc.go @@ -0,0 +1,18 @@ +// Copyright 2018 The Kubeflow Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package config contains API Schema definitions +// +k8s:deepcopy-gen=package + +package config diff --git a/bootstrap/config/gcp_prototype.yaml b/bootstrap/config/gcp_prototype.yaml index 53b27b2d415..d03f5e6f454 100644 --- a/bootstrap/config/gcp_prototype.yaml +++ b/bootstrap/config/gcp_prototype.yaml @@ -29,10 +29,12 @@ app: name: ipName # TODO: make sure value of ipName is the same as property . value: ipName + initRequired: true - component: iap-ingress name: hostname # TODO: replace with Name of GCP project. This is fully qualified domain name to use with ingress. value: kubeflow.endpoints..cloud.goog + initRequired: true - component: jupyterhub name: jupyterHubAuthenticator value: iap diff --git a/bootstrap/config/kfctl_default.yaml b/bootstrap/config/kfctl_default.yaml index 3feff46d0aa..7677f4ffa93 100644 --- a/bootstrap/config/kfctl_default.yaml +++ b/bootstrap/config/kfctl_default.yaml @@ -44,9 +44,3 @@ componentParams: ambassador: - name: ambassadorServiceType value: NodePort - pipeline: - - name: mysqlPd - value: -storage-metadata-store - - name: minioPd - value: -storage-artifact-store -platform: none diff --git a/bootstrap/config/zz_generated.deepcopy.go b/bootstrap/config/zz_generated.deepcopy.go new file mode 100644 index 00000000000..bd65b568b6a --- /dev/null +++ b/bootstrap/config/zz_generated.deepcopy.go @@ -0,0 +1,106 @@ +// +build !ignore_autogenerated + +// Copyright 2018 The Kubeflow 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. + +// Code generated by deepcopy-gen. DO NOT EDIT. + +package config + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ComponentConfig) DeepCopyInto(out *ComponentConfig) { + *out = *in + if in.Components != nil { + in, out := &in.Components, &out.Components + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Packages != nil { + in, out := &in.Packages, &out.Packages + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.ComponentParams != nil { + in, out := &in.ComponentParams, &out.ComponentParams + *out = make(Parameters, len(*in)) + for key, val := range *in { + var outVal []NameValue + if val == nil { + (*out)[key] = nil + } else { + in, out := &val, &outVal + *out = make([]NameValue, len(*in)) + copy(*out, *in) + } + (*out)[key] = outVal + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ComponentConfig. +func (in *ComponentConfig) DeepCopy() *ComponentConfig { + if in == nil { + return nil + } + out := new(ComponentConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NameValue) DeepCopyInto(out *NameValue) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NameValue. +func (in *NameValue) DeepCopy() *NameValue { + if in == nil { + return nil + } + out := new(NameValue) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in Parameters) DeepCopyInto(out *Parameters) { + { + in := &in + *out = make(Parameters, len(*in)) + for key, val := range *in { + var outVal []NameValue + if val == nil { + (*out)[key] = nil + } else { + in, out := &val, &outVal + *out = make([]NameValue, len(*in)) + copy(*out, *in) + } + (*out)[key] = outVal + } + return + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Parameters. +func (in Parameters) DeepCopy() Parameters { + if in == nil { + return nil + } + out := new(Parameters) + in.DeepCopyInto(out) + return *out +} diff --git a/bootstrap/go.mod b/bootstrap/go.mod index 507622e54a1..946263b63c1 100644 --- a/bootstrap/go.mod +++ b/bootstrap/go.mod @@ -2,50 +2,43 @@ module github.com/kubeflow/kubeflow/bootstrap require ( cloud.google.com/go v0.34.0 - contrib.go.opencensus.io/exporter/ocagent v0.4.2 // indirect - github.com/Azure/go-autorest v9.1.0+incompatible // indirect + github.com/Azure/go-autorest v11.1.0+incompatible github.com/BurntSushi/toml v0.3.1 // indirect github.com/GeertJohan/go.rice v0.0.0-20181229193832-0af3f3b09a0a // indirect github.com/MakeNowJust/heredoc v0.0.0-20171113091838-e9091a26100e // indirect + github.com/Masterminds/goutils v1.1.0 // indirect github.com/Masterminds/semver v1.4.2 // indirect - github.com/Masterminds/sprig v2.17.1+incompatible // indirect + github.com/Masterminds/sprig v2.18.0+incompatible // indirect github.com/NYTimes/gziphandler v1.0.1 // indirect - github.com/aokoli/goutils v1.1.0 // indirect github.com/blang/semver v3.5.1+incompatible // indirect github.com/cenkalti/backoff v2.1.1+incompatible github.com/coreos/etcd v3.3.11+incompatible // indirect github.com/coreos/go-systemd v0.0.0-20181031085051-9002847aa142 // indirect github.com/cyphar/filepath-securejoin v0.2.2 // indirect - github.com/daaku/go.zipexe v0.0.0-20150329023125-a5fe2436ffcb // indirect + github.com/daaku/go.zipexe v1.0.0 // indirect github.com/deckarep/golang-set v1.7.1 - github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect - github.com/docker/distribution v2.7.0+incompatible // indirect + github.com/docker/distribution v2.7.1+incompatible // indirect github.com/docker/docker v1.13.1 // indirect github.com/docker/go-connections v0.4.0 // indirect - github.com/docker/go-units v0.3.3 // indirect github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c // indirect - github.com/emicklei/go-restful v2.8.0+incompatible // indirect github.com/emicklei/go-restful-swagger12 v0.0.0-20170926063155-7524189396c6 // indirect - github.com/evanphx/json-patch v4.1.0+incompatible // indirect github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect github.com/fatih/camelcase v1.0.0 // indirect github.com/ghodss/yaml v1.0.0 github.com/go-kit/kit v0.8.0 - github.com/go-logfmt/logfmt v0.4.0 // indirect + github.com/go-openapi/jsonpointer v0.18.0 + github.com/go-openapi/jsonreference v0.18.0 github.com/go-openapi/spec v0.18.0 // indirect github.com/go-openapi/strfmt v0.18.0 // indirect - github.com/go-openapi/validate v0.18.0 // indirect + github.com/go-openapi/swag v0.17.2 github.com/gobwas/glob v0.2.3 // indirect github.com/gogo/protobuf v1.2.0 // indirect github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b github.com/golang/groupcache v0.0.0-20181024230925-c65c006176ff // indirect - github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c // indirect github.com/google/go-github v17.0.0+incompatible // indirect github.com/google/go-jsonnet v0.12.1 // indirect github.com/google/go-querystring v1.0.0 // indirect - github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf // indirect github.com/googleapis/gax-go v2.0.2+incompatible // indirect - github.com/googleapis/gnostic v0.2.0 // indirect github.com/gophercloud/gophercloud v0.0.0-20190116032514-258a61a0642d // indirect github.com/gregjones/httpcache v0.0.0-20181110185634-c63ab54fda8f // indirect github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect @@ -55,68 +48,73 @@ require ( github.com/huandu/xstrings v1.2.0 // indirect github.com/imdario/mergo v0.3.6 github.com/jonboulle/clockwork v0.1.0 // indirect - github.com/json-iterator/go v1.1.5 // indirect - github.com/juju/ratelimit v1.0.1 // indirect github.com/ksonnet/ksonnet v0.13.1 github.com/ksonnet/ksonnet-lib v0.1.12 // indirect - github.com/kubernetes/code-generator v0.0.0-20181206115026-3a2206dd6a78 // indirect github.com/mitchellh/go-homedir v1.0.0 github.com/mitchellh/go-wordwrap v1.0.0 // indirect - github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.1 // indirect - github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d // indirect github.com/onrik/logrus v0.2.1 - github.com/onsi/ginkgo v1.7.0 // indirect github.com/onsi/gomega v1.4.3 github.com/opencontainers/go-digest v1.0.0-rc1 // indirect - github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pkg/errors v0.8.1 // indirect github.com/prometheus/client_golang v0.9.2 - github.com/russross/blackfriday v1.5.2-0.20180428102519-11635eb403ff // indirect + github.com/russross/blackfriday v0.0.0-00010101000000-000000000000 // indirect github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c // indirect - github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect github.com/sirupsen/logrus v1.3.0 github.com/spf13/afero v1.2.0 github.com/spf13/cobra v0.0.3 github.com/spf13/pflag v1.0.3 github.com/spf13/viper v1.3.1 + go.opencensus.io v0.18.1-0.20181204023538-aab39bd6a98b // indirect + golang.org/x/crypto v0.0.0 golang.org/x/net v0.0.0-20190110200230-915654e7eabc golang.org/x/oauth2 v0.0.0-20190115181402-5dab4167f31c golang.org/x/time v0.0.0-20181108054448-85acf8d2951c // indirect google.golang.org/api v0.1.0 google.golang.org/genproto v0.0.0-20190111180523-db91494dd46c - gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect gopkg.in/resty.v1 v1.11.0 - gopkg.in/square/go-jose.v2 v2.2.2 // indirect + gopkg.in/square/go-jose.v2 v2.3.0 // indirect gopkg.in/yaml.v2 v2.2.2 k8s.io/api v0.0.0-20180308224125-73d903622b73 + k8s.io/api/v2 v2.0.0 k8s.io/apiextensions-apiserver v0.0.0-20190116054503-cf30b7cf64c2 + k8s.io/apiextensions-apiserver/v2 v2.0.0 k8s.io/apimachinery v0.0.0-20190111195121-fa6ddc151d63 - k8s.io/apiserver v0.0.0-20190116053355-a39732bdd925 // indirect + k8s.io/apimachinery/v2 v2.0.0 k8s.io/client-go v10.0.0+incompatible - k8s.io/code-generator v0.0.0-20180601180426-9de8e796a74d // indirect - k8s.io/gengo v0.0.0-20190116091435-f8a0810f38af // indirect - k8s.io/helm v2.12.3+incompatible // indirect - k8s.io/klog v0.1.0 // indirect + k8s.io/client-go/v2 v2.0.0 + k8s.io/helm v2.13.0+incompatible // indirect k8s.io/kube-openapi v0.0.0-20190115222348-ced9eb3070a5 // indirect - k8s.io/kubernetes v1.10.2 // indirect - k8s.io/utils v0.0.0-20181221173059-8a16e7dd8fb6 // indirect + k8s.io/kubernetes v0.0.0-00010101000000-000000000000 // indirect + k8s.io/utils v0.0.0-20190308190857-21c4ce38f2a7 // indirect sigs.k8s.io/controller-runtime v0.1.1 - sigs.k8s.io/testing_frameworks v0.1.1 // indirect - sigs.k8s.io/yaml v1.1.0 // indirect + sigs.k8s.io/controller-runtime/v2 v2.0.0 + sigs.k8s.io/kustomize/v2 v2.0.0-00010101000000-000000000000 vbom.ml/util v0.0.0-20180919145318-efcd4e0f9787 // indirect ) replace ( github.com/Azure/go-autorest => github.com/Azure/go-autorest v9.1.0+incompatible + github.com/go-openapi/jsonpointer => github.com/go-openapi/jsonpointer v0.17.0 + github.com/go-openapi/jsonreference => github.com/go-openapi/jsonreference v0.17.0 + github.com/go-openapi/spec => github.com/go-openapi/spec v0.18.0 + github.com/go-openapi/swag => github.com/go-openapi/swag v0.17.0 github.com/mitchellh/go-homedir => github.com/mitchellh/go-homedir v1.0.0 github.com/russross/blackfriday => github.com/russross/blackfriday v1.5.2-0.20180428102519-11635eb403ff // indirect - k8s.io/api => k8s.io/api v0.0.0-20180308224125-73d903622b73 - k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.0.0-20180426153726-e8ab413e0ae1 - k8s.io/apimachinery => k8s.io/apimachinery v0.0.0-20180228050457-302974c03f7e - k8s.io/apiserver => k8s.io/apiserver v0.0.0-20180426121757-0841753fc26e // indirect - k8s.io/client-go => github.com/kubernetes/client-go v6.0.0+incompatible - k8s.io/code-generator/cmd/client-gen => k8s.io/code-generator/cmd/client-gen v0.0.0-20180228050103-7ead8f38b01c - k8s.io/kubernetes => k8s.io/kubernetes v1.10.2+incompatible + golang.org/x/crypto => golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9 + golang.org/x/net => golang.org/x/net v0.0.0-20180124060956-0ed95abb35c4 + k8s.io/api => k8s.io/api v0.0.0-20180601181742-8b7507fac302 + k8s.io/api/v2 => /tmp/v2/k8s.io/api/v2 + k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.0.0-20180601203502-8e7f43002fec + k8s.io/apiextensions-apiserver/v2 => /tmp/v2/k8s.io/apiextensions-apiserver/v2 + k8s.io/apimachinery => k8s.io/apimachinery v0.0.0-20180601181227-17529ec7eadb + k8s.io/apimachinery/v2 => /tmp/v2/k8s.io/apimachinery/v2 + k8s.io/apiserver => k8s.io/apiserver v0.0.0-20180601190550-8378ef881d4f + k8s.io/cli-runtime => k8s.io/cli-runtime v0.0.0-20180907072557-b3b289918979 + k8s.io/client-go => k8s.io/client-go v7.0.0+incompatible + k8s.io/client-go/v2 => /tmp/v2/k8s.io/client-go/v2 + k8s.io/kubernetes => k8s.io/kubernetes v1.10.4 + sigs.k8s.io/controller-runtime => sigs.k8s.io/controller-runtime v0.1.1 + sigs.k8s.io/controller-runtime/v2 => /tmp/v2/sigs.k8s.io/controller-runtime/v2 + sigs.k8s.io/kustomize/v2 => /tmp/v2/sigs.k8s.io/kustomize/v2 ) diff --git a/bootstrap/go.sum b/bootstrap/go.sum index c86592b444e..9f36ad96c88 100644 --- a/bootstrap/go.sum +++ b/bootstrap/go.sum @@ -1,26 +1,41 @@ +bitbucket.org/ww/goautoneg v0.0.0-20120707110453-75cd24fc2f2c/go.mod h1:1vhO7Mn/FZMgOgDVGLy5X1mE6rq1HbkBdkF/yj8zkcg= +cloud.google.com/go v0.0.0-20160913182117-3b1ae45394a2/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= contrib.go.opencensus.io/exporter/ocagent v0.4.2/go.mod h1:YuG83h+XWwqWjvCqn7vK4KSyLKhThY3+gNGQ37iS2V0= git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= +github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/Azure/go-autorest v9.1.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/Azure/go-autorest v10.15.2+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/Azure/go-autorest v11.1.0+incompatible h1:9DfMsQdUMEtg1jKRTjtkNZsvOuZXJOMl4dN1kiQwAc8= +github.com/Azure/go-autorest v11.1.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/GeertJohan/go.rice v0.0.0-20181229193832-0af3f3b09a0a/go.mod h1:DgrzXonpdQbfN3uYaGz1EG4Sbhyum/MMIn6Cphlh2bw= github.com/MakeNowJust/heredoc v0.0.0-20171113091838-e9091a26100e h1:eb0Pzkt15Bm7f2FFYv7sjY7NPFi3cPkS3tv1CcrFBWA= github.com/MakeNowJust/heredoc v0.0.0-20171113091838-e9091a26100e/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E= +github.com/Masterminds/goutils v1.1.0 h1:zukEsf/1JZwCMgHiK3GZftabmxiCw4apj3a28RPBiVg= +github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/semver v1.4.2 h1:WBLTQ37jOCzSLtXNdoo8bNM8876KhNqOKvrlGITgsTc= github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Masterminds/sprig v2.17.1+incompatible h1:PChbxFGKTWsg9IWh+pSZRCSj3zQkVpL6Hd9uWsFwxtc= github.com/Masterminds/sprig v2.17.1+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= +github.com/Masterminds/sprig v2.18.0+incompatible h1:QoGhlbC6pter1jxKnjMFxT8EqsLuDE6FEcNbWEpw+lI= +github.com/Masterminds/sprig v2.18.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= +github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= github.com/NYTimes/gziphandler v1.0.1/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= +github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/aokoli/goutils v1.1.0 h1:jy4ghdcYvs5EIoGssZNslIASX5m+KNMfyyKvRQ0TEVE= github.com/aokoli/goutils v1.1.0/go.mod h1:SijmP0QR8LtwsmDs8Yii5Z/S4trXFGFC2oO5g9DP+DQ= +github.com/appscode/jsonpatch v0.0.0-20190108182946-7c0e3b262f30/go.mod h1:4AJxUpXUhv4N+ziTvIcWWXgeorXpxPZOfk9HdEVr96M= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/aws/aws-sdk-go v1.15.78 h1:LaXy6lWR0YK7LKyuU0QWy2ws/LWTPfYV/UgfiBu4tvY= github.com/aws/aws-sdk-go v1.15.78/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3ATZkfNZeM= +github.com/beorn7/perks v0.0.0-20160229213445-3ac7bf7a47d1/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas= github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4= @@ -29,30 +44,57 @@ github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QH github.com/census-instrumentation/opencensus-proto v0.1.0-0.20181214143942-ba49f56771b8/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cheggaaa/pb v1.0.27/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/coreos/bbolt v1.3.1-coreos.6/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/etcd v3.3.11+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-semver v0.0.0-20180108230905-e214231b295a/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20181031085051-9002847aa142/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cyphar/filepath-securejoin v0.2.2 h1:jCwT2GTP+PY5nBz3c/YL5PAIbusElVrPujOBSCj8xRg= github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= github.com/daaku/go.zipexe v0.0.0-20150329023125-a5fe2436ffcb/go.mod h1:U0vRfAucUOohvdCxt5MWLF+TePIL0xbCkbKIiV8TQCE= +github.com/daaku/go.zipexe v1.0.0 h1:VSOgZtH418pH9L16hC/JrgSNJbbAL26pj7lmD1+CGdY= +github.com/daaku/go.zipexe v1.0.0/go.mod h1:z8IiR6TsVLEYKwXAoE/I+8ys/sDkgTzSL0CLnGVd57E= +github.com/davecgh/go-spew v0.0.0-20170626231645-782f4967f2dc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/deckarep/golang-set v1.7.1 h1:SCQV0S6gTtp6itiFrTqI+pfmJ4LN85S1YzhDf9rTHJQ= github.com/deckarep/golang-set v1.7.1/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ= +github.com/dgrijalva/jwt-go v0.0.0-20160705203006-01aeca54ebda/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/docker/distribution v2.7.0+incompatible h1:neUDAlf3wX6Ml4HdqTrbcOHXtfRN0TFIwt6YFL7N9RU= github.com/docker/distribution v2.7.0+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug= +github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker v0.0.0-20180612054059-a9fbbdc8dd87 h1:a9PI9K38c+lqsMzO5itpsaXd9BhUYWTC9GM7TN5Vn0U= +github.com/docker/docker v0.0.0-20180612054059-a9fbbdc8dd87/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v1.13.1 h1:IkZjBSIc8hBjLpqeAbeE5mca5mNgeatLHBy3GO78BWo= github.com/docker/docker v1.13.1/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-units v0.3.3 h1:Xk8S3Xj5sLGlG5g67hJmYMmUgXv5N4PhkjJHHqrwnTk= github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96 h1:cenwrSVm+Z7QLSV/BsnenAOcDXdX4cMv4wP0B/5QbPg= +github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c h1:ZfSZ3P3BedhKGUhzj7BQlPSU4OvT6tfOKe3DVHzOA7s= github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= +github.com/elazarl/go-bindata-assetfs v0.0.0-20150624150248-3dcc96556217/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4= +github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= +github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/go-restful v2.8.0+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/emicklei/go-restful v2.9.0+incompatible h1:YKhDcF/NL19iSAQcyCATL1MkFXCzxfdaTiuJKr18Ank= +github.com/emicklei/go-restful v2.9.0+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/emicklei/go-restful-swagger12 v0.0.0-20170208215640-dcef7f557305 h1:2vAWk0wMCWb/pYiyat2rRZp5I5ZM+efPlagySNZ3JeM= +github.com/emicklei/go-restful-swagger12 v0.0.0-20170208215640-dcef7f557305/go.mod h1:qr0VowGBT4CS4Q8vFF8BSeKz34PuqKGxs/L0IAQA9DQ= github.com/emicklei/go-restful-swagger12 v0.0.0-20170926063155-7524189396c6/go.mod h1:qr0VowGBT4CS4Q8vFF8BSeKz34PuqKGxs/L0IAQA9DQ= +github.com/emicklei/go-restful/v2 v2.0.0-20180531035034-3658237ded10/go.mod h1:gwwVizmZk4uIMBv4nhnVXCsM2JBhvPqyHlJxjL8t1+o= +github.com/evanphx/json-patch v0.0.0-20180908160633-36442dbdb585/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch v3.0.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch v4.0.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.1.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwCFad8crR9dcMQWvV9Hvulu6hwUh4tWPJnM= github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= @@ -60,45 +102,96 @@ github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8 github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/ghodss/yaml v0.0.0-20180820084758-c7ce16629ff4/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= +github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8 h1:DujepqpGd1hyOd7aW59XpK7Qymp8iy83xq74fLr21is= +github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= +github.com/go-logr/zapr v0.1.0/go.mod h1:tabnROwaDl0UNxkVeFRbY8bwB37GwRv0P8lg6aAiEnk= github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= +github.com/go-openapi/analysis v0.17.2 h1:eYp14J1o8TTSCzndHBtsNuckikV1PfZOSnx4BcBeu0c= +github.com/go-openapi/analysis v0.17.2/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= +github.com/go-openapi/errors v0.17.2 h1:azEQ8Fnx0jmtFF2fxsnmd6I0x6rsweUF63qqSO1NmKk= +github.com/go-openapi/errors v0.17.2/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= +github.com/go-openapi/jsonpointer v0.0.0-20180322222829-3a0015ad55fa/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= +github.com/go-openapi/jsonpointer v0.18.0 h1:KVRzjXpMzgdM4GEMDmDTnGcY5yBwGWreJwmmk4k35yU= +github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= +github.com/go-openapi/jsonreference v0.0.0-20180322222742-3fb327e6747d/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= +github.com/go-openapi/jsonreference v0.18.0 h1:oP2OUNdG1l2r5kYhrfVMXO54gWmzcfAwP/GFuHpNTkE= +github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.17.2 h1:tEXYu6Xc0pevpzzQx5ghrMN9F7IVpN/+u4iD3rkYE5o= +github.com/go-openapi/loads v0.17.2/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA= +github.com/go-openapi/runtime v0.17.2 h1:/ZK67ikFhQAMFFH/aPu2MaGH7QjP4wHBvHYOVIzDAw0= +github.com/go-openapi/runtime v0.17.2/go.mod h1:QO936ZXeisByFmZEO1IS1Dqhtf4QV1sYYFtIq6Ld86Q= +github.com/go-openapi/spec v0.0.0-20180415031709-bcff419492ee/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= +github.com/go-openapi/spec v0.0.0-20180801175345-384415f06ee2/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= +github.com/go-openapi/spec v0.17.2 h1:eb2NbuCnoe8cWAxhtK6CfMWUYmiFEZJ9Hx3Z2WRwJ5M= +github.com/go-openapi/spec v0.17.2/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= +github.com/go-openapi/strfmt v0.17.0 h1:1isAxYf//QDTnVzbLAMrUK++0k1EjeLJU/gTOR0o3Mc= github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= +github.com/go-openapi/swag v0.0.0-20180405201759-811b1089cde9/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= +github.com/go-openapi/swag v0.0.0-20180715190254-becd2f08beaf/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= +github.com/go-openapi/swag v0.17.2 h1:K/ycE/XTUDFltNHSO32cGRUhrVGJD64o8WgAIZNyc3k= +github.com/go-openapi/swag v0.17.2/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= +github.com/go-openapi/validate v0.17.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= +github.com/gogo/protobuf v0.0.0-20171007142547-342cbe0a0415/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.0.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.1.1 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20180513044358-24b0969c4cb7 h1:u4bArs140e9+AfE52mFHOXVFnOSBJBRlzTHrOPLOIhE= +github.com/golang/groupcache v0.0.0-20180513044358-24b0969c4cb7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20181024230925-c65c006176ff h1:kOkM9whyQYodu09SJ6W3NCsHG7crFaJILQ22Gozp3lg= github.com/golang/groupcache v0.0.0-20181024230925-c65c006176ff/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/google/btree v0.0.0-20160524151835-7d79101e329e/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/google/go-jsonnet v0.12.1/go.mod h1:gVu3UVSfOt5fRFq+dh9duBqXa5905QY8S1QvMNcEIVs= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= github.com/google/uuid v1.0.0 h1:b4Gk+7WdP/d3HZH8EJsZpvV7EtDOgaZLtnaNGIu1adA= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go v2.0.2+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= +github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= +github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/googleapis/gnostic v0.2.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= +github.com/gophercloud/gophercloud v0.0.0-20180330165814-781450b3c4fc/go.mod h1:3WdhXV3rUYy9p6AUW8d94kr+HS62Y4VL9mBnFxsD8q4= +github.com/gophercloud/gophercloud v0.0.0-20180823021621-199db9a3a2bf h1:tw9JwSaByRESzxAlUaumNNexufLbUgN5QpyrjsvdI34= +github.com/gophercloud/gophercloud v0.0.0-20180823021621-199db9a3a2bf/go.mod h1:3WdhXV3rUYy9p6AUW8d94kr+HS62Y4VL9mBnFxsD8q4= github.com/gophercloud/gophercloud v0.0.0-20190116032514-258a61a0642d/go.mod h1:3WdhXV3rUYy9p6AUW8d94kr+HS62Y4VL9mBnFxsD8q4= +github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gregjones/httpcache v0.0.0-20170728041850-787624de3eb7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM= +github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/gregjones/httpcache v0.0.0-20181110185634-c63ab54fda8f/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/grpc-ecosystem/go-grpc-middleware v0.0.0-20180830092908-498ae206fc3c/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v0.0.0-20170330212424-2500245aa611/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.3.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= github.com/hashicorp/go-cleanhttp v0.5.0 h1:wvCrVc9TjDls6+YGAF2hAifE1E5U1+b4tH6KdvN3Gig= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= @@ -108,6 +201,9 @@ github.com/hashicorp/go-safetemp v1.0.0 h1:2HR189eFNrjHQyENnQMMpCiBAsRxzbTMIgBhE github.com/hashicorp/go-safetemp v1.0.0/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoDHxNAB65b+Rj1I= github.com/hashicorp/go-version v1.1.0 h1:bPIoEKD27tNdebFGGxxYwcL4nepeY4j1QP23PFRGzg0= github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/golang-lru v0.0.0-20160207214719-a0d98a5f2880/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.0.0-20180201235237-0fb14efe8c47 h1:UnszMmmmm5vLwWzDjTFVIkfhvWF1NdrmChl8L2NUDCw= +github.com/hashicorp/golang-lru v0.0.0-20180201235237-0fb14efe8c47/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= @@ -116,10 +212,15 @@ github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huandu/xstrings v1.2.0 h1:yPeWdRnmynF7p+lLYz0H2tthW9lqhMJrQV/U7yy4wX0= github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4= +github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8 h1:12VvqtR6Aowv3l/EQUlocDHW2Cp4G9WJVH7uyH8QFJE= github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jonboulle/clockwork v0.0.0-20141017032234-72f9bd7c4e0c h1:XpRROA6ssPlTwJI8/pH+61uieOkcJhmAFz25cu0B94Y= +github.com/jonboulle/clockwork v0.0.0-20141017032234-72f9bd7c4e0c/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/json-iterator/go v0.0.0-20180701071628-ab8a2e0c74be/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/juju/ratelimit v1.0.1/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSgWNm/qk= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= @@ -127,9 +228,12 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxv github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/ksonnet/ksonnet v0.13.1/go.mod h1:T8FwyGOX8fMoDyp845QmYDnk8R7AF35pImyfWJ9fj7M= github.com/ksonnet/ksonnet-lib v0.1.12/go.mod h1:2p66Npe1xOUtQGlGzD5aJ3UHEBjG1b6o3nbC1rVNvz4= -github.com/kubernetes/client-go v6.0.0+incompatible/go.mod h1:kszVi2i+FeqECZHhjpkV5h5zM0GnURfJv897YzgoAQ8= +github.com/kubeflow/kubeflow/bootstrap v0.0.0-20190309091928-11ab94fcbf18 h1:xkoYRxxBB436OBk3xl9kq5uM+Bq1ifGyfJlknKZ4E3k= +github.com/kubeflow/kubeflow/bootstrap v0.0.0-20190309091928-11ab94fcbf18/go.mod h1:FANO9rr+1z67kWOgCuVkk9IhGMI5ceamtJebM/hHcro= github.com/kubernetes/code-generator v0.0.0-20181206115026-3a2206dd6a78/go.mod h1:GXXF2gdS/LDGLo7HilXGuIQfo5nLQDhB4eUn/2UMPnk= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mailru/easyjson v0.0.0-20170624190925-2f5df55504eb/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20180606163543-3fdea8d05856/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= @@ -140,57 +244,98 @@ github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdI github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4= github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= +github.com/mitchellh/mapstructure v0.0.0-20170307201123-53818660ed49/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= github.com/onrik/logrus v0.2.1/go.mod h1:qfe9NeZVAJfIxviw3cYkZo3kvBtLoPRJriAO8zl7qTk= +github.com/onsi/ginkgo v1.6.0 h1:Ix8l273rp3QzYgXSR+c8d1fTG7UPgYkOSELPhiY/YGw= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI= +github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= +github.com/pborman/uuid v0.0.0-20150603214016-ca53cad383ca/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34= +github.com/pborman/uuid v0.0.0-20170612153648-e790cca94e6c/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34= github.com/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/petar/GoLLRB v0.0.0-20130427215148-53be0d36a84c/go.mod h1:HUpKUBZnpzkdx0kD/+Yfuft+uD3zHGtXF/XJB14TUr4= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v0.0.0-20170531130054-e7e903064f5e/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= +github.com/prometheus/client_model v0.0.0-20150212101744-fa8ad6fec335/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/common v0.0.0-20170427095455-13ba4ddd0caa/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/procfs v0.0.0-20170519190837-65c1f6f8f0fc/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/russross/blackfriday v1.5.2-0.20180428102519-11635eb403ff h1:bxenFOpdnKzbA1dhcJpgiwjSw7yqvWWY6huCpmsBfv0= github.com/russross/blackfriday v1.5.2-0.20180428102519-11635eb403ff/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c/go.mod h1:/PevMnwAxekIXwN8qQyfc5gl2NlkB3CQlkizAbOkeBs= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v0.0.0-20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/soheilhy/cmux v0.1.3/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.2.0/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.0-20180319062004-c439c4fa0937/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v0.0.2/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/viper v1.3.1/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v0.0.0-20180319223459-c679ae2cc0cb/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/ugorji/go v0.0.0-20171019201919-bdcc60b419d1/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/ulikunitz/xz v0.5.5 h1:pFrO0lVpTBXLpYw+pnLj6TbvHuyjXMfjGeCwSqCVwok= github.com/ulikunitz/xz v0.5.5/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= +github.com/xiang90/probing v0.0.0-20160813154853-07dd2e8dfe18/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +go.opencensus.io v0.18.0 h1:Mk5rgZcggtbvtAun5aJzAtjKKN/t0R3jJPlWILlv938= go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= go.opencensus.io v0.18.1-0.20181204023538-aab39bd6a98b/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= +go.uber.org/atomic v0.0.0-20181018215023-8dc6146f7569/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/multierr v0.0.0-20180122172545-ddea229ff1df/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/zap v0.0.0-20180814183419-67bc79d13d15/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +golang.org/x/crypto v0.0.0-20180808211826-de0752318171/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20180820150726-614d502a4dac/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/net v0.0.0-20180124060956-0ed95abb35c4 h1:BLERX6fu5dNMZcaGP2RzbrDZpHQbDkAoG9oiTRXbWr0= +golang.org/x/net v0.0.0-20180124060956-0ed95abb35c4/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180821023952-922f4815f713/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -198,21 +343,31 @@ golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e h1:bRhVy7zSSasaqNksaRZiA5EEI+Ei4I1nO5Jh72wfHlg= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190110200230-915654e7eabc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/oauth2 v0.0.0-20170412232759-a6bd8cefa181/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190115181402-5dab4167f31c/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20171031081856-95c657629925/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181213200352-4d1cda033e06 h1:0oC8rFnE+74kEmuHZ46F6KHsMr5Gx2gUQPuNz28iQZM= golang.org/x/sys v0.0.0-20181213200352-4d1cda033e06/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/text v0.0.0-20170810154203-b19bf474d317/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/time v0.0.0-20161028155119-f51c12702a4d/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2 h1:+DCIGbF/swA92ohVg0//6X2IVY3KZs6p9mix0ziNYJM= +golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c h1:fqgJT0MGcGpPgpWU7VRdRjuArfcOvC4AoJmILihzhDg= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= @@ -220,10 +375,12 @@ google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx1 google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20170731182057-09f6ed296fc6/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= google.golang.org/genproto v0.0.0-20190111180523-db91494dd46c/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= +google.golang.org/grpc v1.13.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.15.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= @@ -232,34 +389,52 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/cheggaaa/pb.v1 v1.0.27/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/inf.v0 v0.9.0/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/natefinch/lumberjack.v2 v2.0.0-20150622162204-20b71e5b60d7/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/resty.v1 v1.11.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.2.2 h1:orlkJ3myw8CN1nVQHBFfloD+L3egixIa4FvUP6RosSA= gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/square/go-jose.v2 v2.3.0 h1:nLzhkFyl5bkblqYBoiWJUt5JkWOzmiaBtCxdJAqJd3U= +gopkg.in/square/go-jose.v2 v2.3.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -k8s.io/api v0.0.0-20180308224125-73d903622b73/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA= -k8s.io/apiextensions-apiserver v0.0.0-20180426153726-e8ab413e0ae1 h1:7i7e3Zai1/Zxgf3Ang0Yr89hyd9/Nlrfx63jyP3blpI= -k8s.io/apiextensions-apiserver v0.0.0-20180426153726-e8ab413e0ae1/go.mod h1:IxkesAMoaCRoLrPJdZNZUQp9NfZnzqaVzLhb2VEQzXE= -k8s.io/apimachinery v0.0.0-20180228050457-302974c03f7e/go.mod h1:ccL7Eh7zubPUSh9A3USN90/OzHNSVN6zxzde07TDCL0= -k8s.io/apiserver v0.0.0-20180426121757-0841753fc26e h1:Kx5WFa3bGbM4WoxVI4q+yQ21RVnq+nG5DDF+GMGLNqY= -k8s.io/apiserver v0.0.0-20180426121757-0841753fc26e/go.mod h1:6bqaTSOSJavUIXUtfaR9Os9JtTCm8ZqH2SUl2S60C4w= +k8s.io/api v0.0.0-20180601181742-8b7507fac302 h1:Qt1IyBlZWyVK+Xa3yoHb0G7sW6E3+7Zem32d4siDB1M= +k8s.io/api v0.0.0-20180601181742-8b7507fac302/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA= +k8s.io/apiextensions-apiserver v0.0.0-20180601203502-8e7f43002fec h1:4IEgYOmlFYZPBdm6KW6UO6mxqTAoAaSuighdxs9sO38= +k8s.io/apiextensions-apiserver v0.0.0-20180601203502-8e7f43002fec/go.mod h1:IxkesAMoaCRoLrPJdZNZUQp9NfZnzqaVzLhb2VEQzXE= +k8s.io/apimachinery v0.0.0-20180601181227-17529ec7eadb h1:wNISiSgK+4mXexgcfqVaOQgIAOwVzlUGizWC9XCNEQA= +k8s.io/apimachinery v0.0.0-20180601181227-17529ec7eadb/go.mod h1:ccL7Eh7zubPUSh9A3USN90/OzHNSVN6zxzde07TDCL0= +k8s.io/apiserver v0.0.0-20180601190550-8378ef881d4f h1:e+QYJqBV0fPJs2pVcUUta5c42uoOferFgGIA++LmCfU= +k8s.io/apiserver v0.0.0-20180601190550-8378ef881d4f/go.mod h1:6bqaTSOSJavUIXUtfaR9Os9JtTCm8ZqH2SUl2S60C4w= +k8s.io/client-go v7.0.0+incompatible h1:kiH+Y6hn+pc78QS/mtBfMJAMIIaWevHi++JvOGEEQp4= +k8s.io/client-go v7.0.0+incompatible/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s= k8s.io/code-generator v0.0.0-20180601180426-9de8e796a74d/go.mod h1:MYiN+ZJZ9HkETbgVZdWw2AsuAi9PZ4V80cwfuf2axe8= k8s.io/gengo v0.0.0-20190116091435-f8a0810f38af/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/helm v2.12.3+incompatible/go.mod h1:LZzlS4LQBHfciFOurYBFkCMTaZ0D1l+p0teMg7TSULI= +k8s.io/helm v2.13.0+incompatible h1:d1WBmGGoVb5VZcmQbysDbXGR0Kh/IXPe1SXldrdu19U= +k8s.io/helm v2.13.0+incompatible/go.mod h1:LZzlS4LQBHfciFOurYBFkCMTaZ0D1l+p0teMg7TSULI= +k8s.io/klog v0.0.0-20181108234604-8139d8cb77af/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +k8s.io/klog v0.1.0 h1:I5HMfc/DtuVaGR1KPwUrTc476K8NCqNBldC7H4dYEzk= k8s.io/klog v0.1.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +k8s.io/kube-openapi v0.0.0-20180731170545-e3762e86a74c/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc= +k8s.io/kube-openapi v0.0.0-20181109181836-c59034cc13d5 h1:MH8SvyTlIiLt8b1oHy4Dtp1zPpLGp6lTOjvfzPTkoQE= +k8s.io/kube-openapi v0.0.0-20181109181836-c59034cc13d5/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc= k8s.io/kube-openapi v0.0.0-20190115222348-ced9eb3070a5/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc= -k8s.io/kubernetes v1.10.2+incompatible h1:7HdUQB9WQUY4vNed/xJ7E14rpstEE6xIco9PrLbaMG4= -k8s.io/kubernetes v1.10.2+incompatible/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= +k8s.io/kubernetes v1.10.4 h1:TKl0iQgPMIr3pXlekPtLIDTyr8UxIyZ199et5P3jZtw= +k8s.io/kubernetes v1.10.4/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= k8s.io/utils v0.0.0-20181221173059-8a16e7dd8fb6 h1:+jRzzMyx+I9J18BvwHYmZ5hpPwoZfh6g39WfNlsMCkY= k8s.io/utils v0.0.0-20181221173059-8a16e7dd8fb6/go.mod h1:8k8uAuAQ0rXslZKaEWd0c3oVhZz7sSzSiPnVZayjIX0= +k8s.io/utils v0.0.0-20190308190857-21c4ce38f2a7 h1:8r+l4bNWjRlsFYlQJnKJ2p7s1YQPj4XyXiJVqDHRx7c= +k8s.io/utils v0.0.0-20190308190857-21c4ce38f2a7/go.mod h1:8k8uAuAQ0rXslZKaEWd0c3oVhZz7sSzSiPnVZayjIX0= sigs.k8s.io/controller-runtime v0.1.1/go.mod h1:HFAYoOh6XMV+jKF1UjFwrknPbowfyHEHHRdJMf2jMX8= sigs.k8s.io/testing_frameworks v0.1.1 h1:cP2l8fkA3O9vekpy5Ks8mmA0NW/F7yBdXf8brkWhVrs= sigs.k8s.io/testing_frameworks v0.1.1/go.mod h1:VVBKrHmJ6Ekkfz284YKhQePcdycOzNH9qL6ht1zEr/U= +sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= vbom.ml/util v0.0.0-20180919145318-efcd4e0f9787 h1:O69FD9pJA4WUZlEwYatBEEkRWKQ5cKodWpdKTrCS/iQ= vbom.ml/util v0.0.0-20180919145318-efcd4e0f9787/go.mod h1:so/NYdZXCz+E3ZpW0uAoCj6uzU2+8OWDFv/HxUSs7kI= diff --git a/bootstrap/hack/v2.zip b/bootstrap/hack/v2.zip new file mode 100644 index 00000000000..d3f73fb76ca Binary files /dev/null and b/bootstrap/hack/v2.zip differ diff --git a/bootstrap/pkg/apis/addtoscheme_apps_v1alpha1.go b/bootstrap/pkg/apis/addtoscheme_apps_v1alpha1.go index c46fb4b906a..bd869aada25 100644 --- a/bootstrap/pkg/apis/addtoscheme_apps_v1alpha1.go +++ b/bootstrap/pkg/apis/addtoscheme_apps_v1alpha1.go @@ -17,12 +17,10 @@ package apis import ( client "github.com/kubeflow/kubeflow/bootstrap/pkg/apis/apps/client/v1alpha1" gcp "github.com/kubeflow/kubeflow/bootstrap/pkg/apis/apps/gcp/v1alpha1" - ksonnet "github.com/kubeflow/kubeflow/bootstrap/pkg/apis/apps/ksonnet/v1alpha1" ) func init() { // Register the types with the Scheme so the components can map objects to GroupVersionKinds and back - AddToSchemes = append(AddToSchemes, ksonnet.SchemeBuilder.AddToScheme) AddToSchemes = append(AddToSchemes, gcp.SchemeBuilder.AddToScheme) AddToSchemes = append(AddToSchemes, client.SchemeBuilder.AddToScheme) diff --git a/bootstrap/pkg/apis/apps/client/v1alpha1/application_types.go b/bootstrap/pkg/apis/apps/client/v1alpha1/application_types.go index a97dad795ca..ce6e038f1c7 100644 --- a/bootstrap/pkg/apis/apps/client/v1alpha1/application_types.go +++ b/bootstrap/pkg/apis/apps/client/v1alpha1/application_types.go @@ -15,20 +15,16 @@ package v1alpha1 import ( - "github.com/kubeflow/kubeflow/bootstrap/pkg/apis/apps" + "github.com/kubeflow/kubeflow/bootstrap/config" "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) // ClientSpec holds common attributes used by each platform type ClientSpec struct { - AppDir string `json:"appdir,omitempty"` - Platform string `json:"platform,omitempty"` - Version string `json:"version,omitempty"` - Repo string `json:"repo,omitempty"` - Components []string `json:"components,omitempty"` - Packages []string `json:"packages,omitempty"` - Parameters map[string][]apps.NameValue `json:"parameters,omitempty"` + config.ComponentConfig `json:",inline"` + AppDir string `json:"appdir,omitempty"` + Version string `json:"version,omitempty"` } // ClientStatus defines the observed state of Client diff --git a/bootstrap/pkg/apis/apps/client/v1alpha1/zz_generated.deepcopy.go b/bootstrap/pkg/apis/apps/client/v1alpha1/zz_generated.deepcopy.go index 8d7dd6c22a3..547808a01aa 100644 --- a/bootstrap/pkg/apis/apps/client/v1alpha1/zz_generated.deepcopy.go +++ b/bootstrap/pkg/apis/apps/client/v1alpha1/zz_generated.deepcopy.go @@ -1,27 +1,24 @@ // +build !ignore_autogenerated -/* -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. -*/ +// Copyright 2018 The Kubeflow 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. // Code generated by deepcopy-gen. DO NOT EDIT. package v1alpha1 import ( - apps "github.com/kubeflow/kubeflow/bootstrap/pkg/apis/apps" runtime "k8s.io/apimachinery/pkg/runtime" ) @@ -107,31 +104,7 @@ func (in *ClientList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ClientSpec) DeepCopyInto(out *ClientSpec) { *out = *in - if in.Components != nil { - in, out := &in.Components, &out.Components - *out = make([]string, len(*in)) - copy(*out, *in) - } - if in.Packages != nil { - in, out := &in.Packages, &out.Packages - *out = make([]string, len(*in)) - copy(*out, *in) - } - if in.Parameters != nil { - in, out := &in.Parameters, &out.Parameters - *out = make(map[string][]apps.NameValue, len(*in)) - for key, val := range *in { - var outVal []apps.NameValue - if val == nil { - (*out)[key] = nil - } else { - in, out := &val, &outVal - *out = make([]apps.NameValue, len(*in)) - copy(*out, *in) - } - (*out)[key] = outVal - } - } + in.ComponentConfig.DeepCopyInto(&out.ComponentConfig) return } diff --git a/bootstrap/pkg/apis/apps/gcp/v1alpha1/zz_generated.deepcopy.go b/bootstrap/pkg/apis/apps/gcp/v1alpha1/zz_generated.deepcopy.go index 8dca8cb508c..bc26e506811 100644 --- a/bootstrap/pkg/apis/apps/gcp/v1alpha1/zz_generated.deepcopy.go +++ b/bootstrap/pkg/apis/apps/gcp/v1alpha1/zz_generated.deepcopy.go @@ -1,20 +1,18 @@ // +build !ignore_autogenerated -/* -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. -*/ +// Copyright 2018 The Kubeflow 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. // Code generated by deepcopy-gen. DO NOT EDIT. diff --git a/bootstrap/pkg/apis/apps/group.go b/bootstrap/pkg/apis/apps/group.go index 2b98695d428..095e9b1dad6 100644 --- a/bootstrap/pkg/apis/apps/group.go +++ b/bootstrap/pkg/apis/apps/group.go @@ -16,37 +16,26 @@ package apps import ( - "encoding/base64" - - "cloud.google.com/go/container/apiv1" "fmt" log "github.com/sirupsen/logrus" - "golang.org/x/net/context" - "golang.org/x/oauth2/google" - "google.golang.org/api/iam/v1" - "google.golang.org/api/option" - containerpb "google.golang.org/genproto/googleapis/container/v1" "io" ext "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" - "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" - apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1beta1" + crdclientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" + apiext "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1beta1" "k8s.io/client-go/kubernetes" + clientset "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" clientcmdapi "k8s.io/client-go/tools/clientcmd/api" - "math/rand" "os" "path/filepath" - /* PLUGINS "plugin" - /* PLUGINS */ "regexp" "strings" ) const ( DefaultNamespace = "kubeflow" - DefaultPlatform = "none" // TODO: find the latest tag dynamically DefaultVersion = "master" DefaultGitRepo = "https://github.com/kubeflow/kubeflow/tarball" @@ -78,81 +67,24 @@ const ( MOUNT_LOCAL CliOption = "mount-local" SKIP_INIT_GCP_PROJECT CliOption = "skip-init-gcp-project" VERBOSE CliOption = "verbose" - NAMESPACE CliOption = "namespace" - VERSION CliOption = "version" - REPO CliOption = "repo" - PROJECT CliOption = "project" - APPNAME CliOption = "appname" - APPDIR CliOption = "appDir" - DATA CliOption = "Data" - ZONE CliOption = "zone" - USE_BASIC_AUTH CliOption = "use_basic_auth" - OAUTH_ID CliOption = "oauth_id" - OAUTH_SECRET CliOption = "oauth_secret" - DEFAULT_CONFIG CliOption = "default_config" + NAMESPACE CliOption = "namespace" + VERSION CliOption = "version" + REPO CliOption = "repo" + PROJECT CliOption = "project" + APPNAME CliOption = "appname" + APPDIR CliOption = "appDir" + DATA CliOption = "Data" + ZONE CliOption = "zone" + USE_BASIC_AUTH CliOption = "use_basic_auth" + OAUTH_ID CliOption = "oauth_id" + OAUTH_SECRET CliOption = "oauth_secret" + CONFIG CliOption = "config" ) -var DefaultPackages = []string{ - "application", - "argo", - "common", - "examples", - "jupyter", - "katib", - "metacontroller", - "modeldb", - "mpi-job", - "openvino", - "pipeline", - "profiles", - "pytorch-job", - "seldon", - "tensorboard", - "tf-serving", - "tf-training", -} -var DefaultComponents = []string{ - "ambassador", - "application", - "argo", - "centraldashboard", - "jupyter", - "jupyter-web-app", - "katib", - "metacontroller", - "notebooks", - "notebook-controller", - "openvino", - "pipeline", - "profiles", - "pytorch-operator", - "spartakus", - "tensorboard", - "tf-job-operator", -} - -var DefaultParameters = map[string][]NameValue{ - "spartakus": { - NameValue{ - Name: "usageId", - Value: fmt.Sprintf("%08d", 10000000+rand.Intn(90000000)), - }, - NameValue{ - Name: "reportUsage", - Value: "true", - }, - }, -} - -type NameValue struct { - Name string `json:"name,omitempty"` - Value string `json:"value,omitempty"` -} - // // KfApp provides a common // API for platforms like gcp or minikube -// They all implementation the API below +// They all implement the API below // type KfApp interface { Apply(resources ResourceEnum, options map[string]interface{}) error @@ -161,6 +93,13 @@ type KfApp interface { Init(resources ResourceEnum, options map[string]interface{}) error } +// +// This is used in the ksonnet implementation for `ks show` +// +type KfShow interface { + Show(resources ResourceEnum, options map[string]interface{}) error +} + func QuoteItems(items []string) []string { var withQuotes []string for _, item := range items { @@ -189,16 +128,13 @@ func RemoveItems(defaults []string, names ...string) []string { return pkgs } +// Platforms const ( - DOCKER_FOR_DESKTOP = "docker-for-desktop" - GCP = "gcp" - NONE = DefaultPlatform - MINIKUBE = "minikube" + GCP = "gcp" + MINIKUBE = "minikube" ) -func LoadPlatform(options map[string]interface{}) (KfApp, error) { - /* PLUGINS - platform := options[string(PLATFORM)].(string) +func LoadKfApp(platform string, options map[string]interface{}) (KfApp, error) { platform = strings.Replace(platform, "-", "", -1) plugindir := os.Getenv("PLUGINS_ENVIRONMENT") pluginpath := filepath.Join(plugindir, platform+".so") @@ -212,10 +148,6 @@ func LoadPlatform(options map[string]interface{}) (KfApp, error) { return nil, fmt.Errorf("could not find symbol %v for platform %v Error %v", symName, platform, symbolErr) } return symbol.(func(map[string]interface{}) KfApp)(options), nil - /* PLUGINS */ - // STATIC - return nil, fmt.Errorf("could not load platform") - // -STATIC // } // TODO(#2586): Consolidate kubeconfig and API calls. @@ -236,128 +168,56 @@ func KubeConfigPath() string { return kubeconfigEnv } -func GetClusterInfo(ctx context.Context, project string, location string, - cluster string) (*containerpb.Cluster, error) { - ts, err := google.DefaultTokenSource(ctx, iam.CloudPlatformScope) - if err != nil { - return nil, fmt.Errorf("Get token error: %v", err) - } - c, err := container.NewClusterManagerClient(ctx, option.WithTokenSource(ts)) - if err != nil { - return nil, err - } - getClusterReq := &containerpb.GetClusterRequest{ - ProjectId: project, - Zone: location, - ClusterId: cluster, - } - return c.GetCluster(ctx, getClusterReq) -} - -func BuildConfigFromClusterInfo(ctx context.Context, cluster *containerpb.Cluster) (*rest.Config, error) { - ts, err := google.DefaultTokenSource(ctx, iam.CloudPlatformScope) - if err != nil { - return nil, fmt.Errorf("Get token error: %v", err) - } - t, err := ts.Token() - if err != nil { - return nil, fmt.Errorf("Token retrieval error: %v", err) - } - caDec, _ := base64.StdEncoding.DecodeString(cluster.MasterAuth.ClusterCaCertificate) - config := &rest.Config{ - Host: "https://" + cluster.Endpoint, - BearerToken: t.AccessToken, - TLSClientConfig: rest.TLSClientConfig{ - CAData: []byte(string(caDec)), - }, - } - return config, nil -} - -// BuildOutOfClusterConfig returns k8s config -func BuildOutOfClusterConfig() (*rest.Config, error) { +// GetConfig returns rest.Config using $HOME/.kube/config +func GetConfig() (*rest.Config) { loadingRules := clientcmd.NewDefaultClientConfigLoadingRules() loadingRules.ExplicitPath = KubeConfigPath() - config, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig( - loadingRules, &clientcmd.ConfigOverrides{}).ClientConfig() + overrides := &clientcmd.ConfigOverrides{} + config, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, overrides).ClientConfig() if err != nil { - return nil, err + log.Fatalf("could not open %v Error %v", loadingRules.ExplicitPath, err) } - return config, nil + return config } -func ServerVersionWithConfig(client *rest.Config) (host string, version string, err error) { - clnt, clntErr := kubernetes.NewForConfig(client) - if clntErr != nil { - return "", "", fmt.Errorf("couldn't get clientset. Error: %v", err) - } - serverVersion, serverVersionErr := clnt.ServerVersion() +func GetServerVersion(c *clientset.Clientset) string { + serverVersion, serverVersionErr := c.ServerVersion() if serverVersionErr != nil { - return "", "", fmt.Errorf("couldn't get server version info. Error: %v", serverVersionErr) + log.Fatalf("couldn't get server version info. Error: %v", serverVersionErr) } re := regexp.MustCompile("^v[0-9]+.[0-9]+.[0-9]+") - version = re.FindString(serverVersion.String()) - return client.Host, "version:" + version, nil + version := re.FindString(serverVersion.String()) + return "version:"+version } -func ServerVersion() (host string, version string, err error) { - restApi, err := BuildOutOfClusterConfig() - if err != nil { - return "", "", fmt.Errorf("couldn't build out-of-cluster config. Error: %v", err) - } - clnt, clntErr := kubernetes.NewForConfig(restApi) - if clntErr != nil { - return "", "", fmt.Errorf("couldn't get clientset. Error: %v", err) - } - serverVersion, serverVersionErr := clnt.ServerVersion() - if serverVersionErr != nil { - return "", "", fmt.Errorf("couldn't get server version info. Error: %v", serverVersionErr) - } - re := regexp.MustCompile("^v[0-9]+.[0-9]+.[0-9]+") - version = re.FindString(serverVersion.String()) - return restApi.Host, "version:" + version, nil -} - -func GetClientConfig() (*clientcmdapi.Config, error) { +// Get $HOME/.kube/config +func GetKubeConfig() *clientcmdapi.Config { kubeconfig := KubeConfigPath() - log.Infof("Reading config: %v", kubeconfig) config, configErr := clientcmd.LoadFromFile(kubeconfig) if configErr != nil { - return nil, fmt.Errorf("could not load config Error: %v", configErr) - + log.Fatalf("could not load config Error: %v", configErr) } - return config, nil + return config } -// GetClientOutOfCluster returns a k8s clientset to the request from outside of cluster -func GetClientOutOfCluster() (kubernetes.Interface, error) { - config, err := BuildOutOfClusterConfig() - if err != nil { - log.Fatalf("Can not get kubernetes config: %v", err) - } - +// GetClientset returns a k8s clientset using .kube/config credentials +func GetClientset(config *rest.Config) *clientset.Clientset { clientset, err := kubernetes.NewForConfig(config) if err != nil { log.Fatalf("Can not get kubernetes client: %v", err) } - - return clientset, nil + return clientset } -// Gets a client which can query for CRDs -func GetApiExtensionsClientOutOfCluster() (apiextensionsv1beta1.ApiextensionsV1beta1Interface, error) { - config, err := BuildOutOfClusterConfig() - if err != nil { - log.Fatalf("Can not get kubernetes config: %v", err) - } +// Gets a clientset which can query for CRDs +func GetApiExtClientset(config *rest.Config) apiext.ApiextensionsV1beta1Interface { v := ext.SchemeGroupVersion config.GroupVersion = &v - crdClient, err := clientset.NewForConfig(config) + crdClient, err := crdclientset.NewForConfig(config) if err != nil { - log.Fatalf("Can not get dynamic client: %v", err) + log.Fatalf("Can not get apiextensions client: %v", err) } - - return crdClient.ApiextensionsV1beta1(), nil + return crdClient.ApiextensionsV1beta1() } // Capture replaces os.Stdout with a writer that buffers any data written diff --git a/bootstrap/pkg/apis/apps/ksonnet/v1alpha1/zz_generated.deepcopy.go b/bootstrap/pkg/apis/apps/ksonnet/v1alpha1/zz_generated.deepcopy.go index bf81e65e095..6ad2dbcd837 100644 --- a/bootstrap/pkg/apis/apps/ksonnet/v1alpha1/zz_generated.deepcopy.go +++ b/bootstrap/pkg/apis/apps/ksonnet/v1alpha1/zz_generated.deepcopy.go @@ -1,20 +1,18 @@ // +build !ignore_autogenerated -/* -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. -*/ +// Copyright 2018 The Kubeflow 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. // Code generated by deepcopy-gen. DO NOT EDIT. @@ -53,6 +51,11 @@ func (in *AppConfig) DeepCopyInto(out *AppConfig) { *out = make([]KsParameter, len(*in)) copy(*out, *in) } + if in.ApplyParameters != nil { + in, out := &in.ApplyParameters, &out.ApplyParameters + *out = make([]KsParameter, len(*in)) + copy(*out, *in) + } return } diff --git a/bootstrap/pkg/client/coordinator/coordinator.go b/bootstrap/pkg/client/coordinator/coordinator.go index abfbc3f503b..2eb0fddc8af 100644 --- a/bootstrap/pkg/client/coordinator/coordinator.go +++ b/bootstrap/pkg/client/coordinator/coordinator.go @@ -19,11 +19,15 @@ package coordinator import ( "fmt" "github.com/ghodss/yaml" + gogetter "github.com/hashicorp/go-getter" kftypes "github.com/kubeflow/kubeflow/bootstrap/pkg/apis/apps" cltypes "github.com/kubeflow/kubeflow/bootstrap/pkg/apis/apps/client/v1alpha1" "github.com/kubeflow/kubeflow/bootstrap/pkg/client/gcp" "github.com/kubeflow/kubeflow/bootstrap/pkg/client/ksonnet" + "github.com/kubeflow/kubeflow/bootstrap/pkg/client/minikube" + "github.com/kubeflow/kubeflow/bootstrap/v2/pkg/client/kustomize" "github.com/mitchellh/go-homedir" + log "github.com/sirupsen/logrus" "io/ioutil" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "os" @@ -31,11 +35,6 @@ import ( "path/filepath" "regexp" "strings" - // STATIC - "github.com/kubeflow/kubeflow/bootstrap/pkg/client/dockerfordesktop" - - "github.com/kubeflow/kubeflow/bootstrap/pkg/client/minikube" - log "github.com/sirupsen/logrus" ) // The common entry point used to retrieve an implementation of KfApp. @@ -43,27 +42,35 @@ import ( // platform and ksonnet implementations in Children. func GetKfApp(options map[string]interface{}) kftypes.KfApp { _client := &coordinator{ - Children: make(map[string]kftypes.KfApp), + Platforms: make(map[string]kftypes.KfApp), + PackageManagers: make(map[string]kftypes.KfApp), Client: &cltypes.Client{ TypeMeta: metav1.TypeMeta{ Kind: "Client", APIVersion: "client.apps.kubeflow.org/v1alpha1", }, + Spec: cltypes.ClientSpec{}, }, } - _client.Children[kftypes.NONE] = ksonnet.GetKfApp(options) - platform := options[string(kftypes.PLATFORM)].(string) - if !(platform == "" || platform == kftypes.NONE) { - _platform, _platformErr := GetPlatform(options) - if _platformErr != nil { - log.Fatalf("could not get platform %v Error %v **", platform, _platformErr) - return nil + // options[string(kftypes.DATA)] value is the contents of app.yaml + // called from LoadKfApp. Subcommands: {Apply, Delete, Generate} + if options[string(kftypes.DATA)] != nil { + dat := options[string(kftypes.DATA)].([]byte) + specErr := yaml.Unmarshal(dat, _client.Client) + if specErr != nil { + log.Errorf("couldn't unmarshal app.yaml. Error: %v", specErr) } - if _platform != nil { - _client.Children[platform] = _platform + } + // options[string(kftypes.DATA)] value is the contents of one of the config files in bootstrap/config/ + // called from NewKfApp Subcommands: {Init} + if options[string(kftypes.CONFIG)] != nil { + dat := options[string(kftypes.CONFIG)].([]byte) + configErr := yaml.Unmarshal(dat, &_client.Client.Spec) + if configErr != nil { + log.Errorf("couldn't unmarshal config file. Error: %v", configErr) } } - _client.Client.Spec.Platform = options[string(kftypes.PLATFORM)].(string) + // CLI options, REPO will override the CONFIG file value if options[string(kftypes.APPNAME)] != nil { _client.Client.Name = options[string(kftypes.APPNAME)].(string) } @@ -87,33 +94,125 @@ func GetKfApp(options map[string]interface{}) kftypes.KfApp { kubeflowVersion := options[string(kftypes.VERSION)].(string) _client.Client.Spec.Version = kubeflowVersion } - if options[string(kftypes.DATA)] != nil { - dat := options[string(kftypes.DATA)].([]byte) - specErr := yaml.Unmarshal(dat, _client.Client) - if specErr != nil { - log.Errorf("couldn't unmarshal Ksonnet. Error: %v", specErr) + // fetch the platform [gcp,minikube] + platform := options[string(kftypes.PLATFORM)].(string) + if platform != "" { + _platform, _platformErr := GetPlatform(platform, options) + if _platformErr != nil { + log.Fatalf("could not get platform %v Error %v **", platform, _platformErr) + return nil + } + if _platform != nil { + _client.Platforms[platform] = _platform } + _client.Client.Spec.Platform = options[string(kftypes.PLATFORM)].(string) + } + //TODO we need a way to specific different types of package managers + // most likely by specifying them in the app.yaml per package + // + _packagemanager, _packagemanagerErr := GetPackageManager("ksonnet", options) + if _packagemanagerErr != nil { + log.Fatalf("could not get packagemanager %v Error %v **", "ksonnet", _packagemanagerErr) + + } + if _packagemanager != nil { + _client.PackageManagers["ksonnet"] = _packagemanager + } + _packagemanager, _packagemanagerErr = GetPackageManager("kustomize", options) + if _packagemanagerErr != nil { + log.Fatalf("could not get packagemanager %v Error %v **", "kustomize", _packagemanagerErr) + + } + if _packagemanager != nil { + _client.PackageManagers["kustomize"] = _packagemanager } return _client } +func downloadToCache(platform string, appDir string, version string, useBasicAuth bool) ([]byte, error) { + if _, err := os.Stat(appDir); os.IsNotExist(err) { + appdirErr := os.Mkdir(appDir, os.ModePerm) + if appdirErr != nil { + log.Fatalf("couldn't create directory %v Error %v", appDir, appdirErr) + } + } + cacheDir := path.Join(appDir, kftypes.DefaultCacheDir) + cacheDirErr := os.Mkdir(cacheDir, os.ModePerm) + if cacheDirErr != nil { + return nil, fmt.Errorf("couldn't create directory %v Error %v", cacheDir, cacheDirErr) + } + // Version can be + // --version master + // --version tag + // --version pull//head + tarballUrl := kftypes.DefaultGitRepo + "/" + version + "?archive=tar.gz" + tarballUrlErr := gogetter.GetAny(cacheDir, tarballUrl) + if tarballUrlErr != nil { + return nil, fmt.Errorf("couldn't download kubeflow repo %v Error %v", tarballUrl, tarballUrlErr) + } + files, filesErr := ioutil.ReadDir(cacheDir) + if filesErr != nil { + return nil, fmt.Errorf("couldn't read %v Error %v", cacheDir, filesErr) + } + subdir := files[0].Name() + extractedPath := filepath.Join(cacheDir, subdir) + newPath := filepath.Join(cacheDir, version) + if strings.Contains(version, "/") { + parts := strings.Split(version, "/") + versionPath := cacheDir + for i := 0; i < len(parts)-1; i++ { + versionPath = filepath.Join(versionPath, parts[i]) + versionPathErr := os.Mkdir(versionPath, os.ModePerm) + if versionPathErr != nil { + return nil, fmt.Errorf("couldn't create directory %v Error %v", versionPath, versionPathErr) + } + } + } + renameErr := os.Rename(extractedPath, newPath) + if renameErr != nil { + return nil, fmt.Errorf("couldn't rename %v to %v Error %v", extractedPath, newPath, renameErr) + } + //TODO see #2629 + configPath := filepath.Join(newPath, kftypes.DefaultConfigDir) + if platform == "gcp" { + if useBasicAuth { + configPath = filepath.Join(configPath, kftypes.GcpBasicAuth) + } else { + configPath = filepath.Join(configPath, kftypes.GcpIapConfig) + } + } else { + configPath = filepath.Join(configPath, kftypes.DefaultConfigFile) + } + return ioutil.ReadFile(configPath) +} + // GetPlatform will return an implementation of kftypes.KfApp that matches the platform string // It looks for statically compiled-in implementations, otherwise it delegates to -// kftypes.LoadPlatform which will try and dynamically load a .so -func GetPlatform(options map[string]interface{}) (kftypes.KfApp, error) { - platform := options[string(kftypes.PLATFORM)].(string) +// kftypes.LoadKfApp which will try and dynamically load a .so +func GetPlatform(platform string, options map[string]interface{}) (kftypes.KfApp, error) { switch platform { case string(kftypes.MINIKUBE): return minikube.GetKfApp(options), nil case string(kftypes.GCP): return gcp.GetKfApp(options), nil - // STATIC - case string(kftypes.DOCKER_FOR_DESKTOP): - return dockerfordesktop.GetKfApp(options), nil - // -STATIC // default: log.Infof("** loading %v.so for platform %v **", platform, platform) - return kftypes.LoadPlatform(options) + return kftypes.LoadKfApp(platform, options) + } +} + +// GetPackageManager will return an implementation of kftypes.KfApp that matches the packagemanager string +// It looks for statically compiled-in implementations, otherwise it delegates to +// kftypes.LoadKfApp which will try and dynamically load a .so +func GetPackageManager(packagemanager string, options map[string]interface{}) (kftypes.KfApp, error) { + switch packagemanager { + case "ksonnet": + return ksonnet.GetKfApp(options), nil + case "kustomize": + return kustomize.GetKfApp(options), nil + default: + log.Infof("** loading %v.so for package manager %v **", packagemanager, packagemanager) + return kftypes.LoadKfApp(packagemanager, options) } } @@ -154,6 +253,21 @@ and must start and end with an alphanumeric character`, appName) } options[string(kftypes.APPNAME)] = appName options[string(kftypes.APPDIR)] = appDir + platform := options[string(kftypes.PLATFORM)].(string) + version := options[string(kftypes.VERSION)].(string) + if strings.HasPrefix(version, "pull") { + if !strings.HasSuffix(version, "head") { + version = version + "/head" + options[string(kftypes.VERSION)] = version + } + } + useBasicAuth := options[string(kftypes.USE_BASIC_AUTH)].(bool) + + cache, cacheErr := downloadToCache(platform, appDir, version, useBasicAuth) + if cacheErr != nil { + log.Fatalf("could not download repo to cache Error %v", cacheErr) + } + options[string(kftypes.CONFIG)] = cache pApp := GetKfApp(options) return pApp, nil } @@ -180,7 +294,10 @@ func LoadKfApp(options map[string]interface{}) (kftypes.KfApp, error) { data := v.(map[string]interface{}) metadata := data["metadata"].(map[string]interface{}) spec := data["spec"].(map[string]interface{}) - platform := spec["platform"].(string) + platform := "" + if spec["platform"] != nil { + platform = spec["platform"].(string) + } appName = metadata["name"].(string) appDir = spec["appdir"].(string) options[string(kftypes.PLATFORM)] = platform @@ -192,11 +309,13 @@ func LoadKfApp(options map[string]interface{}) (kftypes.KfApp, error) { } // this type holds platform implementations of KfApp and ksonnet (also an implementation of KfApp) -// eg Children[kftypes.GCP], Children[kftypes.MINIKUBE], Children[kftypes.NONE] (ksonnet) +// eg Platforms[kftypes.GCP], Platforms[kftypes.MINIKUBE], PackageManagers["ksonnet"], +// PackageManagers["kustomize"] // The data attributes in cltypes.Client are used by different KfApp implementations type coordinator struct { - Children map[string]kftypes.KfApp - Client *cltypes.Client + Platforms map[string]kftypes.KfApp + PackageManagers map[string]kftypes.KfApp + Client *cltypes.Client } func (kfapp *coordinator) Apply(resources kftypes.ResourceEnum, options map[string]interface{}) error { @@ -206,25 +325,23 @@ func (kfapp *coordinator) Apply(resources kftypes.ResourceEnum, options map[stri case kftypes.PLATFORM: fallthrough case kftypes.ALL: - if !(kfapp.Client.Spec.Platform == "" || kfapp.Client.Spec.Platform == kftypes.NONE) { - platform := kfapp.Children[kfapp.Client.Spec.Platform] + if kfapp.Client.Spec.Platform != "" { + platform := kfapp.Platforms[kfapp.Client.Spec.Platform] if platform != nil { platformErr := platform.Apply(resources, options) if platformErr != nil { - return fmt.Errorf("coordinator Apply failed for %v: %v", kfapp.Client.Spec.Platform, platformErr) + return fmt.Errorf("coordinator Apply failed for %v: %v", + kfapp.Client.Spec.Platform, platformErr) } } else { - return fmt.Errorf("%v not in Children", kfapp.Client.Spec.Platform) + return fmt.Errorf("%v not in Platforms", kfapp.Client.Spec.Platform) } } - none := kfapp.Children[kftypes.NONE] - if none != nil { - noneErr := none.Apply(kftypes.K8S, options) - if noneErr != nil { - return fmt.Errorf("coordinator Apply failed for %v: %v", string(kftypes.NONE), noneErr) + for packageManagerName, packageManager := range kfapp.PackageManagers { + packageManagerErr := packageManager.Apply(kftypes.K8S, options) + if packageManagerErr != nil { + return fmt.Errorf("kfApp Apply failed for %v: %v", packageManagerName, packageManagerErr) } - } else { - return fmt.Errorf("%v not in Children", string(kftypes.NONE)) } } return nil @@ -237,25 +354,23 @@ func (kfapp *coordinator) Delete(resources kftypes.ResourceEnum, options map[str case kftypes.PLATFORM: fallthrough case kftypes.ALL: - if !(kfapp.Client.Spec.Platform == "" || kfapp.Client.Spec.Platform == kftypes.NONE) { - platform := kfapp.Children[kfapp.Client.Spec.Platform] + if kfapp.Client.Spec.Platform != "" { + platform := kfapp.Platforms[kfapp.Client.Spec.Platform] if platform != nil { platformErr := platform.Delete(resources, options) if platformErr != nil { - return fmt.Errorf("coordinator Delete failed for %v: %v", kfapp.Client.Spec.Platform, platformErr) + return fmt.Errorf("coordinator Delete failed for %v: %v", + kfapp.Client.Spec.Platform, platformErr) } } else { - return fmt.Errorf("%v not in Children", kfapp.Client.Spec.Platform) + return fmt.Errorf("%v not in Platforms", kfapp.Client.Spec.Platform) } } - none := kfapp.Children[kftypes.NONE] - if none != nil { - noneErr := none.Delete(kftypes.K8S, options) - if noneErr != nil { - return fmt.Errorf("coordinator Init failed for %v: %v", string(kftypes.NONE), noneErr) + for packageManagerName, packageManager := range kfapp.PackageManagers { + packageManagerErr := packageManager.Delete(kftypes.K8S, options) + if packageManagerErr != nil { + return fmt.Errorf("kfApp Delete failed for %v: %v", packageManagerName, packageManagerErr) } - } else { - return fmt.Errorf("%v not in Children", string(kftypes.NONE)) } } return nil @@ -268,25 +383,23 @@ func (kfapp *coordinator) Generate(resources kftypes.ResourceEnum, options map[s case kftypes.PLATFORM: fallthrough case kftypes.ALL: - if !(kfapp.Client.Spec.Platform == "" || kfapp.Client.Spec.Platform == kftypes.NONE) { - platform := kfapp.Children[kfapp.Client.Spec.Platform] + if kfapp.Client.Spec.Platform != "" { + platform := kfapp.Platforms[kfapp.Client.Spec.Platform] if platform != nil { platformErr := platform.Generate(resources, options) if platformErr != nil { - return fmt.Errorf("coordinator Generate failed for %v: %v", kfapp.Client.Spec.Platform, platformErr) + return fmt.Errorf("coordinator Generate failed for %v: %v", + kfapp.Client.Spec.Platform, platformErr) } } else { - return fmt.Errorf("%v not in Children", kfapp.Client.Spec.Platform) + return fmt.Errorf("%v not in Platforms", kfapp.Client.Spec.Platform) } } - none := kfapp.Children[kftypes.NONE] - if none != nil { - noneGenerateErr := none.Generate(kftypes.K8S, options) - if noneGenerateErr != nil { - return fmt.Errorf("coordinator Generate failed for %v: %v", string(kftypes.NONE), noneGenerateErr) + for packageManagerName, packageManager := range kfapp.PackageManagers { + packageManagerErr := packageManager.Generate(kftypes.K8S, options) + if packageManagerErr != nil { + return fmt.Errorf("coordinator Generate failed for %v: %v", packageManagerName, packageManagerErr) } - } else { - return fmt.Errorf("%v not in Children", string(kftypes.NONE)) } } return nil @@ -299,24 +412,55 @@ func (kfapp *coordinator) Init(resources kftypes.ResourceEnum, options map[strin case kftypes.PLATFORM: fallthrough case kftypes.ALL: - none := kfapp.Children[kftypes.NONE] - if none != nil { - noneErr := none.Init(kftypes.K8S, options) - if noneErr != nil { - return fmt.Errorf("coordinator Init failed for %v: %v", string(kftypes.NONE), noneErr) + for packageManagerName, packageManager := range kfapp.PackageManagers { + packageManagerErr := packageManager.Init(kftypes.K8S, options) + if packageManagerErr != nil { + return fmt.Errorf("kfApp Init failed for %v: %v", packageManagerName, packageManagerErr) } - } else { - return fmt.Errorf("%v not in Children", string(kftypes.NONE)) } - if !(kfapp.Client.Spec.Platform == "" || kfapp.Client.Spec.Platform == kftypes.NONE) { - platform := kfapp.Children[kfapp.Client.Spec.Platform] + if kfapp.Client.Spec.Platform != "" { + platform := kfapp.Platforms[kfapp.Client.Spec.Platform] if platform != nil { platformErr := platform.Init(resources, options) if platformErr != nil { - return fmt.Errorf("coordinator Init failed for %v: %v", kfapp.Client.Spec.Platform, platformErr) + return fmt.Errorf("kfApp Generate failed for %v: %v", + kfapp.Client.Spec.Platform, platformErr) + } + } else { + return fmt.Errorf("%v not in Platforms", kfapp.Client.Spec.Platform) + } + } + } + return nil +} + +func (kfapp *coordinator) Show(resources kftypes.ResourceEnum, options map[string]interface{}) error { + switch resources { + case kftypes.K8S: + fallthrough + case kftypes.PLATFORM: + fallthrough + case kftypes.ALL: + for packageManagerName, packageManager := range kfapp.PackageManagers { + show, ok := packageManager.(kftypes.KfShow) + if ok && show != nil { + showErr := show.Show(kftypes.K8S, options) + if showErr != nil { + return fmt.Errorf("kfApp Show failed for %v: %v", packageManagerName, showErr) + } + } + } + if kfapp.Client.Spec.Platform != "" { + platform := kfapp.Platforms[kfapp.Client.Spec.Platform] + show, ok := platform.(kftypes.KfShow) + if ok && show != nil { + showErr := show.Show(resources, options) + if showErr != nil { + return fmt.Errorf("kfApp Init failed for %v: %v", + kfapp.Client.Spec.Platform, showErr) } } else { - return fmt.Errorf("%v not in Children", kfapp.Client.Spec.Platform) + return fmt.Errorf("%v not in Platforms", kfapp.Client.Spec.Platform) } } } diff --git a/bootstrap/pkg/client/dockerfordesktop/dockerfordesktop.go b/bootstrap/pkg/client/dockerfordesktop/dockerfordesktop.go index 2d726bfa7a3..d85acc973b4 100644 --- a/bootstrap/pkg/client/dockerfordesktop/dockerfordesktop.go +++ b/bootstrap/pkg/client/dockerfordesktop/dockerfordesktop.go @@ -18,20 +18,50 @@ package dockerfordesktop import ( "fmt" + "github.com/ghodss/yaml" + "github.com/kubeflow/kubeflow/bootstrap/config" kftypes "github.com/kubeflow/kubeflow/bootstrap/pkg/apis/apps" + cltypes "github.com/kubeflow/kubeflow/bootstrap/pkg/apis/apps/client/v1alpha1" + log "github.com/sirupsen/logrus" + "io/ioutil" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "os/user" + "path/filepath" "strconv" "strings" ) // DockerForDesktop implements KfApp Interface -// It should include functionality needed for the docker-for-desktop platform +// It should include functionality needed for the dockerfordesktop platform type DockerForDesktop struct { - //Add additional types required for dockerfordesktop platform + cltypes.Client } -func GetKfApp(_ map[string]interface{}) kftypes.KfApp { - return &DockerForDesktop{} +func GetKfApp(options map[string]interface{}) kftypes.KfApp { + _dockerfordesktop := &DockerForDesktop{ + Client: cltypes.Client{ + TypeMeta:metav1.TypeMeta{ + Kind: "Client", + APIVersion: "client.apps.kubeflow.org/v1alpha1", + }, + Spec: cltypes.ClientSpec{}, + }, + } + if options[string(kftypes.DATA)] != nil { + dat := options[string(kftypes.DATA)].([]byte) + specErr := yaml.Unmarshal(dat, _dockerfordesktop) + if specErr != nil { + log.Errorf("couldn't unmarshal Ksonnet. Error: %v", specErr) + } + } + if options[string(kftypes.CONFIG)] != nil { + dat := options[string(kftypes.CONFIG)].([]byte) + specErr := yaml.Unmarshal(dat, &_dockerfordesktop.Spec) + if specErr != nil { + log.Errorf("couldn't unmarshal Ksonnet. Error: %v", specErr) + } + } + return _dockerfordesktop } func (dockerfordesktop *DockerForDesktop) Apply(resources kftypes.ResourceEnum, options map[string]interface{}) error { @@ -51,12 +81,12 @@ func (dockerfordesktop *DockerForDesktop) generate(options map[string]interface{ mountLocal = options[string(kftypes.MOUNT_LOCAL)].(bool) } // remove Katib package and component - kftypes.DefaultPackages = kftypes.RemoveItem(kftypes.DefaultPackages, "katib") - kftypes.DefaultComponents = kftypes.RemoveItem(kftypes.DefaultComponents, "katib") - kftypes.DefaultParameters["application"] = []kftypes.NameValue{ + dockerfordesktop.Spec.Packages = kftypes.RemoveItem(dockerfordesktop.Spec.Packages, "katib") + dockerfordesktop.Spec.Components = kftypes.RemoveItem(dockerfordesktop.Spec.Components, "katib") + dockerfordesktop.Spec.ComponentParams["application"] = []config.NameValue{ { Name: "components", - Value: "[" + strings.Join(kftypes.QuoteItems(kftypes.DefaultComponents), ",") + "]", + Value: "[" + strings.Join(kftypes.QuoteItems(dockerfordesktop.Spec.Components), ",") + "]", }, } usr, err := user.Current() @@ -65,7 +95,7 @@ func (dockerfordesktop *DockerForDesktop) generate(options map[string]interface{ } uid := usr.Uid gid := usr.Gid - kftypes.DefaultParameters["jupyter"] = []kftypes.NameValue{ + dockerfordesktop.Spec.ComponentParams["jupyter"] = []config.NameValue{ { Name: string(kftypes.PLATFORM), Value: platform, @@ -87,7 +117,7 @@ func (dockerfordesktop *DockerForDesktop) generate(options map[string]interface{ Value: gid, }, } - kftypes.DefaultParameters["ambassador"] = []kftypes.NameValue{ + dockerfordesktop.Spec.ComponentParams["ambassador"] = []config.NameValue{ { Name: string(kftypes.PLATFORM), Value: platform, @@ -111,9 +141,27 @@ func (dockerfordesktop *DockerForDesktop) Generate(resources kftypes.ResourceEnu return fmt.Errorf("dockerfordesktop generate failed Error: %v", generateErr) } } + createConfigErr := dockerfordesktop.writeConfigFile(options) + if createConfigErr != nil { + return fmt.Errorf("cannot create config file app.yaml in %v", dockerfordesktop.Client.Spec.AppDir) + } return nil } func (dockerfordesktop *DockerForDesktop) Init(resources kftypes.ResourceEnum, options map[string]interface{}) error { return nil } + +func (dockerfordesktop *DockerForDesktop) writeConfigFile(options map[string]interface{}) error { + buf, bufErr := yaml.Marshal(dockerfordesktop.Client) + if bufErr != nil { + return bufErr + } + options[string(kftypes.CONFIG)] = buf + cfgFilePath := filepath.Join(dockerfordesktop.Client.Spec.AppDir, kftypes.KfConfigFile) + cfgFilePathErr := ioutil.WriteFile(cfgFilePath, buf, 0644) + if cfgFilePathErr != nil { + return cfgFilePathErr + } + return nil +} diff --git a/bootstrap/pkg/client/gcp/gcp.go b/bootstrap/pkg/client/gcp/gcp.go index 432fde24337..6bf7b0226e1 100644 --- a/bootstrap/pkg/client/gcp/gcp.go +++ b/bootstrap/pkg/client/gcp/gcp.go @@ -17,8 +17,8 @@ limitations under the License. package gcp import ( + "cloud.google.com/go/container/apiv1" "encoding/base64" - "fmt" "github.com/cenkalti/backoff" "github.com/ghodss/yaml" @@ -34,7 +34,9 @@ import ( gke "google.golang.org/api/container/v1" "google.golang.org/api/deploymentmanager/v2" "google.golang.org/api/iam/v1" + "google.golang.org/api/option" "google.golang.org/api/serviceusage/v1" + containerpb "google.golang.org/genproto/googleapis/container/v1" "io" "io/ioutil" v1 "k8s.io/api/core/v1" @@ -77,8 +79,8 @@ func GetKfApp(options map[string]interface{}) kftypes.KfApp { _gcp := &Gcp{ GcpApp: &gcptypes.Gcp{ TypeMeta: metav1.TypeMeta{ - Kind: "Gcp", - APIVersion: "gcp.apps.kubeflow.org/v1alpha1", + Kind: "Client", + APIVersion: "client.apps.kubeflow.org/v1alpha1", }, }, } @@ -90,6 +92,13 @@ func GetKfApp(options map[string]interface{}) kftypes.KfApp { return nil } } + if options[string(kftypes.CONFIG)] != nil { + dat := options[string(kftypes.CONFIG)].([]byte) + specErr := yaml.Unmarshal(dat, &_gcp.GcpApp.Spec) + if specErr != nil { + log.Errorf("couldn't unmarshal Ksonnet. Error: %v", specErr) + } + } if options[string(kftypes.PLATFORM)] != nil { _gcp.GcpApp.Spec.Platform = options[string(kftypes.PLATFORM)].(string) } @@ -159,11 +168,50 @@ func GetKfApp(options map[string]interface{}) kftypes.KfApp { return _gcp } +func GetClusterInfo(ctx context.Context, project string, loc string, cluster string) (*containerpb.Cluster, error) { + ts, err := google.DefaultTokenSource(ctx, iam.CloudPlatformScope) + if err != nil { + return nil, fmt.Errorf("Get token error: %v", err) + } + c, err := container.NewClusterManagerClient(ctx, option.WithTokenSource(ts)) + if err != nil { + return nil, err + } + getClusterReq := &containerpb.GetClusterRequest{ + ProjectId: project, + Zone: loc, + ClusterId: cluster, + } + return c.GetCluster(ctx, getClusterReq) +} + +// BuildConfigFromClusterInfo returns k8s config using gcloud Application Default Credentials +// typically $HOME/.config/gcloud/application_default_credentials.json +func BuildConfigFromClusterInfo(ctx context.Context, cluster *containerpb.Cluster) (*rest.Config, error) { + ts, err := google.DefaultTokenSource(ctx, iam.CloudPlatformScope) + if err != nil { + return nil, fmt.Errorf("Get token error: %v", err) + } + t, err := ts.Token() + if err != nil { + return nil, fmt.Errorf("Token retrieval error: %v", err) + } + caDec, _ := base64.StdEncoding.DecodeString(cluster.MasterAuth.ClusterCaCertificate) + config := &rest.Config{ + Host: "https://" + cluster.Endpoint, + BearerToken: t.AccessToken, + TLSClientConfig: rest.TLSClientConfig{ + CAData: []byte(string(caDec)), + }, + } + return config, nil +} + func getSA(name string, nameSuffix string, project string) string { return fmt.Sprintf("%v-%v@%v.iam.gserviceaccount.com", name, nameSuffix, project) } -func (gcp *Gcp) writeConfigFile() error { +func (gcp *Gcp) writeConfigFile(options map[string]interface{}) error { buf, bufErr := yaml.Marshal(gcp.GcpApp) if bufErr != nil { return bufErr @@ -173,6 +221,11 @@ func (gcp *Gcp) writeConfigFile() error { if cfgFilePathErr != nil { return cfgFilePathErr } + buf, bufErr = yaml.Marshal(&gcp.GcpApp.Spec) + if bufErr != nil { + return bufErr + } + options[string(kftypes.CONFIG)] = buf return nil } @@ -232,12 +285,12 @@ func generateTarget(configPath string) (*deploymentmanager.TargetConfiguration, } func (gcp *Gcp) getK8sClientset(ctx context.Context) (*clientset.Clientset, error) { - cluster, err := kftypes.GetClusterInfo(ctx, gcp.GcpApp.Spec.Project, + cluster, err := GetClusterInfo(ctx, gcp.GcpApp.Spec.Project, gcp.GcpApp.Spec.Zone, gcp.GcpApp.Name) if err != nil { return nil, fmt.Errorf("get Cluster error: %v", err) } - config, err := kftypes.BuildConfigFromClusterInfo(ctx, cluster) + config, err := BuildConfigFromClusterInfo(ctx, cluster) if err != nil { return nil, fmt.Errorf("build ClientConfig error: %v", err) } @@ -425,12 +478,12 @@ func (gcp *Gcp) updateDM(resources kftypes.ResourceEnum, options map[string]inte } ctx := context.Background() - cluster, err := kftypes.GetClusterInfo(ctx, gcp.GcpApp.Spec.Project, + cluster, err := GetClusterInfo(ctx, gcp.GcpApp.Spec.Project, gcp.GcpApp.Spec.Zone, gcp.GcpApp.Name) if err != nil { return fmt.Errorf("Get Cluster error: %v", err) } - client, err := kftypes.BuildConfigFromClusterInfo(ctx, cluster) + client, err := BuildConfigFromClusterInfo(ctx, cluster) if err != nil { return fmt.Errorf("Build ClientConfig error: %v", err) } @@ -503,70 +556,22 @@ func (gcp *Gcp) copyFile(source string, dest string) error { return nil } -func setNameVal(entries []configtypes.NameValue, name string, val string) { - for i, nv := range entries { +func setNameVal(entries *[]configtypes.NameValue, name string, val string, required bool) { + for i, nv := range *entries { if nv.Name == name { log.Infof("Setting %v to %v", name, val) - entries[i].Value = val + (*entries)[i].Value = val return } } log.Infof("Appending %v as %v", name, val) - entries = append(entries, configtypes.NameValue{ - Name: name, - Value: val, + *entries = append(*entries, configtypes.NameValue{ + Name: name, + Value: val, + InitRequired: required, }) } -func (gcp *Gcp) generate(options map[string]interface{}) error { - configPath := path.Join(gcp.GcpApp.Spec.AppDir, - kftypes.DefaultCacheDir, - gcp.GcpApp.Spec.Version, - kftypes.DefaultConfigDir) - if gcp.GcpApp.Spec.UseBasicAuth { - configPath = path.Join(configPath, kftypes.GcpBasicAuth) - } else { - configPath = path.Join(configPath, kftypes.GcpIapConfig) - } - if options[string(kftypes.DEFAULT_CONFIG)] == nil { - options[string(kftypes.DEFAULT_CONFIG)] = configPath - } - - if options[string(kftypes.EMAIL)] != nil && - options[string(kftypes.EMAIL)].(string) != "" { - gcp.GcpApp.Spec.Email = options[string(kftypes.EMAIL)].(string) - } else if gcp.GcpApp.Spec.Email == "" { - return fmt.Errorf("email is not set in default nor passed.") - } else { - options[string(kftypes.EMAIL)] = gcp.GcpApp.Spec.Email - } - if options[string(kftypes.IPNAME)] != nil && - options[string(kftypes.IPNAME)].(string) != "" { - gcp.GcpApp.Spec.IpName = options[string(kftypes.IPNAME)].(string) - } else if gcp.GcpApp.Spec.IpName == "" { - return fmt.Errorf("ipName is not set in default nor passed.") - } else { - log.Infof("Using default ipName: %v", gcp.GcpApp.Spec.IpName) - options[string(kftypes.IPNAME)] = gcp.GcpApp.Spec.IpName - } - - if gcp.GcpApp.Spec.UseBasicAuth { - options[string(kftypes.USE_BASIC_AUTH)] = true - } else { - options[string(kftypes.USE_BASIC_AUTH)] = false - } - if options[string(kftypes.HOSTNAME)] != nil && - options[string(kftypes.HOSTNAME)].(string) != "" { - gcp.GcpApp.Spec.Hostname = options[string(kftypes.HOSTNAME)].(string) - } else if gcp.GcpApp.Spec.Hostname == "" { - return fmt.Errorf("hostname is not set in default nor passed.") - } - if options[string(kftypes.ZONE)] != nil { - gcp.GcpApp.Spec.Zone = options[string(kftypes.ZONE)].(string) - } - return nil -} - //TODO(#2515) func (gcp *Gcp) replaceText(regex string, repl string, src []byte) []byte { re := regexp.MustCompile(regex) @@ -807,10 +812,6 @@ func (gcp *Gcp) Generate(resources kftypes.ResourceEnum, options map[string]inte if generateK8sSpecsErr != nil { return fmt.Errorf("could not generate files under %v Error: %v", K8S_SPECS, generateK8sSpecsErr) } - generateErr := gcp.generate(options) - if generateErr != nil { - return fmt.Errorf("generate failed Error: %v", generateErr) - } case kftypes.ALL: gcpConfigFilesErr := gcp.generateDMConfigs(options) if gcpConfigFilesErr != nil { @@ -820,21 +821,36 @@ func (gcp *Gcp) Generate(resources kftypes.ResourceEnum, options map[string]inte if generateK8sSpecsErr != nil { return fmt.Errorf("could not generate files under %v Error: %v", K8S_SPECS, generateK8sSpecsErr) } - generateErr := gcp.generate(options) - if generateErr != nil { - return fmt.Errorf("generate failed Error: %v", generateErr) - } case kftypes.PLATFORM: gcpConfigFilesErr := gcp.generateDMConfigs(options) if gcpConfigFilesErr != nil { return fmt.Errorf("could not generate deployment manager configs under %v Error: %v", GCP_CONFIG, gcpConfigFilesErr) } - generateErr := gcp.generate(options) - if generateErr != nil { - return fmt.Errorf("generate failed Error: %v", generateErr) - } } - createConfigErr := gcp.writeConfigFile() + email := options[string(kftypes.EMAIL)].(string) + nv := gcp.GcpApp.Spec.ComponentParams["cert-manager"] + setNameVal(&nv, "acmeEmail", email, true) + ipName := options[string(kftypes.IPNAME)].(string) + if ipName == "" { + ipName = gcp.GcpApp.Name + "-ip" + } + hostname := options[string(kftypes.HOSTNAME)].(string) + if hostname == "" { + hostname = gcp.GcpApp.Name + ".endpoints." + gcp.GcpApp.Spec.Project + ".cloud.goog" + } + if val, ok := options[string(kftypes.USE_BASIC_AUTH)]; ok && val.(bool) { + nv = gcp.GcpApp.Spec.ComponentParams["basic-auth-ingress"] + setNameVal(&nv, "ipName", ipName, true) + setNameVal(&nv, "hostname", hostname, true) + } else { + nv = gcp.GcpApp.Spec.ComponentParams["iap-ingress"] + setNameVal(&nv, "ipName", ipName, true) + setNameVal(&nv, "hostname", hostname, true) + } + nv = gcp.GcpApp.Spec.ComponentParams["pipeline"] + setNameVal(&nv, "mysqlPd", gcp.GcpApp.Name+"-storage-metadata-store", false) + setNameVal(&nv, "minioPd", gcp.GcpApp.Name+"-storage-artifact-store", false) + createConfigErr := gcp.writeConfigFile(options) if createConfigErr != nil { return fmt.Errorf("cannot create config file app.yaml in %v", gcp.GcpApp.Spec.AppDir) } @@ -889,7 +905,7 @@ func (gcp *Gcp) Init(resources kftypes.ResourceEnum, options map[string]interfac cacheDir := path.Join(gcp.GcpApp.Spec.AppDir, kftypes.DefaultCacheDir) newPath := filepath.Join(cacheDir, gcp.GcpApp.Spec.Version) gcp.GcpApp.Spec.Repo = path.Join(newPath, "kubeflow") - createConfigErr := gcp.writeConfigFile() + createConfigErr := gcp.writeConfigFile(options) if createConfigErr != nil { return fmt.Errorf("cannot create config file app.yaml in %v", gcp.GcpApp.Spec.AppDir) } @@ -901,6 +917,5 @@ func (gcp *Gcp) Init(resources kftypes.ResourceEnum, options map[string]interfac return fmt.Errorf("cannot init gcp project %v", initProjectErr) } } - return nil } diff --git a/bootstrap/pkg/client/ksonnet/ksonnet.go b/bootstrap/pkg/client/ksonnet/ksonnet.go index cee5606fed2..d894a16ab96 100644 --- a/bootstrap/pkg/client/ksonnet/ksonnet.go +++ b/bootstrap/pkg/client/ksonnet/ksonnet.go @@ -20,7 +20,6 @@ import ( "fmt" "github.com/cenkalti/backoff" "github.com/ghodss/yaml" - gogetter "github.com/hashicorp/go-getter" "github.com/ksonnet/ksonnet/pkg/actions" "github.com/ksonnet/ksonnet/pkg/app" "github.com/ksonnet/ksonnet/pkg/client" @@ -31,10 +30,10 @@ import ( kfctlutils "github.com/kubeflow/kubeflow/bootstrap/pkg/utils" log "github.com/sirupsen/logrus" "github.com/spf13/afero" - "golang.org/x/net/context" "io/ioutil" "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" clientcmdapi "k8s.io/client-go/tools/clientcmd/api" "os" @@ -46,7 +45,7 @@ import ( ) // Ksonnet implements the KfApp Interface -type KsApp struct { +type ksApp struct { // ksonnet root name KsName string // ksonnet env name @@ -60,16 +59,30 @@ type KsApp struct { } func GetKfApp(options map[string]interface{}) kftypes.KfApp { - _kfapp := &KsApp{ + _kfapp := &ksApp{ KsName: kstypes.KsName, KsEnvName: kstypes.KsEnvName, KsApp: &kstypes.Ksonnet{ TypeMeta: metav1.TypeMeta{ - Kind: "Ksonnet", - APIVersion: "ksonnet.apps.kubeflow.org/v1alpha1", + Kind: "Client", + APIVersion: "client.apps.kubeflow.org/v1alpha1", }, }, } + if options[string(kftypes.DATA)] != nil { + dat := options[string(kftypes.DATA)].([]byte) + specErr := yaml.Unmarshal(dat, _kfapp.KsApp) + if specErr != nil { + log.Errorf("couldn't unmarshal Ksonnet. Error: %v", specErr) + } + } + if options[string(kftypes.CONFIG)] != nil { + dat := options[string(kftypes.CONFIG)].([]byte) + specErr := yaml.Unmarshal(dat, &_kfapp.KsApp.Spec) + if specErr != nil { + log.Errorf("couldn't unmarshal Ksonnet. Error: %v", specErr) + } + } _kfapp.KsApp.Spec.Platform = options[string(kftypes.PLATFORM)].(string) if options[string(kftypes.APPNAME)] != nil { _kfapp.KsApp.Name = options[string(kftypes.APPNAME)].(string) @@ -103,61 +116,15 @@ func GetKfApp(options map[string]interface{}) kftypes.KfApp { kubeflowVersion := options[string(kftypes.VERSION)].(string) _kfapp.KsApp.Spec.Version = kubeflowVersion } - if options[string(kftypes.DATA)] != nil { - dat := options[string(kftypes.DATA)].([]byte) - specErr := yaml.Unmarshal(dat, _kfapp.KsApp) - if specErr != nil { - log.Errorf("couldn't unmarshal Ksonnet. Error: %v", specErr) - } - } return _kfapp } -func (ksApp *KsApp) writeConfigFile() error { - buf, bufErr := yaml.Marshal(ksApp.KsApp) - if bufErr != nil { - return bufErr - } - cfgFilePath := filepath.Join(ksApp.KsApp.Spec.AppDir, kftypes.KfConfigFile) - cfgFilePathErr := ioutil.WriteFile(cfgFilePath, buf, 0644) - if cfgFilePathErr != nil { - return cfgFilePathErr - } - return nil -} - -func (ksApp *KsApp) Apply(resources kftypes.ResourceEnum, options map[string]interface{}) error { - log.Infof("ks.Apply: project = %v, zone = %v name = %v", options[string(kftypes.PROJECT)], - options[string(kftypes.ZONE)], ksApp.KsApp.Name) - if options[string(kftypes.PROJECT)] == nil || options[string(kftypes.PROJECT)].(string) == "" { - return fmt.Errorf("Couldn't find %v in KSONNET options ...", string(kftypes.PROJECT)) - } - if options[string(kftypes.ZONE)] == nil || options[string(kftypes.ZONE)].(string) == "" { - return fmt.Errorf("Couldn't find %v in KSONNET options ...", string(kftypes.ZONE)) - } - project := options[string(kftypes.PROJECT)].(string) - zone := options[string(kftypes.ZONE)].(string) +func (ksApp *ksApp) Apply(resources kftypes.ResourceEnum, options map[string]interface{}) error { name := ksApp.KsApp.Name - ctx := context.Background() - cluster, err := kftypes.GetClusterInfo(ctx, project, zone, name) - if err != nil { - return err - } - config, err := kftypes.BuildConfigFromClusterInfo(ctx, cluster) - if err != nil { - return err - } - host, _, err := kftypes.ServerVersionWithConfig(config) - if err != nil { - return fmt.Errorf("couldn't get server version: %v", err) - } - log.Infof("ServerVersion: %v", host) - cli, cliErr := kftypes.GetClientOutOfCluster() - if cliErr != nil { - return fmt.Errorf("couldn't create client Error: %v", cliErr) - } + config := kftypes.GetConfig() + clientset := kftypes.GetClientset(config) // TODO(gabrielwen): Make env name an option. - envSetErr := ksApp.envSet(kstypes.KsEnvName, host) + envSetErr := ksApp.envSet(kstypes.KsEnvName, config.Host) if envSetErr != nil { return fmt.Errorf("couldn't create ksonnet env %v Error: %v", kstypes.KsEnvName, envSetErr) } @@ -168,11 +135,11 @@ func (ksApp *KsApp) Apply(resources kftypes.ResourceEnum, options map[string]int } namespace := ksApp.KsApp.ObjectMeta.Namespace log.Infof(string(kftypes.NAMESPACE)+": %v", namespace) - _, nsMissingErr := cli.CoreV1().Namespaces().Get(namespace, metav1.GetOptions{}) + _, nsMissingErr := clientset.CoreV1().Namespaces().Get(namespace, metav1.GetOptions{}) if nsMissingErr != nil { log.Infof("Creating namespace: %v", namespace) nsSpec := &v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: namespace}} - _, nsErr := cli.CoreV1().Namespaces().Create(nsSpec) + _, nsErr := clientset.CoreV1().Namespaces().Create(nsSpec) if nsErr != nil { return fmt.Errorf("couldn't create "+string(kftypes.NAMESPACE)+" %v Error: %v", namespace, nsErr) } @@ -187,19 +154,24 @@ func (ksApp *KsApp) Apply(resources kftypes.ResourceEnum, options map[string]int return fmt.Errorf("could not change directory to %v Error %v", ksApp.KsApp.Spec.AppDir, err) } } + clientConfig := kftypes.GetKubeConfig() + applyErr := ksApp.applyComponent([]string{"metacontroller"}, clientConfig) + if applyErr != nil { + return fmt.Errorf("couldn't create metacontroller component Error: %v", applyErr) + } // TODO(#2391): Fix this and use ks.apply - if err = ksApp.showComponent([]string{"metacontroller", "application"}); err != nil { + if err = ksApp.showComponent([]string{"application"}); err != nil { return fmt.Errorf("Writing config file error: %v", err) } return kfctlutils.RunKubectlApply(ksApp.getCompsFilePath()) } -func (ksApp *KsApp) getCompsFilePath() string { +func (ksApp *ksApp) getCompsFilePath() string { return filepath.Join(ksApp.KsApp.Spec.AppDir, kstypes.KsName, kstypes.KsEnvName+".yaml") } -func (ksApp *KsApp) showComponent(components []string) error { +func (ksApp *ksApp) showComponent(components []string) error { showOptions := map[string]interface{}{ actions.OptionApp: ksApp.KApp, actions.OptionComponentNames: components, @@ -222,12 +194,11 @@ func (ksApp *KsApp) showComponent(components []string) error { os.Stdout = stdout return err } - os.Stdout = stdout return nil } -func (ksApp *KsApp) applyComponent(components []string, cfg *clientcmdapi.Config) error { +func (ksApp *ksApp) applyComponent(components []string, cfg *clientcmdapi.Config) error { applyOptions := map[string]interface{}{ actions.OptionApp: ksApp.KApp, actions.OptionClientConfig: &client.Config{ @@ -271,7 +242,7 @@ func (ksApp *KsApp) applyComponent(components []string, cfg *clientcmdapi.Config } -func (ksApp *KsApp) componentAdd(component kstypes.KsComponent, args []string) error { +func (ksApp *ksApp) componentAdd(component kstypes.KsComponent, args []string) error { componentPath := filepath.Join(ksApp.ksRoot(), "components", component.Name+".jsonnet") componentArgs := make([]string, 0) componentArgs = append(componentArgs, component.Prototype) @@ -295,7 +266,7 @@ func (ksApp *KsApp) componentAdd(component kstypes.KsComponent, args []string) e return nil } -func (ksApp *KsApp) components() (map[string]*kstypes.KsComponent, error) { +func (ksApp *ksApp) components() (map[string]*kstypes.KsComponent, error) { moduleName := "/" topModule := component.NewModule(ksApp.KApp, moduleName) components, err := topModule.Components() @@ -313,16 +284,13 @@ func (ksApp *KsApp) components() (map[string]*kstypes.KsComponent, error) { return comps, nil } -func (ksApp *KsApp) deleteGlobalResources() error { - crdClient, clientErr := kftypes.GetApiExtensionsClientOutOfCluster() - if clientErr != nil { - return fmt.Errorf("couldn't get client Error: %v", clientErr) - } +func (ksApp *ksApp) deleteGlobalResources(config *rest.Config) error { + apiextclientset := kftypes.GetApiExtClientset(config) do := &metav1.DeleteOptions{} lo := metav1.ListOptions{ LabelSelector: kftypes.DefaultAppLabel + "=" + ksApp.KsApp.Name, } - crdsErr := crdClient.CustomResourceDefinitions().DeleteCollection(do, lo) + crdsErr := apiextclientset.CustomResourceDefinitions().DeleteCollection(do, lo) if crdsErr != nil { return fmt.Errorf("couldn't delete customresourcedefinitions Error: %v", crdsErr) } @@ -334,52 +302,39 @@ func (ksApp *KsApp) deleteGlobalResources() error { } for _, crd := range crdsByName { do := &metav1.DeleteOptions{} - dErr := crdClient.CustomResourceDefinitions().Delete(crd, do) + dErr := apiextclientset.CustomResourceDefinitions().Delete(crd, do) if dErr != nil { log.Errorf("could not delete %v Error %v", crd, dErr) } } - cli, cliErr := kftypes.GetClientOutOfCluster() - if cliErr != nil { - return fmt.Errorf("couldn't create client Error: %v", cliErr) - } - crbsErr := cli.RbacV1().ClusterRoleBindings().DeleteCollection(do, lo) + clientset := kftypes.GetClientset(config) + crbsErr := clientset.RbacV1().ClusterRoleBindings().DeleteCollection(do, lo) if crbsErr != nil { return fmt.Errorf("couldn't get list of clusterrolebindings Error: %v", crbsErr) } crbName := "meta-controller-cluster-role-binding" - dErr := cli.RbacV1().ClusterRoleBindings().Delete(crbName, do) + dErr := clientset.RbacV1().ClusterRoleBindings().Delete(crbName, do) if dErr != nil { log.Errorf("could not delete %v Error %v", crbName, dErr) } - crsErr := cli.RbacV1().ClusterRoles().DeleteCollection(do, lo) + crsErr := clientset.RbacV1().ClusterRoles().DeleteCollection(do, lo) if crsErr != nil { return fmt.Errorf("couldn't delete clusterroles Error: %v", crsErr) } return nil } -func (ksApp *KsApp) Delete(resources kftypes.ResourceEnum, options map[string]interface{}) error { - err := ksApp.deleteGlobalResources() +func (ksApp *ksApp) Delete(resources kftypes.ResourceEnum, options map[string]interface{}) error { + config := kftypes.GetConfig() + err := ksApp.deleteGlobalResources(config) if err != nil { log.Errorf("there was a problem deleting global resources: %v", err) } - host, _, serverErr := kftypes.ServerVersion() - if serverErr != nil { - return fmt.Errorf("couldn't get server version: %v", serverErr) - } - cli, cliErr := kftypes.GetClientOutOfCluster() - if cliErr != nil { - return fmt.Errorf("couldn't create client Error: %v", cliErr) - } - envSetErr := ksApp.envSet(kstypes.KsEnvName, host) + envSetErr := ksApp.envSet(kstypes.KsEnvName, config.Host) if envSetErr != nil { return fmt.Errorf("couldn't create ksonnet env %v Error: %v", kstypes.KsEnvName, envSetErr) } - clientConfig, clientConfigErr := kftypes.GetClientConfig() - if clientConfigErr != nil { - return fmt.Errorf("couldn't load client config Error: %v", clientConfigErr) - } + clientConfig := kftypes.GetKubeConfig() components := []string{"application", "metacontroller"} err = actions.RunDelete(map[string]interface{}{ actions.OptionApp: ksApp.KApp, @@ -396,18 +351,18 @@ func (ksApp *KsApp) Delete(resources kftypes.ResourceEnum, options map[string]in } namespace := ksApp.KsApp.ObjectMeta.Namespace log.Infof("deleting namespace: %v", namespace) - ns, nsMissingErr := cli.CoreV1().Namespaces().Get(namespace, metav1.GetOptions{}) + clientset := kftypes.GetClientset(config) + ns, nsMissingErr := clientset.CoreV1().Namespaces().Get(namespace, metav1.GetOptions{}) if nsMissingErr == nil { - nsErr := cli.CoreV1().Namespaces().Delete(ns.Name, metav1.NewDeleteOptions(int64(100))) + nsErr := clientset.CoreV1().Namespaces().Delete(ns.Name, metav1.NewDeleteOptions(int64(100))) if nsErr != nil { return fmt.Errorf("couldn't delete namespace %v Error: %v", namespace, nsErr) } } - name := "meta-controller-cluster-role-binding" - crb, crbErr := cli.RbacV1().ClusterRoleBindings().Get(name, metav1.GetOptions{}) + crb, crbErr := clientset.RbacV1().ClusterRoleBindings().Get(name, metav1.GetOptions{}) if crbErr == nil { - crbDeleteErr := cli.RbacV1().ClusterRoleBindings().Delete(crb.Name, metav1.NewDeleteOptions(int64(5))) + crbDeleteErr := clientset.RbacV1().ClusterRoleBindings().Delete(crb.Name, metav1.NewDeleteOptions(int64(5))) if crbDeleteErr != nil { return fmt.Errorf("couldn't delete clusterrolebinding %v Error: %v", name, crbDeleteErr) } @@ -430,54 +385,28 @@ func setNameVal(entries []configtypes.NameValue, name string, val string) { }) } -func (ksApp *KsApp) Generate(resources kftypes.ResourceEnum, options map[string]interface{}) error { +func (ksApp *ksApp) Generate(resources kftypes.ResourceEnum, options map[string]interface{}) error { log.Infof("Ksonnet.Generate Name %v AppDir %v Platform %v", ksApp.KsApp.Name, ksApp.KsApp.Spec.AppDir, ksApp.KsApp.Spec.Platform) - - configPath := path.Join(ksApp.KsApp.Spec.AppDir, - kftypes.DefaultCacheDir, - ksApp.KsApp.Spec.Version, - kftypes.DefaultConfigDir) - - initErr := ksApp.initKs() + config := kftypes.GetConfig() + initErr := ksApp.initKs(config) if initErr != nil { return fmt.Errorf("couldn't initialize KfApi: %v", initErr) } - if options[string(kftypes.DEFAULT_CONFIG)] == nil { - configPath = filepath.Join(configPath, kftypes.DefaultConfigFile) - options[string(kftypes.DEFAULT_CONFIG)] = configPath - } else { - configPath = options[string(kftypes.DEFAULT_CONFIG)].(string) - } - config := &configtypes.ComponentConfig{} - if buf, bufErr := ioutil.ReadFile(configPath); bufErr == nil { - if readErr := yaml.Unmarshal(buf, config); readErr != nil { - return fmt.Errorf("Unable to parse config: %v", readErr) + if options[string(kftypes.CONFIG)] != nil { + dat := options[string(kftypes.CONFIG)].([]byte) + specErr := yaml.Unmarshal(dat, &ksApp.KsApp.Spec) + if specErr != nil { + log.Errorf("couldn't unmarshal Ksonnet. Error: %v", specErr) } - } else { - return fmt.Errorf("Unable to read config %v: %v", configPath, bufErr) - } - config.Repo = ksApp.KsApp.Spec.Repo - email := options[string(kftypes.EMAIL)].(string) - setNameVal(config.ComponentParams["cert-manager"], "acmeEmail", email) - ipName := options[string(kftypes.IPNAME)].(string) - hostname := options[string(kftypes.HOSTNAME)].(string) - if val, ok := options[string(kftypes.USE_BASIC_AUTH)]; ok && val.(bool) { - setNameVal(config.ComponentParams["basic-auth-ingress"], "ipName", ipName) - setNameVal(config.ComponentParams["basic-auth-ingress"], "hostname", hostname) - } else { - setNameVal(config.ComponentParams["iap-ingress"], "ipName", ipName) - setNameVal(config.ComponentParams["iap-ingress"], "hostname", hostname) } - setNameVal(config.ComponentParams["pipeline"], "mysqlPd", ksApp.KsApp.Name+"-storage-metadata-store") - setNameVal(config.ComponentParams["pipeline"], "minioPd", ksApp.KsApp.Name+"-storage-artifact-store") components := []string{} - for _, c := range config.Components { + for _, c := range ksApp.KsApp.Spec.Components { if c != "application" && c != "metacontroller" { components = append(components, fmt.Sprintf("\"%v\"", c)) } } - setNameVal(config.ComponentParams["application"], "components", + setNameVal(ksApp.KsApp.Spec.ComponentParams["application"], "components", "["+strings.Join(components, " ,")+"]") log.Infof("Configs for generation: %+v", config) @@ -489,7 +418,7 @@ func (ksApp *KsApp) Generate(resources kftypes.ResourceEnum, options map[string] if registryAddErr != nil { return fmt.Errorf("couldn't add registry %v. Error: %v", ksRegistry.Name, registryAddErr) } - for _, pkgName := range config.Packages { + for _, pkgName := range ksApp.KsApp.Spec.Packages { pkg := kstypes.KsPackage{ Name: pkgName, Registry: "kubeflow", @@ -500,13 +429,13 @@ func (ksApp *KsApp) Generate(resources kftypes.ResourceEnum, options map[string] } } componentArray := ksApp.KsApp.Spec.Components - for _, compName := range config.Components { + for _, compName := range ksApp.KsApp.Spec.Components { comp := kstypes.KsComponent{ Name: compName, Prototype: compName, } parameterArgs := []string{} - if val, ok := config.ComponentParams[compName]; ok { + if val, ok := ksApp.KsApp.Spec.ComponentParams[compName]; ok { for _, nv := range val { if nv.InitRequired { name := "--" + nv.Name @@ -527,7 +456,7 @@ func (ksApp *KsApp) Generate(resources kftypes.ResourceEnum, options map[string] return fmt.Errorf("couldn't add comp %v. Error: %v", comp.Name, componentAddErr) } } - for compName, namevals := range config.ComponentParams { + for compName, namevals := range ksApp.KsApp.Spec.ComponentParams { for _, nv := range namevals { args := map[string]interface{}{ actions.OptionAppRoot: ksApp.ksRoot(), @@ -541,43 +470,13 @@ func (ksApp *KsApp) Generate(resources kftypes.ResourceEnum, options map[string] } } } - return nil } -func (ksApp *KsApp) Init(resources kftypes.ResourceEnum, options map[string]interface{}) error { +func (ksApp *ksApp) Init(resources kftypes.ResourceEnum, options map[string]interface{}) error { ksApp.KsApp.Spec.Platform = options[string(kftypes.PLATFORM)].(string) - err := os.Mkdir(ksApp.KsApp.Spec.AppDir, os.ModePerm) - if err != nil { - return fmt.Errorf("couldn't create directory %v, most likely it already exists", ksApp.KsApp.Spec.AppDir) - } - cfgFilePath := filepath.Join(ksApp.KsApp.Spec.AppDir, kftypes.KfConfigFile) - _, appDirErr := afero.NewOsFs().Stat(cfgFilePath) - if appDirErr == nil { - return fmt.Errorf("config file %v already exists in %v", kftypes.KfConfigFile, ksApp.KsApp.Spec.AppDir) - } - cacheDir := path.Join(ksApp.KsApp.Spec.AppDir, kftypes.DefaultCacheDir) - cacheDirErr := os.Mkdir(cacheDir, os.ModePerm) - if cacheDirErr != nil { - return fmt.Errorf("couldn't create directory %v Error %v", cacheDir, cacheDirErr) - } - tarballUrl := kftypes.DefaultGitRepo + "/" + ksApp.KsApp.Spec.Version + "?archive=tar.gz" - tarballUrlErr := gogetter.GetAny(cacheDir, tarballUrl) - if tarballUrlErr != nil { - return fmt.Errorf("couldn't download kubeflow repo %v Error %v", tarballUrl, tarballUrlErr) - } - files, filesErr := ioutil.ReadDir(cacheDir) - if filesErr != nil { - return fmt.Errorf("couldn't read %v Error %v", cacheDir, filesErr) - } - subdir := files[0].Name() - extractedPath := filepath.Join(cacheDir, subdir) - newPath := filepath.Join(cacheDir, ksApp.KsApp.Spec.Version) - renameErr := os.Rename(extractedPath, newPath) - if renameErr != nil { - return fmt.Errorf("couldn't rename %v to %v Error %v", extractedPath, newPath, renameErr) - } - ksApp.KsApp.Spec.Repo = path.Join(newPath, "kubeflow") + repoPath := path.Join(ksApp.KsApp.Spec.AppDir, kftypes.DefaultCacheDir, ksApp.KsApp.Spec.Version) + ksApp.KsApp.Spec.Repo = path.Join(repoPath, "kubeflow") createConfigErr := ksApp.writeConfigFile() if createConfigErr != nil { return fmt.Errorf("cannot create config file app.yaml in %v", ksApp.KsApp.Spec.AppDir) @@ -585,33 +484,29 @@ func (ksApp *KsApp) Init(resources kftypes.ResourceEnum, options map[string]inte return nil } -func (ksApp *KsApp) initKs() error { +func (ksApp *ksApp) initKs(config *rest.Config) error { newRoot := path.Join(ksApp.KsApp.Spec.AppDir, ksApp.KsName) ksApp.KsEnvName = kstypes.KsEnvName - host, k8sSpec, err := kftypes.ServerVersion() - if err != nil { - return fmt.Errorf("couldn't get server version: %v", err) - } + k8sSpec := kftypes.GetServerVersion(kftypes.GetClientset(config)) options := map[string]interface{}{ actions.OptionFs: afero.NewOsFs(), actions.OptionName: ksApp.KsName, actions.OptionEnvName: ksApp.KsEnvName, actions.OptionNewRoot: newRoot, - actions.OptionServer: host, + actions.OptionServer: config.Host, actions.OptionSpecFlag: k8sSpec, actions.OptionNamespace: ksApp.KsApp.Namespace, actions.OptionSkipDefaultRegistries: true, } - err = actions.RunInit(options) + err := actions.RunInit(options) if err != nil { return fmt.Errorf("there was a problem initializing the app: %v", err) } log.Infof("Successfully initialized the app %v.", ksApp.KsApp.Name) - return nil } -func (ksApp *KsApp) envSet(envName string, host string) error { +func (ksApp *ksApp) envSet(envName string, host string) error { ksApp.KsEnvName = envName err := actions.RunEnvSet(map[string]interface{}{ actions.OptionAppRoot: ksApp.ksRoot(), @@ -624,12 +519,12 @@ func (ksApp *KsApp) envSet(envName string, host string) error { return nil } -func (ksApp *KsApp) ksRoot() string { +func (ksApp *ksApp) ksRoot() string { root := path.Join(ksApp.KsApp.Spec.AppDir, ksApp.KsName) return root } -func (ksApp *KsApp) libraries() (map[string]*kstypes.KsLibrary, error) { +func (ksApp *ksApp) libraries() (map[string]*kstypes.KsLibrary, error) { libs, err := ksApp.KApp.Libraries() if err != nil { return nil, fmt.Errorf("there was a problem getting the libraries %v. Error: %v", ksApp.KsApp.Name, err) @@ -646,7 +541,7 @@ func (ksApp *KsApp) libraries() (map[string]*kstypes.KsLibrary, error) { return libraries, nil } -func (ksApp *KsApp) registries() (map[string]*kstypes.Registry, error) { +func (ksApp *ksApp) registries() (map[string]*kstypes.Registry, error) { regs, err := ksApp.KApp.Registries() if err != nil { return nil, fmt.Errorf("There was a problem getting the registries %v. Error: %v", ksApp.KsApp.Name, err) @@ -663,7 +558,7 @@ func (ksApp *KsApp) registries() (map[string]*kstypes.Registry, error) { return registries, nil } -func (ksApp *KsApp) paramSet(component string, name string, value string) error { +func (ksApp *ksApp) paramSet(component string, name string, value string) error { err := actions.RunParamSet(map[string]interface{}{ actions.OptionAppRoot: ksApp.ksRoot(), actions.OptionName: component, @@ -676,7 +571,7 @@ func (ksApp *KsApp) paramSet(component string, name string, value string) error return nil } -func (ksApp *KsApp) pkgInstall(pkg kstypes.KsPackage) error { +func (ksApp *ksApp) pkgInstall(pkg kstypes.KsPackage) error { root := ksApp.ksRoot() err := actions.RunPkgInstall(map[string]interface{}{ actions.OptionAppRoot: root, @@ -690,11 +585,11 @@ func (ksApp *KsApp) pkgInstall(pkg kstypes.KsPackage) error { return nil } -func (ksApp *KsApp) prototypeUse(m map[string]interface{}) error { +func (ksApp *ksApp) prototypeUse(m map[string]interface{}) error { return nil } -func (ksApp *KsApp) registryAdd(registry *kstypes.RegistryConfig) error { +func (ksApp *ksApp) registryAdd(registry *kstypes.RegistryConfig) error { log.Infof("App %v add registry %v URI %v", ksApp.KsApp.Name, registry.Name, registry.RegUri) root := ksApp.ksRoot() options := map[string]interface{}{ @@ -711,3 +606,44 @@ func (ksApp *KsApp) registryAdd(registry *kstypes.RegistryConfig) error { } return nil } + +func (ksApp *ksApp) Show(resources kftypes.ResourceEnum, options map[string]interface{}) error { + capture := kftypes.Capture() + err := actions.RunShow(map[string]interface{}{ + actions.OptionApp: ksApp.KApp, + actions.OptionComponentNames: []string{}, + actions.OptionEnvName: kstypes.KsEnvName, + actions.OptionFormat: "yaml", + }) + if err != nil { + return fmt.Errorf("there was a problem calling show: %v", err) + } + yamlDir := filepath.Join(ksApp.KsApp.Spec.AppDir, "yamls") + err = os.Mkdir(yamlDir, os.ModePerm) + if err != nil { + return fmt.Errorf("couldn't create directory %v, most likely it already exists", yamlDir) + } + output, outputErr := capture() + if outputErr != nil { + return fmt.Errorf("there was a problem calling capture: %v", outputErr) + } + yamlFile := filepath.Join(yamlDir, "default.yaml") + yamlFileErr := ioutil.WriteFile(yamlFile, []byte(output), 0644) + if yamlFileErr != nil { + return fmt.Errorf("could not write to %v Error %v", yamlFile, yamlFileErr) + } + return nil +} + +func (ksApp *ksApp) writeConfigFile() error { + buf, bufErr := yaml.Marshal(ksApp.KsApp) + if bufErr != nil { + return bufErr + } + cfgFilePath := filepath.Join(ksApp.KsApp.Spec.AppDir, kftypes.KfConfigFile) + cfgFilePathErr := ioutil.WriteFile(cfgFilePath, buf, 0644) + if cfgFilePathErr != nil { + return cfgFilePathErr + } + return nil +} diff --git a/bootstrap/pkg/client/minikube/minikube.go b/bootstrap/pkg/client/minikube/minikube.go index 14b787cc1fe..d5f8baeeee1 100644 --- a/bootstrap/pkg/client/minikube/minikube.go +++ b/bootstrap/pkg/client/minikube/minikube.go @@ -18,19 +18,49 @@ package minikube import ( "fmt" + "github.com/ghodss/yaml" + "github.com/kubeflow/kubeflow/bootstrap/config" kftypes "github.com/kubeflow/kubeflow/bootstrap/pkg/apis/apps" + cltypes "github.com/kubeflow/kubeflow/bootstrap/pkg/apis/apps/client/v1alpha1" + log "github.com/sirupsen/logrus" + "io/ioutil" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "os/user" + "path/filepath" "strconv" "strings" ) // Minikube implements KfApp Interface type Minikube struct { - //TODO add additional types required for minikube platform + cltypes.Client } func GetKfApp(options map[string]interface{}) kftypes.KfApp { - return &Minikube{} + _minikube := &Minikube{ + Client: cltypes.Client{ + TypeMeta: metav1.TypeMeta{ + Kind: "Client", + APIVersion: "client.apps.kubeflow.org/v1alpha1", + }, + Spec: cltypes.ClientSpec{}, + }, + } + if options[string(kftypes.DATA)] != nil { + dat := options[string(kftypes.DATA)].([]byte) + specErr := yaml.Unmarshal(dat, _minikube) + if specErr != nil { + log.Errorf("couldn't unmarshal Ksonnet. Error: %v", specErr) + } + } + if options[string(kftypes.CONFIG)] != nil { + dat := options[string(kftypes.CONFIG)].([]byte) + specErr := yaml.Unmarshal(dat, &_minikube.Spec) + if specErr != nil { + log.Errorf("couldn't unmarshal Ksonnet. Error: %v", specErr) + } + } + return _minikube } func (minikube *Minikube) Apply(resources kftypes.ResourceEnum, options map[string]interface{}) error { @@ -50,12 +80,12 @@ func (minikube *Minikube) generate(options map[string]interface{}) error { mountLocal = options[string(kftypes.MOUNT_LOCAL)].(bool) } // remove Katib package and component - kftypes.DefaultPackages = kftypes.RemoveItem(kftypes.DefaultPackages, "katib") - kftypes.DefaultComponents = kftypes.RemoveItem(kftypes.DefaultComponents, "katib") - kftypes.DefaultParameters["application"] = []kftypes.NameValue{ + minikube.Spec.Packages = kftypes.RemoveItem(minikube.Spec.Packages, "katib") + minikube.Spec.Components = kftypes.RemoveItem(minikube.Spec.Components, "katib") + minikube.Spec.ComponentParams["application"] = []config.NameValue{ { Name: "components", - Value: "[" + strings.Join(kftypes.QuoteItems(kftypes.DefaultComponents), ",") + "]", + Value: "[" + strings.Join(kftypes.QuoteItems(minikube.Spec.Components), ",") + "]", }, } usr, err := user.Current() @@ -64,7 +94,7 @@ func (minikube *Minikube) generate(options map[string]interface{}) error { } uid := usr.Uid gid := usr.Gid - kftypes.DefaultParameters["jupyter"] = []kftypes.NameValue{ + minikube.Spec.ComponentParams["jupyter"] = []config.NameValue{ { Name: string(kftypes.PLATFORM), Value: platform, @@ -86,7 +116,7 @@ func (minikube *Minikube) generate(options map[string]interface{}) error { Value: gid, }, } - kftypes.DefaultParameters["ambassador"] = []kftypes.NameValue{ + minikube.Spec.ComponentParams["ambassador"] = []config.NameValue{ { Name: string(kftypes.PLATFORM), Value: platform, @@ -110,9 +140,31 @@ func (minikube *Minikube) Generate(resources kftypes.ResourceEnum, options map[s return fmt.Errorf("minikube generate failed Error: %v", generateErr) } } + createConfigErr := minikube.writeConfigFile(options) + if createConfigErr != nil { + return fmt.Errorf("cannot create config file app.yaml in %v", minikube.Client.Spec.AppDir) + } return nil } func (minikube *Minikube) Init(kftypes.ResourceEnum, map[string]interface{}) error { return nil } + +func (minikube *Minikube) writeConfigFile(options map[string]interface{}) error { + buf, bufErr := yaml.Marshal(minikube.Client) + if bufErr != nil { + return bufErr + } + cfgFilePath := filepath.Join(minikube.Client.Spec.AppDir, kftypes.KfConfigFile) + cfgFilePathErr := ioutil.WriteFile(cfgFilePath, buf, 0644) + if cfgFilePathErr != nil { + return cfgFilePathErr + } + buf, bufErr = yaml.Marshal(&minikube.Client.Spec) + if bufErr != nil { + return bufErr + } + options[string(kftypes.CONFIG)] = buf + return nil +} diff --git a/bootstrap/pkg/utils/k8utils.go b/bootstrap/pkg/utils/k8utils.go index d713472e5f9..c399a3aae69 100644 --- a/bootstrap/pkg/utils/k8utils.go +++ b/bootstrap/pkg/utils/k8utils.go @@ -16,23 +16,23 @@ package utils import ( "bytes" "fmt" + "github.com/cenkalti/backoff" "github.com/ghodss/yaml" log "github.com/sirupsen/logrus" "io/ioutil" - "os" - "os/exec" - "strings" - "sync" - - "github.com/cenkalti/backoff" - ksUtil "github.com/ksonnet/ksonnet/utils" k8serrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/serializer" k8stypes "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/discovery" + "k8s.io/client-go/discovery/cached" "k8s.io/client-go/dynamic" + "os" + "os/exec" + "strings" + "sync" + "k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/rest" "time" @@ -213,8 +213,8 @@ func CreateResourceFromFile(config *rest.Config, filename string) error { if err != nil { return err } - cacheClient := ksUtil.NewMemcachedDiscoveryClient(discoveryClient) - mapper := discovery.NewDeferredDiscoveryRESTMapper(cacheClient, dynamic.VersionInterfaces) + cached := cached.NewMemCacheClient(discoveryClient) + mapper := discovery.NewDeferredDiscoveryRESTMapper(cached, dynamic.VersionInterfaces) data, err := ioutil.ReadFile(filename) if err != nil { diff --git a/bootstrap/v2/pkg/apis/addtoscheme_apps_v1alpha1.go b/bootstrap/v2/pkg/apis/addtoscheme_apps_v1alpha1.go new file mode 100644 index 00000000000..c41166f5388 --- /dev/null +++ b/bootstrap/v2/pkg/apis/addtoscheme_apps_v1alpha1.go @@ -0,0 +1,25 @@ +// Copyright 2018 The Kubeflow 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 apis + +import ( + client "github.com/kubeflow/kubeflow/bootstrap/v2/pkg/apis/apps/client/v1alpha1" +) + +func init() { + // Register the types with the Scheme so the components can map objects to GroupVersionKinds and back + AddToSchemes = append(AddToSchemes, client.SchemeBuilder.AddToScheme) + +} diff --git a/bootstrap/v2/pkg/apis/apis.go b/bootstrap/v2/pkg/apis/apis.go new file mode 100644 index 00000000000..9a3c47acc5f --- /dev/null +++ b/bootstrap/v2/pkg/apis/apis.go @@ -0,0 +1,31 @@ +// Copyright 2018 The Kubeflow 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. + +// Generate deepcopy for apis +// go:generate deepcopy-gen -O zz_generated.deepcopy -i ./... -h ../../hack/boilerplate.go.txt + +// Package apis contains Kubernetes API groups. +package apis + +import ( + "k8s.io/apimachinery/v2/pkg/runtime" +) + +// AddToSchemes may be used to add all resources defined in the project to a Scheme +var AddToSchemes runtime.SchemeBuilder + +// AddToScheme adds all Resources to the Scheme +func AddToScheme(s *runtime.Scheme) error { + return AddToSchemes.AddToScheme(s) +} diff --git a/bootstrap/v2/pkg/apis/apps/client/client.go b/bootstrap/v2/pkg/apis/apps/client/client.go new file mode 100644 index 00000000000..3682aa54c71 --- /dev/null +++ b/bootstrap/v2/pkg/apis/apps/client/client.go @@ -0,0 +1,16 @@ +// Copyright 2018 The Kubeflow 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 client contains client related types +package client diff --git a/bootstrap/v2/pkg/apis/apps/client/v1alpha1/application_types.go b/bootstrap/v2/pkg/apis/apps/client/v1alpha1/application_types.go new file mode 100644 index 00000000000..50492eec0cd --- /dev/null +++ b/bootstrap/v2/pkg/apis/apps/client/v1alpha1/application_types.go @@ -0,0 +1,68 @@ +// Copyright 2018 The Kubeflow 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 v1alpha1 + +import ( + cltypes "github.com/kubeflow/kubeflow/bootstrap/pkg/apis/apps/client/v1alpha1" + "k8s.io/api/v2/core/v1" + metav1 "k8s.io/apimachinery/v2/pkg/apis/meta/v1" +) + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/v2/pkg/runtime.Object + +// Client is the Schema for the applications API +// +k8s:openapi-gen=true +type Client struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec cltypes.ClientSpec `json:"spec,omitempty"` + Status ClientStatus `json:"status,omitempty"` +} + +// ClientStatus defines the observed state of Client +type ClientStatus struct { + Conditions []ClientCondition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type" protobuf:"bytes,6,rep,name=conditions"` +} + +type ClientCondition struct { + // Type of deployment condition. + Type cltypes.ClientConditionType `json:"type" protobuf:"bytes,1,opt,name=type,casttype=ClientConditionType"` + // Status of the condition, one of True, False, Unknown. + Status v1.ConditionStatus `json:"status" protobuf:"bytes,2,opt,name=status,casttype=k8s.io/api/core/v1.ConditionStatus"` + // The last time this condition was updated. + LastUpdateTime metav1.Time `json:"lastUpdateTime,omitempty" protobuf:"bytes,6,opt,name=lastUpdateTime"` + // Last time the condition transitioned from one status to another. + LastTransitionTime metav1.Time `json:"lastTransitionTime,omitempty" protobuf:"bytes,7,opt,name=lastTransitionTime"` + // The reason for the condition's last transition. + Reason string `json:"reason,omitempty" protobuf:"bytes,4,opt,name=reason"` + // A human readable message indicating details about the transition. + Message string `json:"message,omitempty" protobuf:"bytes,5,opt,name=message"` +} + + + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/v2/pkg/runtime.Object + +// ClientList contains a list of Client +type ClientList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []Client `json:"items"` +} + +func init() { + SchemeBuilder.Register(&Client{}, &ClientList{}) +} diff --git a/bootstrap/v2/pkg/apis/apps/client/v1alpha1/application_types_test.go b/bootstrap/v2/pkg/apis/apps/client/v1alpha1/application_types_test.go new file mode 100644 index 00000000000..3bc36cfb682 --- /dev/null +++ b/bootstrap/v2/pkg/apis/apps/client/v1alpha1/application_types_test.go @@ -0,0 +1,56 @@ +// Copyright 2018 The Kubeflow 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 v1alpha1 + +import ( + "testing" + + "github.com/onsi/gomega" + "golang.org/x/net/context" + metav1 "k8s.io/apimachinery/v2/pkg/apis/meta/v1" + "k8s.io/apimachinery/v2/pkg/types" +) + +func TestStorageApplication(t *testing.T) { + key := types.NamespacedName{ + Name: "foo", + Namespace: "default", + } + created := &Client{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: "default", + }} + g := gomega.NewGomegaWithT(t) + + // Test Create + fetched := &Client{} + g.Expect(c.Create(context.TODO(), created)).NotTo(gomega.HaveOccurred()) + + g.Expect(c.Get(context.TODO(), key, fetched)).NotTo(gomega.HaveOccurred()) + g.Expect(fetched).To(gomega.Equal(created)) + + // Test Updating the Labels + updated := fetched.DeepCopy() + updated.Labels = map[string]string{"hello": "world"} + g.Expect(c.Update(context.TODO(), updated)).NotTo(gomega.HaveOccurred()) + + g.Expect(c.Get(context.TODO(), key, fetched)).NotTo(gomega.HaveOccurred()) + g.Expect(fetched).To(gomega.Equal(updated)) + + // Test Delete + g.Expect(c.Delete(context.TODO(), fetched)).NotTo(gomega.HaveOccurred()) + g.Expect(c.Get(context.TODO(), key, fetched)).To(gomega.HaveOccurred()) +} diff --git a/bootstrap/v2/pkg/apis/apps/client/v1alpha1/doc.go b/bootstrap/v2/pkg/apis/apps/client/v1alpha1/doc.go new file mode 100644 index 00000000000..45d22b4e3ad --- /dev/null +++ b/bootstrap/v2/pkg/apis/apps/client/v1alpha1/doc.go @@ -0,0 +1,22 @@ +// Copyright 2018 The Kubeflow 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 v1alpha1 contains API Schema definitions for the client v1alpha1 API group +// +k8s:openapi-gen=true +// +k8s:deepcopy-gen=package,register +// +k8s:conversion-gen=github.com/kubeflow/kubeflow/bootstrap/v2/pkg/apis/apps/client +// +k8s:defaulter-gen=TypeMeta +// +groupName=client.apps.kubeflow.org + +package v1alpha1 diff --git a/bootstrap/v2/pkg/apis/apps/client/v1alpha1/register.go b/bootstrap/v2/pkg/apis/apps/client/v1alpha1/register.go new file mode 100644 index 00000000000..29f34c86be7 --- /dev/null +++ b/bootstrap/v2/pkg/apis/apps/client/v1alpha1/register.go @@ -0,0 +1,44 @@ +// Copyright 2018 The Kubeflow 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. + +// NOTE: Boilerplate only. Ignore this file. + +// Package v1alpha1 contains API Schema definitions for the client v1alpha1 API group +// +k8s:openapi-gen=true +// +k8s:deepcopy-gen=package,register +// +k8s:conversion-gen=github.com/kubeflow/kubeflow/bootstrap/v2/pkg/apis/apps/client +// +k8s:defaulter-gen=TypeMeta +// +groupName=client.apps.kubeflow.org +package v1alpha1 + +import ( + "k8s.io/apimachinery/v2/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/v2/pkg/runtime/scheme" +) + +var ( + // SchemeGroupVersion is group version used to register these objects + SchemeGroupVersion = schema.GroupVersion{Group: "client.apps.kubeflow.org", Version: "v1alpha1"} + + // SchemeBuilder is used to add go types to the GroupVersionKind scheme + SchemeBuilder = &scheme.Builder{GroupVersion: SchemeGroupVersion} + + // AddToScheme is required by pkg/client/... + AddToScheme = SchemeBuilder.AddToScheme +) + +// Resource is required by pkg/client/listers/... +func Resource(resource string) schema.GroupResource { + return SchemeGroupVersion.WithResource(resource).GroupResource() +} diff --git a/bootstrap/v2/pkg/apis/apps/client/v1alpha1/v1alpha1_suite_test.go b/bootstrap/v2/pkg/apis/apps/client/v1alpha1/v1alpha1_suite_test.go new file mode 100644 index 00000000000..bd62b7751d4 --- /dev/null +++ b/bootstrap/v2/pkg/apis/apps/client/v1alpha1/v1alpha1_suite_test.go @@ -0,0 +1,53 @@ +// Copyright 2018 The Kubeflow 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 v1alpha1 + +import ( + "log" + "os" + "path/filepath" + "testing" + + "k8s.io/client-go/v2/kubernetes/scheme" + "k8s.io/client-go/v2/rest" + "sigs.k8s.io/controller-runtime/v2/pkg/client" + "sigs.k8s.io/controller-runtime/v2/pkg/envtest" +) + +var cfg *rest.Config +var c client.Client + +func TestMain(m *testing.M) { + t := &envtest.Environment{ + CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "..", "config", "crds")}, + } + + err := SchemeBuilder.AddToScheme(scheme.Scheme) + if err != nil { + log.Fatal(err) + } + + if cfg, err = t.Start(); err != nil { + log.Fatal(err) + } + + if c, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}); err != nil { + log.Fatal(err) + } + + code := m.Run() + t.Stop() + os.Exit(code) +} diff --git a/bootstrap/v2/pkg/apis/apps/client/v1alpha1/zz_generated.deepcopy.go b/bootstrap/v2/pkg/apis/apps/client/v1alpha1/zz_generated.deepcopy.go new file mode 100644 index 00000000000..498d22007a6 --- /dev/null +++ b/bootstrap/v2/pkg/apis/apps/client/v1alpha1/zz_generated.deepcopy.go @@ -0,0 +1,125 @@ +// +build !ignore_autogenerated + +// Copyright 2018 The Kubeflow 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. + +// Code generated by deepcopy-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "k8s.io/apimachinery/v2/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Client) DeepCopyInto(out *Client) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Client. +func (in *Client) DeepCopy() *Client { + if in == nil { + return nil + } + out := new(Client) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Client) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClientCondition) DeepCopyInto(out *ClientCondition) { + *out = *in + in.LastUpdateTime.DeepCopyInto(&out.LastUpdateTime) + in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClientCondition. +func (in *ClientCondition) DeepCopy() *ClientCondition { + if in == nil { + return nil + } + out := new(ClientCondition) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClientList) DeepCopyInto(out *ClientList) { + *out = *in + out.TypeMeta = in.TypeMeta + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Client, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClientList. +func (in *ClientList) DeepCopy() *ClientList { + if in == nil { + return nil + } + out := new(ClientList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ClientList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClientStatus) DeepCopyInto(out *ClientStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]ClientCondition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClientStatus. +func (in *ClientStatus) DeepCopy() *ClientStatus { + if in == nil { + return nil + } + out := new(ClientStatus) + in.DeepCopyInto(out) + return out +} diff --git a/bootstrap/v2/pkg/apis/apps/group.go b/bootstrap/v2/pkg/apis/apps/group.go new file mode 100644 index 00000000000..265884ef631 --- /dev/null +++ b/bootstrap/v2/pkg/apis/apps/group.go @@ -0,0 +1,169 @@ +// Copyright 2018 The Kubeflow 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 apps contains apps API versions +package apps + +import ( + "fmt" + kftypes "github.com/kubeflow/kubeflow/bootstrap/pkg/apis/apps" + log "github.com/sirupsen/logrus" + "io" + ext "k8s.io/apiextensions-apiserver/v2/pkg/apis/apiextensions/v1beta1" + "k8s.io/apiextensions-apiserver/v2/pkg/client/clientset/clientset" + apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/v2/pkg/client/clientset/clientset/typed/apiextensions/v1beta1" + "k8s.io/client-go/v2/kubernetes" + "k8s.io/client-go/v2/rest" + "k8s.io/client-go/v2/tools/clientcmd" + clientcmdapi "k8s.io/client-go/v2/tools/clientcmd/api" + "os" + "path/filepath" + "plugin" + "regexp" + "strings" +) + + +func LoadKfApp(platform string, options map[string]interface{}) (kftypes.KfApp, error) { + platform = strings.Replace(platform, "-", "", -1) + plugindir := os.Getenv("PLUGINS_ENVIRONMENT") + pluginpath := filepath.Join(plugindir, platform+".so") + p, err := plugin.Open(pluginpath) + if err != nil { + return nil, fmt.Errorf("could not load plugin %v for platform %v Error %v", pluginpath, platform, err) + } + symName := "GetKfApp" + symbol, symbolErr := p.Lookup(symName) + if symbolErr != nil { + return nil, fmt.Errorf("could not find symbol %v for platform %v Error %v", symName, platform, symbolErr) + } + return symbol.(func(map[string]interface{}) kftypes.KfApp)(options), nil +} + +func KubeConfigPath() string { + kubeconfigEnv := os.Getenv("KUBECONFIG") + if kubeconfigEnv == "" { + home := os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH") + if home == "" { + for _, h := range []string{"HOME", "USERPROFILE"} { + if home = os.Getenv(h); home != "" { + break + } + } + } + kubeconfigPath := filepath.Join(home, ".kube", "config") + return kubeconfigPath + } + return kubeconfigEnv +} + +// GetConfig returns k8s config +func BuildOutOfClusterConfig() (*rest.Config, error) { + loadingRules := clientcmd.NewDefaultClientConfigLoadingRules() + loadingRules.ExplicitPath = KubeConfigPath() + config, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig( + loadingRules, &clientcmd.ConfigOverrides{}).ClientConfig() + if err != nil { + return nil, err + } + return config, nil +} + +func ServerVersion() (host string, version string, err error) { + restApi, err := BuildOutOfClusterConfig() + if err != nil { + return "", "", fmt.Errorf("couldn't build out-of-cluster config. Error: %v", err) + } + clnt, clntErr := kubernetes.NewForConfig(restApi) + if clntErr != nil { + return "", "", fmt.Errorf("couldn't get clientset. Error: %v", err) + } + serverVersion, serverVersionErr := clnt.ServerVersion() + if serverVersionErr != nil { + return "", "", fmt.Errorf("couldn't get server version info. Error: %v", serverVersionErr) + } + re := regexp.MustCompile("^v[0-9]+.[0-9]+.[0-9]+") + version = re.FindString(serverVersion.String()) + return restApi.Host, "version:" + version, nil +} + +func GetClientConfig() (*clientcmdapi.Config, error) { + kubeconfig := KubeConfigPath() + config, configErr := clientcmd.LoadFromFile(kubeconfig) + if configErr != nil { + return nil, fmt.Errorf("could not load config Error: %v", configErr) + + } + return config, nil +} + +// GetClientset returns a k8s clientset to the request from outside of cluster +func GetClientOutOfCluster() (kubernetes.Interface, error) { + config, err := BuildOutOfClusterConfig() + if err != nil { + log.Fatalf("Can not get kubernetes config: %v", err) + } + + clientset, err := kubernetes.NewForConfig(config) + if err != nil { + log.Fatalf("Can not get kubernetes client: %v", err) + } + + return clientset, nil +} + +func GetApiExtensionsClientOutOfCluster() (apiextensionsv1beta1.ApiextensionsV1beta1Interface, error) { + config, err := BuildOutOfClusterConfig() + if err != nil { + log.Fatalf("Can not get kubernetes config: %v", err) + } + v := ext.SchemeGroupVersion + config.GroupVersion = &v + crdClient, err := clientset.NewForConfig(config) + if err != nil { + log.Fatalf("Can not get apiextensions client: %v", err) + } + + return crdClient.ApiextensionsV1beta1(), nil +} + +// Capture replaces os.Stdout with a writer that buffers any data written +// to os.Stdout. Call the returned function to cleanup and get the data +// as a string. +func Capture() func() (string, error) { + r, w, err := os.Pipe() + if err != nil { + panic(err) + } + + done := make(chan error, 1) + + save := os.Stdout + os.Stdout = w + + var buf strings.Builder + + go func() { + _, err := io.Copy(&buf, r) + _ = r.Close() + done <- err + }() + + return func() (string, error) { + os.Stdout = save + _ = w.Close() + err := <-done + return buf.String(), err + } +} diff --git a/bootstrap/v2/pkg/client/client.go b/bootstrap/v2/pkg/client/client.go new file mode 100644 index 00000000000..baa2e87ad6d --- /dev/null +++ b/bootstrap/v2/pkg/client/client.go @@ -0,0 +1,16 @@ +// Copyright 2018 The Kubeflow 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 client +package client diff --git a/bootstrap/v2/pkg/client/kustomize/kustomize.go b/bootstrap/v2/pkg/client/kustomize/kustomize.go new file mode 100644 index 00000000000..2f8c5f0c0cc --- /dev/null +++ b/bootstrap/v2/pkg/client/kustomize/kustomize.go @@ -0,0 +1,225 @@ +/* +Copyright 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 kustomize + +import ( + "fmt" + "github.com/ghodss/yaml" + gogetter "github.com/hashicorp/go-getter" + kftypes "github.com/kubeflow/kubeflow/bootstrap/pkg/apis/apps" + cltypes "github.com/kubeflow/kubeflow/bootstrap/v2/pkg/apis/apps/client/v1alpha1" + log "github.com/sirupsen/logrus" + "io/ioutil" + metav1 "k8s.io/apimachinery/v2/pkg/apis/meta/v1" + "os" + "path" + "path/filepath" + "regexp" + "sigs.k8s.io/kustomize/v2/k8sdeps" + "sigs.k8s.io/kustomize/v2/pkg/factory" + "sigs.k8s.io/kustomize/v2/pkg/fs" + "sigs.k8s.io/kustomize/v2/pkg/loader" + "sigs.k8s.io/kustomize/v2/pkg/target" +) + +// Kustomize implements KfApp Interface +// It should include functionality needed for the kustomize platform +// In addition to `kustomize build`, there is `kustomize edit ...` +// As noted below there are lots of different ways to use edit +// kustomize edit add configmap my-configmap --from-file=my-key=file/path --from-literal=my-literal=12345 +// kustomize edit add configmap my-configmap --from-file=file/path +// kustomize edit add configmap my-configmap --from-env-file=env/path.env +// kustomize edit add configmap NAME --from-literal=k=v +// kustomize edit add resource +// kustomize edit add patch +// kustomize edit add base ,, +// kustomize edit set nameprefix + +// A good example is kustomize/pkg/examplelayout/simple +// which creates an instance from a package, this may be the most similar to ksonnet packages +// and is taken from [Declarative Application Management in Kubernetes] +// (https://docs.google.com/document/d/1cLPGweVEYrVqQvBLJg6sxV-TrE5Rm2MNOBA_cxZP2WU) +type kustomize struct { + factory *factory.KustFactory + fsys fs.FileSystem + outputFile string + out *os.File + err *os.File + Kustomize *cltypes.Client +} + +func GetKfApp(options map[string]interface{}) kftypes.KfApp { + _kustomize := &kustomize{ + factory: k8sdeps.NewFactory(), + fsys: fs.MakeRealFS(), + outputFile: "output.yaml", + out: os.Stdout, + err: os.Stderr, + Kustomize: &cltypes.Client{ + TypeMeta: metav1.TypeMeta{ + Kind: "Client", + APIVersion: "client.apps.kubeflow.org/v1alpha1", + }, + }, + } + if options[string(kftypes.DATA)] != nil { + dat := options[string(kftypes.DATA)].([]byte) + specErr := yaml.Unmarshal(dat, _kustomize.Kustomize) + if specErr != nil { + log.Errorf("couldn't unmarshal DATA. Error: %v", specErr) + } + } + if options[string(kftypes.CONFIG)] != nil { + dat := options[string(kftypes.CONFIG)].([]byte) + specErr := yaml.Unmarshal(dat, &_kustomize.Kustomize.Spec) + if specErr != nil { + log.Errorf("couldn't unmarshal config file. Error: %v", specErr) + } + } + _kustomize.Kustomize.Spec.Platform = options[string(kftypes.PLATFORM)].(string) + if options[string(kftypes.APPNAME)] != nil { + _kustomize.Kustomize.Name = options[string(kftypes.APPNAME)].(string) + } + if options[string(kftypes.APPDIR)] != nil { + _kustomize.Kustomize.Spec.AppDir = options[string(kftypes.APPDIR)].(string) + } + if options[string(kftypes.NAMESPACE)] != nil { + namespace := options[string(kftypes.NAMESPACE)].(string) + _kustomize.Kustomize.Namespace = namespace + } + if options[string(kftypes.REPO)] != nil { + kubeflowRepo := options[string(kftypes.REPO)].(string) + re := regexp.MustCompile(`(^\$GOPATH)(.*$)`) + goPathVar := os.Getenv("GOPATH") + if goPathVar != "" { + kubeflowRepo = re.ReplaceAllString(kubeflowRepo, goPathVar+`$2`) + } + _kustomize.Kustomize.Spec.Repo = path.Join(kubeflowRepo, "kubeflow") + } + if options[string(kftypes.VERSION)] != nil { + kubeflowVersion := options[string(kftypes.VERSION)].(string) + _kustomize.Kustomize.Spec.Version = kubeflowVersion + } + return _kustomize +} + +func (kustomize *kustomize) Apply(resources kftypes.ResourceEnum, options map[string]interface{}) error { + return nil +} + +func (kustomize *kustomize) Delete(resources kftypes.ResourceEnum, options map[string]interface{}) error { + return nil +} + +func (kustomize *kustomize) generate(options map[string]interface{}) error { + //TODO see #2629 + kustomizeDir := path.Join(kustomize.Kustomize.Spec.AppDir, "manifests", "master") + loader, loaderErr := loader.NewLoader(kustomizeDir, kustomize.fsys) + if loaderErr != nil { + return fmt.Errorf("could not load kustomize loader: %v", loaderErr) + } + defer loader.Cleanup() + kt, err := target.NewKustTarget(loader, kustomize.factory.ResmapF, kustomize.factory.TransformerF) + if err != nil { + return err + } + allResources, err := kt.MakeCustomizedResMap() + if err != nil { + return err + } + // Output the objects. + res, err := allResources.EncodeAsYaml() + if err != nil { + return err + } + kustomizeFile := filepath.Join(kustomize.Kustomize.Spec.AppDir, kustomize.outputFile) + kustomizeFileErr := kustomize.fsys.WriteFile(kustomizeFile, res) + if kustomizeFileErr != nil { + return kustomizeFileErr + } + return err +} + +// kfctl generate all -V --email @.iam.gserviceaccount.com +func (kustomize *kustomize) Generate(resources kftypes.ResourceEnum, options map[string]interface{}) error { + if options[string(kftypes.CONFIG)] != nil { + dat := options[string(kftypes.CONFIG)].([]byte) + specErr := yaml.Unmarshal(dat, &kustomize.Kustomize.Spec) + if specErr != nil { + log.Errorf("couldn't unmarshal Ksonnet. Error: %v", specErr) + } + } + switch resources { + case kftypes.PLATFORM: + case kftypes.ALL: + fallthrough + case kftypes.K8S: + generateErr := kustomize.generate(options) + if generateErr != nil { + return fmt.Errorf("kustomize generate failed Error: %v", generateErr) + } + } + return nil +} + +// kfctl init kustomize -V --platform kustomize --project +func (kustomize *kustomize) Init(resources kftypes.ResourceEnum, options map[string]interface{}) error { + kustomizeDir := path.Join(kustomize.Kustomize.Spec.AppDir, "manifests") + kustomizeDirErr := os.Mkdir(kustomizeDir, os.ModePerm) + if kustomizeDirErr != nil { + return fmt.Errorf("couldn't create directory %v Error %v", kustomizeDir, kustomizeDirErr) + } + //TODO see #2629 + version := kustomize.Kustomize.Spec.Version + //tarballUrl := "https://github.com/kubeflow/manifests/tarball/" + version + "?archive=tar.gz" + tarballUrl := "https://github.com/kubeflow/manifests/tarball/master?archive=tar.gz" + tarballUrlErr := gogetter.GetAny(kustomizeDir, tarballUrl) + if tarballUrlErr != nil { + return fmt.Errorf("couldn't download kustomize manifests repo %v Error %v", tarballUrl, tarballUrlErr) + } + files, filesErr := ioutil.ReadDir(kustomizeDir) + if filesErr != nil { + return fmt.Errorf("couldn't read %v Error %v", kustomizeDir, filesErr) + } + subdir := files[0].Name() + extractedPath := filepath.Join(kustomizeDir, subdir) + newPath := filepath.Join(kustomizeDir, "master") + renameErr := os.Rename(extractedPath, newPath) + if renameErr != nil { + return fmt.Errorf("couldn't rename %v to %v Error %v", extractedPath, newPath, renameErr) + } + repoPath := path.Join(kustomize.Kustomize.Spec.AppDir, kftypes.DefaultCacheDir, version) + kustomize.Kustomize.Spec.Repo = path.Join(repoPath, "kubeflow") + createConfigErr := kustomize.writeConfigFile() + if createConfigErr != nil { + return fmt.Errorf("cannot create config file app.yaml in %v", kustomize.Kustomize.Spec.AppDir) + } + return nil +} + +func (kustomize *kustomize) writeConfigFile() error { + buf, bufErr := yaml.Marshal(kustomize.Kustomize) + if bufErr != nil { + return bufErr + } + cfgFilePath := filepath.Join(kustomize.Kustomize.Spec.AppDir, kftypes.KfConfigFile) + cfgFilePathErr := ioutil.WriteFile(cfgFilePath, buf, 0644) + if cfgFilePathErr != nil { + return cfgFilePathErr + } + return nil +} diff --git a/bootstrap/v2/pkg/utils/k8utils.go b/bootstrap/v2/pkg/utils/k8utils.go new file mode 100644 index 00000000000..2d748c1dcbc --- /dev/null +++ b/bootstrap/v2/pkg/utils/k8utils.go @@ -0,0 +1,136 @@ +/* +Copyright (c) 2016-2017 Bitnami +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 utils + +import ( + "bytes" + "encoding/json" + "github.com/ghodss/yaml" + log "github.com/sirupsen/logrus" + "io/ioutil" + "k8s.io/client-go/v2/dynamic" + "strings" + + "k8s.io/apimachinery/v2/pkg/runtime/schema" + "k8s.io/apimachinery/v2/pkg/runtime/serializer" + "k8s.io/client-go/v2/discovery" + "k8s.io/client-go/v2/discovery/cached" + + "k8s.io/client-go/v2/kubernetes/scheme" + "k8s.io/client-go/v2/rest" + + // Auth plugins + _ "k8s.io/client-go/v2/plugin/pkg/client/auth/gcp" + _ "k8s.io/client-go/v2/plugin/pkg/client/auth/oidc" +) + +// RecommendedConfigPathEnvVar is a environment variable for path configuration +const RecommendedConfigPathEnvVar = "KUBECONFIG" + +const ( + yamlSeparator = "---" +) + +// TODO COPIED from bootstrap/app/k8sUtil.go. Need to merge. +// CreateResourceFromFile creates resources from a file, just like `kubectl create -f filename` +// We use some libraries in an old way (e.g. the RestMapper is in discovery instead of restmapper) +// because ksonnet (one of our dependency) is using the old library version. +// TODO: it can't handle "kind: list" yet. +func CreateResourceFromFile(config *rest.Config, filename string) error { + // Create a restmapper to determine the resource type. + discoveryClient, err := discovery.NewDiscoveryClientForConfig(config) + if err != nil { + return err + } + cached := cached.NewMemCacheClient(discoveryClient) + mapper := discovery.NewDeferredDiscoveryRESTMapper(cached, dynamic.VersionInterfaces) + + data, err := ioutil.ReadFile(filename) + if err != nil { + return err + } + objects := bytes.Split(data, []byte(yamlSeparator)) + var o map[string]interface{} + for _, object := range objects { + if err = yaml.Unmarshal(object, &o); err != nil { + return err + } + a := o["apiVersion"] + if a == nil { + log.Warnf("Unknown resource: %v", object) + continue + } + apiVersion := strings.Split(a.(string), "/") + var group, version string + if len(apiVersion) == 1 { + // core v1, no group. e.g. namespace + group, version = "", apiVersion[0] + } else { + group, version = apiVersion[0], apiVersion[1] + } + kind := o["kind"].(string) + gk := schema.GroupKind{ + Group: group, + Kind: kind, + } + result, err := mapper.RESTMapping(gk, version) + // result.resource is the resource we need (e.g. pods, services) + if err != nil { + return err + } + + // build config for restClient + c := rest.CopyConfig(config) + c.GroupVersion = &schema.GroupVersion{ + Group: group, + Version: version, + } + c.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: scheme.Codecs} + if group == "" { + c.APIPath = "/api" + } else { + c.APIPath = "/apis" + } + restClient, err := rest.RESTClientFor(c) + if err != nil { + return err + } + + // build the request + metadata := o["metadata"].(map[string]interface{}) + name := metadata["name"].(string) + log.Infof("creating %v\n", name) + + var namespace string + if metadata["namespace"] != nil { + namespace = metadata["namespace"].(string) + } else { + namespace = "default" + } + body, err := json.Marshal(o) + if err != nil { + return err + } + request := restClient.Post().Resource(result.Resource).Body(body) + if result.Scope.Name() == "namespace" { + request = request.Namespace(namespace) + } + _, err = request.DoRaw() + if err != nil { + return err + } + } + + return nil +}