├── .circleci └── config.yml ├── .gitignore ├── Makefile ├── README.md ├── cmd └── virtual-kubelet │ ├── commands │ ├── providers │ │ └── provider.go │ ├── root │ │ ├── flag.go │ │ ├── http.go │ │ ├── node.go │ │ ├── opts.go │ │ ├── root.go │ │ ├── tracing.go │ │ ├── tracing_register.go │ │ ├── tracing_register_jaeger.go │ │ ├── tracing_register_ocagent.go │ │ └── tracing_register_test.go │ └── version │ │ └── version.go │ └── main.go ├── config.go ├── eci.go ├── eci.svg ├── eci.toml ├── eci ├── client.go ├── create_container_group.go ├── delete_container_group.go ├── describe_container_groups.go ├── describe_container_log.go ├── struct_config_file_to_path.go ├── struct_container.go ├── struct_container_group.go ├── struct_container_port.go ├── struct_container_state.go ├── struct_environment_var.go ├── struct_event.go ├── struct_tag.go ├── struct_volume.go └── struct_volume_mount.go ├── errors.go ├── go.mod ├── go.sum └── hack └── ci └── check_mods.sh /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | jobs: 3 | validate: 4 | docker: 5 | - image: golang:1.12 6 | working_directory: /go/src/github.com/virtual-kubelet/alibabacloud-eci 7 | steps: 8 | - checkout 9 | - run: 10 | name: go vet 11 | command: make vet 12 | - run: 13 | name: Install linters 14 | command: curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | bash -s v1.16.0 15 | - run: 16 | name: Lint 17 | command: make LINTER_BIN="./bin/golangci-lint" lint 18 | - run: 19 | name: Check go modules 20 | command: make check-mod 21 | test: 22 | docker: 23 | - image: golang:1.12 24 | working_directory: /go/src/github.com/virtual-kubelet/alibabacloud-eci 25 | steps: 26 | - checkout 27 | - run: 28 | name: Build 29 | command: make build 30 | - run: 31 | name: Tests 32 | command: make test 33 | 34 | workflows: 35 | version: 2 36 | validate_and_test: 37 | jobs: 38 | - validate 39 | - test 40 | 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Binaries for programs and plugins 3 | *.exe 4 | *.exe~ 5 | *.dll 6 | *.so 7 | *.dylib 8 | 9 | # Test binary, build with `go test -c` 10 | *.test 11 | 12 | # Output of the go coverage tool, specifically when used with LiteIDE 13 | *.out 14 | 15 | vendor/ 16 | 17 | bin/ 18 | 19 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | LINTER_BIN ?= golangci-lint 2 | 3 | GO111MODULE := on 4 | export GO111MODULE 5 | 6 | .PHONY: build 7 | build: bin/virtual-kubelet 8 | 9 | .PHONY: clean 10 | clean: files := bin/virtual-kubelet 11 | clean: 12 | @rm $(files) &>/dev/null || exit 0 13 | 14 | .PHONY: test 15 | test: 16 | @echo running tests 17 | go test -v ./... 18 | 19 | .PHONY: vet 20 | vet: 21 | @go vet ./... #$(packages) 22 | 23 | .PHONY: lint 24 | lint: 25 | @$(LINTER_BIN) run --new-from-rev "HEAD~$(git rev-list master.. --count)" ./... 26 | 27 | .PHONY: check-mod 28 | check-mod: # verifies that module changes for go.mod and go.sum are checked in 29 | @hack/ci/check_mods.sh 30 | 31 | .PHONY: mod 32 | mod: 33 | @go mod tidy 34 | 35 | bin/virtual-kubelet: BUILD_VERSION ?= $(shell git describe --tags --always --dirty="-dev") 36 | bin/virtual-kubelet: BUILD_DATE ?= $(shell date -u '+%Y-%m-%d-%H:%M UTC') 37 | bin/virtual-kubelet: VERSION_FLAGS := -ldflags='-X "main.buildVersion=$(BUILD_VERSION)" -X "main.buildTime=$(BUILD_DATE)"' 38 | 39 | bin/%: 40 | CGO_ENABLED=0 go build -ldflags '-extldflags "-static"' -o bin/$(*) $(VERSION_FLAGS) ./cmd/$(*) 41 | 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Alibaba Cloud ECI 2 | 3 | 4 | 5 | Alibaba Cloud ECI(Elastic Container Instance) is a service that allow you run containers without having to manage servers or clusters. 6 | 7 | You can find more infomation via [alibaba cloud ECI web portal](https://www.aliyun.com/product/eci) 8 | 9 | ## Alibaba Cloud ECI Virtual-Kubelet Provider 10 | Alibaba ECI provider is an adapter to connect between k8s and ECI service to implement pod from k8s cluster on alibaba cloud platform 11 | 12 | ## Prerequisites 13 | To using ECI service on alibaba cloud, you may need open ECI service on [web portal](https://www.aliyun.com/product/eci), and then the ECI service will be available 14 | 15 | ## Deployment of the ECI provider in your cluster 16 | configure and launch virtual kubelet 17 | ``` 18 | export ECI_REGION=cn-hangzhou 19 | export ECI_SECURITY_GROUP=sg-123 20 | export ECI_VSWITCH=vsw-123 21 | export ECI_ACCESS_KEY=123 22 | export ECI_SECRET_KEY=123 23 | 24 | VKUBELET_TAINT_KEY=alibabacloud.com/eci virtual-kubelet --provider alibabacloud 25 | ``` 26 | confirm the virtual kubelet is connected to k8s cluster 27 | ``` 28 | $kubectl get node 29 | NAME STATUS ROLES AGE VERSION 30 | cn-shanghai.i-uf69qodr5ntaxleqdhhk Ready 1d v1.9.3 31 | virtual-kubelet Ready agent 10s v1.8.3 32 | ``` 33 | 34 | ## Schedule K8s Pod to ECI via virtual kubelet 35 | You can assign pod to virtual kubelet via node-selector and toleration. 36 | ``` 37 | apiVersion: v1 38 | kind: Pod 39 | metadata: 40 | name: mypod 41 | spec: 42 | nodeName: virtual-kubelet 43 | containers: 44 | - name: nginx 45 | image: nginx 46 | tolerations: 47 | - key: alibabacloud.com/eci 48 | operator: "Exists" 49 | effect: NoSchedule 50 | ``` 51 | 52 | # Alibaba Cloud Serverless Kubernetes 53 | Alibaba Cloud serverless kubernetes allows you to quickly create kubernetes container applications without 54 | having to manage and maintain clusters and servers. It is based on ECI and fully compatible with the Kuberentes API. 55 | 56 | You can find more infomation via [alibaba cloud serverless kubernetes product doc](https://www.alibabacloud.com/help/doc-detail/94078.htm) 57 | 58 | -------------------------------------------------------------------------------- /cmd/virtual-kubelet/commands/providers/provider.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2017 The virtual-kubelet authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package providers 16 | 17 | import ( 18 | "fmt" 19 | "os" 20 | 21 | "github.com/spf13/cobra" 22 | ) 23 | 24 | // NewCommand creates a new providers subcommand 25 | // This subcommand is used to determine which providers are registered. 26 | func NewCommand() *cobra.Command { 27 | return &cobra.Command{ 28 | Use: "providers", 29 | Short: "Show the list of supported providers", 30 | Long: "Show the list of supported providers", 31 | Args: cobra.MaximumNArgs(2), 32 | Run: func(cmd *cobra.Command, args []string) { 33 | switch len(args) { 34 | case 0: 35 | fmt.Fprintln(cmd.OutOrStdout(), "alibabacloud") 36 | case 1: 37 | if args[0] != "alibabacloud" { 38 | fmt.Fprintln(cmd.OutOrStderr(), "no such provider", args[0]) 39 | 40 | // TODO(@cpuuy83): would be nice to not short-circuit the exit here 41 | // But at the momemt this seems to be the only way to exit non-zero and 42 | // handle our own error output 43 | os.Exit(1) 44 | } 45 | fmt.Fprintln(cmd.OutOrStdout(), args[0]) 46 | } 47 | return 48 | }, 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /cmd/virtual-kubelet/commands/root/flag.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2017 The virtual-kubelet authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package root 16 | 17 | import ( 18 | "flag" 19 | "fmt" 20 | "os" 21 | "strings" 22 | 23 | "github.com/pkg/errors" 24 | "github.com/spf13/pflag" 25 | "k8s.io/klog" 26 | ) 27 | 28 | type mapVar map[string]string 29 | 30 | func (mv mapVar) String() string { 31 | var s string 32 | for k, v := range mv { 33 | if s == "" { 34 | s = fmt.Sprintf("%s=%v", k, v) 35 | } else { 36 | s += fmt.Sprintf(", %s=%v", k, v) 37 | } 38 | } 39 | return s 40 | } 41 | 42 | func (mv mapVar) Set(s string) error { 43 | split := strings.SplitN(s, "=", 2) 44 | if len(split) != 2 { 45 | return errors.Errorf("invalid format, must be `key=value`: %s", s) 46 | } 47 | 48 | _, ok := mv[split[0]] 49 | if ok { 50 | return errors.Errorf("duplicate key: %s", split[0]) 51 | } 52 | mv[split[0]] = split[1] 53 | return nil 54 | } 55 | 56 | func (mv mapVar) Type() string { 57 | return "map" 58 | } 59 | 60 | func installFlags(flags *pflag.FlagSet, c *Opts) { 61 | flags.StringVar(&c.KubeConfigPath, "kubeconfig", c.KubeConfigPath, "kube config file to use for connecting to the Kubernetes API server") 62 | flags.StringVar(&c.KubeNamespace, "namespace", c.KubeNamespace, "kubernetes namespace (default is 'all')") 63 | flags.StringVar(&c.NodeName, "nodename", c.NodeName, "kubernetes node name") 64 | flags.StringVar(&c.OperatingSystem, "os", c.OperatingSystem, "Operating System (Linux/Windows)") 65 | 66 | flags.StringVar(&c.Provider, "provider", c.Provider, "cloud provider") 67 | if err := flags.MarkDeprecated("provider", "this flag is not used, alibabacloud is the only supported provider"); err != nil { 68 | panic(err) 69 | } 70 | 71 | flags.StringVar(&c.ProviderConfigPath, "provider-config", c.ProviderConfigPath, "cloud provider configuration file") 72 | flags.StringVar(&c.MetricsAddr, "metrics-addr", c.MetricsAddr, "address to listen for metrics/stats requests") 73 | 74 | flags.StringVar(&c.TaintKey, "taint", c.TaintKey, "Set node taint key") 75 | flags.BoolVar(&c.DisableTaint, "disable-taint", c.DisableTaint, "disable the virtual-kubelet node taint") 76 | flags.MarkDeprecated("taint", "Taint key should now be configured using the VK_TAINT_KEY environment variable") 77 | 78 | flags.IntVar(&c.PodSyncWorkers, "pod-sync-workers", c.PodSyncWorkers, `set the number of pod synchronization workers`) 79 | flags.BoolVar(&c.EnableNodeLease, "enable-node-lease", c.EnableNodeLease, `use node leases (1.13) for node heartbeats`) 80 | 81 | flags.StringSliceVar(&c.TraceExporters, "trace-exporter", c.TraceExporters, fmt.Sprintf("sets the tracing exporter to use, available exporters: %s", AvailableTraceExporters())) 82 | flags.StringVar(&c.TraceConfig.ServiceName, "trace-service-name", c.TraceConfig.ServiceName, "sets the name of the service used to register with the trace exporter") 83 | flags.Var(mapVar(c.TraceConfig.Tags), "trace-tag", "add tags to include with traces in key=value form") 84 | flags.StringVar(&c.TraceSampleRate, "trace-sample-rate", c.TraceSampleRate, "set probability of tracing samples") 85 | 86 | flags.DurationVar(&c.InformerResyncPeriod, "full-resync-period", c.InformerResyncPeriod, "how often to perform a full resync of pods between kubernetes and the provider") 87 | flags.DurationVar(&c.StartupTimeout, "startup-timeout", c.StartupTimeout, "How long to wait for the virtual-kubelet to start") 88 | 89 | flagset := flag.NewFlagSet("klog", flag.PanicOnError) 90 | klog.InitFlags(flagset) 91 | flagset.VisitAll(func(f *flag.Flag) { 92 | f.Name = "klog." + f.Name 93 | flags.AddGoFlag(f) 94 | }) 95 | } 96 | 97 | func getEnv(key, defaultValue string) string { 98 | value, found := os.LookupEnv(key) 99 | if found { 100 | return value 101 | } 102 | return defaultValue 103 | } 104 | -------------------------------------------------------------------------------- /cmd/virtual-kubelet/commands/root/http.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2017 The virtual-kubelet authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package root 16 | 17 | import ( 18 | "context" 19 | "crypto/tls" 20 | "fmt" 21 | "io" 22 | "net" 23 | "net/http" 24 | "os" 25 | 26 | "github.com/pkg/errors" 27 | "github.com/virtual-kubelet/virtual-kubelet/log" 28 | "github.com/virtual-kubelet/virtual-kubelet/node/api" 29 | "github.com/virtual-kubelet/virtual-kubelet/providers" 30 | ) 31 | 32 | // AcceptedCiphers is the list of accepted TLS ciphers, with known weak ciphers elided 33 | // Note this list should be a moving target. 34 | var AcceptedCiphers = []uint16{ 35 | tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, 36 | tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, 37 | tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, 38 | tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, 39 | 40 | tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, 41 | tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, 42 | tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 43 | tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 44 | } 45 | 46 | func loadTLSConfig(certPath, keyPath string) (*tls.Config, error) { 47 | cert, err := tls.LoadX509KeyPair(certPath, keyPath) 48 | if err != nil { 49 | return nil, errors.Wrap(err, "error loading tls certs") 50 | } 51 | 52 | return &tls.Config{ 53 | Certificates: []tls.Certificate{cert}, 54 | MinVersion: tls.VersionTLS12, 55 | PreferServerCipherSuites: true, 56 | CipherSuites: AcceptedCiphers, 57 | }, nil 58 | } 59 | 60 | func setupHTTPServer(ctx context.Context, p providers.Provider, cfg *apiServerConfig) (_ func(), retErr error) { 61 | var closers []io.Closer 62 | cancel := func() { 63 | for _, c := range closers { 64 | c.Close() 65 | } 66 | } 67 | defer func() { 68 | if retErr != nil { 69 | cancel() 70 | } 71 | }() 72 | 73 | if cfg.CertPath == "" || cfg.KeyPath == "" { 74 | log.G(ctx). 75 | WithField("certPath", cfg.CertPath). 76 | WithField("keyPath", cfg.KeyPath). 77 | Error("TLS certificates not provided, not setting up pod http server") 78 | } else { 79 | tlsCfg, err := loadTLSConfig(cfg.CertPath, cfg.KeyPath) 80 | if err != nil { 81 | return nil, err 82 | } 83 | l, err := tls.Listen("tcp", cfg.Addr, tlsCfg) 84 | if err != nil { 85 | return nil, errors.Wrap(err, "error setting up listener for pod http server") 86 | } 87 | 88 | mux := http.NewServeMux() 89 | 90 | podRoutes := api.PodHandlerConfig{ 91 | RunInContainer: p.RunInContainer, 92 | GetContainerLogs: p.GetContainerLogs, 93 | GetPods: p.GetPods, 94 | } 95 | api.AttachPodRoutes(podRoutes, mux, true) 96 | 97 | s := &http.Server{ 98 | Handler: mux, 99 | TLSConfig: tlsCfg, 100 | } 101 | go serveHTTP(ctx, s, l, "pods") 102 | closers = append(closers, s) 103 | } 104 | 105 | if cfg.MetricsAddr == "" { 106 | log.G(ctx).Info("Pod metrics server not setup due to empty metrics address") 107 | } else { 108 | l, err := net.Listen("tcp", cfg.MetricsAddr) 109 | if err != nil { 110 | return nil, errors.Wrap(err, "could not setup listener for pod metrics http server") 111 | } 112 | 113 | mux := http.NewServeMux() 114 | 115 | var summaryHandlerFunc api.PodStatsSummaryHandlerFunc 116 | if mp, ok := p.(providers.PodMetricsProvider); ok { 117 | summaryHandlerFunc = mp.GetStatsSummary 118 | } 119 | podMetricsRoutes := api.PodMetricsConfig{ 120 | GetStatsSummary: summaryHandlerFunc, 121 | } 122 | api.AttachPodMetricsRoutes(podMetricsRoutes, mux) 123 | s := &http.Server{ 124 | Handler: mux, 125 | } 126 | go serveHTTP(ctx, s, l, "pod metrics") 127 | closers = append(closers, s) 128 | } 129 | 130 | return cancel, nil 131 | } 132 | 133 | func serveHTTP(ctx context.Context, s *http.Server, l net.Listener, name string) { 134 | if err := s.Serve(l); err != nil { 135 | select { 136 | case <-ctx.Done(): 137 | default: 138 | log.G(ctx).WithError(err).Errorf("Error setting up %s http server", name) 139 | } 140 | } 141 | l.Close() 142 | } 143 | 144 | type apiServerConfig struct { 145 | CertPath string 146 | KeyPath string 147 | Addr string 148 | MetricsAddr string 149 | } 150 | 151 | func getAPIConfig(c Opts) (*apiServerConfig, error) { 152 | config := apiServerConfig{ 153 | CertPath: os.Getenv("APISERVER_CERT_LOCATION"), 154 | KeyPath: os.Getenv("APISERVER_KEY_LOCATION"), 155 | } 156 | 157 | config.Addr = fmt.Sprintf(":%d", c.ListenPort) 158 | config.MetricsAddr = c.MetricsAddr 159 | 160 | return &config, nil 161 | } 162 | -------------------------------------------------------------------------------- /cmd/virtual-kubelet/commands/root/node.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2017 The virtual-kubelet authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package root 16 | 17 | import ( 18 | "context" 19 | "strings" 20 | 21 | "github.com/virtual-kubelet/virtual-kubelet/errdefs" 22 | "github.com/virtual-kubelet/virtual-kubelet/providers" 23 | corev1 "k8s.io/api/core/v1" 24 | v1 "k8s.io/api/core/v1" 25 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 | ) 27 | 28 | // NodeFromProvider builds a kubernetes node object from a provider 29 | // This is a temporary solution until node stuff actually split off from the provider interface itself. 30 | func NodeFromProvider(ctx context.Context, name string, taint *v1.Taint, p providers.Provider, version string) *v1.Node { 31 | taints := make([]v1.Taint, 0) 32 | 33 | if taint != nil { 34 | taints = append(taints, *taint) 35 | } 36 | 37 | node := &v1.Node{ 38 | ObjectMeta: metav1.ObjectMeta{ 39 | Name: name, 40 | Labels: map[string]string{ 41 | "type": "virtual-kubelet", 42 | "kubernetes.io/role": "agent", 43 | "beta.kubernetes.io/os": strings.ToLower(p.OperatingSystem()), 44 | "kubernetes.io/hostname": name, 45 | "alpha.service-controller.kubernetes.io/exclude-balancer": "true", 46 | }, 47 | }, 48 | Spec: v1.NodeSpec{ 49 | Taints: taints, 50 | }, 51 | Status: v1.NodeStatus{ 52 | NodeInfo: v1.NodeSystemInfo{ 53 | OperatingSystem: p.OperatingSystem(), 54 | Architecture: "amd64", 55 | KubeletVersion: version, 56 | }, 57 | Capacity: p.Capacity(ctx), 58 | Allocatable: p.Capacity(ctx), 59 | Conditions: p.NodeConditions(ctx), 60 | Addresses: p.NodeAddresses(ctx), 61 | DaemonEndpoints: *p.NodeDaemonEndpoints(ctx), 62 | }, 63 | } 64 | return node 65 | } 66 | 67 | // getTaint creates a taint using the provided key/value. 68 | // Taint effect is read from the environment 69 | // The taint key/value may be overwritten by the environment. 70 | func getTaint(c Opts) (*corev1.Taint, error) { 71 | value := c.Provider 72 | 73 | key := c.TaintKey 74 | if key == "" { 75 | key = DefaultTaintKey 76 | } 77 | 78 | if c.TaintEffect == "" { 79 | c.TaintEffect = DefaultTaintEffect 80 | } 81 | 82 | key = getEnv("VKUBELET_TAINT_KEY", key) 83 | value = getEnv("VKUBELET_TAINT_VALUE", value) 84 | effectEnv := getEnv("VKUBELET_TAINT_EFFECT", string(c.TaintEffect)) 85 | 86 | var effect corev1.TaintEffect 87 | switch effectEnv { 88 | case "NoSchedule": 89 | effect = corev1.TaintEffectNoSchedule 90 | case "NoExecute": 91 | effect = corev1.TaintEffectNoExecute 92 | case "PreferNoSchedule": 93 | effect = corev1.TaintEffectPreferNoSchedule 94 | default: 95 | return nil, errdefs.InvalidInputf("taint effect %q is not supported", effectEnv) 96 | } 97 | 98 | return &corev1.Taint{ 99 | Key: key, 100 | Value: value, 101 | Effect: effect, 102 | }, nil 103 | } 104 | -------------------------------------------------------------------------------- /cmd/virtual-kubelet/commands/root/opts.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2017 The virtual-kubelet authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package root 16 | 17 | import ( 18 | "os" 19 | "path/filepath" 20 | "strconv" 21 | "time" 22 | 23 | "github.com/mitchellh/go-homedir" 24 | "github.com/pkg/errors" 25 | corev1 "k8s.io/api/core/v1" 26 | ) 27 | 28 | // Defaults for root command options 29 | const ( 30 | DefaultNodeName = "virtual-kubelet" 31 | DefaultOperatingSystem = "Linux" 32 | DefaultInformerResyncPeriod = 1 * time.Minute 33 | DefaultMetricsAddr = ":10255" 34 | DefaultListenPort = 10250 // TODO(cpuguy83)(VK1.0): Change this to an addr instead of just a port.. we should not be listening on all interfaces. 35 | DefaultPodSyncWorkers = 10 36 | DefaultKubeNamespace = corev1.NamespaceAll 37 | 38 | DefaultTaintEffect = string(corev1.TaintEffectNoSchedule) 39 | DefaultTaintKey = "virtual-kubelet.io/provider" 40 | ) 41 | 42 | // Opts stores all the options for configuring the root virtual-kubelet command. 43 | // It is used for setting flag values. 44 | // 45 | // You can set the default options by creating a new `Opts` struct and passing 46 | // it into `SetDefaultOpts` 47 | type Opts struct { 48 | // Path to the kubeconfig to use to connect to the Kubernetes API server. 49 | KubeConfigPath string 50 | // Namespace to watch for pods and other resources 51 | KubeNamespace string 52 | // Sets the port to listen for requests from the Kubernetes API server 53 | ListenPort int32 54 | 55 | // Node name to use when creating a node in Kubernetes 56 | NodeName string 57 | 58 | // Operating system to run pods for 59 | OperatingSystem string 60 | 61 | Provider string 62 | ProviderConfigPath string 63 | 64 | TaintKey string 65 | TaintEffect string 66 | DisableTaint bool 67 | 68 | MetricsAddr string 69 | 70 | // Number of workers to use to handle pod notifications 71 | PodSyncWorkers int 72 | InformerResyncPeriod time.Duration 73 | 74 | // Use node leases when supported by Kubernetes (instead of node status updates) 75 | EnableNodeLease bool 76 | 77 | TraceExporters []string 78 | TraceSampleRate string 79 | TraceConfig TracingExporterOptions 80 | 81 | // Startup Timeout is how long to wait for the kubelet to start 82 | StartupTimeout time.Duration 83 | 84 | Version string 85 | } 86 | 87 | // SetDefaultOpts sets default options for unset values on the passed in option struct. 88 | // Fields tht are already set will not be modified. 89 | func SetDefaultOpts(c *Opts) error { 90 | if c.OperatingSystem == "" { 91 | c.OperatingSystem = DefaultOperatingSystem 92 | } 93 | 94 | if c.NodeName == "" { 95 | c.NodeName = getEnv("DEFAULT_NODE_NAME", DefaultNodeName) 96 | } 97 | 98 | if c.InformerResyncPeriod == 0 { 99 | c.InformerResyncPeriod = DefaultInformerResyncPeriod 100 | } 101 | 102 | if c.MetricsAddr == "" { 103 | c.MetricsAddr = DefaultMetricsAddr 104 | } 105 | 106 | if c.PodSyncWorkers == 0 { 107 | c.PodSyncWorkers = DefaultPodSyncWorkers 108 | } 109 | 110 | if c.TraceConfig.ServiceName == "" { 111 | c.TraceConfig.ServiceName = DefaultNodeName 112 | } 113 | 114 | if c.ListenPort == 0 { 115 | if kp := os.Getenv("KUBELET_PORT"); kp != "" { 116 | p, err := strconv.Atoi(kp) 117 | if err != nil { 118 | return errors.Wrap(err, "error parsing KUBELET_PORT environment variable") 119 | } 120 | c.ListenPort = int32(p) 121 | } else { 122 | c.ListenPort = DefaultListenPort 123 | } 124 | } 125 | 126 | if c.KubeNamespace == "" { 127 | c.KubeNamespace = DefaultKubeNamespace 128 | } 129 | 130 | if c.TaintKey == "" { 131 | c.TaintKey = DefaultTaintKey 132 | } 133 | if c.TaintEffect == "" { 134 | c.TaintEffect = DefaultTaintEffect 135 | } 136 | 137 | if c.KubeConfigPath == "" { 138 | c.KubeConfigPath = os.Getenv("KUBECONFIG") 139 | if c.KubeConfigPath == "" { 140 | home, _ := homedir.Dir() 141 | if home != "" { 142 | c.KubeConfigPath = filepath.Join(home, ".kube", "config") 143 | } 144 | } 145 | } 146 | 147 | return nil 148 | } 149 | -------------------------------------------------------------------------------- /cmd/virtual-kubelet/commands/root/root.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2017 The virtual-kubelet authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package root 16 | 17 | import ( 18 | "context" 19 | "os" 20 | "path" 21 | "time" 22 | 23 | "github.com/pkg/errors" 24 | "github.com/spf13/cobra" 25 | "github.com/virtual-kubelet/alibabacloud-eci" 26 | "github.com/virtual-kubelet/virtual-kubelet/errdefs" 27 | "github.com/virtual-kubelet/virtual-kubelet/log" 28 | "github.com/virtual-kubelet/virtual-kubelet/manager" 29 | "github.com/virtual-kubelet/virtual-kubelet/node" 30 | "github.com/virtual-kubelet/virtual-kubelet/providers" 31 | corev1 "k8s.io/api/core/v1" 32 | k8serrors "k8s.io/apimachinery/pkg/api/errors" 33 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 34 | "k8s.io/apimachinery/pkg/fields" 35 | kubeinformers "k8s.io/client-go/informers" 36 | "k8s.io/client-go/kubernetes" 37 | "k8s.io/client-go/kubernetes/scheme" 38 | "k8s.io/client-go/kubernetes/typed/coordination/v1beta1" 39 | corev1client "k8s.io/client-go/kubernetes/typed/core/v1" 40 | "k8s.io/client-go/rest" 41 | "k8s.io/client-go/tools/clientcmd" 42 | "k8s.io/client-go/tools/record" 43 | ) 44 | 45 | // NewCommand creates a new top-level command. 46 | // This command is used to start the virtual-kubelet daemon 47 | func NewCommand(ctx context.Context, name string, c Opts) *cobra.Command { 48 | cmd := &cobra.Command{ 49 | Use: name, 50 | Short: name + " provides a virtual kubelet interface for your kubernetes cluster.", 51 | Long: name + ` implements the Kubelet interface with a pluggable 52 | backend implementation allowing users to create kubernetes nodes without running the kubelet. 53 | This allows users to schedule kubernetes workloads on nodes that aren't running Kubernetes.`, 54 | RunE: func(cmd *cobra.Command, args []string) error { 55 | return runRootCommand(ctx, c) 56 | }, 57 | } 58 | 59 | installFlags(cmd.Flags(), &c) 60 | return cmd 61 | } 62 | 63 | func runRootCommand(ctx context.Context, c Opts) error { 64 | ctx, cancel := context.WithCancel(ctx) 65 | defer cancel() 66 | 67 | if ok := providers.ValidOperatingSystems[c.OperatingSystem]; !ok { 68 | return errdefs.InvalidInputf("operating system %q is not supported", c.OperatingSystem) 69 | } 70 | 71 | if c.PodSyncWorkers == 0 { 72 | return errdefs.InvalidInput("pod sync workers must be greater than 0") 73 | } 74 | 75 | var taint *corev1.Taint 76 | if !c.DisableTaint { 77 | var err error 78 | taint, err = getTaint(c) 79 | if err != nil { 80 | return err 81 | } 82 | } 83 | 84 | client, err := newClient(c.KubeConfigPath) 85 | if err != nil { 86 | return err 87 | } 88 | 89 | // Create a shared informer factory for Kubernetes pods in the current namespace (if specified) and scheduled to the current node. 90 | podInformerFactory := kubeinformers.NewSharedInformerFactoryWithOptions( 91 | client, 92 | c.InformerResyncPeriod, 93 | kubeinformers.WithNamespace(c.KubeNamespace), 94 | kubeinformers.WithTweakListOptions(func(options *metav1.ListOptions) { 95 | options.FieldSelector = fields.OneTermEqualSelector("spec.nodeName", c.NodeName).String() 96 | })) 97 | podInformer := podInformerFactory.Core().V1().Pods() 98 | 99 | // Create another shared informer factory for Kubernetes secrets and configmaps (not subject to any selectors). 100 | scmInformerFactory := kubeinformers.NewSharedInformerFactoryWithOptions(client, c.InformerResyncPeriod) 101 | // Create a secret informer and a config map informer so we can pass their listers to the resource manager. 102 | secretInformer := scmInformerFactory.Core().V1().Secrets() 103 | configMapInformer := scmInformerFactory.Core().V1().ConfigMaps() 104 | serviceInformer := scmInformerFactory.Core().V1().Services() 105 | 106 | go podInformerFactory.Start(ctx.Done()) 107 | go scmInformerFactory.Start(ctx.Done()) 108 | 109 | rm, err := manager.NewResourceManager(podInformer.Lister(), secretInformer.Lister(), configMapInformer.Lister(), serviceInformer.Lister()) 110 | if err != nil { 111 | return errors.Wrap(err, "could not create resource manager") 112 | } 113 | 114 | apiConfig, err := getAPIConfig(c) 115 | if err != nil { 116 | return err 117 | } 118 | 119 | if err := setupTracing(ctx, c); err != nil { 120 | return err 121 | } 122 | 123 | if c.Provider != "" && c.Provider != "alibabacloud" { 124 | return errors.Errorf("provider not supported %q: alibabacloud is the only support provider", c.Provider) 125 | } 126 | 127 | p, err := alibabacloud.NewECIProvider( 128 | c.ProviderConfigPath, 129 | rm, 130 | c.NodeName, 131 | c.OperatingSystem, 132 | os.Getenv("VKUBELET_POD_IP"), 133 | c.ListenPort, 134 | ) 135 | if err != nil { 136 | return err 137 | } 138 | 139 | ctx = log.WithLogger(ctx, log.G(ctx).WithFields(log.Fields{ 140 | "provider": c.Provider, 141 | "operatingSystem": c.OperatingSystem, 142 | "node": c.NodeName, 143 | "watchedNamespace": c.KubeNamespace, 144 | })) 145 | 146 | var leaseClient v1beta1.LeaseInterface 147 | if c.EnableNodeLease { 148 | leaseClient = client.CoordinationV1beta1().Leases(corev1.NamespaceNodeLease) 149 | } 150 | 151 | pNode := NodeFromProvider(ctx, c.NodeName, taint, p, c.Version) 152 | nodeRunner, err := node.NewNodeController( 153 | node.NaiveNodeProvider{}, 154 | pNode, 155 | client.CoreV1().Nodes(), 156 | node.WithNodeEnableLeaseV1Beta1(leaseClient, nil), 157 | node.WithNodeStatusUpdateErrorHandler(func(ctx context.Context, err error) error { 158 | if !k8serrors.IsNotFound(err) { 159 | return err 160 | } 161 | 162 | log.G(ctx).Debug("node not found") 163 | newNode := pNode.DeepCopy() 164 | newNode.ResourceVersion = "" 165 | _, err = client.CoreV1().Nodes().Create(newNode) 166 | if err != nil { 167 | return err 168 | } 169 | log.G(ctx).Debug("created new node") 170 | return nil 171 | }), 172 | ) 173 | if err != nil { 174 | log.G(ctx).Fatal(err) 175 | } 176 | 177 | eb := record.NewBroadcaster() 178 | eb.StartLogging(log.G(ctx).Infof) 179 | eb.StartRecordingToSink(&corev1client.EventSinkImpl{Interface: client.CoreV1().Events(c.KubeNamespace)}) 180 | 181 | pc, err := node.NewPodController(node.PodControllerConfig{ 182 | PodClient: client.CoreV1(), 183 | PodInformer: podInformer, 184 | EventRecorder: eb.NewRecorder(scheme.Scheme, corev1.EventSource{Component: path.Join(pNode.Name, "pod-controller")}), 185 | Provider: p, 186 | SecretLister: secretInformer.Lister(), 187 | ConfigMapLister: configMapInformer.Lister(), 188 | ServiceLister: serviceInformer.Lister(), 189 | }) 190 | if err != nil { 191 | return errors.Wrap(err, "error setting up pod controller") 192 | } 193 | 194 | cancelHTTP, err := setupHTTPServer(ctx, p, apiConfig) 195 | if err != nil { 196 | return err 197 | } 198 | defer cancelHTTP() 199 | 200 | go func() { 201 | if err := pc.Run(ctx, c.PodSyncWorkers); err != nil && errors.Cause(err) != context.Canceled { 202 | log.G(ctx).Fatal(err) 203 | } 204 | }() 205 | 206 | if c.StartupTimeout > 0 { 207 | // If there is a startup timeout, it does two things: 208 | // 1. It causes the VK to shutdown if we haven't gotten into an operational state in a time period 209 | // 2. It prevents node advertisement from happening until we're in an operational state 210 | err = waitFor(ctx, c.StartupTimeout, pc.Ready()) 211 | if err != nil { 212 | return err 213 | } 214 | } 215 | 216 | go func() { 217 | if err := nodeRunner.Run(ctx); err != nil { 218 | log.G(ctx).Fatal(err) 219 | } 220 | }() 221 | 222 | log.G(ctx).Info("Initialized") 223 | 224 | <-ctx.Done() 225 | return nil 226 | } 227 | 228 | func waitFor(ctx context.Context, time time.Duration, ready <-chan struct{}) error { 229 | ctx, cancel := context.WithTimeout(ctx, time) 230 | defer cancel() 231 | 232 | // Wait for the VK / PC close the the ready channel, or time out and return 233 | log.G(ctx).Info("Waiting for pod controller / VK to be ready") 234 | 235 | select { 236 | case <-ready: 237 | return nil 238 | case <-ctx.Done(): 239 | return errors.Wrap(ctx.Err(), "Error while starting up VK") 240 | } 241 | } 242 | 243 | func newClient(configPath string) (*kubernetes.Clientset, error) { 244 | var config *rest.Config 245 | 246 | // Check if the kubeConfig file exists. 247 | if _, err := os.Stat(configPath); !os.IsNotExist(err) { 248 | // Get the kubeconfig from the filepath. 249 | config, err = clientcmd.BuildConfigFromFlags("", configPath) 250 | if err != nil { 251 | return nil, errors.Wrap(err, "error building client config") 252 | } 253 | } else { 254 | // Set to in-cluster config. 255 | config, err = rest.InClusterConfig() 256 | if err != nil { 257 | return nil, errors.Wrap(err, "error building in cluster config") 258 | } 259 | } 260 | 261 | if masterURI := os.Getenv("MASTER_URI"); masterURI != "" { 262 | config.Host = masterURI 263 | } 264 | 265 | return kubernetes.NewForConfig(config) 266 | } 267 | -------------------------------------------------------------------------------- /cmd/virtual-kubelet/commands/root/tracing.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2017 The virtual-kubelet authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package root 16 | 17 | import ( 18 | "context" 19 | "net" 20 | "net/http" 21 | "os" 22 | "strconv" 23 | "strings" 24 | 25 | "github.com/pkg/errors" 26 | "github.com/virtual-kubelet/virtual-kubelet/errdefs" 27 | "github.com/virtual-kubelet/virtual-kubelet/log" 28 | octrace "go.opencensus.io/trace" 29 | "go.opencensus.io/zpages" 30 | ) 31 | 32 | var ( 33 | reservedTagNames = map[string]bool{ 34 | "operatingSystem": true, 35 | "provider": true, 36 | "nodeName": true, 37 | } 38 | ) 39 | 40 | func setupTracing(ctx context.Context, c Opts) error { 41 | for k := range c.TraceConfig.Tags { 42 | if reservedTagNames[k] { 43 | return errdefs.InvalidInputf("invalid trace tag %q, must not use a reserved tag key", k) 44 | } 45 | } 46 | if c.TraceConfig.Tags == nil { 47 | c.TraceConfig.Tags = make(map[string]string, 3) 48 | } 49 | c.TraceConfig.Tags["operatingSystem"] = c.OperatingSystem 50 | c.TraceConfig.Tags["provider"] = c.Provider 51 | c.TraceConfig.Tags["nodeName"] = c.NodeName 52 | for _, e := range c.TraceExporters { 53 | if e == "zpages" { 54 | setupZpages(ctx) 55 | continue 56 | } 57 | exporter, err := GetTracingExporter(e, c.TraceConfig) 58 | if err != nil { 59 | return err 60 | } 61 | octrace.RegisterExporter(exporter) 62 | } 63 | if len(c.TraceExporters) > 0 { 64 | var s octrace.Sampler 65 | switch strings.ToLower(c.TraceSampleRate) { 66 | case "": 67 | case "always": 68 | s = octrace.AlwaysSample() 69 | case "never": 70 | s = octrace.NeverSample() 71 | default: 72 | rate, err := strconv.Atoi(c.TraceSampleRate) 73 | if err != nil { 74 | return errdefs.AsInvalidInput(errors.Wrap(err, "unsupported trace sample rate")) 75 | } 76 | if rate < 0 || rate > 100 { 77 | return errdefs.AsInvalidInput(errors.Wrap(err, "trace sample rate must be between 0 and 100")) 78 | } 79 | s = octrace.ProbabilitySampler(float64(rate) / 100) 80 | } 81 | 82 | if s != nil { 83 | octrace.ApplyConfig( 84 | octrace.Config{ 85 | DefaultSampler: s, 86 | }, 87 | ) 88 | } 89 | } 90 | 91 | return nil 92 | } 93 | 94 | func setupZpages(ctx context.Context) { 95 | p := os.Getenv("ZPAGES_PORT") 96 | if p == "" { 97 | log.G(ctx).Error("Missing ZPAGES_PORT env var, cannot setup zpages endpoint") 98 | } 99 | listener, err := net.Listen("tcp", p) 100 | if err != nil { 101 | log.G(ctx).WithError(err).Error("Cannot bind to ZPAGES PORT, cannot setup listener") 102 | return 103 | } 104 | mux := http.NewServeMux() 105 | zpages.Handle(mux, "/debug") 106 | go func() { 107 | // This should never terminate, if it does, it will always terminate with an error 108 | e := http.Serve(listener, mux) 109 | if e == http.ErrServerClosed { 110 | return 111 | } 112 | log.G(ctx).WithError(e).Error("Zpages server exited") 113 | }() 114 | } 115 | -------------------------------------------------------------------------------- /cmd/virtual-kubelet/commands/root/tracing_register.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2017 The virtual-kubelet authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package root 16 | 17 | import ( 18 | "github.com/virtual-kubelet/virtual-kubelet/errdefs" 19 | "go.opencensus.io/trace" 20 | ) 21 | 22 | type TracingExporterOptions struct { 23 | Tags map[string]string 24 | ServiceName string 25 | } 26 | 27 | var ( 28 | tracingExporters = make(map[string]TracingExporterInitFunc) 29 | ) 30 | 31 | // TracingExporterInitFunc is the function that is called to initialize an exporter. 32 | // This is used when registering an exporter and called when a user specifed they want to use the exporter. 33 | type TracingExporterInitFunc func(TracingExporterOptions) (trace.Exporter, error) 34 | 35 | // RegisterTracingExporter registers a tracing exporter. 36 | // For a user to select an exporter, it must be registered here. 37 | func RegisterTracingExporter(name string, f TracingExporterInitFunc) { 38 | tracingExporters[name] = f 39 | } 40 | 41 | // GetTracingExporter gets the specified tracing exporter passing in the options to the exporter init function. 42 | // For an exporter to be availbale here it must be registered with `RegisterTracingExporter`. 43 | func GetTracingExporter(name string, opts TracingExporterOptions) (trace.Exporter, error) { 44 | f, ok := tracingExporters[name] 45 | if !ok { 46 | return nil, errdefs.NotFoundf("tracing exporter %q not found", name) 47 | } 48 | return f(opts) 49 | } 50 | 51 | // AvailableTraceExporters gets the list of registered exporters 52 | func AvailableTraceExporters() []string { 53 | out := make([]string, 0, len(tracingExporters)) 54 | for k := range tracingExporters { 55 | out = append(out, k) 56 | } 57 | return out 58 | } 59 | -------------------------------------------------------------------------------- /cmd/virtual-kubelet/commands/root/tracing_register_jaeger.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2017 The virtual-kubelet authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // +build !no_jaeger_exporter 16 | 17 | package root 18 | 19 | import ( 20 | "errors" 21 | "os" 22 | 23 | "contrib.go.opencensus.io/exporter/jaeger" 24 | "go.opencensus.io/trace" 25 | ) 26 | 27 | func init() { 28 | RegisterTracingExporter("jaeger", NewJaegerExporter) 29 | } 30 | 31 | // NewJaegerExporter creates a new opencensus tracing exporter. 32 | func NewJaegerExporter(opts TracingExporterOptions) (trace.Exporter, error) { 33 | jOpts := jaeger.Options{ 34 | Endpoint: os.Getenv("JAEGER_ENDPOINT"), 35 | AgentEndpoint: os.Getenv("JAEGER_AGENT_ENDPOINT"), 36 | Username: os.Getenv("JAEGER_USER"), 37 | Password: os.Getenv("JAEGER_PASSWORD"), 38 | Process: jaeger.Process{ 39 | ServiceName: opts.ServiceName, 40 | }, 41 | } 42 | 43 | if jOpts.Endpoint == "" && jOpts.AgentEndpoint == "" { 44 | return nil, errors.New("Must specify either JAEGER_ENDPOINT or JAEGER_AGENT_ENDPOINT") 45 | } 46 | 47 | for k, v := range opts.Tags { 48 | jOpts.Process.Tags = append(jOpts.Process.Tags, jaeger.StringTag(k, v)) 49 | } 50 | return jaeger.NewExporter(jOpts) 51 | } 52 | -------------------------------------------------------------------------------- /cmd/virtual-kubelet/commands/root/tracing_register_ocagent.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2017 The virtual-kubelet authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // +build !no_ocagent_exporter 16 | 17 | package root 18 | 19 | import ( 20 | "os" 21 | 22 | "contrib.go.opencensus.io/exporter/ocagent" 23 | "github.com/virtual-kubelet/virtual-kubelet/errdefs" 24 | "go.opencensus.io/trace" 25 | ) 26 | 27 | func init() { 28 | RegisterTracingExporter("ocagent", NewOCAgentExporter) 29 | } 30 | 31 | // NewOCAgentExporter creates a new opencensus tracing exporter using the opencensus agent forwarder. 32 | func NewOCAgentExporter(opts TracingExporterOptions) (trace.Exporter, error) { 33 | agentOpts := append([]ocagent.ExporterOption{}, ocagent.WithServiceName(opts.ServiceName)) 34 | 35 | if endpoint := os.Getenv("OCAGENT_ENDPOINT"); endpoint != "" { 36 | agentOpts = append(agentOpts, ocagent.WithAddress(endpoint)) 37 | } else { 38 | return nil, errdefs.InvalidInput("must set endpoint address in OCAGENT_ENDPOINT") 39 | } 40 | 41 | switch os.Getenv("OCAGENT_INSECURE") { 42 | case "0", "no", "n", "off", "": 43 | case "1", "yes", "y", "on": 44 | agentOpts = append(agentOpts, ocagent.WithInsecure()) 45 | default: 46 | return nil, errdefs.InvalidInput("invalid value for OCAGENT_INSECURE") 47 | } 48 | 49 | return ocagent.NewExporter(agentOpts...) 50 | } 51 | -------------------------------------------------------------------------------- /cmd/virtual-kubelet/commands/root/tracing_register_test.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2017 The virtual-kubelet authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package root 16 | 17 | import ( 18 | "testing" 19 | 20 | "github.com/virtual-kubelet/virtual-kubelet/errdefs" 21 | "go.opencensus.io/trace" 22 | ) 23 | 24 | func TestGetTracingExporter(t *testing.T) { 25 | defer delete(tracingExporters, "mock") 26 | 27 | mockExporterFn := func(_ TracingExporterOptions) (trace.Exporter, error) { 28 | return nil, nil 29 | } 30 | 31 | _, err := GetTracingExporter("notexist", TracingExporterOptions{}) 32 | if !errdefs.IsNotFound(err) { 33 | t.Fatalf("expected not found error, got: %v", err) 34 | } 35 | 36 | RegisterTracingExporter("mock", mockExporterFn) 37 | 38 | if _, err := GetTracingExporter("mock", TracingExporterOptions{}); err != nil { 39 | t.Fatal(err) 40 | } 41 | } 42 | 43 | func TestAvailableExporters(t *testing.T) { 44 | defer delete(tracingExporters, "mock") 45 | 46 | mockExporterFn := func(_ TracingExporterOptions) (trace.Exporter, error) { 47 | return nil, nil 48 | } 49 | RegisterTracingExporter("mock", mockExporterFn) 50 | 51 | for _, e := range AvailableTraceExporters() { 52 | if e == "mock" { 53 | return 54 | } 55 | } 56 | 57 | t.Fatal("could not find mock exporter in list of registered exporters") 58 | } 59 | -------------------------------------------------------------------------------- /cmd/virtual-kubelet/commands/version/version.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2017 The virtual-kubelet authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package version 16 | 17 | import ( 18 | "fmt" 19 | 20 | "github.com/spf13/cobra" 21 | ) 22 | 23 | // NewCommand creates a new version subcommand command 24 | func NewCommand(version, buildTime string) *cobra.Command { 25 | return &cobra.Command{ 26 | Use: "version", 27 | Short: "Show the version of the program", 28 | Long: `Show the version of the program`, 29 | Run: func(cmd *cobra.Command, args []string) { 30 | fmt.Printf("Version: %s, Built: %s\n", version, buildTime) 31 | }, 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /cmd/virtual-kubelet/main.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2017 The virtual-kubelet authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package main 16 | 17 | import ( 18 | "context" 19 | "os" 20 | "os/signal" 21 | "path/filepath" 22 | "strings" 23 | "syscall" 24 | 25 | "github.com/pkg/errors" 26 | "github.com/sirupsen/logrus" 27 | "github.com/spf13/cobra" 28 | "github.com/virtual-kubelet/alibabacloud-eci/cmd/virtual-kubelet/commands/providers" 29 | "github.com/virtual-kubelet/alibabacloud-eci/cmd/virtual-kubelet/commands/root" 30 | "github.com/virtual-kubelet/alibabacloud-eci/cmd/virtual-kubelet/commands/version" 31 | "github.com/virtual-kubelet/virtual-kubelet/log" 32 | logruslogger "github.com/virtual-kubelet/virtual-kubelet/log/logrus" 33 | "github.com/virtual-kubelet/virtual-kubelet/trace" 34 | "github.com/virtual-kubelet/virtual-kubelet/trace/opencensus" 35 | ) 36 | 37 | var ( 38 | buildVersion = "N/A" 39 | buildTime = "N/A" 40 | k8sVersion = "v1.13.1" // This should follow the version of k8s.io/kubernetes we are importing 41 | ) 42 | 43 | func main() { 44 | ctx, cancel := context.WithCancel(context.Background()) 45 | sig := make(chan os.Signal, 1) 46 | signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM) 47 | go func() { 48 | <-sig 49 | cancel() 50 | }() 51 | 52 | log.L = logruslogger.FromLogrus(logrus.NewEntry(logrus.StandardLogger())) 53 | trace.T = opencensus.Adapter{} 54 | 55 | var opts root.Opts 56 | optsErr := root.SetDefaultOpts(&opts) 57 | opts.Version = strings.Join([]string{k8sVersion, "vk-alibabacloud", buildVersion}, "-") 58 | 59 | rootCmd := root.NewCommand(ctx, filepath.Base(os.Args[0]), opts) 60 | rootCmd.AddCommand(version.NewCommand(buildVersion, buildTime), providers.NewCommand()) 61 | preRun := rootCmd.PreRunE 62 | 63 | var logLevel string 64 | rootCmd.PreRunE = func(cmd *cobra.Command, args []string) error { 65 | if optsErr != nil { 66 | return optsErr 67 | } 68 | if preRun != nil { 69 | return preRun(cmd, args) 70 | } 71 | return nil 72 | } 73 | 74 | rootCmd.PersistentFlags().StringVar(&logLevel, "log-level", "info", `set the log level, e.g. "debug", "info", "warn", "error"`) 75 | 76 | rootCmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error { 77 | if logLevel != "" { 78 | lvl, err := logrus.ParseLevel(logLevel) 79 | if err != nil { 80 | return errors.Wrap(err, "could not parse log level") 81 | } 82 | logrus.SetLevel(lvl) 83 | } 84 | return nil 85 | } 86 | 87 | if err := rootCmd.Execute(); err != nil && errors.Cause(err) != context.Canceled { 88 | log.G(ctx).Fatal(err) 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /config.go: -------------------------------------------------------------------------------- 1 | package alibabacloud 2 | 3 | import ( 4 | "io" 5 | 6 | "github.com/BurntSushi/toml" 7 | "github.com/virtual-kubelet/virtual-kubelet/providers" 8 | ) 9 | 10 | type providerConfig struct { 11 | Region string 12 | OperatingSystem string 13 | CPU string 14 | Memory string 15 | Pods string 16 | VSwitch string 17 | SecureGroup string 18 | ClusterName string 19 | } 20 | 21 | func (p *ECIProvider) loadConfig(r io.Reader) error { 22 | var config providerConfig 23 | if _, err := toml.DecodeReader(r, &config); err != nil { 24 | return err 25 | } 26 | 27 | p.region = config.Region 28 | if p.region == "" { 29 | p.region = "cn-hangzhou" 30 | } 31 | 32 | p.vSwitch = config.VSwitch 33 | p.secureGroup = config.SecureGroup 34 | 35 | p.cpu = config.CPU 36 | if p.cpu == "" { 37 | p.cpu = "20" 38 | } 39 | p.memory = config.Memory 40 | if p.memory == "" { 41 | p.memory = "100Gi" 42 | } 43 | p.pods = config.Pods 44 | if p.pods == "" { 45 | p.pods = "20" 46 | } 47 | p.operatingSystem = config.OperatingSystem 48 | if p.operatingSystem == "" { 49 | p.operatingSystem = providers.OperatingSystemLinux 50 | } 51 | p.clusterName = config.ClusterName 52 | if p.clusterName == "" { 53 | p.clusterName = "default" 54 | } 55 | return nil 56 | } 57 | -------------------------------------------------------------------------------- /eci.go: -------------------------------------------------------------------------------- 1 | package alibabacloud 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "crypto/sha256" 7 | "encoding/base64" 8 | "encoding/hex" 9 | "encoding/json" 10 | "errors" 11 | "fmt" 12 | "io" 13 | "io/ioutil" 14 | "os" 15 | "strconv" 16 | "strings" 17 | "time" 18 | 19 | "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" 20 | "github.com/virtual-kubelet/alibabacloud-eci/eci" 21 | "github.com/virtual-kubelet/virtual-kubelet/errdefs" 22 | "github.com/virtual-kubelet/virtual-kubelet/log" 23 | "github.com/virtual-kubelet/virtual-kubelet/manager" 24 | "github.com/virtual-kubelet/virtual-kubelet/node/api" 25 | v1 "k8s.io/api/core/v1" 26 | k8serr "k8s.io/apimachinery/pkg/api/errors" 27 | "k8s.io/apimachinery/pkg/api/resource" 28 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 29 | "k8s.io/apimachinery/pkg/types" 30 | ) 31 | 32 | // The service account secret mount path. 33 | const serviceAccountSecretMountPath = "/var/run/secrets/kubernetes.io/serviceaccount" 34 | 35 | const podTagTimeFormat = "2006-01-02T15-04-05Z" 36 | const timeFormat = "2006-01-02T15:04:05Z" 37 | 38 | // ECIProvider implements the virtual-kubelet provider interface and communicates with Alibaba Cloud's ECI APIs. 39 | type ECIProvider struct { 40 | eciClient *eci.Client 41 | resourceManager *manager.ResourceManager 42 | resourceGroup string 43 | region string 44 | nodeName string 45 | operatingSystem string 46 | clusterName string 47 | cpu string 48 | memory string 49 | pods string 50 | internalIP string 51 | daemonEndpointPort int32 52 | secureGroup string 53 | vSwitch string 54 | } 55 | 56 | // AuthConfig is the secret returned from an ImageRegistryCredential 57 | type AuthConfig struct { 58 | Username string `json:"username,omitempty"` 59 | Password string `json:"password,omitempty"` 60 | Auth string `json:"auth,omitempty"` 61 | Email string `json:"email,omitempty"` 62 | ServerAddress string `json:"serveraddress,omitempty"` 63 | IdentityToken string `json:"identitytoken,omitempty"` 64 | RegistryToken string `json:"registrytoken,omitempty"` 65 | } 66 | 67 | var validEciRegions = []string{ 68 | "cn-hangzhou", 69 | "cn-shanghai", 70 | "cn-beijing", 71 | "us-west-1", 72 | } 73 | 74 | // isValidECIRegion checks to make sure we're using a valid ECI region 75 | func isValidECIRegion(region string) bool { 76 | regionLower := strings.ToLower(region) 77 | regionTrimmed := strings.Replace(regionLower, " ", "", -1) 78 | 79 | for _, validRegion := range validEciRegions { 80 | if regionTrimmed == validRegion { 81 | return true 82 | } 83 | } 84 | 85 | return false 86 | } 87 | 88 | // NewECIProvider creates a new ECIProvider. 89 | func NewECIProvider(config string, rm *manager.ResourceManager, nodeName, operatingSystem string, internalIP string, daemonEndpointPort int32) (*ECIProvider, error) { 90 | var p ECIProvider 91 | var err error 92 | 93 | p.resourceManager = rm 94 | 95 | if config != "" { 96 | f, err := os.Open(config) 97 | if err != nil { 98 | return nil, err 99 | } 100 | defer f.Close() 101 | 102 | if err := p.loadConfig(f); err != nil { 103 | return nil, err 104 | } 105 | } 106 | if r := os.Getenv("ECI_CLUSTER_NAME"); r != "" { 107 | p.clusterName = r 108 | } 109 | if p.clusterName == "" { 110 | p.clusterName = "default" 111 | } 112 | if r := os.Getenv("ECI_REGION"); r != "" { 113 | p.region = r 114 | } 115 | if p.region == "" { 116 | return nil, errors.New("Region can't be empty please set ECI_REGION\n") 117 | } 118 | if r := p.region; !isValidECIRegion(r) { 119 | unsupportedRegionMessage := fmt.Sprintf("Region %s is invalid. Current supported regions are: %s", 120 | r, strings.Join(validEciRegions, ", ")) 121 | 122 | return nil, errors.New(unsupportedRegionMessage) 123 | } 124 | 125 | var accessKey, secretKey string 126 | 127 | if ak := os.Getenv("ECI_ACCESS_KEY"); ak != "" { 128 | accessKey = ak 129 | } 130 | if sk := os.Getenv("ECI_SECRET_KEY"); sk != "" { 131 | secretKey = sk 132 | } 133 | if sg := os.Getenv("ECI_SECURITY_GROUP"); sg != "" { 134 | p.secureGroup = sg 135 | } 136 | if vsw := os.Getenv("ECI_VSWITCH"); vsw != "" { 137 | p.vSwitch = vsw 138 | } 139 | if p.secureGroup == "" { 140 | return nil, errors.New("secureGroup can't be empty\n") 141 | } 142 | 143 | if p.vSwitch == "" { 144 | return nil, errors.New("vSwitch can't be empty\n") 145 | } 146 | 147 | p.eciClient, err = eci.NewClientWithAccessKey(p.region, accessKey, secretKey) 148 | if err != nil { 149 | return nil, err 150 | } 151 | 152 | p.cpu = "1000" 153 | p.memory = "4Ti" 154 | p.pods = "1000" 155 | 156 | if cpuQuota := os.Getenv("ECI_QUOTA_CPU"); cpuQuota != "" { 157 | p.cpu = cpuQuota 158 | } 159 | 160 | if memoryQuota := os.Getenv("ECI_QUOTA_MEMORY"); memoryQuota != "" { 161 | p.memory = memoryQuota 162 | } 163 | 164 | if podsQuota := os.Getenv("ECI_QUOTA_POD"); podsQuota != "" { 165 | p.pods = podsQuota 166 | } 167 | 168 | p.operatingSystem = operatingSystem 169 | p.nodeName = nodeName 170 | p.internalIP = internalIP 171 | p.daemonEndpointPort = daemonEndpointPort 172 | return &p, err 173 | } 174 | 175 | // CreatePod accepts a Pod definition and creates 176 | // an ECI deployment 177 | func (p *ECIProvider) CreatePod(ctx context.Context, pod *v1.Pod) error { 178 | //Ignore daemonSet Pod 179 | if pod != nil && pod.OwnerReferences != nil && len(pod.OwnerReferences) != 0 && pod.OwnerReferences[0].Kind == "DaemonSet" { 180 | msg := fmt.Sprintf("Skip to create DaemonSet pod %q", pod.Name) 181 | log.G(ctx).WithField("Method", "CreatePod").Info(msg) 182 | return nil 183 | } 184 | 185 | request := eci.CreateCreateContainerGroupRequest() 186 | request.RestartPolicy = string(pod.Spec.RestartPolicy) 187 | 188 | // get containers 189 | containers, err := p.getContainers(pod, false) 190 | initContainers, err := p.getContainers(pod, true) 191 | if err != nil { 192 | return err 193 | } 194 | 195 | // get registry creds 196 | creds, err := p.getImagePullSecrets(pod) 197 | if err != nil { 198 | return err 199 | } 200 | 201 | // get volumes 202 | volumes, err := p.getVolumes(pod) 203 | if err != nil { 204 | return err 205 | } 206 | 207 | // assign all the things 208 | request.Containers = containers 209 | request.InitContainers = initContainers 210 | request.Volumes = volumes 211 | request.ImageRegistryCredentials = creds 212 | CreationTimestamp := pod.CreationTimestamp.UTC().Format(podTagTimeFormat) 213 | tags := []eci.Tag{ 214 | eci.Tag{Key: "ClusterName", Value: p.clusterName}, 215 | eci.Tag{Key: "NodeName", Value: p.nodeName}, 216 | eci.Tag{Key: "NameSpace", Value: pod.Namespace}, 217 | eci.Tag{Key: "PodName", Value: pod.Name}, 218 | eci.Tag{Key: "UID", Value: string(pod.UID)}, 219 | eci.Tag{Key: "CreationTimestamp", Value: CreationTimestamp}, 220 | } 221 | 222 | ContainerGroupName := containerGroupName(pod) 223 | request.Tags = tags 224 | request.SecurityGroupId = p.secureGroup 225 | request.VSwitchId = p.vSwitch 226 | request.ContainerGroupName = ContainerGroupName 227 | msg := fmt.Sprintf("CreateContainerGroup request %+v", request) 228 | log.G(ctx).WithField("Method", "CreatePod").Info(msg) 229 | response, err := p.eciClient.CreateContainerGroup(request) 230 | if err != nil { 231 | return err 232 | } 233 | msg = fmt.Sprintf("CreateContainerGroup successed. %s, %s, %s", response.RequestId, response.ContainerGroupId, ContainerGroupName) 234 | log.G(ctx).WithField("Method", "CreatePod").Info(msg) 235 | return nil 236 | } 237 | 238 | func containerGroupName(pod *v1.Pod) string { 239 | return fmt.Sprintf("%s-%s", pod.Namespace, pod.Name) 240 | } 241 | 242 | // UpdatePod is a noop, ECI currently does not support live updates of a pod. 243 | func (p *ECIProvider) UpdatePod(ctx context.Context, pod *v1.Pod) error { 244 | return nil 245 | } 246 | 247 | // DeletePod deletes the specified pod out of ECI. 248 | func (p *ECIProvider) DeletePod(ctx context.Context, pod *v1.Pod) error { 249 | eciId := "" 250 | for _, cg := range p.GetCgs() { 251 | if getECITagValue(&cg, "PodName") == pod.Name && getECITagValue(&cg, "NameSpace") == pod.Namespace { 252 | eciId = cg.ContainerGroupId 253 | break 254 | } 255 | } 256 | if eciId == "" { 257 | return errdefs.NotFoundf("DeletePod can't find Pod %s-%s", pod.Namespace, pod.Name) 258 | } 259 | 260 | request := eci.CreateDeleteContainerGroupRequest() 261 | request.ContainerGroupId = eciId 262 | _, err := p.eciClient.DeleteContainerGroup(request) 263 | return wrapError(err) 264 | } 265 | 266 | // GetPod returns a pod by name that is running inside ECI 267 | // returns nil if a pod by that name is not found. 268 | func (p *ECIProvider) GetPod(ctx context.Context, namespace, name string) (*v1.Pod, error) { 269 | pods, err := p.GetPods(ctx) 270 | if err != nil { 271 | return nil, err 272 | } 273 | for _, pod := range pods { 274 | if pod.Name == name && pod.Namespace == namespace { 275 | return pod, nil 276 | } 277 | } 278 | return nil, nil 279 | } 280 | 281 | // GetContainerLogs returns the logs of a pod by name that is running inside ECI. 282 | func (p *ECIProvider) GetContainerLogs(ctx context.Context, namespace, podName, containerName string, opts api.ContainerLogOpts) (io.ReadCloser, error) { 283 | eciId := "" 284 | for _, cg := range p.GetCgs() { 285 | if getECITagValue(&cg, "PodName") == podName && getECITagValue(&cg, "NameSpace") == namespace { 286 | eciId = cg.ContainerGroupId 287 | break 288 | } 289 | } 290 | if eciId == "" { 291 | return nil, errors.New(fmt.Sprintf("GetContainerLogs can't find Pod %s-%s", namespace, podName)) 292 | } 293 | 294 | request := eci.CreateDescribeContainerLogRequest() 295 | request.ContainerGroupId = eciId 296 | request.ContainerName = containerName 297 | request.Tail = requests.Integer(opts.Tail) 298 | 299 | // get logs from cg 300 | logContent := "" 301 | retry := 10 302 | for i := 0; i < retry; i++ { 303 | response, err := p.eciClient.DescribeContainerLog(request) 304 | if err != nil { 305 | msg := fmt.Sprint("Error getting container logs, retrying") 306 | log.G(ctx).WithField("Method", "GetContainerLogs").Info(msg) 307 | time.Sleep(5000 * time.Millisecond) 308 | } else { 309 | logContent = response.Content 310 | break 311 | } 312 | } 313 | 314 | return ioutil.NopCloser(strings.NewReader(logContent)), nil 315 | } 316 | 317 | // Get full pod name as defined in the provider context 318 | func (p *ECIProvider) GetPodFullName(namespace string, pod string) string { 319 | return fmt.Sprintf("%s-%s", namespace, pod) 320 | } 321 | 322 | // RunInContainer executes a command in a container in the pod, copying data 323 | // between in/out/err and the container's stdin/stdout/stderr. 324 | func (p *ECIProvider) RunInContainer(ctx context.Context, namespace, podName, containerName string, cmd []string, attach api.AttachIO) error { 325 | return nil 326 | } 327 | 328 | // GetPodStatus returns the status of a pod by name that is running inside ECI 329 | // returns nil if a pod by that name is not found. 330 | func (p *ECIProvider) GetPodStatus(ctx context.Context, namespace, name string) (*v1.PodStatus, error) { 331 | pod, err := p.GetPod(ctx, namespace, name) 332 | if err != nil { 333 | return nil, err 334 | } 335 | 336 | if pod == nil { 337 | return nil, nil 338 | } 339 | 340 | return &pod.Status, nil 341 | } 342 | 343 | func (p *ECIProvider) GetCgs() []eci.ContainerGroup { 344 | cgs := make([]eci.ContainerGroup, 0) 345 | request := eci.CreateDescribeContainerGroupsRequest() 346 | for { 347 | cgsResponse, err := p.eciClient.DescribeContainerGroups(request) 348 | if err != nil || len(cgsResponse.ContainerGroups) == 0 { 349 | break 350 | } 351 | request.NextToken = cgsResponse.NextToken 352 | 353 | for _, cg := range cgsResponse.ContainerGroups { 354 | if getECITagValue(&cg, "NodeName") != p.nodeName { 355 | continue 356 | } 357 | cn := getECITagValue(&cg, "ClusterName") 358 | if cn == "" { 359 | cn = "default" 360 | } 361 | if cn != p.clusterName { 362 | continue 363 | } 364 | cgs = append(cgs, cg) 365 | } 366 | if request.NextToken == "" { 367 | break 368 | } 369 | } 370 | return cgs 371 | } 372 | 373 | // GetPods returns a list of all pods known to be running within ECI. 374 | func (p *ECIProvider) GetPods(ctx context.Context) ([]*v1.Pod, error) { 375 | pods := make([]*v1.Pod, 0) 376 | for _, cg := range p.GetCgs() { 377 | c := cg 378 | pod, err := containerGroupToPod(&c) 379 | if err != nil { 380 | msg := fmt.Sprint("error converting container group to pod", cg.ContainerGroupId, err) 381 | log.G(context.TODO()).WithField("Method", "GetPods").Info(msg) 382 | continue 383 | } 384 | pods = append(pods, pod) 385 | } 386 | return pods, nil 387 | } 388 | 389 | // Capacity returns a resource list containing the capacity limits set for ECI. 390 | func (p *ECIProvider) Capacity(ctx context.Context) v1.ResourceList { 391 | return v1.ResourceList{ 392 | "cpu": resource.MustParse(p.cpu), 393 | "memory": resource.MustParse(p.memory), 394 | "pods": resource.MustParse(p.pods), 395 | } 396 | } 397 | 398 | // NodeConditions returns a list of conditions (Ready, OutOfDisk, etc), for updates to the node status 399 | // within Kubernetes. 400 | func (p *ECIProvider) NodeConditions(ctx context.Context) []v1.NodeCondition { 401 | // TODO: Make these dynamic and augment with custom ECI specific conditions of interest 402 | return []v1.NodeCondition{ 403 | { 404 | Type: "Ready", 405 | Status: v1.ConditionTrue, 406 | LastHeartbeatTime: metav1.Now(), 407 | LastTransitionTime: metav1.Now(), 408 | Reason: "KubeletReady", 409 | Message: "kubelet is ready.", 410 | }, 411 | { 412 | Type: "OutOfDisk", 413 | Status: v1.ConditionFalse, 414 | LastHeartbeatTime: metav1.Now(), 415 | LastTransitionTime: metav1.Now(), 416 | Reason: "KubeletHasSufficientDisk", 417 | Message: "kubelet has sufficient disk space available", 418 | }, 419 | { 420 | Type: "MemoryPressure", 421 | Status: v1.ConditionFalse, 422 | LastHeartbeatTime: metav1.Now(), 423 | LastTransitionTime: metav1.Now(), 424 | Reason: "KubeletHasSufficientMemory", 425 | Message: "kubelet has sufficient memory available", 426 | }, 427 | { 428 | Type: "DiskPressure", 429 | Status: v1.ConditionFalse, 430 | LastHeartbeatTime: metav1.Now(), 431 | LastTransitionTime: metav1.Now(), 432 | Reason: "KubeletHasNoDiskPressure", 433 | Message: "kubelet has no disk pressure", 434 | }, 435 | { 436 | Type: "NetworkUnavailable", 437 | Status: v1.ConditionFalse, 438 | LastHeartbeatTime: metav1.Now(), 439 | LastTransitionTime: metav1.Now(), 440 | Reason: "RouteCreated", 441 | Message: "RouteController created a route", 442 | }, 443 | } 444 | } 445 | 446 | // NodeAddresses returns a list of addresses for the node status 447 | // within Kubernetes. 448 | func (p *ECIProvider) NodeAddresses(ctx context.Context) []v1.NodeAddress { 449 | // TODO: Make these dynamic and augment with custom ECI specific conditions of interest 450 | return []v1.NodeAddress{ 451 | { 452 | Type: "InternalIP", 453 | Address: p.internalIP, 454 | }, 455 | } 456 | } 457 | 458 | // NodeDaemonEndpoints returns NodeDaemonEndpoints for the node status 459 | // within Kubernetes. 460 | func (p *ECIProvider) NodeDaemonEndpoints(ctx context.Context) *v1.NodeDaemonEndpoints { 461 | return &v1.NodeDaemonEndpoints{ 462 | KubeletEndpoint: v1.DaemonEndpoint{ 463 | Port: p.daemonEndpointPort, 464 | }, 465 | } 466 | } 467 | 468 | // OperatingSystem returns the operating system that was provided by the config. 469 | func (p *ECIProvider) OperatingSystem() string { 470 | return p.operatingSystem 471 | } 472 | 473 | func (p *ECIProvider) getImagePullSecrets(pod *v1.Pod) ([]eci.ImageRegistryCredential, error) { 474 | ips := make([]eci.ImageRegistryCredential, 0, len(pod.Spec.ImagePullSecrets)) 475 | for _, ref := range pod.Spec.ImagePullSecrets { 476 | secret, err := p.resourceManager.GetSecret(ref.Name, pod.Namespace) 477 | if err != nil { 478 | return ips, err 479 | } 480 | if secret == nil { 481 | return nil, fmt.Errorf("error getting image pull secret") 482 | } 483 | // TODO: Check if secret type is v1.SecretTypeDockercfg and use DockerConfigKey instead of hardcoded value 484 | // TODO: Check if secret type is v1.SecretTypeDockerConfigJson and use DockerConfigJsonKey to determine if it's in json format 485 | // TODO: Return error if it's not one of these two types 486 | switch secret.Type { 487 | case v1.SecretTypeDockercfg: 488 | ips, err = readDockerCfgSecret(secret, ips) 489 | case v1.SecretTypeDockerConfigJson: 490 | ips, err = readDockerConfigJSONSecret(secret, ips) 491 | default: 492 | return nil, fmt.Errorf("image pull secret type is not one of kubernetes.io/dockercfg or kubernetes.io/dockerconfigjson") 493 | } 494 | 495 | if err != nil { 496 | return ips, err 497 | } 498 | } 499 | return ips, nil 500 | } 501 | 502 | func readDockerCfgSecret(secret *v1.Secret, ips []eci.ImageRegistryCredential) ([]eci.ImageRegistryCredential, error) { 503 | var err error 504 | var authConfigs map[string]AuthConfig 505 | repoData, ok := secret.Data[string(v1.DockerConfigKey)] 506 | 507 | if !ok { 508 | return ips, fmt.Errorf("no dockercfg present in secret") 509 | } 510 | 511 | err = json.Unmarshal(repoData, &authConfigs) 512 | if err != nil { 513 | return ips, fmt.Errorf("failed to unmarshal auth config %+v", err) 514 | } 515 | 516 | for server, authConfig := range authConfigs { 517 | ips = append(ips, eci.ImageRegistryCredential{ 518 | Password: authConfig.Password, 519 | Server: server, 520 | UserName: authConfig.Username, 521 | }) 522 | } 523 | 524 | return ips, err 525 | } 526 | 527 | func readDockerConfigJSONSecret(secret *v1.Secret, ips []eci.ImageRegistryCredential) ([]eci.ImageRegistryCredential, error) { 528 | var err error 529 | repoData, ok := secret.Data[string(v1.DockerConfigJsonKey)] 530 | 531 | if !ok { 532 | return ips, fmt.Errorf("no dockerconfigjson present in secret") 533 | } 534 | 535 | var authConfigs map[string]map[string]AuthConfig 536 | 537 | err = json.Unmarshal(repoData, &authConfigs) 538 | if err != nil { 539 | return ips, err 540 | } 541 | 542 | auths, ok := authConfigs["auths"] 543 | 544 | if !ok { 545 | return ips, fmt.Errorf("malformed dockerconfigjson in secret") 546 | } 547 | 548 | for server, authConfig := range auths { 549 | ips = append(ips, eci.ImageRegistryCredential{ 550 | Password: authConfig.Password, 551 | Server: server, 552 | UserName: authConfig.Username, 553 | }) 554 | } 555 | 556 | return ips, err 557 | } 558 | 559 | func (p *ECIProvider) getContainers(pod *v1.Pod, init bool) ([]eci.CreateContainer, error) { 560 | podContainers := pod.Spec.Containers 561 | if init { 562 | podContainers = pod.Spec.InitContainers 563 | } 564 | containers := make([]eci.CreateContainer, 0, len(podContainers)) 565 | for _, container := range podContainers { 566 | c := eci.CreateContainer{ 567 | Name: container.Name, 568 | Image: container.Image, 569 | Commands: append(container.Command, container.Args...), 570 | Ports: make([]eci.ContainerPort, 0, len(container.Ports)), 571 | } 572 | 573 | for _, p := range container.Ports { 574 | c.Ports = append(c.Ports, eci.ContainerPort{ 575 | Port: requests.Integer(strconv.FormatInt(int64(p.ContainerPort), 10)), 576 | Protocol: string(p.Protocol), 577 | }) 578 | } 579 | 580 | c.VolumeMounts = make([]eci.VolumeMount, 0, len(container.VolumeMounts)) 581 | for _, v := range container.VolumeMounts { 582 | c.VolumeMounts = append(c.VolumeMounts, eci.VolumeMount{ 583 | Name: v.Name, 584 | MountPath: v.MountPath, 585 | ReadOnly: requests.Boolean(strconv.FormatBool(v.ReadOnly)), 586 | }) 587 | } 588 | 589 | c.EnvironmentVars = make([]eci.EnvironmentVar, 0, len(container.Env)) 590 | for _, e := range container.Env { 591 | c.EnvironmentVars = append(c.EnvironmentVars, eci.EnvironmentVar{Key: e.Name, Value: e.Value}) 592 | } 593 | 594 | cpuRequest := 1.00 595 | if _, ok := container.Resources.Requests[v1.ResourceCPU]; ok { 596 | cpuRequest = float64(container.Resources.Requests.Cpu().MilliValue()) / 1000.00 597 | } 598 | 599 | c.Cpu = requests.Float(fmt.Sprintf("%.3f", cpuRequest)) 600 | 601 | memoryRequest := 2.0 602 | if _, ok := container.Resources.Requests[v1.ResourceMemory]; ok { 603 | memoryRequest = float64(container.Resources.Requests.Memory().Value()) / 1024.0 / 1024.0 / 1024.0 604 | } 605 | 606 | c.Memory = requests.Float(fmt.Sprintf("%.3f", memoryRequest)) 607 | 608 | c.ImagePullPolicy = string(container.ImagePullPolicy) 609 | c.WorkingDir = container.WorkingDir 610 | 611 | containers = append(containers, c) 612 | } 613 | return containers, nil 614 | } 615 | 616 | func (p *ECIProvider) getVolumes(pod *v1.Pod) ([]eci.Volume, error) { 617 | volumes := make([]eci.Volume, 0, len(pod.Spec.Volumes)) 618 | for _, v := range pod.Spec.Volumes { 619 | // Handle the case for the EmptyDir. 620 | if v.EmptyDir != nil { 621 | volumes = append(volumes, eci.Volume{ 622 | Type: eci.VOL_TYPE_EMPTYDIR, 623 | Name: v.Name, 624 | EmptyDirVolumeEnable: requests.Boolean(strconv.FormatBool(true)), 625 | }) 626 | continue 627 | } 628 | 629 | // Handle the case for the NFS. 630 | if v.NFS != nil { 631 | volumes = append(volumes, eci.Volume{ 632 | Type: eci.VOL_TYPE_NFS, 633 | Name: v.Name, 634 | NfsVolumeServer: v.NFS.Server, 635 | NfsVolumePath: v.NFS.Path, 636 | NfsVolumeReadOnly: requests.Boolean(strconv.FormatBool(v.NFS.ReadOnly)), 637 | }) 638 | continue 639 | } 640 | 641 | // Handle the case for ConfigMap volume. 642 | if v.ConfigMap != nil { 643 | ConfigFileToPaths := make([]eci.ConfigFileToPath, 0) 644 | configMap, err := p.resourceManager.GetConfigMap(v.ConfigMap.Name, pod.Namespace) 645 | if v.ConfigMap.Optional != nil && !*v.ConfigMap.Optional && k8serr.IsNotFound(err) { 646 | return nil, fmt.Errorf("ConfigMap %s is required by Pod %s and does not exist", v.ConfigMap.Name, pod.Name) 647 | } 648 | if configMap == nil { 649 | continue 650 | } 651 | 652 | for k, v := range configMap.Data { 653 | var b bytes.Buffer 654 | enc := base64.NewEncoder(base64.StdEncoding, &b) 655 | enc.Write([]byte(v)) 656 | 657 | ConfigFileToPaths = append(ConfigFileToPaths, eci.ConfigFileToPath{Path: k, Content: b.String()}) 658 | } 659 | 660 | if len(ConfigFileToPaths) != 0 { 661 | volumes = append(volumes, eci.Volume{ 662 | Type: eci.VOL_TYPE_CONFIGFILEVOLUME, 663 | Name: v.Name, 664 | ConfigFileToPaths: ConfigFileToPaths, 665 | }) 666 | } 667 | continue 668 | } 669 | 670 | if v.Secret != nil { 671 | ConfigFileToPaths := make([]eci.ConfigFileToPath, 0) 672 | secret, err := p.resourceManager.GetSecret(v.Secret.SecretName, pod.Namespace) 673 | if v.Secret.Optional != nil && !*v.Secret.Optional && k8serr.IsNotFound(err) { 674 | return nil, fmt.Errorf("Secret %s is required by Pod %s and does not exist", v.Secret.SecretName, pod.Name) 675 | } 676 | if secret == nil { 677 | continue 678 | } 679 | for k, v := range secret.Data { 680 | var b bytes.Buffer 681 | enc := base64.NewEncoder(base64.StdEncoding, &b) 682 | enc.Write(v) 683 | ConfigFileToPaths = append(ConfigFileToPaths, eci.ConfigFileToPath{Path: k, Content: b.String()}) 684 | } 685 | 686 | if len(ConfigFileToPaths) != 0 { 687 | volumes = append(volumes, eci.Volume{ 688 | Type: eci.VOL_TYPE_CONFIGFILEVOLUME, 689 | Name: v.Name, 690 | ConfigFileToPaths: ConfigFileToPaths, 691 | }) 692 | } 693 | continue 694 | } 695 | 696 | // If we've made it this far we have found a volume type that isn't supported 697 | return nil, fmt.Errorf("Pod %s requires volume %s which is of an unsupported type\n", pod.Name, v.Name) 698 | } 699 | 700 | return volumes, nil 701 | } 702 | 703 | func containerGroupToPod(cg *eci.ContainerGroup) (*v1.Pod, error) { 704 | var podCreationTimestamp, containerStartTime metav1.Time 705 | 706 | CreationTimestamp := getECITagValue(cg, "CreationTimestamp") 707 | if CreationTimestamp != "" { 708 | if t, err := time.Parse(podTagTimeFormat, CreationTimestamp); err == nil { 709 | podCreationTimestamp = metav1.NewTime(t) 710 | } 711 | } 712 | 713 | if t, err := time.Parse(timeFormat, cg.Containers[0].CurrentState.StartTime); err == nil { 714 | containerStartTime = metav1.NewTime(t) 715 | } 716 | 717 | // Use the Provisioning State if it's not Succeeded, 718 | // otherwise use the state of the instance. 719 | eciState := cg.Status 720 | 721 | containers := make([]v1.Container, 0, len(cg.Containers)) 722 | containerStatuses := make([]v1.ContainerStatus, 0, len(cg.Containers)) 723 | for _, c := range cg.Containers { 724 | container := v1.Container{ 725 | Name: c.Name, 726 | Image: c.Image, 727 | Command: c.Commands, 728 | Resources: v1.ResourceRequirements{ 729 | Requests: v1.ResourceList{ 730 | v1.ResourceCPU: resource.MustParse(fmt.Sprintf("%.2f", c.Cpu)), 731 | v1.ResourceMemory: resource.MustParse(fmt.Sprintf("%.1fG", c.Memory)), 732 | }, 733 | }, 734 | } 735 | 736 | container.Resources.Limits = v1.ResourceList{ 737 | v1.ResourceCPU: resource.MustParse(fmt.Sprintf("%.2f", c.Cpu)), 738 | v1.ResourceMemory: resource.MustParse(fmt.Sprintf("%.1fG", c.Memory)), 739 | } 740 | 741 | containers = append(containers, container) 742 | containerStatus := v1.ContainerStatus{ 743 | Name: c.Name, 744 | State: eciContainerStateToContainerState(c.CurrentState), 745 | LastTerminationState: eciContainerStateToContainerState(c.PreviousState), 746 | Ready: eciStateToPodPhase(c.CurrentState.State) == v1.PodRunning, 747 | RestartCount: int32(c.RestartCount), 748 | Image: c.Image, 749 | ImageID: "", 750 | ContainerID: getContainerID(cg.ContainerGroupId, c.Name), 751 | } 752 | 753 | // Add to containerStatuses 754 | containerStatuses = append(containerStatuses, containerStatus) 755 | } 756 | 757 | pod := v1.Pod{ 758 | TypeMeta: metav1.TypeMeta{ 759 | Kind: "Pod", 760 | APIVersion: "v1", 761 | }, 762 | ObjectMeta: metav1.ObjectMeta{ 763 | Name: getECITagValue(cg, "PodName"), 764 | Namespace: getECITagValue(cg, "NameSpace"), 765 | ClusterName: getECITagValue(cg, "ClusterName"), 766 | UID: types.UID(getECITagValue(cg, "UID")), 767 | CreationTimestamp: podCreationTimestamp, 768 | }, 769 | Spec: v1.PodSpec{ 770 | NodeName: getECITagValue(cg, "NodeName"), 771 | Volumes: []v1.Volume{}, 772 | Containers: containers, 773 | }, 774 | Status: v1.PodStatus{ 775 | Phase: eciStateToPodPhase(eciState), 776 | Conditions: eciStateToPodConditions(eciState, podCreationTimestamp), 777 | Message: "", 778 | Reason: "", 779 | HostIP: "", 780 | PodIP: cg.IntranetIp, 781 | StartTime: &containerStartTime, 782 | ContainerStatuses: containerStatuses, 783 | }, 784 | } 785 | 786 | return &pod, nil 787 | } 788 | 789 | func getContainerID(cgID, containerName string) string { 790 | if cgID == "" { 791 | return "" 792 | } 793 | 794 | containerResourceID := fmt.Sprintf("%s/containers/%s", cgID, containerName) 795 | 796 | h := sha256.New() 797 | h.Write([]byte(strings.ToUpper(containerResourceID))) 798 | hashBytes := h.Sum(nil) 799 | return fmt.Sprintf("eci://%s", hex.EncodeToString(hashBytes)) 800 | } 801 | 802 | func eciStateToPodPhase(state string) v1.PodPhase { 803 | switch state { 804 | case "Scheduling": 805 | return v1.PodPending 806 | case "ScheduleFailed": 807 | return v1.PodFailed 808 | case "Pending": 809 | return v1.PodPending 810 | case "Running": 811 | return v1.PodRunning 812 | case "Failed": 813 | return v1.PodFailed 814 | case "Succeeded": 815 | return v1.PodSucceeded 816 | } 817 | return v1.PodUnknown 818 | } 819 | 820 | func eciStateToPodConditions(state string, transitionTime metav1.Time) []v1.PodCondition { 821 | switch state { 822 | case "Running", "Succeeded": 823 | return []v1.PodCondition{ 824 | v1.PodCondition{ 825 | Type: v1.PodReady, 826 | Status: v1.ConditionTrue, 827 | LastTransitionTime: transitionTime, 828 | }, v1.PodCondition{ 829 | Type: v1.PodInitialized, 830 | Status: v1.ConditionTrue, 831 | LastTransitionTime: transitionTime, 832 | }, v1.PodCondition{ 833 | Type: v1.PodScheduled, 834 | Status: v1.ConditionTrue, 835 | LastTransitionTime: transitionTime, 836 | }, 837 | } 838 | } 839 | return []v1.PodCondition{} 840 | } 841 | 842 | func eciContainerStateToContainerState(cs eci.ContainerState) v1.ContainerState { 843 | t1, err := time.Parse(timeFormat, cs.StartTime) 844 | if err != nil { 845 | return v1.ContainerState{} 846 | } 847 | 848 | startTime := metav1.NewTime(t1) 849 | 850 | // Handle the case where the container is running. 851 | if cs.State == "Running" || cs.State == "Succeeded" { 852 | return v1.ContainerState{ 853 | Running: &v1.ContainerStateRunning{ 854 | StartedAt: startTime, 855 | }, 856 | } 857 | } 858 | 859 | t2, err := time.Parse(timeFormat, cs.FinishTime) 860 | if err != nil { 861 | return v1.ContainerState{} 862 | } 863 | 864 | finishTime := metav1.NewTime(t2) 865 | 866 | // Handle the case where the container failed. 867 | if cs.State == "Failed" || cs.State == "Canceled" { 868 | return v1.ContainerState{ 869 | Terminated: &v1.ContainerStateTerminated{ 870 | ExitCode: int32(cs.ExitCode), 871 | Reason: cs.State, 872 | Message: cs.DetailStatus, 873 | StartedAt: startTime, 874 | FinishedAt: finishTime, 875 | }, 876 | } 877 | } 878 | 879 | // Handle the case where the container is pending. 880 | // Which should be all other eci states. 881 | return v1.ContainerState{ 882 | Waiting: &v1.ContainerStateWaiting{ 883 | Reason: cs.State, 884 | Message: cs.DetailStatus, 885 | }, 886 | } 887 | } 888 | 889 | func getECITagValue(cg *eci.ContainerGroup, key string) string { 890 | for _, tag := range cg.Tags { 891 | if tag.Key == key { 892 | return tag.Value 893 | } 894 | } 895 | return "" 896 | } 897 | -------------------------------------------------------------------------------- /eci.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 9 | 11 | 12 | 13 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /eci.toml: -------------------------------------------------------------------------------- 1 | Region = "cn-hangzhou" 2 | OperatingSystem = "Linux" 3 | CPU = "20" 4 | Memory = "100Gi" 5 | Pods = "20" 6 | ClusterName = "default" 7 | -------------------------------------------------------------------------------- /eci/client.go: -------------------------------------------------------------------------------- 1 | package eci 2 | 3 | //Licensed under the Apache License, Version 2.0 (the "License"); 4 | //you may not use this file except in compliance with the License. 5 | //You may obtain a copy of the License at 6 | // 7 | //http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | //Unless required by applicable law or agreed to in writing, software 10 | //distributed under the License is distributed on an "AS IS" BASIS, 11 | //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | //See the License for the specific language governing permissions and 13 | //limitations under the License. 14 | // 15 | // Code generated by Alibaba Cloud SDK Code Generator. 16 | // Changes may cause incorrect behavior and will be lost if the code is regenerated. 17 | 18 | import ( 19 | "github.com/aliyun/alibaba-cloud-sdk-go/sdk" 20 | "github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth" 21 | ) 22 | 23 | // Client is the sdk client struct, each func corresponds to an OpenAPI 24 | type Client struct { 25 | sdk.Client 26 | } 27 | 28 | // NewClient creates a sdk client with environment variables 29 | func NewClient() (client *Client, err error) { 30 | client = &Client{} 31 | err = client.Init() 32 | return 33 | } 34 | 35 | // NewClientWithOptions creates a sdk client with regionId/sdkConfig/credential 36 | // this is the common api to create a sdk client 37 | func NewClientWithOptions(regionId string, config *sdk.Config, credential auth.Credential) (client *Client, err error) { 38 | client = &Client{} 39 | err = client.InitWithOptions(regionId, config, credential) 40 | return 41 | } 42 | 43 | // NewClientWithAccessKey is a shortcut to create sdk client with accesskey 44 | // usage: https://help.aliyun.com/document_detail/66217.html 45 | func NewClientWithAccessKey(regionId, accessKeyId, accessKeySecret string) (client *Client, err error) { 46 | client = &Client{} 47 | err = client.InitWithAccessKey(regionId, accessKeyId, accessKeySecret) 48 | return 49 | } 50 | 51 | // NewClientWithStsToken is a shortcut to create sdk client with sts token 52 | // usage: https://help.aliyun.com/document_detail/66222.html 53 | func NewClientWithStsToken(regionId, stsAccessKeyId, stsAccessKeySecret, stsToken string) (client *Client, err error) { 54 | client = &Client{} 55 | err = client.InitWithStsToken(regionId, stsAccessKeyId, stsAccessKeySecret, stsToken) 56 | return 57 | } 58 | 59 | // NewClientWithRamRoleArn is a shortcut to create sdk client with ram roleArn 60 | // usage: https://help.aliyun.com/document_detail/66222.html 61 | func NewClientWithRamRoleArn(regionId string, accessKeyId, accessKeySecret, roleArn, roleSessionName string) (client *Client, err error) { 62 | client = &Client{} 63 | err = client.InitWithRamRoleArn(regionId, accessKeyId, accessKeySecret, roleArn, roleSessionName) 64 | return 65 | } 66 | 67 | // NewClientWithEcsRamRole is a shortcut to create sdk client with ecs ram role 68 | // usage: https://help.aliyun.com/document_detail/66223.html 69 | func NewClientWithEcsRamRole(regionId string, roleName string) (client *Client, err error) { 70 | client = &Client{} 71 | err = client.InitWithEcsRamRole(regionId, roleName) 72 | return 73 | } 74 | 75 | // NewClientWithRsaKeyPair is a shortcut to create sdk client with rsa key pair 76 | // attention: rsa key pair auth is only Japan regions available 77 | func NewClientWithRsaKeyPair(regionId string, publicKeyId, privateKey string, sessionExpiration int) (client *Client, err error) { 78 | client = &Client{} 79 | err = client.InitWithRsaKeyPair(regionId, publicKeyId, privateKey, sessionExpiration) 80 | return 81 | } 82 | -------------------------------------------------------------------------------- /eci/create_container_group.go: -------------------------------------------------------------------------------- 1 | package eci 2 | 3 | //Licensed under the Apache License, Version 2.0 (the "License"); 4 | //you may not use this file except in compliance with the License. 5 | //You may obtain a copy of the License at 6 | // 7 | //http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | //Unless required by applicable law or agreed to in writing, software 10 | //distributed under the License is distributed on an "AS IS" BASIS, 11 | //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | //See the License for the specific language governing permissions and 13 | //limitations under the License. 14 | // 15 | // Code generated by Alibaba Cloud SDK Code Generator. 16 | // Changes may cause incorrect behavior and will be lost if the code is regenerated. 17 | 18 | import ( 19 | "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" 20 | "github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses" 21 | ) 22 | 23 | // CreateContainerGroup invokes the eci.CreateContainerGroup API synchronously 24 | // api document: https://help.aliyun.com/api/eci/createcontainergroup.html 25 | func (client *Client) CreateContainerGroup(request *CreateContainerGroupRequest) (response *CreateContainerGroupResponse, err error) { 26 | response = CreateCreateContainerGroupResponse() 27 | err = client.DoAction(request, response) 28 | return 29 | } 30 | 31 | // CreateContainerGroupWithChan invokes the eci.CreateContainerGroup API asynchronously 32 | // api document: https://help.aliyun.com/api/eci/createcontainergroup.html 33 | // asynchronous document: https://help.aliyun.com/document_detail/66220.html 34 | func (client *Client) CreateContainerGroupWithChan(request *CreateContainerGroupRequest) (<-chan *CreateContainerGroupResponse, <-chan error) { 35 | responseChan := make(chan *CreateContainerGroupResponse, 1) 36 | errChan := make(chan error, 1) 37 | err := client.AddAsyncTask(func() { 38 | defer close(responseChan) 39 | defer close(errChan) 40 | response, err := client.CreateContainerGroup(request) 41 | if err != nil { 42 | errChan <- err 43 | } else { 44 | responseChan <- response 45 | } 46 | }) 47 | if err != nil { 48 | errChan <- err 49 | close(responseChan) 50 | close(errChan) 51 | } 52 | return responseChan, errChan 53 | } 54 | 55 | // CreateContainerGroupWithCallback invokes the eci.CreateContainerGroup API asynchronously 56 | // api document: https://help.aliyun.com/api/eci/createcontainergroup.html 57 | // asynchronous document: https://help.aliyun.com/document_detail/66220.html 58 | func (client *Client) CreateContainerGroupWithCallback(request *CreateContainerGroupRequest, callback func(response *CreateContainerGroupResponse, err error)) <-chan int { 59 | result := make(chan int, 1) 60 | err := client.AddAsyncTask(func() { 61 | var response *CreateContainerGroupResponse 62 | var err error 63 | defer close(result) 64 | response, err = client.CreateContainerGroup(request) 65 | callback(response, err) 66 | result <- 1 67 | }) 68 | if err != nil { 69 | defer close(result) 70 | callback(nil, err) 71 | result <- 0 72 | } 73 | return result 74 | } 75 | 76 | // CreateContainerGroupRequest is the request struct for api CreateContainerGroup 77 | type CreateContainerGroupRequest struct { 78 | *requests.RpcRequest 79 | Containers []CreateContainer `position:"Query" name:"Container" type:"Repeated"` 80 | InitContainers []CreateContainer `position:"Query" name:"InitContainer" type:"Repeated"` 81 | ResourceOwnerId requests.Integer `position:"Query" name:"ResourceOwnerId"` 82 | SecurityGroupId string `position:"Query" name:"SecurityGroupId"` 83 | ImageRegistryCredentials []ImageRegistryCredential `position:"Query" name:"ImageRegistryCredential" type:"Repeated"` 84 | Tags []Tag `position:"Query" name:"Tag" type:"Repeated"` 85 | ResourceOwnerAccount string `position:"Query" name:"ResourceOwnerAccount"` 86 | RestartPolicy string `position:"Query" name:"RestartPolicy"` 87 | OwnerAccount string `position:"Query" name:"OwnerAccount"` 88 | OwnerId requests.Integer `position:"Query" name:"OwnerId"` 89 | VSwitchId string `position:"Query" name:"VSwitchId"` 90 | Volumes []Volume `position:"Query" name:"Volume" type:"Repeated"` 91 | ContainerGroupName string `position:"Query" name:"ContainerGroupName"` 92 | ZoneId string `position:"Query" name:"ZoneId"` 93 | } 94 | 95 | type CreateContainer struct { 96 | Name string `name:"Name"` 97 | Image string `name:"Image"` 98 | Memory requests.Float `name:"Memory"` 99 | Cpu requests.Float `name:"Cpu"` 100 | WorkingDir string `name:"WorkingDir"` 101 | ImagePullPolicy string `name:"ImagePullPolicy"` 102 | Commands []string `name:"Command" type:"Repeated"` 103 | Args []string `name:"Arg" type:"Repeated"` 104 | VolumeMounts []VolumeMount `name:"VolumeMount" type:"Repeated"` 105 | Ports []ContainerPort `name:"Port" type:"Repeated"` 106 | EnvironmentVars []EnvironmentVar `name:"EnvironmentVar" type:"Repeated"` 107 | } 108 | 109 | // CreateContainerGroupImageRegistryCredential is a repeated param struct in CreateContainerGroupRequest 110 | type ImageRegistryCredential struct { 111 | Server string `name:"Server"` 112 | UserName string `name:"UserName"` 113 | Password string `name:"Password"` 114 | } 115 | 116 | // CreateContainerGroupResponse is the response struct for api CreateContainerGroup 117 | type CreateContainerGroupResponse struct { 118 | *responses.BaseResponse 119 | RequestId string 120 | ContainerGroupId string 121 | } 122 | 123 | // CreateCreateContainerGroupRequest creates a request to invoke CreateContainerGroup API 124 | func CreateCreateContainerGroupRequest() (request *CreateContainerGroupRequest) { 125 | request = &CreateContainerGroupRequest{ 126 | RpcRequest: &requests.RpcRequest{}, 127 | } 128 | request.InitWithApiInfo("Eci", "2018-08-08", "CreateContainerGroup", "eci", "openAPI") 129 | return 130 | } 131 | 132 | // CreateCreateContainerGroupResponse creates a response to parse from CreateContainerGroup response 133 | func CreateCreateContainerGroupResponse() (response *CreateContainerGroupResponse) { 134 | response = &CreateContainerGroupResponse{ 135 | BaseResponse: &responses.BaseResponse{}, 136 | } 137 | return 138 | } 139 | -------------------------------------------------------------------------------- /eci/delete_container_group.go: -------------------------------------------------------------------------------- 1 | package eci 2 | 3 | //Licensed under the Apache License, Version 2.0 (the "License"); 4 | //you may not use this file except in compliance with the License. 5 | //You may obtain a copy of the License at 6 | // 7 | //http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | //Unless required by applicable law or agreed to in writing, software 10 | //distributed under the License is distributed on an "AS IS" BASIS, 11 | //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | //See the License for the specific language governing permissions and 13 | //limitations under the License. 14 | // 15 | // Code generated by Alibaba Cloud SDK Code Generator. 16 | // Changes may cause incorrect behavior and will be lost if the code is regenerated. 17 | 18 | import ( 19 | "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" 20 | "github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses" 21 | ) 22 | 23 | // DeleteContainerGroup invokes the eci.DeleteContainerGroup API synchronously 24 | // api document: https://help.aliyun.com/api/eci/deletecontainergroup.html 25 | func (client *Client) DeleteContainerGroup(request *DeleteContainerGroupRequest) (response *DeleteContainerGroupResponse, err error) { 26 | response = CreateDeleteContainerGroupResponse() 27 | err = client.DoAction(request, response) 28 | return 29 | } 30 | 31 | // DeleteContainerGroupWithChan invokes the eci.DeleteContainerGroup API asynchronously 32 | // api document: https://help.aliyun.com/api/eci/deletecontainergroup.html 33 | // asynchronous document: https://help.aliyun.com/document_detail/66220.html 34 | func (client *Client) DeleteContainerGroupWithChan(request *DeleteContainerGroupRequest) (<-chan *DeleteContainerGroupResponse, <-chan error) { 35 | responseChan := make(chan *DeleteContainerGroupResponse, 1) 36 | errChan := make(chan error, 1) 37 | err := client.AddAsyncTask(func() { 38 | defer close(responseChan) 39 | defer close(errChan) 40 | response, err := client.DeleteContainerGroup(request) 41 | if err != nil { 42 | errChan <- err 43 | } else { 44 | responseChan <- response 45 | } 46 | }) 47 | if err != nil { 48 | errChan <- err 49 | close(responseChan) 50 | close(errChan) 51 | } 52 | return responseChan, errChan 53 | } 54 | 55 | // DeleteContainerGroupWithCallback invokes the eci.DeleteContainerGroup API asynchronously 56 | // api document: https://help.aliyun.com/api/eci/deletecontainergroup.html 57 | // asynchronous document: https://help.aliyun.com/document_detail/66220.html 58 | func (client *Client) DeleteContainerGroupWithCallback(request *DeleteContainerGroupRequest, callback func(response *DeleteContainerGroupResponse, err error)) <-chan int { 59 | result := make(chan int, 1) 60 | err := client.AddAsyncTask(func() { 61 | var response *DeleteContainerGroupResponse 62 | var err error 63 | defer close(result) 64 | response, err = client.DeleteContainerGroup(request) 65 | callback(response, err) 66 | result <- 1 67 | }) 68 | if err != nil { 69 | defer close(result) 70 | callback(nil, err) 71 | result <- 0 72 | } 73 | return result 74 | } 75 | 76 | // DeleteContainerGroupRequest is the request struct for api DeleteContainerGroup 77 | type DeleteContainerGroupRequest struct { 78 | *requests.RpcRequest 79 | ResourceOwnerId requests.Integer `position:"Query" name:"ResourceOwnerId"` 80 | ContainerGroupId string `position:"Query" name:"ContainerGroupId"` 81 | ResourceOwnerAccount string `position:"Query" name:"ResourceOwnerAccount"` 82 | OwnerAccount string `position:"Query" name:"OwnerAccount"` 83 | OwnerId requests.Integer `position:"Query" name:"OwnerId"` 84 | } 85 | 86 | // DeleteContainerGroupResponse is the response struct for api DeleteContainerGroup 87 | type DeleteContainerGroupResponse struct { 88 | *responses.BaseResponse 89 | RequestId string `json:"RequestId" xml:"RequestId"` 90 | } 91 | 92 | // CreateDeleteContainerGroupRequest creates a request to invoke DeleteContainerGroup API 93 | func CreateDeleteContainerGroupRequest() (request *DeleteContainerGroupRequest) { 94 | request = &DeleteContainerGroupRequest{ 95 | RpcRequest: &requests.RpcRequest{}, 96 | } 97 | request.InitWithApiInfo("Eci", "2018-08-08", "DeleteContainerGroup", "eci", "openAPI") 98 | return 99 | } 100 | 101 | // CreateDeleteContainerGroupResponse creates a response to parse from DeleteContainerGroup response 102 | func CreateDeleteContainerGroupResponse() (response *DeleteContainerGroupResponse) { 103 | response = &DeleteContainerGroupResponse{ 104 | BaseResponse: &responses.BaseResponse{}, 105 | } 106 | return 107 | } 108 | -------------------------------------------------------------------------------- /eci/describe_container_groups.go: -------------------------------------------------------------------------------- 1 | package eci 2 | 3 | //Licensed under the Apache License, Version 2.0 (the "License"); 4 | //you may not use this file except in compliance with the License. 5 | //You may obtain a copy of the License at 6 | // 7 | //http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | //Unless required by applicable law or agreed to in writing, software 10 | //distributed under the License is distributed on an "AS IS" BASIS, 11 | //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | //See the License for the specific language governing permissions and 13 | //limitations under the License. 14 | // 15 | // Code generated by Alibaba Cloud SDK Code Generator. 16 | // Changes may cause incorrect behavior and will be lost if the code is regenerated. 17 | 18 | import ( 19 | "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" 20 | "github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses" 21 | ) 22 | 23 | // DescribeContainerGroups invokes the eci.DescribeContainerGroups API synchronously 24 | // api document: https://help.aliyun.com/api/eci/describecontainergroups.html 25 | func (client *Client) DescribeContainerGroups(request *DescribeContainerGroupsRequest) (response *DescribeContainerGroupsResponse, err error) { 26 | response = CreateDescribeContainerGroupsResponse() 27 | err = client.DoAction(request, response) 28 | return 29 | } 30 | 31 | // DescribeContainerGroupsWithChan invokes the eci.DescribeContainerGroups API asynchronously 32 | // api document: https://help.aliyun.com/api/eci/describecontainergroups.html 33 | // asynchronous document: https://help.aliyun.com/document_detail/66220.html 34 | func (client *Client) DescribeContainerGroupsWithChan(request *DescribeContainerGroupsRequest) (<-chan *DescribeContainerGroupsResponse, <-chan error) { 35 | responseChan := make(chan *DescribeContainerGroupsResponse, 1) 36 | errChan := make(chan error, 1) 37 | err := client.AddAsyncTask(func() { 38 | defer close(responseChan) 39 | defer close(errChan) 40 | response, err := client.DescribeContainerGroups(request) 41 | if err != nil { 42 | errChan <- err 43 | } else { 44 | responseChan <- response 45 | } 46 | }) 47 | if err != nil { 48 | errChan <- err 49 | close(responseChan) 50 | close(errChan) 51 | } 52 | return responseChan, errChan 53 | } 54 | 55 | // DescribeContainerGroupsWithCallback invokes the eci.DescribeContainerGroups API asynchronously 56 | // api document: https://help.aliyun.com/api/eci/describecontainergroups.html 57 | // asynchronous document: https://help.aliyun.com/document_detail/66220.html 58 | func (client *Client) DescribeContainerGroupsWithCallback(request *DescribeContainerGroupsRequest, callback func(response *DescribeContainerGroupsResponse, err error)) <-chan int { 59 | result := make(chan int, 1) 60 | err := client.AddAsyncTask(func() { 61 | var response *DescribeContainerGroupsResponse 62 | var err error 63 | defer close(result) 64 | response, err = client.DescribeContainerGroups(request) 65 | callback(response, err) 66 | result <- 1 67 | }) 68 | if err != nil { 69 | defer close(result) 70 | callback(nil, err) 71 | result <- 0 72 | } 73 | return result 74 | } 75 | 76 | // DescribeContainerGroupsRequest is the request struct for api DescribeContainerGroups 77 | type DescribeContainerGroupsRequest struct { 78 | *requests.RpcRequest 79 | ResourceOwnerId requests.Integer `position:"Query" name:"ResourceOwnerId"` 80 | NextToken string `position:"Query" name:"NextToken"` 81 | Limit requests.Integer `position:"Query" name:"Limit"` 82 | Tags *[]DescribeContainerGroupsTag `position:"Query" name:"Tag" type:"Repeated"` 83 | ContainerGroupId string `position:"Query" name:"ContainerGroupId"` 84 | ResourceOwnerAccount string `position:"Query" name:"ResourceOwnerAccount"` 85 | OwnerAccount string `position:"Query" name:"OwnerAccount"` 86 | OwnerId requests.Integer `position:"Query" name:"OwnerId"` 87 | VSwitchId string `position:"Query" name:"VSwitchId"` 88 | ContainerGroupName string `position:"Query" name:"ContainerGroupName"` 89 | ZoneId string `position:"Query" name:"ZoneId"` 90 | } 91 | 92 | // DescribeContainerGroupsTag is a repeated param struct in DescribeContainerGroupsRequest 93 | type DescribeContainerGroupsTag struct { 94 | Key string `name:"Key"` 95 | Value string `name:"Value"` 96 | } 97 | 98 | // DescribeContainerGroupsResponse is the response struct for api DescribeContainerGroups 99 | type DescribeContainerGroupsResponse struct { 100 | *responses.BaseResponse 101 | RequestId string `json:"RequestId" xml:"RequestId"` 102 | NextToken string `json:"NextToken" xml:"NextToken"` 103 | TotalCount int `json:"TotalCount" xml:"TotalCount"` 104 | ContainerGroups []ContainerGroup `json:"ContainerGroups" xml:"ContainerGroups"` 105 | } 106 | 107 | // CreateDescribeContainerGroupsRequest creates a request to invoke DescribeContainerGroups API 108 | func CreateDescribeContainerGroupsRequest() (request *DescribeContainerGroupsRequest) { 109 | request = &DescribeContainerGroupsRequest{ 110 | RpcRequest: &requests.RpcRequest{}, 111 | } 112 | request.InitWithApiInfo("Eci", "2018-08-08", "DescribeContainerGroups", "eci", "openAPI") 113 | return 114 | } 115 | 116 | // CreateDescribeContainerGroupsResponse creates a response to parse from DescribeContainerGroups response 117 | func CreateDescribeContainerGroupsResponse() (response *DescribeContainerGroupsResponse) { 118 | response = &DescribeContainerGroupsResponse{ 119 | BaseResponse: &responses.BaseResponse{}, 120 | } 121 | return 122 | } 123 | -------------------------------------------------------------------------------- /eci/describe_container_log.go: -------------------------------------------------------------------------------- 1 | package eci 2 | 3 | //Licensed under the Apache License, Version 2.0 (the "License"); 4 | //you may not use this file except in compliance with the License. 5 | //You may obtain a copy of the License at 6 | // 7 | //http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | //Unless required by applicable law or agreed to in writing, software 10 | //distributed under the License is distributed on an "AS IS" BASIS, 11 | //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | //See the License for the specific language governing permissions and 13 | //limitations under the License. 14 | // 15 | // Code generated by Alibaba Cloud SDK Code Generator. 16 | // Changes may cause incorrect behavior and will be lost if the code is regenerated. 17 | 18 | import ( 19 | "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" 20 | "github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses" 21 | ) 22 | 23 | // DescribeContainerLog invokes the eci.DescribeContainerLog API synchronously 24 | // api document: https://help.aliyun.com/api/eci/describecontainerlog.html 25 | func (client *Client) DescribeContainerLog(request *DescribeContainerLogRequest) (response *DescribeContainerLogResponse, err error) { 26 | response = CreateDescribeContainerLogResponse() 27 | err = client.DoAction(request, response) 28 | return 29 | } 30 | 31 | // DescribeContainerLogWithChan invokes the eci.DescribeContainerLog API asynchronously 32 | // api document: https://help.aliyun.com/api/eci/describecontainerlog.html 33 | // asynchronous document: https://help.aliyun.com/document_detail/66220.html 34 | func (client *Client) DescribeContainerLogWithChan(request *DescribeContainerLogRequest) (<-chan *DescribeContainerLogResponse, <-chan error) { 35 | responseChan := make(chan *DescribeContainerLogResponse, 1) 36 | errChan := make(chan error, 1) 37 | err := client.AddAsyncTask(func() { 38 | defer close(responseChan) 39 | defer close(errChan) 40 | response, err := client.DescribeContainerLog(request) 41 | if err != nil { 42 | errChan <- err 43 | } else { 44 | responseChan <- response 45 | } 46 | }) 47 | if err != nil { 48 | errChan <- err 49 | close(responseChan) 50 | close(errChan) 51 | } 52 | return responseChan, errChan 53 | } 54 | 55 | // DescribeContainerLogWithCallback invokes the eci.DescribeContainerLog API asynchronously 56 | // api document: https://help.aliyun.com/api/eci/describecontainerlog.html 57 | // asynchronous document: https://help.aliyun.com/document_detail/66220.html 58 | func (client *Client) DescribeContainerLogWithCallback(request *DescribeContainerLogRequest, callback func(response *DescribeContainerLogResponse, err error)) <-chan int { 59 | result := make(chan int, 1) 60 | err := client.AddAsyncTask(func() { 61 | var response *DescribeContainerLogResponse 62 | var err error 63 | defer close(result) 64 | response, err = client.DescribeContainerLog(request) 65 | callback(response, err) 66 | result <- 1 67 | }) 68 | if err != nil { 69 | defer close(result) 70 | callback(nil, err) 71 | result <- 0 72 | } 73 | return result 74 | } 75 | 76 | // DescribeContainerLogRequest is the request struct for api DescribeContainerLog 77 | type DescribeContainerLogRequest struct { 78 | *requests.RpcRequest 79 | ResourceOwnerId requests.Integer `position:"Query" name:"ResourceOwnerId"` 80 | ContainerName string `position:"Query" name:"ContainerName"` 81 | StartTime string `position:"Query" name:"StartTime"` 82 | ContainerGroupId string `position:"Query" name:"ContainerGroupId"` 83 | ResourceOwnerAccount string `position:"Query" name:"ResourceOwnerAccount"` 84 | Tail requests.Integer `position:"Query" name:"Tail"` 85 | OwnerAccount string `position:"Query" name:"OwnerAccount"` 86 | OwnerId requests.Integer `position:"Query" name:"OwnerId"` 87 | } 88 | 89 | // DescribeContainerLogResponse is the response struct for api DescribeContainerLog 90 | type DescribeContainerLogResponse struct { 91 | *responses.BaseResponse 92 | RequestId string `json:"RequestId" xml:"RequestId"` 93 | ContainerName string `json:"ContainerName" xml:"ContainerName"` 94 | Content string `json:"Content" xml:"Content"` 95 | } 96 | 97 | // CreateDescribeContainerLogRequest creates a request to invoke DescribeContainerLog API 98 | func CreateDescribeContainerLogRequest() (request *DescribeContainerLogRequest) { 99 | request = &DescribeContainerLogRequest{ 100 | RpcRequest: &requests.RpcRequest{}, 101 | } 102 | request.InitWithApiInfo("Eci", "2018-08-08", "DescribeContainerLog", "eci", "openAPI") 103 | return 104 | } 105 | 106 | // CreateDescribeContainerLogResponse creates a response to parse from DescribeContainerLog response 107 | func CreateDescribeContainerLogResponse() (response *DescribeContainerLogResponse) { 108 | response = &DescribeContainerLogResponse{ 109 | BaseResponse: &responses.BaseResponse{}, 110 | } 111 | return 112 | } 113 | -------------------------------------------------------------------------------- /eci/struct_config_file_to_path.go: -------------------------------------------------------------------------------- 1 | package eci 2 | 3 | //Licensed under the Apache License, Version 2.0 (the "License"); 4 | //you may not use this file except in compliance with the License. 5 | //You may obtain a copy of the License at 6 | // 7 | //http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | //Unless required by applicable law or agreed to in writing, software 10 | //distributed under the License is distributed on an "AS IS" BASIS, 11 | //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | //See the License for the specific language governing permissions and 13 | //limitations under the License. 14 | // 15 | // Code generated by Alibaba Cloud SDK Code Generator. 16 | // Changes may cause incorrect behavior and will be lost if the code is regenerated. 17 | 18 | // ConfigFileVolumeConfigFileToPath is a nested struct in eci response 19 | type ConfigFileToPath struct { 20 | Content string `name:"Content"` 21 | Path string `name:"Path"` 22 | } 23 | -------------------------------------------------------------------------------- /eci/struct_container.go: -------------------------------------------------------------------------------- 1 | package eci 2 | 3 | //Licensed under the Apache License, Version 2.0 (the "License"); 4 | //you may not use this file except in compliance with the License. 5 | //You may obtain a copy of the License at 6 | // 7 | //http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | //Unless required by applicable law or agreed to in writing, software 10 | //distributed under the License is distributed on an "AS IS" BASIS, 11 | //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | //See the License for the specific language governing permissions and 13 | //limitations under the License. 14 | // 15 | // Code generated by Alibaba Cloud SDK Code Generator. 16 | // Changes may cause incorrect behavior and will be lost if the code is regenerated. 17 | 18 | // Container is a nested struct in eci response 19 | type Container struct { 20 | Name string `json:"Name" xml:"Name" ` 21 | Image string `json:"Image" xml:"Image"` 22 | Memory float64 `json:"Memory" xml:"Memory"` 23 | Cpu float64 `json:"Cpu" xml:"Cpu"` 24 | RestartCount int `json:"RestartCount" xml:"RestartCount"` 25 | WorkingDir string `json:"WorkingDir" xml:"WorkingDir"` 26 | ImagePullPolicy string `json:"ImagePullPolicy" xml:"ImagePullPolicy"` 27 | Commands []string `json:"Commands" xml:"Commands"` 28 | Args []string `json:"Args" xml:"Args"` 29 | PreviousState ContainerState `json:"PreviousState" xml:"PreviousState"` 30 | CurrentState ContainerState `json:"CurrentState" xml:"CurrentState"` 31 | VolumeMounts []VolumeMount `json:"VolumeMounts" xml:"VolumeMounts"` 32 | Ports []ContainerPort `json:"Ports" xml:"Ports"` 33 | EnvironmentVars []EnvironmentVar `json:"EnvironmentVars" xml:"EnvironmentVars"` 34 | } 35 | -------------------------------------------------------------------------------- /eci/struct_container_group.go: -------------------------------------------------------------------------------- 1 | package eci 2 | 3 | //Licensed under the Apache License, Version 2.0 (the "License"); 4 | //you may not use this file except in compliance with the License. 5 | //You may obtain a copy of the License at 6 | // 7 | //http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | //Unless required by applicable law or agreed to in writing, software 10 | //distributed under the License is distributed on an "AS IS" BASIS, 11 | //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | //See the License for the specific language governing permissions and 13 | //limitations under the License. 14 | // 15 | // Code generated by Alibaba Cloud SDK Code Generator. 16 | // Changes may cause incorrect behavior and will be lost if the code is regenerated. 17 | 18 | // ContainerGroup is a nested struct in eci response 19 | type ContainerGroup struct { 20 | ContainerGroupId string `json:"ContainerGroupId" xml:"ContainerGroupId"` 21 | ContainerGroupName string `json:"ContainerGroupName" xml:"ContainerGroupName"` 22 | RegionId string `json:"RegionId" xml:"RegionId"` 23 | ZoneId string `json:"ZoneId" xml:"ZoneId"` 24 | Memory float64 `json:"Memory" xml:"Memory"` 25 | Cpu float64 `json:"Cpu" xml:"Cpu"` 26 | VSwitchId string `json:"VSwitchId" xml:"VSwitchId"` 27 | SecurityGroupId string `json:"SecurityGroupId" xml:"SecurityGroupId"` 28 | RestartPolicy string `json:"RestartPolicy" xml:"RestartPolicy"` 29 | IntranetIp string `json:"IntranetIp" xml:"IntranetIp"` 30 | Status string `json:"Status" xml:"Status"` 31 | InternetIp string `json:"InternetIp" xml:"InternetIp"` 32 | CreationTime string `json:"CreationTime" xml:"CreationTime"` 33 | SucceededTime string `json:"SucceededTime" xml:"SucceededTime"` 34 | Tags []Tag `json:"Tags" xml:"Tags"` 35 | Events []Event `json:"Events" xml:"Events"` 36 | Containers []Container `json:"Containers" xml:"Containers"` 37 | Volumes []Volume `json:"Volumes" xml:"Volumes"` 38 | } 39 | -------------------------------------------------------------------------------- /eci/struct_container_port.go: -------------------------------------------------------------------------------- 1 | package eci 2 | 3 | import ( 4 | "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" 5 | ) 6 | 7 | //Licensed under the Apache License, Version 2.0 (the "License"); 8 | //you may not use this file except in compliance with the License. 9 | //You may obtain a copy of the License at 10 | // 11 | //http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | //Unless required by applicable law or agreed to in writing, software 14 | //distributed under the License is distributed on an "AS IS" BASIS, 15 | //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | //See the License for the specific language governing permissions and 17 | //limitations under the License. 18 | // 19 | // Code generated by Alibaba Cloud SDK Code Generator. 20 | // Changes may cause incorrect behavior and will be lost if the code is regenerated. 21 | 22 | // ContainerPort is a nested struct in eci response 23 | type ContainerPort struct { 24 | Port requests.Integer `name:"Port"` 25 | Protocol string `name:"Protocol"` 26 | } 27 | -------------------------------------------------------------------------------- /eci/struct_container_state.go: -------------------------------------------------------------------------------- 1 | package eci 2 | 3 | //Licensed under the Apache License, Version 2.0 (the "License"); 4 | //you may not use this file except in compliance with the License. 5 | //You may obtain a copy of the License at 6 | // 7 | //http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | //Unless required by applicable law or agreed to in writing, software 10 | //distributed under the License is distributed on an "AS IS" BASIS, 11 | //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | //See the License for the specific language governing permissions and 13 | //limitations under the License. 14 | // 15 | // Code generated by Alibaba Cloud SDK Code Generator. 16 | // Changes may cause incorrect behavior and will be lost if the code is regenerated. 17 | 18 | // CurrentState is a nested struct in eci response 19 | type ContainerState struct { 20 | State string `json:"State" xml:"State"` 21 | DetailStatus string `json:"DetailStatus" xml:"DetailStatus"` 22 | ExitCode int `json:"ExitCode" xml:"ExitCode"` 23 | StartTime string `json:"StartTime" xml:"StartTime"` 24 | FinishTime string `json:"FinishTime" xml:"FinishTime"` 25 | } 26 | -------------------------------------------------------------------------------- /eci/struct_environment_var.go: -------------------------------------------------------------------------------- 1 | package eci 2 | 3 | //Licensed under the Apache License, Version 2.0 (the "License"); 4 | //you may not use this file except in compliance with the License. 5 | //You may obtain a copy of the License at 6 | // 7 | //http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | //Unless required by applicable law or agreed to in writing, software 10 | //distributed under the License is distributed on an "AS IS" BASIS, 11 | //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | //See the License for the specific language governing permissions and 13 | //limitations under the License. 14 | // 15 | // Code generated by Alibaba Cloud SDK Code Generator. 16 | // Changes may cause incorrect behavior and will be lost if the code is regenerated. 17 | 18 | // EnvironmentVar is a nested struct in eci response 19 | type EnvironmentVar struct { 20 | Key string `name:"Key"` 21 | Value string `name:"Value"` 22 | } 23 | -------------------------------------------------------------------------------- /eci/struct_event.go: -------------------------------------------------------------------------------- 1 | package eci 2 | 3 | //Licensed under the Apache License, Version 2.0 (the "License"); 4 | //you may not use this file except in compliance with the License. 5 | //You may obtain a copy of the License at 6 | // 7 | //http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | //Unless required by applicable law or agreed to in writing, software 10 | //distributed under the License is distributed on an "AS IS" BASIS, 11 | //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | //See the License for the specific language governing permissions and 13 | //limitations under the License. 14 | // 15 | // Code generated by Alibaba Cloud SDK Code Generator. 16 | // Changes may cause incorrect behavior and will be lost if the code is regenerated. 17 | 18 | // Event is a nested struct in eci response 19 | type Event struct { 20 | Count int `json:"Count" xml:"Count"` 21 | Type string `json:"Type" xml:"Type"` 22 | Name string `json:"Name" xml:"Name"` 23 | Message string `json:"Message" xml:"Message"` 24 | FirstTimestamp string `json:"FirstTimestamp" xml:"FirstTimestamp"` 25 | LastTimestamp string `json:"LastTimestamp" xml:"LastTimestamp"` 26 | } 27 | -------------------------------------------------------------------------------- /eci/struct_tag.go: -------------------------------------------------------------------------------- 1 | package eci 2 | 3 | //Licensed under the Apache License, Version 2.0 (the "License"); 4 | //you may not use this file except in compliance with the License. 5 | //You may obtain a copy of the License at 6 | // 7 | //http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | //Unless required by applicable law or agreed to in writing, software 10 | //distributed under the License is distributed on an "AS IS" BASIS, 11 | //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | //See the License for the specific language governing permissions and 13 | //limitations under the License. 14 | // 15 | // Code generated by Alibaba Cloud SDK Code Generator. 16 | // Changes may cause incorrect behavior and will be lost if the code is regenerated. 17 | 18 | // Label is a nested struct in eci response 19 | type Tag struct { 20 | Key string `name:"Key"` 21 | Value string `name:"Value"` 22 | } 23 | -------------------------------------------------------------------------------- /eci/struct_volume.go: -------------------------------------------------------------------------------- 1 | package eci 2 | 3 | import "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" 4 | 5 | //Licensed under the Apache License, Version 2.0 (the "License"); 6 | //you may not use this file except in compliance with the License. 7 | //You may obtain a copy of the License at 8 | // 9 | //http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | //Unless required by applicable law or agreed to in writing, software 12 | //distributed under the License is distributed on an "AS IS" BASIS, 13 | //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | //See the License for the specific language governing permissions and 15 | //limitations under the License. 16 | // 17 | // Code generated by Alibaba Cloud SDK Code Generator. 18 | // Changes may cause incorrect behavior and will be lost if the code is regenerated. 19 | 20 | // Volume is a nested struct in eci response 21 | const ( 22 | VOL_TYPE_NFS = "NFSVolume" 23 | VOL_TYPE_EMPTYDIR = "EmptyDirVolume" 24 | VOL_TYPE_CONFIGFILEVOLUME = "ConfigFileVolume" 25 | ) 26 | 27 | type Volume struct { 28 | Type string `name:"Type"` 29 | Name string `name:"Name"` 30 | NfsVolumePath string `name:"NFSVolume.Path"` 31 | NfsVolumeServer string `name:"NFSVolume.Server"` 32 | NfsVolumeReadOnly requests.Boolean `name:"NFSVolume.ReadOnly"` 33 | EmptyDirVolumeEnable requests.Boolean `name:"EmptyDirVolume.Enable"` 34 | ConfigFileToPaths []ConfigFileToPath `name:"ConfigFileVolume.ConfigFileToPath" type:"Repeated"` 35 | } 36 | -------------------------------------------------------------------------------- /eci/struct_volume_mount.go: -------------------------------------------------------------------------------- 1 | package eci 2 | 3 | import "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" 4 | 5 | //Licensed under the Apache License, Version 2.0 (the "License"); 6 | //you may not use this file except in compliance with the License. 7 | //You may obtain a copy of the License at 8 | // 9 | //http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | //Unless required by applicable law or agreed to in writing, software 12 | //distributed under the License is distributed on an "AS IS" BASIS, 13 | //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | //See the License for the specific language governing permissions and 15 | //limitations under the License. 16 | // 17 | // Code generated by Alibaba Cloud SDK Code Generator. 18 | // Changes may cause incorrect behavior and will be lost if the code is regenerated. 19 | 20 | // VolumeMount is a nested struct in eci response 21 | type VolumeMount struct { 22 | MountPath string `name:"MountPath"` 23 | ReadOnly requests.Boolean `name:"ReadOnly"` 24 | Name string `name:"Name"` 25 | } 26 | -------------------------------------------------------------------------------- /errors.go: -------------------------------------------------------------------------------- 1 | package alibabacloud 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/aliyun/alibaba-cloud-sdk-go/sdk/errors" 7 | "github.com/virtual-kubelet/virtual-kubelet/errdefs" 8 | ) 9 | 10 | func wrapError(err error) error { 11 | if err == nil { 12 | return nil 13 | } 14 | 15 | se, ok := err.(*errors.ServerError) 16 | if !ok { 17 | return err 18 | } 19 | 20 | switch se.HttpStatus() { 21 | case http.StatusNotFound: 22 | return errdefs.AsNotFound(err) 23 | default: 24 | return err 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/virtual-kubelet/alibabacloud-eci 2 | 3 | require ( 4 | contrib.go.opencensus.io/exporter/jaeger v0.2.0 5 | contrib.go.opencensus.io/exporter/ocagent v0.5.0 6 | github.com/BurntSushi/toml v0.3.1 7 | github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190614054433-c6dd452c6c95 8 | github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c // indirect 9 | github.com/elazarl/goproxy v0.0.0-20190421051319-9d40249d3c2f // indirect 10 | github.com/elazarl/goproxy/ext v0.0.0-20190421051319-9d40249d3c2f // indirect 11 | github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef // indirect 12 | github.com/gorilla/mux v1.7.2 // indirect 13 | github.com/imdario/mergo v0.3.7 // indirect 14 | github.com/mitchellh/go-homedir v1.1.0 15 | github.com/onsi/ginkgo v1.8.0 // indirect 16 | github.com/onsi/gomega v1.5.0 // indirect 17 | github.com/pkg/errors v0.8.1 18 | github.com/sirupsen/logrus v1.4.2 19 | github.com/spf13/cobra v0.0.5 20 | github.com/spf13/pflag v1.0.3 21 | github.com/virtual-kubelet/virtual-kubelet v0.10.0 22 | go.opencensus.io v0.21.0 23 | gopkg.in/inf.v0 v0.9.1 // indirect 24 | k8s.io/api v0.0.0 25 | k8s.io/apimachinery v0.0.0 26 | k8s.io/apiserver v0.0.0-20190805142138-368b2058237c // indirect 27 | k8s.io/client-go v11.0.0+incompatible 28 | k8s.io/klog v0.3.3 29 | k8s.io/kubernetes v1.14.3 // indirect 30 | k8s.io/utils v0.0.0-20190607212802-c55fbcfc754a // indirect 31 | ) 32 | 33 | replace k8s.io/api => k8s.io/api v0.0.0-20190805141119-fdd30b57c827 34 | 35 | replace k8s.io/apimachinery => k8s.io/apimachinery v0.0.0-20190612205821-1799e75a0719 36 | 37 | replace k8s.io/client-go => k8s.io/client-go v0.0.0-20190805141520-2fe0317bcee0 38 | 39 | replace k8s.io/kubernetes => k8s.io/kubernetes v1.14.3 40 | 41 | go 1.13 42 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 3 | contrib.go.opencensus.io/exporter/jaeger v0.2.0 h1:nhTv/Ry3lGmqbJ/JGvCjWxBl5ozRfqo86Ngz59UAlfk= 4 | contrib.go.opencensus.io/exporter/jaeger v0.2.0/go.mod h1:ukdzwIYYHgZ7QYtwVFQUjiT28BJHiMhTERo32s6qVgM= 5 | contrib.go.opencensus.io/exporter/ocagent v0.5.0 h1:TKXjQSRS0/cCDrP7KvkgU6SmILtF/yV2TOs/02K/WZQ= 6 | contrib.go.opencensus.io/exporter/ocagent v0.5.0/go.mod h1:ImxhfLRpxoYiSq891pBrLVhN+qmP8BTVvdH2YLs7Gl0= 7 | github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= 8 | github.com/Azure/go-autorest v11.1.2+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= 9 | github.com/BurntSushi/toml v0.3.0/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 10 | github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= 11 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 12 | github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= 13 | github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= 14 | github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= 15 | github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190614054433-c6dd452c6c95 h1:iSvMRmpVM99X8jvQoPVGogrJwKAidk72gnZdgbL0hCo= 16 | github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190614054433-c6dd452c6c95/go.mod h1:myCDvQSzCW+wB1WAlocEru4wMGJxy+vlxHdhegi1CDQ= 17 | github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20190307165228-86c17b95fcd5/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8= 18 | github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= 19 | github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc= 20 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= 21 | github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= 22 | github.com/census-instrumentation/opencensus-proto v0.2.0 h1:LzQXZOgg4CQfE6bFvXGM30YZL1WW/M337pXml+GrcZ4= 23 | github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 24 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 25 | github.com/coreos/bbolt v1.3.1-coreos.6/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= 26 | github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= 27 | github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= 28 | github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= 29 | github.com/coreos/go-oidc v0.0.0-20180117170138-065b426bd416/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= 30 | github.com/coreos/go-semver v0.0.0-20180108230905-e214231b295a/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 31 | github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 32 | github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 33 | github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= 34 | github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= 35 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 36 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 37 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 38 | github.com/dgrijalva/jwt-go v0.0.0-20160705203006-01aeca54ebda/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= 39 | github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= 40 | github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= 41 | github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c h1:ZfSZ3P3BedhKGUhzj7BQlPSU4OvT6tfOKe3DVHzOA7s= 42 | github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= 43 | github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= 44 | github.com/elazarl/goproxy v0.0.0-20190421051319-9d40249d3c2f h1:8GDPb0tCY8LQ+OJ3dbHb5sA6YZWXFORQYZx5sdsTlMs= 45 | github.com/elazarl/goproxy v0.0.0-20190421051319-9d40249d3c2f/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= 46 | github.com/elazarl/goproxy/ext v0.0.0-20190421051319-9d40249d3c2f h1:AUj1VoZUfhPhOPHULCQQDnGhRelpFWHMLhQVWDsS0v4= 47 | github.com/elazarl/goproxy/ext v0.0.0-20190421051319-9d40249d3c2f/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8= 48 | github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= 49 | github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550 h1:mV9jbLoSW/8m4VK16ZkHTozJa8sesK5u5kTMFysTYac= 50 | github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= 51 | github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= 52 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 53 | github.com/ghodss/yaml v0.0.0-20180820084758-c7ce16629ff4/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 54 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 55 | github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= 56 | github.com/go-openapi/jsonpointer v0.19.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= 57 | github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= 58 | github.com/go-openapi/jsonreference v0.19.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= 59 | github.com/go-openapi/spec v0.17.2/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= 60 | github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= 61 | github.com/go-openapi/swag v0.17.2/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= 62 | github.com/gogo/protobuf v0.0.0-20171007142547-342cbe0a0415 h1:WSBJMqJbLxsn+bTCPyPYZfqHdJmc8MK4wrBjMft6BAM= 63 | github.com/gogo/protobuf v0.0.0-20171007142547-342cbe0a0415/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 64 | github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A= 65 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= 66 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 67 | github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 68 | github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef h1:veQD95Isof8w9/WXiA+pa3tz3fJXkt5B7QaRBrM62gk= 69 | github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 70 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 71 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 72 | github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= 73 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 74 | github.com/google/btree v0.0.0-20160524151835-7d79101e329e/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 75 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 76 | github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= 77 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 78 | github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf h1:+RRA9JqSOZFfKrOeqr2z77+8R2RKyh8PG66dcu1V0ck= 79 | github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= 80 | github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 81 | github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d h1:7XGaL1e6bYS1yIonGp9761ExpPPV1ui0SAC59Yube9k= 82 | github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= 83 | github.com/gophercloud/gophercloud v0.0.0-20190126172459-c818fa66e4c8/go.mod h1:3WdhXV3rUYy9p6AUW8d94kr+HS62Y4VL9mBnFxsD8q4= 84 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= 85 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= 86 | github.com/gorilla/mux v1.7.2 h1:zoNxOV7WjqXptQOVngLmcSQgXmgk4NMz1HibBchjl/I= 87 | github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= 88 | github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= 89 | github.com/gregjones/httpcache v0.0.0-20170728041850-787624de3eb7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= 90 | github.com/grpc-ecosystem/go-grpc-middleware v0.0.0-20190222133341-cfaf5686ec79/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= 91 | github.com/grpc-ecosystem/go-grpc-prometheus v0.0.0-20170330212424-2500245aa611/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= 92 | github.com/grpc-ecosystem/grpc-gateway v1.3.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= 93 | github.com/grpc-ecosystem/grpc-gateway v1.8.5 h1:2+KSC78XiO6Qy0hIjfc1OD9H+hsaJdJlb8Kqsd41CTE= 94 | github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= 95 | github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo= 96 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 97 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= 98 | github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= 99 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 100 | github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= 101 | github.com/imdario/mergo v0.3.7 h1:Y+UAYTZ7gDEuOfhxKWy+dvb5dRQ6rJjFSdX2HZY1/gI= 102 | github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= 103 | github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= 104 | github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= 105 | github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM= 106 | github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= 107 | github.com/jonboulle/clockwork v0.0.0-20141017032234-72f9bd7c4e0c/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= 108 | github.com/json-iterator/go v0.0.0-20180701071628-ab8a2e0c74be/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 109 | github.com/json-iterator/go v1.1.5 h1:gL2yXlmiIo4+t+y32d4WGwOjKGYcGOuyrg46vadswDE= 110 | github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 111 | github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= 112 | github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= 113 | github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= 114 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 115 | github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= 116 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 117 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 118 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 119 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 120 | github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= 121 | github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= 122 | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 123 | github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= 124 | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 125 | github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 126 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 127 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 128 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 129 | github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= 130 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 131 | github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= 132 | github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= 133 | github.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk= 134 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 135 | github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w= 136 | github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 137 | github.com/onsi/gomega v0.0.0-20190113212917-5533ce8a0da3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= 138 | github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo= 139 | github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= 140 | github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= 141 | github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= 142 | github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= 143 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 144 | github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= 145 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 146 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 147 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 148 | github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= 149 | github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= 150 | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 151 | github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= 152 | github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 153 | github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= 154 | github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc= 155 | github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= 156 | github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= 157 | github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= 158 | github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= 159 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= 160 | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= 161 | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= 162 | github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a h1:pa8hGb/2YqsZKovtsgrwcDH1RZhVbTKCjLp47XpqCDs= 163 | github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= 164 | github.com/soheilhy/cmux v0.1.3/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= 165 | github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= 166 | github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= 167 | github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= 168 | github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s= 169 | github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= 170 | github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= 171 | github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 172 | github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= 173 | github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 174 | github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= 175 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 176 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 177 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 178 | github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= 179 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 180 | github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= 181 | github.com/uber/jaeger-client-go v2.15.0+incompatible h1:NP3qsSqNxh8VYr956ur1N/1C1PjvOJnJykCzcD5QHbk= 182 | github.com/uber/jaeger-client-go v2.15.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= 183 | github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= 184 | github.com/virtual-kubelet/virtual-kubelet v0.10.0 h1:RtdhW7iPak1lPRmXZZ/lc674piSFGE8gVcedUa/RLiY= 185 | github.com/virtual-kubelet/virtual-kubelet v0.10.0/go.mod h1:GTnTwt2tO+sTCJtAzyTbazuGA9pOKJL2u5g1K43b8pQ= 186 | github.com/xiang90/probing v0.0.0-20160813154853-07dd2e8dfe18/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= 187 | github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= 188 | go.opencensus.io v0.21.0 h1:mU6zScU4U1YAFPHEHYk+3JC4SY7JxgkqS10ZOSyksNg= 189 | go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= 190 | go.uber.org/atomic v0.0.0-20181018215023-8dc6146f7569/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 191 | go.uber.org/multierr v0.0.0-20180122172545-ddea229ff1df/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= 192 | go.uber.org/zap v0.0.0-20180814183419-67bc79d13d15/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 193 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 194 | golang.org/x/crypto v0.0.0-20181025213731-e84da0312774/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 195 | golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 196 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= 197 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 198 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 199 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 200 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 201 | golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 202 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 203 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 204 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 205 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 206 | golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 207 | golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 208 | golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 209 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 210 | golang.org/x/net v0.0.0-20190206173232-65e2d4e15006/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 211 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 212 | golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628= 213 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 214 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 215 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 216 | golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a h1:tImsplftrFpALCYumobsd0K86vlAs/eXGFms2txfJfA= 217 | golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 218 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 219 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 220 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 221 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6 h1:bjcUS9ztw9kFmmIxJInhon/0Is3p+EHBKNgquIzo1OI= 222 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 223 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 224 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 225 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 226 | golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 227 | golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 228 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 229 | golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 230 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc= 231 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 232 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 233 | golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db h1:6/JqlYfC1CCaLnGceQTI+sDGhC9UBSPAsBqI0Gun6kU= 234 | golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 235 | golang.org/x/time v0.0.0-20161028155119-f51c12702a4d/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 236 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ= 237 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 238 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 239 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 240 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 241 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 242 | golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 243 | golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 244 | google.golang.org/api v0.4.0 h1:KKgc1aqhV8wDPbDzlDtpvyjZFY3vjz85FP7p4wcQUyI= 245 | google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= 246 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 247 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 248 | google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c= 249 | google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 250 | google.golang.org/genproto v0.0.0-20170731182057-09f6ed296fc6/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 251 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 252 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19 h1:Lj2SnHtxkRGJDqnGaSjo+CCdIieEnwVazbOXILwQemk= 253 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 254 | google.golang.org/grpc v1.13.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= 255 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 256 | google.golang.org/grpc v1.20.1 h1:Hz2g2wirWK7H0qIIhGIqRGTuMwTE8HEKFnDZZ7lm9NU= 257 | google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= 258 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 259 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= 260 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 261 | gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= 262 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 263 | gopkg.in/inf.v0 v0.9.0/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= 264 | gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= 265 | gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= 266 | gopkg.in/ini.v1 v1.42.0 h1:7N3gPTt50s8GuLortA00n8AqRTk75qOP98+mTPpgzRk= 267 | gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= 268 | gopkg.in/natefinch/lumberjack.v2 v2.0.0-20150622162204-20b71e5b60d7/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= 269 | gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= 270 | gopkg.in/square/go-jose.v2 v2.0.0-20180411045311-89060dee6a84/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= 271 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= 272 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 273 | gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg= 274 | gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= 275 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 276 | gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= 277 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 278 | gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= 279 | gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= 280 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 281 | k8s.io/api v0.0.0-20190805141119-fdd30b57c827 h1:Yf7m8lslHFWm22YDRTAHrGPh729A6Lmxcm1weHHBTuw= 282 | k8s.io/api v0.0.0-20190805141119-fdd30b57c827/go.mod h1:TBhBqb1AWbBQbW3XRusr7n7E4v2+5ZY8r8sAMnyFC5A= 283 | k8s.io/apimachinery v0.0.0-20190612205821-1799e75a0719 h1:uV4S5IB5g4Nvi+TBVNf3e9L4wrirlwYJ6w88jUQxTUw= 284 | k8s.io/apimachinery v0.0.0-20190612205821-1799e75a0719/go.mod h1:I4A+glKBHiTgiEjQiCCQfCAIcIMFGt291SmsvcrFzJA= 285 | k8s.io/apiserver v0.0.0-20190805142138-368b2058237c h1:OxQmmVHy+tsC9ciM88NSUWyX3PWastYCO192eVJ7HNg= 286 | k8s.io/apiserver v0.0.0-20190805142138-368b2058237c/go.mod h1:k9Vk6Fiw9pZljxzTtvH2MAfADQK6+hPgf7/eRaZb//o= 287 | k8s.io/client-go v0.0.0-20190805141520-2fe0317bcee0 h1:BtLpkscF7UZVmtKshdjDIcWLnfGOY01MRIdtYTUme+o= 288 | k8s.io/client-go v0.0.0-20190805141520-2fe0317bcee0/go.mod h1:ayzmabJptoFlxo7SQxN2Oz3a12t9kmpMKADzQmr5Zbc= 289 | k8s.io/component-base v0.0.0-20190805141645-3a5e5ac800ae/go.mod h1:VLedAFwENz2swOjm0zmUXpAP2mV55c49xgaOzPBI/QQ= 290 | k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= 291 | k8s.io/klog v0.3.1 h1:RVgyDHY/kFKtLqh67NvEWIgkMneNoIrdkN0CxDSQc68= 292 | k8s.io/klog v0.3.1/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= 293 | k8s.io/klog v0.3.3 h1:niceAagH1tzskmaie/icWd7ci1wbG7Bf2c6YGcQv+3c= 294 | k8s.io/klog v0.3.3/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= 295 | k8s.io/kube-openapi v0.0.0-20190228160746-b3a7cee44a30 h1:TRb4wNWoBVrH9plmkp2q86FIDppkbrEXdXlxU3a3BMI= 296 | k8s.io/kube-openapi v0.0.0-20190228160746-b3a7cee44a30/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc= 297 | k8s.io/kubernetes v1.14.3 h1:/FQkOJpjc1jGA37s7Rt3U10VwIKW685ejrgOp4UDRFE= 298 | k8s.io/kubernetes v1.14.3/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= 299 | k8s.io/utils v0.0.0-20190221042446-c2654d5206da/go.mod h1:8k8uAuAQ0rXslZKaEWd0c3oVhZz7sSzSiPnVZayjIX0= 300 | k8s.io/utils v0.0.0-20190607212802-c55fbcfc754a h1:2jUDc9gJja832Ftp+QbDV0tVhQHMISFn01els+2ZAcw= 301 | k8s.io/utils v0.0.0-20190607212802-c55fbcfc754a/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= 302 | sigs.k8s.io/structured-merge-diff v0.0.0-20190302045857-e85c7b244fd2/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= 303 | sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= 304 | sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= 305 | -------------------------------------------------------------------------------- /hack/ci/check_mods.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | exit_code=0 6 | 7 | make mod 8 | git diff --exit-code go.mod go.sum || exit_code=$? 9 | 10 | if [ ${exit_code} -eq 0 ]; then 11 | exit 0 12 | fi 13 | 14 | echo "please run \`make mod\` and check in the changes" 15 | exit ${exit_code} 16 | 17 | --------------------------------------------------------------------------------