├── .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 |
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 |
--------------------------------------------------------------------------------