mirror of
https://github.com/replicatedhq/troubleshoot.git
synced 2026-08-27 00:37:20 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c962dc59f0 | ||
|
|
8ba2c576c3 | ||
|
|
fd47d23ac0 | ||
|
|
59a2281290 | ||
|
|
1465af2c09 | ||
|
|
7f82ec1f09 | ||
|
|
4338b22505 | ||
|
|
1757af58eb | ||
|
|
d8bae5dc8b | ||
|
|
ca9106e7c9 | ||
|
|
57094dbb44 | ||
|
|
7ac973647d | ||
|
|
94f812c7bd | ||
|
|
e5f0e09236 | ||
|
|
1ee662944f | ||
|
|
7ab1734cb6 | ||
|
|
c61acdc5de | ||
|
|
98f926b46c | ||
|
|
d0a0d925bb | ||
|
|
fd257b5618 | ||
|
|
3afe32cd1e | ||
|
|
029282451e | ||
|
|
68c76867c7 | ||
|
|
e13cb35c82 | ||
|
|
9c4fa8ac14 | ||
|
|
1d3831f6d1 | ||
|
|
5812414093 | ||
|
|
94e468ade3 | ||
|
|
3dc78d2ce8 | ||
|
|
c4dd568267 | ||
|
|
00fa236525 | ||
|
|
ec24967e50 | ||
|
|
430fdaa9e4 | ||
|
|
58ed4c8fc2 | ||
|
|
353569d59a | ||
|
|
cd43fd6f56 | ||
|
|
ed8453ce6b | ||
|
|
5744d608a4 | ||
|
|
0e3bcd29e4 | ||
|
|
2a3e4e7d75 | ||
|
|
67b25e319b | ||
|
|
b0bbaa4e6b | ||
|
|
8a1e129dbe | ||
|
|
b926ff6fa2 | ||
|
|
fb5f1522ea | ||
|
|
6e7715a145 | ||
|
|
c0415dd100 | ||
|
|
1d031d6897 | ||
|
|
b19ac90512 | ||
|
|
13fb6abcaf | ||
|
|
218b9c006d | ||
|
|
0b493a533b | ||
|
|
5830eb34a0 | ||
|
|
8a1bb8a422 | ||
|
|
14ace5294f | ||
|
|
25ec38bdf5 | ||
|
|
223a4a6e0a | ||
|
|
3b6ab41e1b | ||
|
|
60ca35a5a6 | ||
|
|
36e3bf294d | ||
|
|
3ea71c1d5a | ||
|
|
26baaacee8 | ||
|
|
6f868877d8 | ||
|
|
075c0f6f16 | ||
|
|
2455f87ee1 | ||
|
|
a4c482c74c | ||
|
|
642c1d7e5b | ||
|
|
e88e3a4694 | ||
|
|
e23f799bed | ||
|
|
25e884c43a | ||
|
|
481aab7820 | ||
|
|
0b7aff6597 | ||
|
|
4153b9ac44 | ||
|
|
48750ba1e5 | ||
|
|
086d5569b3 | ||
|
|
996a55bf90 | ||
|
|
8125d5534b | ||
|
|
09deffb507 | ||
|
|
a8bdda483d | ||
|
|
c3e2748f3c | ||
|
|
0c7c8c3927 | ||
|
|
9a1bb9fa7b | ||
|
|
143f24b1e6 | ||
|
|
45d537744f | ||
|
|
1855c42b16 | ||
|
|
2fb3a17783 | ||
|
|
b195b34fbe | ||
|
|
1c6fe9f262 | ||
|
|
ed9264ed04 | ||
|
|
68344b25a4 | ||
|
|
123225c2ce | ||
|
|
b6ca58dbcf | ||
|
|
beb0514243 | ||
|
|
a71f2e4e0f | ||
|
|
ff81cac235 | ||
|
|
91b33b0316 | ||
|
|
3b1428a225 | ||
|
|
c6da43b73f | ||
|
|
4d61c49619 | ||
|
|
0f421ce104 | ||
|
|
d37d037ad1 | ||
|
|
e6ecddf42e | ||
|
|
07fedd29ad | ||
|
|
c83184d9f5 | ||
|
|
37ab1960d5 | ||
|
|
5811a7b0ae | ||
|
|
bc7614f875 | ||
|
|
d900950c5f | ||
|
|
c84bc86a50 | ||
|
|
2e61a8e9a9 | ||
|
|
012df52f11 | ||
|
|
f9efac3182 | ||
|
|
920e1efc27 | ||
|
|
e90b39733d | ||
|
|
8718888af1 | ||
|
|
64119e48c6 | ||
|
|
afae2ae06f |
@@ -0,0 +1,35 @@
|
||||
name: "Run preflights on a cluster"
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
compile-preflight:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/setup-go@v1
|
||||
with:
|
||||
go-version: '1.12.14'
|
||||
- name: setup env
|
||||
run: |
|
||||
echo "::set-env name=GOPATH::$(go env GOPATH)"
|
||||
echo "::add-path::$(go env GOPATH)/bin"
|
||||
shell: bash
|
||||
- uses: actions/checkout@master
|
||||
- run: make preflight
|
||||
- uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: preflight
|
||||
path: bin/preflight
|
||||
|
||||
run-preflights:
|
||||
runs-on: ubuntu-latest
|
||||
needs: compile-preflight
|
||||
steps:
|
||||
- name: Download preflight binary
|
||||
uses: actions/download-artifact@v1
|
||||
with:
|
||||
name: preflight
|
||||
path: bin/
|
||||
- uses: engineerd/setup-kind@v0.2.0
|
||||
- run: chmod +x bin/preflight
|
||||
- run: bin/preflight --interactive=false --format=json https://preflight.replicated.com
|
||||
|
||||
@@ -2,32 +2,59 @@
|
||||
# Image URL to use all building/pushing image targets
|
||||
IMG ?= controller:latest
|
||||
export GO111MODULE=on
|
||||
export GOPROXY=https://proxy.golang.org
|
||||
|
||||
all: test manager
|
||||
SHELL := /bin/bash -o pipefail
|
||||
VERSION_PACKAGE = github.com/replicatedhq/troubleshoot/pkg/version
|
||||
VERSION ?=`git describe --tags --dirty`
|
||||
DATE=`date -u +"%Y-%m-%dT%H:%M:%SZ"`
|
||||
|
||||
GIT_TREE = $(shell git rev-parse --is-inside-work-tree 2>/dev/null)
|
||||
ifneq "$(GIT_TREE)" ""
|
||||
define GIT_UPDATE_INDEX_CMD
|
||||
git update-index --assume-unchanged
|
||||
endef
|
||||
define GIT_SHA
|
||||
`git rev-parse HEAD`
|
||||
endef
|
||||
else
|
||||
define GIT_UPDATE_INDEX_CMD
|
||||
echo "Not a git repo, skipping git update-index"
|
||||
endef
|
||||
define GIT_SHA
|
||||
""
|
||||
endef
|
||||
endif
|
||||
|
||||
define LDFLAGS
|
||||
-ldflags "\
|
||||
-X ${VERSION_PACKAGE}.version=${VERSION} \
|
||||
-X ${VERSION_PACKAGE}.gitSHA=${GIT_SHA} \
|
||||
-X ${VERSION_PACKAGE}.buildTime=${DATE} \
|
||||
"
|
||||
endef
|
||||
|
||||
all: test
|
||||
|
||||
.PHONY: ffi
|
||||
ffi: fmt vet
|
||||
go build ${LDFLAGS} -o bin/troubleshoot.so -buildmode=c-shared ffi/main.go
|
||||
|
||||
# Run tests
|
||||
test: generate fmt vet manifests
|
||||
go test ./pkg/... ./cmd/... -coverprofile cover.out
|
||||
|
||||
.PHONY: manager
|
||||
manager: generate fmt vet
|
||||
go build -o bin/manager github.com/replicatedhq/troubleshoot/cmd/manager
|
||||
|
||||
.PHONY: troubleshoot
|
||||
troubleshoot: generate fmt vet
|
||||
go build -o bin/troubleshoot github.com/replicatedhq/troubleshoot/cmd/troubleshoot
|
||||
|
||||
.PHONY: collector
|
||||
collector: generate fmt vet
|
||||
go build -o bin/collector github.com/replicatedhq/troubleshoot/cmd/collector
|
||||
.PHONY: support-bundle
|
||||
support-bundle: generate fmt vet
|
||||
go build ${LDFLAGS} -o bin/support-bundle github.com/replicatedhq/troubleshoot/cmd/troubleshoot
|
||||
|
||||
.PHONY: preflight
|
||||
preflight: generate fmt vet
|
||||
go build -o bin/preflight github.com/replicatedhq/troubleshoot/cmd/preflight
|
||||
go build ${LDFLAGS} -o bin/preflight github.com/replicatedhq/troubleshoot/cmd/preflight
|
||||
|
||||
.PHONY: run
|
||||
run: generate fmt vet
|
||||
TROUBLESHOOT_EXTERNAL_MANAGER=1 go run ./cmd/manager/main.go
|
||||
.PHONY: analyze
|
||||
analyze: generate fmt vet
|
||||
go build ${LDFLAGS} -o bin/analyze github.com/replicatedhq/troubleshoot/cmd/analyze
|
||||
|
||||
.PHONY: install
|
||||
install: manifests
|
||||
@@ -74,38 +101,33 @@ else
|
||||
CLIENT_GEN=$(shell which client-gen)
|
||||
endif
|
||||
|
||||
.PHONY: release
|
||||
release: export GITHUB_TOKEN = $(shell echo ${GITHUB_TOKEN_TROUBLESHOOT})
|
||||
release:
|
||||
curl -sL https://git.io/goreleaser | bash -s -- --rm-dist --config deploy/.goreleaser.yml
|
||||
|
||||
.PHONY: snapshot-release
|
||||
snapshot-release:
|
||||
curl -sL https://git.io/goreleaser | bash -s -- --rm-dist --snapshot --config deploy/.goreleaser.snapshot.yml
|
||||
docker push replicated/troubleshoot:alpha
|
||||
docker push replicated/preflight:alpha
|
||||
docker push replicated/troubleshoot-manager:alpha
|
||||
|
||||
.PHONY: release
|
||||
release: export GITHUB_TOKEN = $(shell echo ${GITHUB_TOKEN_TROUBLESHOOT})
|
||||
release:
|
||||
curl -sL https://git.io/goreleaser | bash -s -- --rm-dist --config deploy/.goreleaser.yml
|
||||
|
||||
.PHONY: local-release
|
||||
local-release:
|
||||
curl -sL https://git.io/goreleaser | bash -s -- --rm-dist --snapshot --config deploy/.goreleaser.local.yml
|
||||
docker tag replicated/troubleshoot:alpha localhost:32000/troubleshoot:alpha
|
||||
docker tag replicated/preflight:alpha localhost:32000/preflight:alpha
|
||||
docker tag replicated/troubleshoot-manager:alpha localhost:32000/troubleshoot-manager:alpha
|
||||
docker push localhost:32000/troubleshoot:alpha
|
||||
docker push localhost:32000/preflight:alpha
|
||||
docker push localhost:32000/troubleshoot-manager:alpha
|
||||
|
||||
.PHONY: run-preflight
|
||||
run-preflight: preflight
|
||||
./bin/preflight \
|
||||
--image=localhost:32000/troubleshoot:alpha \
|
||||
--pullpolicy=Always \
|
||||
./config/samples/troubleshoot_v1beta1_preflight.yaml
|
||||
./bin/preflight ./examples/preflight/sample-preflight.yaml
|
||||
|
||||
.PHONY: run-troubleshoot
|
||||
run-troubleshoot: troubleshoot
|
||||
./bin/troubleshoot \
|
||||
--image=localhost:32000/troubleshoot:alpha \
|
||||
--pullpolicy=Always \
|
||||
./config/samples/troubleshoot_v1beta1_collector.yaml
|
||||
run-troubleshoot: support-bundle
|
||||
./bin/support-bundle ./examples/troubleshoot/sample-troubleshoot.yaml
|
||||
|
||||
.PHONY: run-analyze
|
||||
run-analyze: analyze
|
||||
./bin/analyze --analyzers ./examples/troubleshoot/sample-analyzers.yaml ./support-bundle.tar.gz
|
||||
|
||||
@@ -19,5 +19,5 @@ A support bundle is an archive that's created in-cluster, by collecting logs, cl
|
||||
To collect a sample support bundle, [install the troubleshoot kubectl plugin](https://help.replicated.com/docs/troubleshoot/kubernetes/support-bundle/collecting/) and run:
|
||||
|
||||
```shell
|
||||
kubectl troubleshoot https://troubleshoot.replicated.com
|
||||
kubectl support-bundle https://troubleshoot.replicated.com
|
||||
```
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/replicatedhq/troubleshoot/pkg/logger"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||
)
|
||||
|
||||
var (
|
||||
KubernetesConfigFlags *genericclioptions.ConfigFlags
|
||||
)
|
||||
|
||||
func RootCmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "analyze [url]",
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
Short: "Analyze a support bundle",
|
||||
Long: `Run a series of analyzers on a support bundle archive`,
|
||||
PreRun: func(cmd *cobra.Command, args []string) {
|
||||
viper.BindPFlags(cmd.Flags())
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
v := viper.GetViper()
|
||||
|
||||
logger.SetQuiet(v.GetBool("quiet"))
|
||||
|
||||
return runAnalyzers(v, args[0])
|
||||
},
|
||||
}
|
||||
|
||||
cobra.OnInitialize(initConfig)
|
||||
|
||||
cmd.Flags().String("analyzers", "", "filename or url of the analyzers to use")
|
||||
|
||||
viper.BindPFlags(cmd.Flags())
|
||||
|
||||
viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
|
||||
|
||||
KubernetesConfigFlags = genericclioptions.NewConfigFlags(false)
|
||||
KubernetesConfigFlags.AddFlags(cmd.Flags())
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func InitAndExecute() {
|
||||
if err := RootCmd().Execute(); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func initConfig() {
|
||||
viper.SetEnvPrefix("TROUBLESHOOT")
|
||||
viper.AutomaticEnv()
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
analyzer "github.com/replicatedhq/troubleshoot/pkg/analyze"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
func runAnalyzers(v *viper.Viper, bundlePath string) error {
|
||||
specPath := v.GetString("analyzers")
|
||||
|
||||
specContent := ""
|
||||
if !isURL(specPath) {
|
||||
if _, err := os.Stat(specPath); os.IsNotExist(err) {
|
||||
return fmt.Errorf("%s was not found", specPath)
|
||||
}
|
||||
|
||||
b, err := ioutil.ReadFile(specPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
specContent = string(b)
|
||||
} else {
|
||||
req, err := http.NewRequest("GET", specPath, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Set("User-Agent", "Replicated_Analyzer/v1beta1")
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
specContent = string(body)
|
||||
}
|
||||
|
||||
analyzeResults, err := analyzer.DownloadAndAnalyze(specContent, bundlePath)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to download and analyze bundle")
|
||||
}
|
||||
|
||||
for _, analyzeResult := range analyzeResults {
|
||||
if analyzeResult.IsPass {
|
||||
fmt.Printf("Pass: %s\n %s\n", analyzeResult.Title, analyzeResult.Message)
|
||||
} else if analyzeResult.IsWarn {
|
||||
fmt.Printf("Warn: %s\n %s\n", analyzeResult.Title, analyzeResult.Message)
|
||||
} else if analyzeResult.IsFail {
|
||||
fmt.Printf("Fail: %s\n %s\n", analyzeResult.Title, analyzeResult.Message)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func isURL(str string) bool {
|
||||
parsed, err := url.ParseRequestURI(str)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return parsed.Scheme != ""
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/replicatedhq/troubleshoot/cmd/analyze/cli"
|
||||
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cli.InitAndExecute()
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
func RootCmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "collector",
|
||||
Short: "Run the cluster-side server for bundle collection",
|
||||
Long: ``,
|
||||
SilenceUsage: true,
|
||||
}
|
||||
|
||||
cobra.OnInitialize(initConfig)
|
||||
|
||||
cmd.AddCommand(Run())
|
||||
cmd.AddCommand(Server())
|
||||
|
||||
viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
|
||||
return cmd
|
||||
}
|
||||
|
||||
func InitAndExecute() {
|
||||
if err := RootCmd().Execute(); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func initConfig() {
|
||||
viper.SetEnvPrefix("TROUBLESHOOT")
|
||||
viper.AutomaticEnv()
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/replicatedhq/troubleshoot/pkg/collect"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
func Run() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "run",
|
||||
Short: "run a single collector",
|
||||
Long: `...`,
|
||||
PreRun: func(cmd *cobra.Command, args []string) {
|
||||
viper.BindPFlag("collector", cmd.Flags().Lookup("collector"))
|
||||
viper.BindPFlag("redact", cmd.Flags().Lookup("redact"))
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
v := viper.GetViper()
|
||||
|
||||
specContents, err := ioutil.ReadFile(v.GetString("collector"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
collector := collect.Collector{
|
||||
Spec: string(specContents),
|
||||
Redact: v.GetBool("redact"),
|
||||
}
|
||||
if err := collector.RunCollectorSync(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().String("collector", "", "path to a single collector spec to collect")
|
||||
cmd.Flags().Bool("redact", true, "enable/disable default redactions")
|
||||
|
||||
cmd.MarkFlagRequired("collector")
|
||||
|
||||
viper.BindPFlags(cmd.Flags())
|
||||
|
||||
return cmd
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
|
||||
"github.com/replicatedhq/troubleshoot/pkg/server"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
func Server() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "server",
|
||||
Short: "run the http server",
|
||||
Hidden: true,
|
||||
Long: `...`,
|
||||
PreRun: func(cmd *cobra.Command, args []string) {
|
||||
viper.BindPFlag("port", cmd.Flags().Lookup("port"))
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
v := viper.GetViper()
|
||||
|
||||
server.ServeCollector(context.Background(), fmt.Sprintf(":%d", v.GetInt("port")))
|
||||
|
||||
c := make(chan os.Signal, 1)
|
||||
signal.Notify(c, os.Interrupt)
|
||||
|
||||
select {
|
||||
case <-c:
|
||||
return nil
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().Int("port", 8000, "port to listen on")
|
||||
|
||||
viper.BindPFlags(cmd.Flags())
|
||||
|
||||
return cmd
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
package main
|
||||
|
||||
import "github.com/replicatedhq/troubleshoot/cmd/collector/cli"
|
||||
|
||||
func main() {
|
||||
cli.InitAndExecute()
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
/*
|
||||
Copyright 2019 Replicated, Inc..
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"os"
|
||||
|
||||
"github.com/replicatedhq/troubleshoot/pkg/apis"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/controller"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/webhook"
|
||||
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/config"
|
||||
"sigs.k8s.io/controller-runtime/pkg/manager"
|
||||
logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
|
||||
"sigs.k8s.io/controller-runtime/pkg/runtime/signals"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var metricsAddr string
|
||||
flag.StringVar(&metricsAddr, "metrics-addr", ":8088", "The address the metric endpoint binds to.")
|
||||
flag.Parse()
|
||||
logf.SetLogger(logf.ZapLogger(false))
|
||||
log := logf.Log.WithName("entrypoint")
|
||||
|
||||
// Get a config to talk to the apiserver
|
||||
log.Info("setting up client for manager")
|
||||
cfg, err := config.GetConfig()
|
||||
if err != nil {
|
||||
log.Error(err, "unable to set up client config")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Create a new Cmd to provide shared dependencies and start components
|
||||
log.Info("setting up manager")
|
||||
mgr, err := manager.New(cfg, manager.Options{MetricsBindAddress: metricsAddr})
|
||||
if err != nil {
|
||||
log.Error(err, "unable to set up overall controller manager")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
log.Info("Registering Components.")
|
||||
|
||||
// Setup Scheme for all resources
|
||||
log.Info("setting up scheme")
|
||||
if err := apis.AddToScheme(mgr.GetScheme()); err != nil {
|
||||
log.Error(err, "unable add APIs to scheme")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Setup all Controllers
|
||||
log.Info("Setting up controller")
|
||||
if err := controller.AddToManager(mgr); err != nil {
|
||||
log.Error(err, "unable to register controllers to the manager")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
log.Info("setting up webhooks")
|
||||
if err := webhook.AddToManager(mgr); err != nil {
|
||||
log.Error(err, "unable to register webhooks to the manager")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Start the Cmd
|
||||
log.Info("Starting the Cmd.")
|
||||
if err := mgr.Start(signals.SetupSignalHandler()); err != nil {
|
||||
log.Error(err, "unable to run the manager")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
|
||||
func receivePreflightResults(preflightJobNamespace string, preflightJobName string) error {
|
||||
// poll until there are no more "running" collectors
|
||||
troubleshootClient, err := createTroubleshootK8sClient()
|
||||
troubleshootClient, err := createTroubleshootK8sClient(KubernetesConfigFlags)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
+13
-12
@@ -3,17 +3,22 @@ package cli
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||
)
|
||||
|
||||
var (
|
||||
KubernetesConfigFlags *genericclioptions.ConfigFlags
|
||||
)
|
||||
|
||||
func RootCmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "preflight [url]",
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
Short: "Run and retrieve preflight checks in a cluster",
|
||||
Long: `A preflight check is a set of validations that can and should be run to ensure
|
||||
that a cluster meets the requirements to run an application.`,
|
||||
@@ -22,25 +27,17 @@ that a cluster meets the requirements to run an application.`,
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
v := viper.GetViper()
|
||||
|
||||
if len(args) == 0 {
|
||||
return runPreflightsCRD(v)
|
||||
}
|
||||
|
||||
return runPreflightsNoCRD(v, args[0])
|
||||
return runPreflights(v, args[0])
|
||||
},
|
||||
}
|
||||
|
||||
cobra.OnInitialize(initConfig)
|
||||
|
||||
cmd.AddCommand(VersionCmd())
|
||||
|
||||
cmd.Flags().Bool("interactive", true, "interactive preflights")
|
||||
cmd.Flags().String("format", "human", "output format, one of human, json, yaml. only used when interactive is set to false")
|
||||
|
||||
cmd.Flags().String("preflight", "", "name of the preflight to use")
|
||||
cmd.Flags().String("namespace", "default", "namespace the preflight can be found in")
|
||||
|
||||
cmd.Flags().String("kubecontext", filepath.Join(homeDir(), ".kube", "config"), "the kubecontext to use when connecting")
|
||||
|
||||
cmd.Flags().String("image", "", "the full name of the preflight image to use")
|
||||
cmd.Flags().String("pullpolicy", "", "the pull policy of the preflight image")
|
||||
cmd.Flags().String("collector-image", "", "the full name of the collector image to use")
|
||||
@@ -49,6 +46,10 @@ that a cluster meets the requirements to run an application.`,
|
||||
cmd.Flags().String("serviceaccount", "", "name of the service account to use. if not provided, one will be created")
|
||||
|
||||
viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
|
||||
|
||||
KubernetesConfigFlags = genericclioptions.NewConfigFlags(false)
|
||||
KubernetesConfigFlags.AddFlags(cmd.Flags())
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,205 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/ahmetalpbalkan/go-cursor"
|
||||
"github.com/pkg/errors"
|
||||
analyzerunner "github.com/replicatedhq/troubleshoot/pkg/analyze"
|
||||
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/collect"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/logger"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/tj/go-spin"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
func runPreflights(v *viper.Viper, arg string) error {
|
||||
fmt.Print(cursor.Hide())
|
||||
defer fmt.Print(cursor.Show())
|
||||
|
||||
preflightContent := ""
|
||||
if !isURL(arg) {
|
||||
if _, err := os.Stat(arg); os.IsNotExist(err) {
|
||||
return fmt.Errorf("%s was not found", arg)
|
||||
}
|
||||
|
||||
b, err := ioutil.ReadFile(arg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
preflightContent = string(b)
|
||||
} else {
|
||||
req, err := http.NewRequest("GET", arg, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Set("User-Agent", "Replicated_Preflight/v1beta1")
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
preflightContent = string(body)
|
||||
}
|
||||
|
||||
preflight := troubleshootv1beta1.Preflight{}
|
||||
if err := yaml.Unmarshal([]byte(preflightContent), &preflight); err != nil {
|
||||
return fmt.Errorf("unable to parse %s as a preflight", arg)
|
||||
}
|
||||
|
||||
s := spin.New()
|
||||
finishedCh := make(chan bool, 1)
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-finishedCh:
|
||||
fmt.Printf("\r")
|
||||
return
|
||||
case <-time.After(time.Millisecond * 100):
|
||||
fmt.Printf("\r \033[36mRunning Preflight checks\033[m %s ", s.Next())
|
||||
}
|
||||
}
|
||||
}()
|
||||
defer func() {
|
||||
finishedCh <- true
|
||||
}()
|
||||
|
||||
allCollectedData, err := runCollectors(v, preflight)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
getCollectedFileContents := func(fileName string) ([]byte, error) {
|
||||
contents, ok := allCollectedData[fileName]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("file %s was not collected", fileName)
|
||||
}
|
||||
|
||||
return contents, nil
|
||||
}
|
||||
getChildCollectedFileContents := func(prefix string) (map[string][]byte, error) {
|
||||
matching := make(map[string][]byte)
|
||||
for k, v := range allCollectedData {
|
||||
if strings.HasPrefix(k, prefix) {
|
||||
matching[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
return matching, nil
|
||||
}
|
||||
|
||||
analyzeResults := []*analyzerunner.AnalyzeResult{}
|
||||
for _, analyzer := range preflight.Spec.Analyzers {
|
||||
analyzeResult, err := analyzerunner.Analyze(analyzer, getCollectedFileContents, getChildCollectedFileContents)
|
||||
if err != nil {
|
||||
logger.Printf("an analyzer failed to run: %v\n", err)
|
||||
continue
|
||||
}
|
||||
|
||||
if analyzeResult != nil {
|
||||
analyzeResults = append(analyzeResults, analyzeResult)
|
||||
}
|
||||
}
|
||||
|
||||
finishedCh <- true
|
||||
|
||||
if preflight.Spec.UploadResultsTo != "" {
|
||||
tryUploadResults(preflight.Spec.UploadResultsTo, preflight.Name, analyzeResults)
|
||||
}
|
||||
if v.GetBool("interactive") {
|
||||
return showInteractiveResults(preflight.Name, analyzeResults)
|
||||
}
|
||||
|
||||
return showStdoutResults(v.GetString("format"), preflight.Name, analyzeResults)
|
||||
}
|
||||
|
||||
func runCollectors(v *viper.Viper, preflight troubleshootv1beta1.Preflight) (map[string][]byte, error) {
|
||||
desiredCollectors := make([]*troubleshootv1beta1.Collect, 0, 0)
|
||||
for _, definedCollector := range preflight.Spec.Collectors {
|
||||
desiredCollectors = append(desiredCollectors, definedCollector)
|
||||
}
|
||||
desiredCollectors = ensureCollectorInList(desiredCollectors, troubleshootv1beta1.Collect{ClusterInfo: &troubleshootv1beta1.ClusterInfo{}})
|
||||
desiredCollectors = ensureCollectorInList(desiredCollectors, troubleshootv1beta1.Collect{ClusterResources: &troubleshootv1beta1.ClusterResources{}})
|
||||
|
||||
allCollectedData := make(map[string][]byte)
|
||||
|
||||
config, err := KubernetesConfigFlags.ToRESTConfig()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to convert kube flags to rest config")
|
||||
}
|
||||
|
||||
// Run preflights collectors synchronously
|
||||
for _, desiredCollector := range desiredCollectors {
|
||||
collector := collect.Collector{
|
||||
Redact: true,
|
||||
Collect: desiredCollector,
|
||||
ClientConfig: config,
|
||||
Namespace: v.GetString("namespace"),
|
||||
}
|
||||
|
||||
result, err := collector.RunCollectorSync()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to run collector")
|
||||
}
|
||||
|
||||
if result != nil {
|
||||
output, err := parseCollectorOutput(string(result))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to parse collector output")
|
||||
}
|
||||
for k, v := range output {
|
||||
allCollectedData[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return allCollectedData, nil
|
||||
}
|
||||
|
||||
func parseCollectorOutput(output string) (map[string][]byte, error) {
|
||||
input := make(map[string]interface{})
|
||||
files := make(map[string][]byte)
|
||||
if err := json.Unmarshal([]byte(output), &input); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for filename, maybeContents := range input {
|
||||
fileDir, fileName := filepath.Split(filename)
|
||||
|
||||
switch maybeContents.(type) {
|
||||
case string:
|
||||
decoded, err := base64.StdEncoding.DecodeString(maybeContents.(string))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
files[filepath.Join(fileDir, fileName)] = decoded
|
||||
|
||||
case map[string]interface{}:
|
||||
for k, v := range maybeContents.(map[string]interface{}) {
|
||||
decoded, err := base64.StdEncoding.DecodeString(v.(string))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
files[filepath.Join(fileDir, fileName, k)] = decoded
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return files, nil
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/k8sutil"
|
||||
"github.com/spf13/viper"
|
||||
kuberneteserrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func runPreflightsCRD(v *viper.Viper) error {
|
||||
troubleshootClient, err := createTroubleshootK8sClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
preflightName := v.GetString("preflight")
|
||||
if preflightName == "" {
|
||||
preflights, err := troubleshootClient.Preflights(v.GetString("namespace")).List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(preflights.Items) == 1 {
|
||||
preflightName = preflights.Items[0].Name
|
||||
}
|
||||
}
|
||||
|
||||
if preflightName == "" {
|
||||
return errors.New("unable to preflight, try using the --preflight flags")
|
||||
}
|
||||
|
||||
// generate a unique name
|
||||
now := time.Now()
|
||||
suffix := fmt.Sprintf("%d", now.Unix())
|
||||
|
||||
preflightJobName := fmt.Sprintf("%s-job-%s", preflightName, suffix[len(suffix)-4:])
|
||||
preflightJob := troubleshootv1beta1.PreflightJob{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: preflightJobName,
|
||||
Namespace: v.GetString("namespace"),
|
||||
},
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "v1",
|
||||
Kind: "preflightjob.troubleshoot.replicated.com",
|
||||
},
|
||||
Spec: troubleshootv1beta1.PreflightJobSpec{
|
||||
Preflight: troubleshootv1beta1.PreflightRef{
|
||||
Name: preflightName,
|
||||
Namespace: v.GetString("namespace"),
|
||||
},
|
||||
Image: v.GetString("image"),
|
||||
ImagePullPolicy: v.GetString("pullpolicy"),
|
||||
CollectorImage: v.GetString("collector-image"),
|
||||
CollectorImagePullPolicy: v.GetString("collector-pullpolicy"),
|
||||
},
|
||||
}
|
||||
if _, err := troubleshootClient.PreflightJobs(v.GetString("namespace")).Create(&preflightJob); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Poll the status of the Custom Resource for it to include a callback
|
||||
var found *troubleshootv1beta1.PreflightJob
|
||||
start := time.Now()
|
||||
for {
|
||||
current, err := troubleshootClient.PreflightJobs(v.GetString("namespace")).Get(preflightJobName, metav1.GetOptions{})
|
||||
if err != nil && kuberneteserrors.IsNotFound(err) {
|
||||
continue
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if current.Status.IsServerReady {
|
||||
found = current
|
||||
break
|
||||
}
|
||||
|
||||
if time.Now().Sub(start) > time.Duration(time.Second*10) {
|
||||
return errors.New("preflightjob failed to start")
|
||||
}
|
||||
|
||||
time.Sleep(time.Millisecond * 200)
|
||||
}
|
||||
|
||||
// Connect to the callback
|
||||
stopChan, err := k8sutil.PortForward(v.GetString("kubecontext"), 8000, 8000, found.Status.ServerPodNamespace, found.Status.ServerPodName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := receivePreflightResults(found.Namespace, found.Name); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Write
|
||||
|
||||
close(stopChan)
|
||||
return nil
|
||||
}
|
||||
@@ -1,343 +0,0 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
analyzerunner "github.com/replicatedhq/troubleshoot/pkg/analyze"
|
||||
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
|
||||
collectrunner "github.com/replicatedhq/troubleshoot/pkg/collect"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/logger"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/tj/go-spin"
|
||||
"gopkg.in/yaml.v2"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
kuberneteserrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/fields"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
types "k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/config"
|
||||
)
|
||||
|
||||
func runPreflightsNoCRD(v *viper.Viper, arg string) error {
|
||||
preflightContent := ""
|
||||
if !isURL(arg) {
|
||||
if _, err := os.Stat(arg); os.IsNotExist(err) {
|
||||
return fmt.Errorf("%s was not found", arg)
|
||||
}
|
||||
|
||||
b, err := ioutil.ReadFile(arg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
preflightContent = string(b)
|
||||
} else {
|
||||
req, err := http.NewRequest("GET", arg, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Set("User-Agent", "Replicated_Preflight/v1beta1")
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
preflightContent = string(body)
|
||||
}
|
||||
|
||||
preflight := troubleshootv1beta1.Preflight{}
|
||||
if err := yaml.Unmarshal([]byte(preflightContent), &preflight); err != nil {
|
||||
return fmt.Errorf("unable to parse %s as a preflight", arg)
|
||||
}
|
||||
|
||||
s := spin.New()
|
||||
finishedCh := make(chan bool, 1)
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-finishedCh:
|
||||
fmt.Printf("\r")
|
||||
return
|
||||
case <-time.After(time.Millisecond * 100):
|
||||
fmt.Printf("\r \033[36mRunning Preflight checks\033[m %s ", s.Next())
|
||||
}
|
||||
}
|
||||
}()
|
||||
defer func() {
|
||||
finishedCh <- true
|
||||
}()
|
||||
|
||||
allCollectedData, err := runCollectors(v, preflight)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
getCollectedFileContents := func(fileName string) ([]byte, error) {
|
||||
contents, ok := allCollectedData[fileName]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("file %s was not collected", fileName)
|
||||
}
|
||||
|
||||
return contents, nil
|
||||
}
|
||||
getChildCollectedFileContents := func(prefix string) (map[string][]byte, error) {
|
||||
matching := make(map[string][]byte)
|
||||
for k, v := range allCollectedData {
|
||||
if strings.HasPrefix(k, prefix) {
|
||||
matching[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
return matching, nil
|
||||
}
|
||||
|
||||
analyzeResults := []*analyzerunner.AnalyzeResult{}
|
||||
for _, analyzer := range preflight.Spec.Analyzers {
|
||||
analyzeResult, err := analyzerunner.Analyze(analyzer, getCollectedFileContents, getChildCollectedFileContents)
|
||||
if err != nil {
|
||||
logger.Printf("an analyzer failed to run: %v\n", err)
|
||||
continue
|
||||
}
|
||||
|
||||
analyzeResults = append(analyzeResults, analyzeResult)
|
||||
}
|
||||
|
||||
finishedCh <- true
|
||||
|
||||
if preflight.Spec.UploadResultsTo != "" {
|
||||
tryUploadResults(preflight.Spec.UploadResultsTo, preflight.Name, analyzeResults)
|
||||
}
|
||||
if v.GetBool("interactive") {
|
||||
return showInteractiveResults(preflight.Name, analyzeResults)
|
||||
}
|
||||
|
||||
logger.Printf("only interactive results are supported\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
func runCollectors(v *viper.Viper, preflight troubleshootv1beta1.Preflight) (map[string][]byte, error) {
|
||||
cfg, err := config.GetConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client, err := client.New(cfg, client.Options{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
clientset, err := kubernetes.NewForConfig(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
restClient := clientset.CoreV1().RESTClient()
|
||||
|
||||
serviceAccountName := v.GetString("serviceaccount")
|
||||
if serviceAccountName == "" {
|
||||
generatedServiceAccountName, err := createServiceAccount(preflight, v.GetString("namespace"), clientset)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer removeServiceAccount(generatedServiceAccountName, v.GetString("namespace"), clientset)
|
||||
|
||||
serviceAccountName = generatedServiceAccountName
|
||||
}
|
||||
|
||||
// deploy an object that "owns" everything to aid in cleanup
|
||||
configMapNamespacedName := types.NamespacedName{
|
||||
Name: fmt.Sprintf("preflight-%s-owner", preflight.Name),
|
||||
Namespace: v.GetString("namespace"),
|
||||
}
|
||||
|
||||
foundConfigMap := &corev1.ConfigMap{}
|
||||
err = client.Get(context.Background(), configMapNamespacedName, foundConfigMap)
|
||||
if err == nil || !kuberneteserrors.IsNotFound(err) {
|
||||
return nil, errors.Wrap(err, "failed to get existing config map")
|
||||
}
|
||||
owner := corev1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: configMapNamespacedName.Name,
|
||||
Namespace: configMapNamespacedName.Namespace,
|
||||
},
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "v1",
|
||||
Kind: "ConfigMap",
|
||||
},
|
||||
Data: make(map[string]string),
|
||||
}
|
||||
if err := client.Create(context.Background(), &owner); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to create config map")
|
||||
}
|
||||
defer func() {
|
||||
if err := client.Delete(context.Background(), &owner); err != nil {
|
||||
fmt.Println("failed to clean up preflight.")
|
||||
}
|
||||
}()
|
||||
|
||||
// deploy all collectors
|
||||
desiredCollectors := make([]*troubleshootv1beta1.Collect, 0, 0)
|
||||
for _, definedCollector := range preflight.Spec.Collectors {
|
||||
desiredCollectors = append(desiredCollectors, definedCollector)
|
||||
}
|
||||
desiredCollectors = ensureCollectorInList(desiredCollectors, troubleshootv1beta1.Collect{ClusterInfo: &troubleshootv1beta1.ClusterInfo{}})
|
||||
desiredCollectors = ensureCollectorInList(desiredCollectors, troubleshootv1beta1.Collect{ClusterResources: &troubleshootv1beta1.ClusterResources{}})
|
||||
|
||||
podsCreated := make([]*corev1.Pod, 0, 0)
|
||||
podsDeleted := make([]*corev1.Pod, 0, 0)
|
||||
allCollectedData := make(map[string][]byte)
|
||||
|
||||
resyncPeriod := time.Second
|
||||
ctx := context.Background()
|
||||
watchList := cache.NewListWatchFromClient(restClient, "pods", "", fields.Everything())
|
||||
_, controller := cache.NewInformer(watchList, &corev1.Pod{}, resyncPeriod,
|
||||
cache.ResourceEventHandlerFuncs{
|
||||
UpdateFunc: func(oldObj interface{}, newObj interface{}) {
|
||||
newPod, ok := newObj.(*corev1.Pod)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
oldPod, ok := oldObj.(*corev1.Pod)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
labels := newPod.Labels
|
||||
troubleshootRole, ok := labels["troubleshoot-role"]
|
||||
if !ok || troubleshootRole != "preflight" {
|
||||
return
|
||||
}
|
||||
preflightName, ok := labels["preflight"]
|
||||
if !ok || preflightName != preflight.Name {
|
||||
return
|
||||
}
|
||||
|
||||
if oldPod.Status.Phase == newPod.Status.Phase {
|
||||
return
|
||||
}
|
||||
|
||||
if newPod.Status.Phase == corev1.PodFailed {
|
||||
podsDeleted = append(podsDeleted, newPod)
|
||||
return
|
||||
}
|
||||
|
||||
if newPod.Status.Phase != corev1.PodSucceeded {
|
||||
return
|
||||
}
|
||||
|
||||
podLogOpts := corev1.PodLogOptions{}
|
||||
|
||||
req := clientset.CoreV1().Pods(newPod.Namespace).GetLogs(newPod.Name, &podLogOpts)
|
||||
podLogs, err := req.Stream()
|
||||
if err != nil {
|
||||
fmt.Println("get stream")
|
||||
return
|
||||
}
|
||||
defer podLogs.Close()
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
_, err = io.Copy(buf, podLogs)
|
||||
if err != nil {
|
||||
fmt.Println("copy logs")
|
||||
return
|
||||
}
|
||||
|
||||
collectedData, err := parseCollectorOutput(buf.String())
|
||||
if err != nil {
|
||||
logger.Printf("parse collected data: %v\n", err)
|
||||
return
|
||||
}
|
||||
for k, v := range collectedData {
|
||||
allCollectedData[k] = v
|
||||
}
|
||||
|
||||
if err := client.Delete(context.Background(), newPod); err != nil {
|
||||
fmt.Println("delete pod")
|
||||
}
|
||||
podsDeleted = append(podsDeleted, newPod)
|
||||
},
|
||||
})
|
||||
go func() {
|
||||
controller.Run(ctx.Done())
|
||||
}()
|
||||
|
||||
s := runtime.NewScheme()
|
||||
s.AddKnownTypes(schema.GroupVersion{Group: "", Version: "v1"}, &corev1.ConfigMap{})
|
||||
for _, collector := range desiredCollectors {
|
||||
_, pod, err := collectrunner.CreateCollector(client, s, &owner, preflight.Name, v.GetString("namespace"), serviceAccountName, "preflight", collector, v.GetString("image"), v.GetString("pullpolicy"))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to create collector")
|
||||
}
|
||||
podsCreated = append(podsCreated, pod)
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
for {
|
||||
if start.Add(time.Second * 30).Before(time.Now()) {
|
||||
fmt.Println("timeout running preflight")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(podsDeleted) == len(podsCreated) {
|
||||
break
|
||||
}
|
||||
|
||||
time.Sleep(time.Millisecond * 200)
|
||||
}
|
||||
|
||||
ctx.Done()
|
||||
|
||||
return allCollectedData, nil
|
||||
}
|
||||
|
||||
func parseCollectorOutput(output string) (map[string][]byte, error) {
|
||||
input := make(map[string]interface{})
|
||||
files := make(map[string][]byte)
|
||||
if err := json.Unmarshal([]byte(output), &input); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for filename, maybeContents := range input {
|
||||
fileDir, fileName := filepath.Split(filename)
|
||||
|
||||
switch maybeContents.(type) {
|
||||
case string:
|
||||
decoded, err := base64.StdEncoding.DecodeString(maybeContents.(string))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
files[filepath.Join(fileDir, fileName)] = decoded
|
||||
|
||||
case map[string]interface{}:
|
||||
for k, v := range maybeContents.(map[string]interface{}) {
|
||||
decoded, err := base64.StdEncoding.DecodeString(v.(string))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
files[filepath.Join(fileDir, fileName, k)] = decoded
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return files, nil
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
analyzerunner "github.com/replicatedhq/troubleshoot/pkg/analyze"
|
||||
)
|
||||
|
||||
func showStdoutResults(format string, preflightName string, analyzeResults []*analyzerunner.AnalyzeResult) error {
|
||||
if format == "human" {
|
||||
return showStdoutResultsHuman(preflightName, analyzeResults)
|
||||
} else if format == "json" {
|
||||
return showStdoutResultsJSON(preflightName, analyzeResults)
|
||||
}
|
||||
|
||||
return errors.Errorf("unknown output format: %q", format)
|
||||
}
|
||||
|
||||
func showStdoutResultsHuman(preflightName string, analyzeResults []*analyzerunner.AnalyzeResult) error {
|
||||
var failed bool
|
||||
for _, analyzeResult := range analyzeResults {
|
||||
testResultfailed := outputResult(analyzeResult)
|
||||
if testResultfailed {
|
||||
failed = true
|
||||
}
|
||||
}
|
||||
if failed {
|
||||
fmt.Printf("--- FAIL %s\n", preflightName)
|
||||
fmt.Println("FAILED")
|
||||
} else {
|
||||
fmt.Printf("--- PASS %s\n", preflightName)
|
||||
fmt.Println("PASS")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func showStdoutResultsJSON(preflightName string, analyzeResults []*analyzerunner.AnalyzeResult) error {
|
||||
type ResultOutput struct {
|
||||
Title string `json:"title"`
|
||||
Message string `json:"message"`
|
||||
URI string `json:"uri,omitempty"`
|
||||
}
|
||||
type Output struct {
|
||||
Pass []ResultOutput `json:"pass,omitempty"`
|
||||
Warn []ResultOutput `json:"warn,omitempty"`
|
||||
Fail []ResultOutput `json:"fail,omitempty"`
|
||||
}
|
||||
|
||||
output := Output{
|
||||
Pass: []ResultOutput{},
|
||||
Warn: []ResultOutput{},
|
||||
Fail: []ResultOutput{},
|
||||
}
|
||||
|
||||
for _, analyzeResult := range analyzeResults {
|
||||
resultOutput := ResultOutput{
|
||||
Title: analyzeResult.Title,
|
||||
Message: analyzeResult.Message,
|
||||
URI: analyzeResult.URI,
|
||||
}
|
||||
|
||||
if analyzeResult.IsPass {
|
||||
output.Pass = append(output.Pass, resultOutput)
|
||||
} else if analyzeResult.IsWarn {
|
||||
output.Warn = append(output.Warn, resultOutput)
|
||||
} else if analyzeResult.IsFail {
|
||||
output.Fail = append(output.Fail, resultOutput)
|
||||
}
|
||||
}
|
||||
|
||||
b, err := json.MarshalIndent(output, "", " ")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to marshal results")
|
||||
}
|
||||
|
||||
fmt.Printf("%s\n", b)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func outputResult(analyzeResult *analyzerunner.AnalyzeResult) bool {
|
||||
if analyzeResult.IsPass {
|
||||
fmt.Printf(" --- PASS %s\n", analyzeResult.Title)
|
||||
fmt.Printf(" --- %s\n", analyzeResult.Message)
|
||||
} else if analyzeResult.IsWarn {
|
||||
fmt.Printf(" --- WARN: %s\n", analyzeResult.Title)
|
||||
fmt.Printf(" --- %s\n", analyzeResult.Message)
|
||||
} else if analyzeResult.IsFail {
|
||||
fmt.Printf(" --- FAIL: %s\n", analyzeResult.Title)
|
||||
fmt.Printf(" --- %s\n", analyzeResult.Message)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -4,9 +4,9 @@ import (
|
||||
"net/url"
|
||||
"os"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
troubleshootclientv1beta1 "github.com/replicatedhq/troubleshoot/pkg/client/troubleshootclientset/typed/troubleshoot/v1beta1"
|
||||
"github.com/spf13/viper"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||
)
|
||||
|
||||
func homeDir() string {
|
||||
@@ -17,24 +17,23 @@ func homeDir() string {
|
||||
}
|
||||
|
||||
func isURL(str string) bool {
|
||||
_, err := url.ParseRequestURI(str)
|
||||
parsed, err := url.ParseRequestURI(str)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
return parsed.Scheme != ""
|
||||
}
|
||||
|
||||
func createTroubleshootK8sClient() (*troubleshootclientv1beta1.TroubleshootV1beta1Client, error) {
|
||||
v := viper.GetViper()
|
||||
|
||||
config, err := clientcmd.BuildConfigFromFlags("", v.GetString("kubecontext"))
|
||||
func createTroubleshootK8sClient(configFlags *genericclioptions.ConfigFlags) (*troubleshootclientv1beta1.TroubleshootV1beta1Client, error) {
|
||||
config, err := configFlags.ToRESTConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, errors.Wrap(err, "failed to convert kube flags to rest config")
|
||||
}
|
||||
|
||||
troubleshootClient, err := troubleshootclientv1beta1.NewForConfig(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, errors.Wrap(err, "failed to create troubleshoot client")
|
||||
}
|
||||
|
||||
return troubleshootClient, nil
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/replicatedhq/troubleshoot/pkg/version"
|
||||
)
|
||||
|
||||
func VersionCmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "version",
|
||||
Short: "Print the current version and exit",
|
||||
Long: `Print the current version and exit`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
fmt.Printf("Replicated Preflight %s\n", version.Version())
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
@@ -1,6 +1,9 @@
|
||||
package main
|
||||
|
||||
import "github.com/replicatedhq/troubleshoot/cmd/preflight/cli"
|
||||
import (
|
||||
"github.com/replicatedhq/troubleshoot/cmd/preflight/cli"
|
||||
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cli.InitAndExecute()
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
@@ -28,7 +27,7 @@ func Analyze() *cobra.Command {
|
||||
|
||||
logger.SetQuiet(v.GetBool("quiet"))
|
||||
|
||||
result, err := analyzer.DownloadAndAnalyze(context.TODO(), v.GetString("url"))
|
||||
result, err := analyzer.DownloadAndAnalyze("", v.GetString("url"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/mholt/archiver"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/logger"
|
||||
kuberneteserrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@@ -17,7 +16,7 @@ import (
|
||||
|
||||
func receiveSupportBundle(collectorJobNamespace string, collectorJobName string) error {
|
||||
// poll until there are no more "running" collectors
|
||||
troubleshootClient, err := createTroubleshootK8sClient()
|
||||
troubleshootClient, err := createTroubleshootK8sClient(KubernetesConfigFlags)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -28,8 +27,7 @@ func receiveSupportBundle(collectorJobNamespace string, collectorJobName string)
|
||||
}
|
||||
defer os.RemoveAll(bundlePath)
|
||||
|
||||
versionFilename, err := writeVersionFile(bundlePath)
|
||||
if err != nil {
|
||||
if err = writeVersionFile(bundlePath); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -117,23 +115,15 @@ func receiveSupportBundle(collectorJobNamespace string, collectorJobName string)
|
||||
}
|
||||
|
||||
if len(job.Status.Running) == 0 {
|
||||
tarGz := archiver.TarGz{
|
||||
Tar: &archiver.Tar{
|
||||
ImplicitTopLevelFolder: false,
|
||||
},
|
||||
}
|
||||
|
||||
// version file should be first in tar archive for quick extraction
|
||||
paths := []string{
|
||||
versionFilename,
|
||||
}
|
||||
for _, id := range receivedCollectors {
|
||||
paths = append(paths, filepath.Join(bundlePath, id))
|
||||
}
|
||||
|
||||
if err := tarGz.Archive(paths, "support-bundle.tar.gz"); err != nil {
|
||||
filename, err := findFileName("support-bundle", "tar.gz")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := tarSupportBundleDir(bundlePath, filename); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/k8sutil"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/logger"
|
||||
@@ -19,13 +17,11 @@ func Retrieve() *cobra.Command {
|
||||
Long: `...`,
|
||||
PreRun: func(cmd *cobra.Command, args []string) {
|
||||
viper.BindPFlag("collectors", cmd.Flags().Lookup("collectors"))
|
||||
viper.BindPFlag("namespace", cmd.Flags().Lookup("namespace"))
|
||||
viper.BindPFlag("kubecontext", cmd.Flags().Lookup("kubecontext"))
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
v := viper.GetViper()
|
||||
|
||||
troubleshootClient, err := createTroubleshootK8sClient()
|
||||
troubleshootClient, err := createTroubleshootK8sClient(KubernetesConfigFlags)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -53,7 +49,12 @@ func Retrieve() *cobra.Command {
|
||||
|
||||
logger.Printf("connecting to collector job %s\n", collectorJob.Name)
|
||||
|
||||
stopChan, err := k8sutil.PortForward(v.GetString("kubecontext"), 8000, 8000, collectorJob.Status.ServerPodNamespace, collectorJob.Status.ServerPodName)
|
||||
config, err := KubernetesConfigFlags.ToRESTConfig()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to convert kube flags to rest config")
|
||||
}
|
||||
|
||||
stopChan, err := k8sutil.PortForward(config, 8000, 8000, collectorJob.Status.ServerPodNamespace, collectorJob.Status.ServerPodName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -70,9 +71,6 @@ func Retrieve() *cobra.Command {
|
||||
}
|
||||
|
||||
cmd.Flags().String("collectors", "", "name of the collectors to use")
|
||||
cmd.Flags().String("namespace", "", "namespace the collectors can be found in")
|
||||
|
||||
cmd.Flags().String("kubecontext", filepath.Join(homeDir(), ".kube", "config"), "the kubecontext to use when connecting")
|
||||
|
||||
viper.BindPFlags(cmd.Flags())
|
||||
|
||||
|
||||
@@ -3,13 +3,17 @@ package cli
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/logger"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||
)
|
||||
|
||||
var (
|
||||
KubernetesConfigFlags *genericclioptions.ConfigFlags
|
||||
)
|
||||
|
||||
func RootCmd() *cobra.Command {
|
||||
@@ -26,24 +30,16 @@ from a server that can be used to assist when troubleshooting a server.`,
|
||||
v := viper.GetViper()
|
||||
|
||||
logger.SetQuiet(v.GetBool("quiet"))
|
||||
|
||||
if len(args) == 0 {
|
||||
return runTroubleshootCRD(v)
|
||||
}
|
||||
|
||||
return runTroubleshootNoCRD(v, args[0])
|
||||
return runTroubleshoot(v, args[0])
|
||||
},
|
||||
}
|
||||
|
||||
cobra.OnInitialize(initConfig)
|
||||
|
||||
cmd.AddCommand(Analyze())
|
||||
cmd.AddCommand(VersionCmd())
|
||||
|
||||
cmd.Flags().String("collectors", "", "name of the collectors to use")
|
||||
cmd.Flags().String("namespace", "default", "namespace the collectors can be found in")
|
||||
|
||||
cmd.Flags().String("kubecontext", filepath.Join(homeDir(), ".kube", "config"), "the kubecontext to use when connecting")
|
||||
|
||||
cmd.Flags().String("image", "", "the full name of the collector image to use")
|
||||
cmd.Flags().String("pullpolicy", "", "the pull policy of the collector image")
|
||||
cmd.Flags().Bool("redact", true, "enable/disable default redactions")
|
||||
@@ -52,6 +48,10 @@ from a server that can be used to assist when troubleshooting a server.`,
|
||||
viper.BindPFlags(cmd.Flags())
|
||||
|
||||
viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
|
||||
|
||||
KubernetesConfigFlags = genericclioptions.NewConfigFlags(false)
|
||||
KubernetesConfigFlags.AddFlags(cmd.Flags())
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,340 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/ahmetalpbalkan/go-cursor"
|
||||
"github.com/fatih/color"
|
||||
"github.com/mholt/archiver"
|
||||
"github.com/pkg/errors"
|
||||
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/collect"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/tj/go-spin"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
func runTroubleshoot(v *viper.Viper, arg string) error {
|
||||
fmt.Print(cursor.Hide())
|
||||
defer fmt.Print(cursor.Show())
|
||||
|
||||
collectorContent := ""
|
||||
if !isURL(arg) {
|
||||
if _, err := os.Stat(arg); os.IsNotExist(err) {
|
||||
return fmt.Errorf("%s was not found", arg)
|
||||
}
|
||||
|
||||
b, err := ioutil.ReadFile(arg)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "read spec file")
|
||||
}
|
||||
|
||||
collectorContent = string(b)
|
||||
} else {
|
||||
req, err := http.NewRequest("GET", arg, nil)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "make request")
|
||||
}
|
||||
req.Header.Set("User-Agent", "Replicated_Troubleshoot/v1beta1")
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "execute request")
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "read responce body")
|
||||
}
|
||||
|
||||
collectorContent = string(body)
|
||||
}
|
||||
|
||||
collector := troubleshootv1beta1.Collector{}
|
||||
if err := yaml.Unmarshal([]byte(collectorContent), &collector); err != nil {
|
||||
return fmt.Errorf("unable to parse %s collectors", arg)
|
||||
}
|
||||
|
||||
s := spin.New()
|
||||
finishedCh := make(chan bool, 1)
|
||||
progressChan := make(chan interface{}, 1)
|
||||
go func() {
|
||||
currentDir := ""
|
||||
for {
|
||||
select {
|
||||
case msg := <-progressChan:
|
||||
switch msg := msg.(type) {
|
||||
case error:
|
||||
c := color.New(color.FgHiRed)
|
||||
c.Println(fmt.Sprintf("%s\r * %v", cursor.ClearEntireLine(), msg))
|
||||
case string:
|
||||
currentDir = filepath.Base(msg)
|
||||
}
|
||||
case <-finishedCh:
|
||||
fmt.Printf("\r%s\r", cursor.ClearEntireLine())
|
||||
return
|
||||
case <-time.After(time.Millisecond * 100):
|
||||
if currentDir == "" {
|
||||
fmt.Printf("\r%s \033[36mCollecting support bundle\033[m %s", cursor.ClearEntireLine(), s.Next())
|
||||
} else {
|
||||
fmt.Printf("\r%s \033[36mCollecting support bundle\033[m %s %s", cursor.ClearEntireLine(), s.Next(), currentDir)
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
defer func() {
|
||||
finishedCh <- true
|
||||
}()
|
||||
|
||||
archivePath, err := runCollectors(v, collector, progressChan)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "run collectors")
|
||||
}
|
||||
|
||||
fmt.Printf("\r%s\r", cursor.ClearEntireLine())
|
||||
|
||||
if len(collector.Spec.AfterCollection) == 0 {
|
||||
msg := archivePath
|
||||
if appName := collector.Labels["applicationName"]; appName != "" {
|
||||
f := `A support bundle for %s has been created in this directory
|
||||
named %s. Please upload it on the Troubleshoot page of
|
||||
the %s Admin Console to begin analysis.`
|
||||
msg = fmt.Sprintf(f, appName, archivePath, appName)
|
||||
}
|
||||
|
||||
fmt.Printf("%s\n", msg)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
fileUploaded := false
|
||||
for _, ac := range collector.Spec.AfterCollection {
|
||||
if ac.UploadResultsTo != nil {
|
||||
if err := uploadSupportBundle(ac.UploadResultsTo, archivePath); err != nil {
|
||||
return errors.Wrap(err, "upload support bundle")
|
||||
}
|
||||
fileUploaded = true
|
||||
} else if ac.Callback != nil {
|
||||
if err := callbackSupportBundleAPI(ac.Callback, archivePath); err != nil {
|
||||
return errors.Wrap(err, "execute callback")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Printf("\r%s\r", cursor.ClearEntireLine())
|
||||
if fileUploaded {
|
||||
fmt.Printf("A support bundle has been created and uploaded to your cluster for analysis. Please visit the Troubleshoot page to continue.\n")
|
||||
fmt.Printf("A copy of this support bundle was written to the current directory, named %q\n", archivePath)
|
||||
} else {
|
||||
fmt.Printf("A support bundle has been created in the current directory named %q\n", archivePath)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func runCollectors(v *viper.Viper, collector troubleshootv1beta1.Collector, progressChan chan interface{}) (string, error) {
|
||||
bundlePath, err := ioutil.TempDir("", "troubleshoot")
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "create temp dir")
|
||||
}
|
||||
defer os.RemoveAll(bundlePath)
|
||||
|
||||
if err = writeVersionFile(bundlePath); err != nil {
|
||||
return "", errors.Wrap(err, "write version file")
|
||||
}
|
||||
|
||||
desiredCollectors := make([]*troubleshootv1beta1.Collect, 0, 0)
|
||||
for _, definedCollector := range collector.Spec.Collectors {
|
||||
desiredCollectors = append(desiredCollectors, definedCollector)
|
||||
}
|
||||
desiredCollectors = ensureCollectorInList(desiredCollectors, troubleshootv1beta1.Collect{ClusterInfo: &troubleshootv1beta1.ClusterInfo{}})
|
||||
desiredCollectors = ensureCollectorInList(desiredCollectors, troubleshootv1beta1.Collect{ClusterResources: &troubleshootv1beta1.ClusterResources{}})
|
||||
|
||||
config, err := KubernetesConfigFlags.ToRESTConfig()
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "failed to convert kube flags to rest config")
|
||||
}
|
||||
|
||||
// Run preflights collectors synchronously
|
||||
for _, desiredCollector := range desiredCollectors {
|
||||
collector := collect.Collector{
|
||||
Redact: true,
|
||||
Collect: desiredCollector,
|
||||
ClientConfig: config,
|
||||
Namespace: v.GetString("namespace"),
|
||||
}
|
||||
|
||||
progressChan <- collector.GetDisplayName()
|
||||
|
||||
result, err := collector.RunCollectorSync()
|
||||
if err != nil {
|
||||
progressChan <- fmt.Errorf("failed to run collector %q: %v", collector.GetDisplayName(), err)
|
||||
continue
|
||||
}
|
||||
|
||||
if result != nil {
|
||||
err = parseAndSaveCollectorOutput(string(result), bundlePath)
|
||||
if err != nil {
|
||||
progressChan <- fmt.Errorf("failed to parse collector spec %q: %v", collector.GetDisplayName(), err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
filename, err := findFileName("support-bundle", "tar.gz")
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "find file name")
|
||||
}
|
||||
|
||||
if err := tarSupportBundleDir(bundlePath, filename); err != nil {
|
||||
return "", errors.Wrap(err, "create bundle file")
|
||||
}
|
||||
|
||||
return filename, nil
|
||||
}
|
||||
|
||||
func parseAndSaveCollectorOutput(output string, bundlePath string) error {
|
||||
input := make(map[string]interface{})
|
||||
if err := json.Unmarshal([]byte(output), &input); err != nil {
|
||||
return errors.Wrap(err, "unmarshal output")
|
||||
}
|
||||
|
||||
for filename, maybeContents := range input {
|
||||
fileDir, fileName := filepath.Split(filename)
|
||||
outPath := filepath.Join(bundlePath, fileDir)
|
||||
|
||||
if err := os.MkdirAll(outPath, 0777); err != nil {
|
||||
return errors.Wrap(err, "create output file")
|
||||
}
|
||||
|
||||
switch maybeContents.(type) {
|
||||
case string:
|
||||
decoded, err := base64.StdEncoding.DecodeString(maybeContents.(string))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "decode collector output")
|
||||
}
|
||||
|
||||
if err := writeFile(filepath.Join(outPath, fileName), decoded); err != nil {
|
||||
return errors.Wrap(err, "write collector output")
|
||||
}
|
||||
|
||||
case map[string]interface{}:
|
||||
for k, v := range maybeContents.(map[string]interface{}) {
|
||||
s, _ := filepath.Split(filepath.Join(outPath, fileName, k))
|
||||
if err := os.MkdirAll(s, 0777); err != nil {
|
||||
return errors.Wrap(err, "write output directories")
|
||||
}
|
||||
|
||||
decoded, err := base64.StdEncoding.DecodeString(v.(string))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "decode output")
|
||||
}
|
||||
if err := writeFile(filepath.Join(outPath, fileName, k), decoded); err != nil {
|
||||
return errors.Wrap(err, "write output")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func uploadSupportBundle(r *troubleshootv1beta1.ResultRequest, archivePath string) error {
|
||||
contentType := getExpectedContentType(r.URI)
|
||||
if contentType != "" && contentType != "application/tar+gzip" {
|
||||
return fmt.Errorf("cannot upload content type %s", contentType)
|
||||
}
|
||||
|
||||
f, err := os.Open(archivePath)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "open file")
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
fileStat, err := f.Stat()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "stat file")
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(r.Method, r.URI, f)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "create request")
|
||||
}
|
||||
req.ContentLength = fileStat.Size()
|
||||
if contentType != "" {
|
||||
req.Header.Set("Content-Type", contentType)
|
||||
}
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "execute request")
|
||||
}
|
||||
|
||||
if resp.StatusCode >= 300 {
|
||||
return fmt.Errorf("unexpected status code %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getExpectedContentType(uploadURL string) string {
|
||||
parsedURL, err := url.Parse(uploadURL)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return parsedURL.Query().Get("Content-Type")
|
||||
}
|
||||
|
||||
func callbackSupportBundleAPI(r *troubleshootv1beta1.ResultRequest, archivePath string) error {
|
||||
req, err := http.NewRequest(r.Method, r.URI, nil)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "create request")
|
||||
}
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "execute request")
|
||||
}
|
||||
|
||||
if resp.StatusCode >= 300 {
|
||||
return fmt.Errorf("unexpected status code %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func tarSupportBundleDir(inputDir, outputFilename string) error {
|
||||
tarGz := archiver.TarGz{
|
||||
Tar: &archiver.Tar{
|
||||
ImplicitTopLevelFolder: false,
|
||||
},
|
||||
}
|
||||
|
||||
paths := []string{
|
||||
filepath.Join(inputDir, VersionFilename), // version file should be first in tar archive for quick extraction
|
||||
}
|
||||
|
||||
topLevelFiles, err := ioutil.ReadDir(inputDir)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "list bundle directory contents")
|
||||
}
|
||||
for _, f := range topLevelFiles {
|
||||
if f.Name() == VersionFilename {
|
||||
continue
|
||||
}
|
||||
paths = append(paths, filepath.Join(inputDir, f.Name()))
|
||||
}
|
||||
|
||||
if err := tarGz.Archive(paths, outputFilename); err != nil {
|
||||
return errors.Wrap(err, "create archive")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/k8sutil"
|
||||
"github.com/spf13/viper"
|
||||
kuberneteserrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func runTroubleshootCRD(v *viper.Viper) error {
|
||||
troubleshootClient, err := createTroubleshootK8sClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
collectorName := v.GetString("collectors")
|
||||
if collectorName == "" {
|
||||
collectors, err := troubleshootClient.Collectors(v.GetString("namespace")).List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(collectors.Items) == 1 {
|
||||
collectorName = collectors.Items[0].Name
|
||||
}
|
||||
}
|
||||
|
||||
if collectorName == "" {
|
||||
return errors.New("unknown collectors, try using the --collectors flags")
|
||||
}
|
||||
|
||||
// generate a unique name
|
||||
now := time.Now()
|
||||
suffix := fmt.Sprintf("%d", now.Unix())
|
||||
|
||||
collectorJobName := fmt.Sprintf("%s-job-%s", collectorName, suffix[len(suffix)-4:])
|
||||
collectorJob := troubleshootv1beta1.CollectorJob{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: collectorJobName,
|
||||
Namespace: v.GetString("namespace"),
|
||||
},
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "v1",
|
||||
Kind: "collectorjob.troubleshoot.replicated.com",
|
||||
},
|
||||
Spec: troubleshootv1beta1.CollectorJobSpec{
|
||||
Collector: troubleshootv1beta1.CollectorRef{
|
||||
Name: collectorName,
|
||||
Namespace: v.GetString("namespace"),
|
||||
},
|
||||
Image: v.GetString("image"),
|
||||
ImagePullPolicy: v.GetString("pullpolicy"),
|
||||
Redact: v.GetBool("redact"),
|
||||
},
|
||||
}
|
||||
if _, err := troubleshootClient.CollectorJobs(v.GetString("namespace")).Create(&collectorJob); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Poll the status of the Custom Resource for it to include a callback
|
||||
var found *troubleshootv1beta1.CollectorJob
|
||||
start := time.Now()
|
||||
for {
|
||||
current, err := troubleshootClient.CollectorJobs(v.GetString("namespace")).Get(collectorJobName, metav1.GetOptions{})
|
||||
if err != nil && kuberneteserrors.IsNotFound(err) {
|
||||
continue
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if current.Status.IsServerReady {
|
||||
found = current
|
||||
break
|
||||
}
|
||||
|
||||
if time.Now().Sub(start) > time.Duration(time.Second*10) {
|
||||
return errors.New("collectorjob failed to start")
|
||||
}
|
||||
|
||||
time.Sleep(time.Millisecond * 200)
|
||||
}
|
||||
|
||||
// Connect to the callback
|
||||
stopChan, err := k8sutil.PortForward(v.GetString("kubecontext"), 8000, 8000, found.Status.ServerPodNamespace, found.Status.ServerPodName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := receiveSupportBundle(found.Namespace, found.Name); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Write
|
||||
|
||||
close(stopChan)
|
||||
return nil
|
||||
}
|
||||
@@ -1,328 +0,0 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/mholt/archiver"
|
||||
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
|
||||
collectrunner "github.com/replicatedhq/troubleshoot/pkg/collect"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/logger"
|
||||
"github.com/spf13/viper"
|
||||
"gopkg.in/yaml.v2"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/fields"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/config"
|
||||
)
|
||||
|
||||
func runTroubleshootNoCRD(v *viper.Viper, arg string) error {
|
||||
collectorContent := ""
|
||||
if !isURL(arg) {
|
||||
if _, err := os.Stat(arg); os.IsNotExist(err) {
|
||||
return fmt.Errorf("%s was not found", arg)
|
||||
}
|
||||
|
||||
b, err := ioutil.ReadFile(arg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
collectorContent = string(b)
|
||||
} else {
|
||||
req, err := http.NewRequest("GET", arg, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Set("User-Agent", "Replicated_Troubleshoot/v1beta1")
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
collectorContent = string(body)
|
||||
}
|
||||
|
||||
collector := troubleshootv1beta1.Collector{}
|
||||
if err := yaml.Unmarshal([]byte(collectorContent), &collector); err != nil {
|
||||
return fmt.Errorf("unable to parse %s collectors", arg)
|
||||
}
|
||||
|
||||
archivePath, err := runCollectors(v, collector)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("%s\n", archivePath)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func runCollectors(v *viper.Viper, collector troubleshootv1beta1.Collector) (string, error) {
|
||||
cfg, err := config.GetConfig()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
client, err := client.New(cfg, client.Options{})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
clientset, err := kubernetes.NewForConfig(cfg)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
restClient := clientset.CoreV1().RESTClient()
|
||||
|
||||
serviceAccountName := v.GetString("serviceaccount")
|
||||
if serviceAccountName == "" {
|
||||
generatedServiceAccountName, err := createServiceAccount(collector, v.GetString("namespace"), clientset)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer removeServiceAccount(generatedServiceAccountName, v.GetString("namespace"), clientset)
|
||||
|
||||
serviceAccountName = generatedServiceAccountName
|
||||
}
|
||||
|
||||
// deploy an object that "owns" everything to aid in cleanup
|
||||
owner := corev1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: fmt.Sprintf("troubleshoot-%s-owner", collector.Name),
|
||||
Namespace: v.GetString("namespace"),
|
||||
},
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "v1",
|
||||
Kind: "ConfigMap",
|
||||
},
|
||||
Data: make(map[string]string),
|
||||
}
|
||||
if err := client.Create(context.Background(), &owner); err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer func() {
|
||||
if err := client.Delete(context.Background(), &owner); err != nil {
|
||||
fmt.Println("failed to clean up preflight.")
|
||||
}
|
||||
}()
|
||||
|
||||
// deploy all collectors
|
||||
desiredCollectors := make([]*troubleshootv1beta1.Collect, 0, 0)
|
||||
for _, definedCollector := range collector.Spec {
|
||||
desiredCollectors = append(desiredCollectors, definedCollector)
|
||||
}
|
||||
desiredCollectors = ensureCollectorInList(desiredCollectors, troubleshootv1beta1.Collect{ClusterInfo: &troubleshootv1beta1.ClusterInfo{}})
|
||||
desiredCollectors = ensureCollectorInList(desiredCollectors, troubleshootv1beta1.Collect{ClusterResources: &troubleshootv1beta1.ClusterResources{}})
|
||||
|
||||
podsCreated := make([]*corev1.Pod, 0, 0)
|
||||
podsDeleted := make([]*corev1.Pod, 0, 0)
|
||||
|
||||
collectorDirs := []string{}
|
||||
|
||||
bundlePath, err := ioutil.TempDir("", "troubleshoot")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer os.RemoveAll(bundlePath)
|
||||
|
||||
versionFilename, err := writeVersionFile(bundlePath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
resyncPeriod := time.Second
|
||||
ctx := context.Background()
|
||||
watchList := cache.NewListWatchFromClient(restClient, "pods", "", fields.Everything())
|
||||
_, controller := cache.NewInformer(watchList, &corev1.Pod{}, resyncPeriod,
|
||||
cache.ResourceEventHandlerFuncs{
|
||||
UpdateFunc: func(oldObj interface{}, newObj interface{}) {
|
||||
newPod, ok := newObj.(*corev1.Pod)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
oldPod, ok := oldObj.(*corev1.Pod)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
labels := newPod.Labels
|
||||
|
||||
troubleshootRole, ok := labels["troubleshoot-role"]
|
||||
if !ok || troubleshootRole != "troubleshoot" {
|
||||
return
|
||||
}
|
||||
|
||||
collectorName, ok := labels["troubleshoot"]
|
||||
if !ok || collectorName != collector.Name {
|
||||
return
|
||||
}
|
||||
|
||||
if oldPod.Status.Phase == newPod.Status.Phase {
|
||||
return
|
||||
}
|
||||
|
||||
if newPod.Status.Phase == corev1.PodFailed {
|
||||
podsDeleted = append(podsDeleted, newPod)
|
||||
return
|
||||
}
|
||||
|
||||
if newPod.Status.Phase != corev1.PodSucceeded {
|
||||
return
|
||||
}
|
||||
|
||||
podLogOpts := corev1.PodLogOptions{}
|
||||
|
||||
req := clientset.CoreV1().Pods(newPod.Namespace).GetLogs(newPod.Name, &podLogOpts)
|
||||
podLogs, err := req.Stream()
|
||||
if err != nil {
|
||||
fmt.Println("get stream")
|
||||
return
|
||||
}
|
||||
defer podLogs.Close()
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
_, err = io.Copy(buf, podLogs)
|
||||
if err != nil {
|
||||
fmt.Println("copy logs")
|
||||
return
|
||||
}
|
||||
|
||||
collectorDir, err := parseAndSaveCollectorOutput(buf.String(), bundlePath)
|
||||
if err != nil {
|
||||
logger.Printf("parse collected data: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
// empty dir name will make tar fail
|
||||
if collectorDir == "" {
|
||||
logger.Printf("pod %s did not return any files\n", newPod.Name)
|
||||
return
|
||||
}
|
||||
|
||||
collectorDirs = append(collectorDirs, collectorDir)
|
||||
|
||||
if err := client.Delete(context.Background(), newPod); err != nil {
|
||||
fmt.Println("delete pod error", err)
|
||||
}
|
||||
podsDeleted = append(podsDeleted, newPod)
|
||||
},
|
||||
})
|
||||
go func() {
|
||||
controller.Run(ctx.Done())
|
||||
}()
|
||||
|
||||
s := runtime.NewScheme()
|
||||
s.AddKnownTypes(schema.GroupVersion{Group: "", Version: "v1"}, &corev1.ConfigMap{})
|
||||
for _, collect := range desiredCollectors {
|
||||
_, pod, err := collectrunner.CreateCollector(client, s, &owner, collector.Name, v.GetString("namespace"), serviceAccountName, "troubleshoot", collect, v.GetString("image"), v.GetString("pullpolicy"))
|
||||
if err != nil {
|
||||
logger.Printf("A collector pod cannot be created: %v\n", err)
|
||||
continue
|
||||
}
|
||||
podsCreated = append(podsCreated, pod)
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
for {
|
||||
if start.Add(time.Second * 30).Before(time.Now()) {
|
||||
fmt.Println("timeout running troubleshoot")
|
||||
return "", err
|
||||
}
|
||||
|
||||
if len(podsDeleted) == len(podsCreated) {
|
||||
break
|
||||
}
|
||||
|
||||
time.Sleep(time.Millisecond * 200)
|
||||
}
|
||||
|
||||
ctx.Done()
|
||||
|
||||
tarGz := archiver.TarGz{
|
||||
Tar: &archiver.Tar{
|
||||
ImplicitTopLevelFolder: false,
|
||||
},
|
||||
}
|
||||
|
||||
// version file should be first in tar archive for quick extraction
|
||||
paths := []string{
|
||||
versionFilename,
|
||||
}
|
||||
for _, collectorDir := range collectorDirs {
|
||||
paths = append(paths, collectorDir)
|
||||
}
|
||||
|
||||
if err := tarGz.Archive(paths, "support-bundle.tar.gz"); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return "support-bundle.tar.gz", nil
|
||||
}
|
||||
|
||||
func parseAndSaveCollectorOutput(output string, bundlePath string) (string, error) {
|
||||
dir := ""
|
||||
|
||||
input := make(map[string]interface{})
|
||||
if err := json.Unmarshal([]byte(output), &input); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
for filename, maybeContents := range input {
|
||||
fileDir, fileName := filepath.Split(filename)
|
||||
outPath := filepath.Join(bundlePath, fileDir)
|
||||
dir = outPath
|
||||
|
||||
if err := os.MkdirAll(outPath, 0777); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
switch maybeContents.(type) {
|
||||
case string:
|
||||
decoded, err := base64.StdEncoding.DecodeString(maybeContents.(string))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if err := writeFile(filepath.Join(outPath, fileName), decoded); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
case map[string]interface{}:
|
||||
for k, v := range maybeContents.(map[string]interface{}) {
|
||||
s, _ := filepath.Split(filepath.Join(outPath, fileName, k))
|
||||
if err := os.MkdirAll(s, 0777); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
decoded, err := base64.StdEncoding.DecodeString(v.(string))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := writeFile(filepath.Join(outPath, fileName, k), decoded); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dir, nil
|
||||
}
|
||||
@@ -1,12 +1,13 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
troubleshootclientv1beta1 "github.com/replicatedhq/troubleshoot/pkg/client/troubleshootclientset/typed/troubleshoot/v1beta1"
|
||||
"github.com/spf13/viper"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||
)
|
||||
|
||||
func homeDir() string {
|
||||
@@ -17,25 +18,40 @@ func homeDir() string {
|
||||
}
|
||||
|
||||
func isURL(str string) bool {
|
||||
_, err := url.ParseRequestURI(str)
|
||||
parsed, err := url.ParseRequestURI(str)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
return parsed.Scheme != ""
|
||||
}
|
||||
|
||||
func createTroubleshootK8sClient() (*troubleshootclientv1beta1.TroubleshootV1beta1Client, error) {
|
||||
v := viper.GetViper()
|
||||
|
||||
config, err := clientcmd.BuildConfigFromFlags("", v.GetString("kubecontext"))
|
||||
func createTroubleshootK8sClient(configFlags *genericclioptions.ConfigFlags) (*troubleshootclientv1beta1.TroubleshootV1beta1Client, error) {
|
||||
config, err := configFlags.ToRESTConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, errors.Wrap(err, "failed to convert kube flags to rest config")
|
||||
}
|
||||
|
||||
troubleshootClient, err := troubleshootclientv1beta1.NewForConfig(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, errors.Wrap(err, "failed to create troubleshoot client")
|
||||
}
|
||||
|
||||
return troubleshootClient, nil
|
||||
}
|
||||
|
||||
func findFileName(basename, extension string) (string, error) {
|
||||
n := 1
|
||||
name := basename
|
||||
for {
|
||||
filename := name + "." + extension
|
||||
if _, err := os.Stat(filename); os.IsNotExist(err) {
|
||||
return filename, nil
|
||||
} else if err != nil {
|
||||
return "", errors.Wrap(err, "check file exists")
|
||||
}
|
||||
|
||||
name = fmt.Sprintf("%s (%d)", basename, n)
|
||||
n = n + 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,27 +1,48 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
|
||||
"gopkg.in/yaml.v2"
|
||||
"fmt"
|
||||
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
|
||||
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/version"
|
||||
"github.com/spf13/cobra"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
func writeVersionFile(path string) (string, error) {
|
||||
func VersionCmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "version",
|
||||
Short: "Print the current version and exit",
|
||||
Long: `Print the current version and exit`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
fmt.Printf("Replicated Preflight %s\n", version.Version())
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
const VersionFilename = "version.yaml"
|
||||
|
||||
func writeVersionFile(path string) error {
|
||||
version := troubleshootv1beta1.SupportBundleVersion{
|
||||
ApiVersion: "troubleshoot.replicated.com/v1beta1",
|
||||
Kind: "SupportBundle",
|
||||
}
|
||||
b, err := yaml.Marshal(version)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return err
|
||||
}
|
||||
|
||||
filename := filepath.Join(path, "version.yaml")
|
||||
filename := filepath.Join(path, VersionFilename)
|
||||
err = ioutil.WriteFile(filename, b, 0644)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return err
|
||||
}
|
||||
|
||||
return filename, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package main
|
||||
|
||||
import "github.com/replicatedhq/troubleshoot/cmd/troubleshoot/cli"
|
||||
import (
|
||||
"github.com/replicatedhq/troubleshoot/cmd/troubleshoot/cli"
|
||||
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cli.InitAndExecute()
|
||||
|
||||
@@ -390,6 +390,457 @@ spec:
|
||||
type: string
|
||||
type: object
|
||||
spec:
|
||||
properties:
|
||||
analyzers:
|
||||
items:
|
||||
properties:
|
||||
clusterVersion:
|
||||
properties:
|
||||
checkName:
|
||||
type: string
|
||||
exclude:
|
||||
type: boolean
|
||||
outcomes:
|
||||
items:
|
||||
properties:
|
||||
fail:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
pass:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
warn:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
type: array
|
||||
required:
|
||||
- outcomes
|
||||
type: object
|
||||
containerRuntime:
|
||||
properties:
|
||||
checkName:
|
||||
type: string
|
||||
exclude:
|
||||
type: boolean
|
||||
outcomes:
|
||||
items:
|
||||
properties:
|
||||
fail:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
pass:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
warn:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
type: array
|
||||
required:
|
||||
- outcomes
|
||||
type: object
|
||||
customResourceDefinition:
|
||||
properties:
|
||||
checkName:
|
||||
type: string
|
||||
customResourceDefinitionName:
|
||||
type: string
|
||||
exclude:
|
||||
type: boolean
|
||||
outcomes:
|
||||
items:
|
||||
properties:
|
||||
fail:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
pass:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
warn:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
type: array
|
||||
required:
|
||||
- outcomes
|
||||
- customResourceDefinitionName
|
||||
type: object
|
||||
deploymentStatus:
|
||||
properties:
|
||||
checkName:
|
||||
type: string
|
||||
exclude:
|
||||
type: boolean
|
||||
name:
|
||||
type: string
|
||||
namespace:
|
||||
type: string
|
||||
outcomes:
|
||||
items:
|
||||
properties:
|
||||
fail:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
pass:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
warn:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
type: array
|
||||
required:
|
||||
- outcomes
|
||||
- namespace
|
||||
- name
|
||||
type: object
|
||||
distribution:
|
||||
properties:
|
||||
checkName:
|
||||
type: string
|
||||
exclude:
|
||||
type: boolean
|
||||
outcomes:
|
||||
items:
|
||||
properties:
|
||||
fail:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
pass:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
warn:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
type: array
|
||||
required:
|
||||
- outcomes
|
||||
type: object
|
||||
imagePullSecret:
|
||||
properties:
|
||||
checkName:
|
||||
type: string
|
||||
exclude:
|
||||
type: boolean
|
||||
outcomes:
|
||||
items:
|
||||
properties:
|
||||
fail:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
pass:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
warn:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
type: array
|
||||
registryName:
|
||||
type: string
|
||||
required:
|
||||
- outcomes
|
||||
- registryName
|
||||
type: object
|
||||
ingress:
|
||||
properties:
|
||||
checkName:
|
||||
type: string
|
||||
exclude:
|
||||
type: boolean
|
||||
ingressName:
|
||||
type: string
|
||||
namespace:
|
||||
type: string
|
||||
outcomes:
|
||||
items:
|
||||
properties:
|
||||
fail:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
pass:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
warn:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
type: array
|
||||
required:
|
||||
- outcomes
|
||||
- ingressName
|
||||
- namespace
|
||||
type: object
|
||||
secret:
|
||||
properties:
|
||||
checkName:
|
||||
type: string
|
||||
exclude:
|
||||
type: boolean
|
||||
key:
|
||||
type: string
|
||||
namespace:
|
||||
type: string
|
||||
outcomes:
|
||||
items:
|
||||
properties:
|
||||
fail:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
pass:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
warn:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
type: array
|
||||
secretName:
|
||||
type: string
|
||||
required:
|
||||
- outcomes
|
||||
- secretName
|
||||
- namespace
|
||||
type: object
|
||||
statefulsetStatus:
|
||||
properties:
|
||||
checkName:
|
||||
type: string
|
||||
exclude:
|
||||
type: boolean
|
||||
name:
|
||||
type: string
|
||||
namespace:
|
||||
type: string
|
||||
outcomes:
|
||||
items:
|
||||
properties:
|
||||
fail:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
pass:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
warn:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
type: array
|
||||
required:
|
||||
- outcomes
|
||||
- namespace
|
||||
- name
|
||||
type: object
|
||||
storageClass:
|
||||
properties:
|
||||
checkName:
|
||||
type: string
|
||||
exclude:
|
||||
type: boolean
|
||||
outcomes:
|
||||
items:
|
||||
properties:
|
||||
fail:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
pass:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
warn:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
type: array
|
||||
storageClassName:
|
||||
type: string
|
||||
required:
|
||||
- outcomes
|
||||
- storageClassName
|
||||
type: object
|
||||
type: object
|
||||
type: array
|
||||
type: object
|
||||
status:
|
||||
type: object
|
||||
|
||||
@@ -390,167 +390,267 @@ spec:
|
||||
type: string
|
||||
type: object
|
||||
spec:
|
||||
items:
|
||||
properties:
|
||||
clusterInfo:
|
||||
type: object
|
||||
clusterResources:
|
||||
type: object
|
||||
copy:
|
||||
properties:
|
||||
afterCollection:
|
||||
items:
|
||||
properties:
|
||||
collectorName:
|
||||
type: string
|
||||
containerName:
|
||||
type: string
|
||||
containerPath:
|
||||
type: string
|
||||
namespace:
|
||||
type: string
|
||||
selector:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
required:
|
||||
- selector
|
||||
- namespace
|
||||
- containerPath
|
||||
type: object
|
||||
exec:
|
||||
properties:
|
||||
args:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
collectorName:
|
||||
type: string
|
||||
command:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
containerName:
|
||||
type: string
|
||||
namespace:
|
||||
type: string
|
||||
selector:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
timeout:
|
||||
type: string
|
||||
required:
|
||||
- selector
|
||||
- namespace
|
||||
type: object
|
||||
http:
|
||||
properties:
|
||||
collectorName:
|
||||
type: string
|
||||
get:
|
||||
callback:
|
||||
properties:
|
||||
headers:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
insecureSkipVerify:
|
||||
type: boolean
|
||||
url:
|
||||
method:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
required:
|
||||
- url
|
||||
- uri
|
||||
- method
|
||||
type: object
|
||||
post:
|
||||
uploadResultsTo:
|
||||
properties:
|
||||
body:
|
||||
method:
|
||||
type: string
|
||||
headers:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
insecureSkipVerify:
|
||||
type: boolean
|
||||
url:
|
||||
uri:
|
||||
type: string
|
||||
required:
|
||||
- url
|
||||
- uri
|
||||
- method
|
||||
type: object
|
||||
put:
|
||||
type: object
|
||||
type: array
|
||||
collectors:
|
||||
items:
|
||||
properties:
|
||||
clusterInfo:
|
||||
properties:
|
||||
body:
|
||||
collectorName:
|
||||
type: string
|
||||
headers:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
insecureSkipVerify:
|
||||
when:
|
||||
type: boolean
|
||||
url:
|
||||
required:
|
||||
- when
|
||||
type: object
|
||||
clusterResources:
|
||||
properties:
|
||||
collectorName:
|
||||
type: string
|
||||
when:
|
||||
type: boolean
|
||||
required:
|
||||
- when
|
||||
type: object
|
||||
copy:
|
||||
properties:
|
||||
collectorName:
|
||||
type: string
|
||||
containerName:
|
||||
type: string
|
||||
containerPath:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
namespace:
|
||||
type: string
|
||||
selector:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
when:
|
||||
type: boolean
|
||||
required:
|
||||
- selector
|
||||
- namespace
|
||||
- containerPath
|
||||
- when
|
||||
type: object
|
||||
data:
|
||||
properties:
|
||||
collectorName:
|
||||
type: string
|
||||
data:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
when:
|
||||
type: boolean
|
||||
required:
|
||||
- data
|
||||
- when
|
||||
type: object
|
||||
data:
|
||||
properties:
|
||||
collectorName:
|
||||
type: string
|
||||
data:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
required:
|
||||
- url
|
||||
- data
|
||||
type: object
|
||||
type: object
|
||||
logs:
|
||||
properties:
|
||||
collectorName:
|
||||
type: string
|
||||
limits:
|
||||
exec:
|
||||
properties:
|
||||
maxAge:
|
||||
args:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
collectorName:
|
||||
type: string
|
||||
maxLines:
|
||||
format: int64
|
||||
type: integer
|
||||
command:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
containerName:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
namespace:
|
||||
type: string
|
||||
selector:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
timeout:
|
||||
type: string
|
||||
when:
|
||||
type: boolean
|
||||
required:
|
||||
- selector
|
||||
- namespace
|
||||
- when
|
||||
type: object
|
||||
http:
|
||||
properties:
|
||||
collectorName:
|
||||
type: string
|
||||
get:
|
||||
properties:
|
||||
headers:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
insecureSkipVerify:
|
||||
type: boolean
|
||||
url:
|
||||
type: string
|
||||
required:
|
||||
- url
|
||||
type: object
|
||||
name:
|
||||
type: string
|
||||
post:
|
||||
properties:
|
||||
body:
|
||||
type: string
|
||||
headers:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
insecureSkipVerify:
|
||||
type: boolean
|
||||
url:
|
||||
type: string
|
||||
required:
|
||||
- url
|
||||
type: object
|
||||
put:
|
||||
properties:
|
||||
body:
|
||||
type: string
|
||||
headers:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
insecureSkipVerify:
|
||||
type: boolean
|
||||
url:
|
||||
type: string
|
||||
required:
|
||||
- url
|
||||
type: object
|
||||
when:
|
||||
type: boolean
|
||||
required:
|
||||
- when
|
||||
type: object
|
||||
logs:
|
||||
properties:
|
||||
collectorName:
|
||||
type: string
|
||||
containerNames:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
limits:
|
||||
properties:
|
||||
maxAge:
|
||||
type: string
|
||||
maxLines:
|
||||
format: int64
|
||||
type: integer
|
||||
type: object
|
||||
name:
|
||||
type: string
|
||||
namespace:
|
||||
type: string
|
||||
selector:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
when:
|
||||
type: boolean
|
||||
required:
|
||||
- selector
|
||||
- when
|
||||
type: object
|
||||
run:
|
||||
properties:
|
||||
args:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
collectorName:
|
||||
type: string
|
||||
command:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
image:
|
||||
type: string
|
||||
imagePullPolicy:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
namespace:
|
||||
type: string
|
||||
timeout:
|
||||
type: string
|
||||
when:
|
||||
type: boolean
|
||||
required:
|
||||
- namespace
|
||||
- image
|
||||
- when
|
||||
type: object
|
||||
secret:
|
||||
properties:
|
||||
collectorName:
|
||||
type: string
|
||||
includeValue:
|
||||
type: boolean
|
||||
key:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
namespace:
|
||||
type: string
|
||||
when:
|
||||
type: boolean
|
||||
required:
|
||||
- name
|
||||
- when
|
||||
type: object
|
||||
namespace:
|
||||
type: string
|
||||
selector:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
required:
|
||||
- selector
|
||||
type: object
|
||||
run:
|
||||
properties:
|
||||
args:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
collectorName:
|
||||
type: string
|
||||
command:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
image:
|
||||
type: string
|
||||
imagePullPolicy:
|
||||
type: string
|
||||
namespace:
|
||||
type: string
|
||||
timeout:
|
||||
type: string
|
||||
required:
|
||||
- namespace
|
||||
- image
|
||||
type: object
|
||||
secret:
|
||||
properties:
|
||||
collectorName:
|
||||
type: string
|
||||
includeValue:
|
||||
type: boolean
|
||||
key:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
namespace:
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
type: object
|
||||
type: object
|
||||
type: array
|
||||
type: array
|
||||
type: object
|
||||
status:
|
||||
type: object
|
||||
type: object
|
||||
|
||||
@@ -398,6 +398,49 @@ spec:
|
||||
properties:
|
||||
checkName:
|
||||
type: string
|
||||
exclude:
|
||||
type: boolean
|
||||
outcomes:
|
||||
items:
|
||||
properties:
|
||||
fail:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
pass:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
warn:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
type: array
|
||||
required:
|
||||
- outcomes
|
||||
type: object
|
||||
containerRuntime:
|
||||
properties:
|
||||
checkName:
|
||||
type: string
|
||||
exclude:
|
||||
type: boolean
|
||||
outcomes:
|
||||
items:
|
||||
properties:
|
||||
@@ -439,6 +482,8 @@ spec:
|
||||
type: string
|
||||
customResourceDefinitionName:
|
||||
type: string
|
||||
exclude:
|
||||
type: boolean
|
||||
outcomes:
|
||||
items:
|
||||
properties:
|
||||
@@ -475,10 +520,100 @@ spec:
|
||||
- outcomes
|
||||
- customResourceDefinitionName
|
||||
type: object
|
||||
deploymentStatus:
|
||||
properties:
|
||||
checkName:
|
||||
type: string
|
||||
exclude:
|
||||
type: boolean
|
||||
name:
|
||||
type: string
|
||||
namespace:
|
||||
type: string
|
||||
outcomes:
|
||||
items:
|
||||
properties:
|
||||
fail:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
pass:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
warn:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
type: array
|
||||
required:
|
||||
- outcomes
|
||||
- namespace
|
||||
- name
|
||||
type: object
|
||||
distribution:
|
||||
properties:
|
||||
checkName:
|
||||
type: string
|
||||
exclude:
|
||||
type: boolean
|
||||
outcomes:
|
||||
items:
|
||||
properties:
|
||||
fail:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
pass:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
warn:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
type: array
|
||||
required:
|
||||
- outcomes
|
||||
type: object
|
||||
imagePullSecret:
|
||||
properties:
|
||||
checkName:
|
||||
type: string
|
||||
exclude:
|
||||
type: boolean
|
||||
outcomes:
|
||||
items:
|
||||
properties:
|
||||
@@ -521,6 +656,8 @@ spec:
|
||||
properties:
|
||||
checkName:
|
||||
type: string
|
||||
exclude:
|
||||
type: boolean
|
||||
ingressName:
|
||||
type: string
|
||||
namespace:
|
||||
@@ -566,6 +703,8 @@ spec:
|
||||
properties:
|
||||
checkName:
|
||||
type: string
|
||||
exclude:
|
||||
type: boolean
|
||||
key:
|
||||
type: string
|
||||
namespace:
|
||||
@@ -609,10 +748,59 @@ spec:
|
||||
- secretName
|
||||
- namespace
|
||||
type: object
|
||||
statefulsetStatus:
|
||||
properties:
|
||||
checkName:
|
||||
type: string
|
||||
exclude:
|
||||
type: boolean
|
||||
name:
|
||||
type: string
|
||||
namespace:
|
||||
type: string
|
||||
outcomes:
|
||||
items:
|
||||
properties:
|
||||
fail:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
pass:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
warn:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
type: array
|
||||
required:
|
||||
- outcomes
|
||||
- namespace
|
||||
- name
|
||||
type: object
|
||||
storageClass:
|
||||
properties:
|
||||
checkName:
|
||||
type: string
|
||||
exclude:
|
||||
type: boolean
|
||||
outcomes:
|
||||
items:
|
||||
properties:
|
||||
@@ -657,8 +845,22 @@ spec:
|
||||
items:
|
||||
properties:
|
||||
clusterInfo:
|
||||
properties:
|
||||
collectorName:
|
||||
type: string
|
||||
when:
|
||||
type: boolean
|
||||
required:
|
||||
- when
|
||||
type: object
|
||||
clusterResources:
|
||||
properties:
|
||||
collectorName:
|
||||
type: string
|
||||
when:
|
||||
type: boolean
|
||||
required:
|
||||
- when
|
||||
type: object
|
||||
copy:
|
||||
properties:
|
||||
@@ -668,16 +870,46 @@ spec:
|
||||
type: string
|
||||
containerPath:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
namespace:
|
||||
type: string
|
||||
selector:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
when:
|
||||
type: boolean
|
||||
required:
|
||||
- selector
|
||||
- namespace
|
||||
- containerPath
|
||||
- when
|
||||
type: object
|
||||
data:
|
||||
properties:
|
||||
collectorName:
|
||||
type: string
|
||||
data:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
when:
|
||||
type: boolean
|
||||
required:
|
||||
- data
|
||||
- when
|
||||
type: object
|
||||
data:
|
||||
properties:
|
||||
collectorName:
|
||||
type: string
|
||||
data:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
required:
|
||||
- data
|
||||
type: object
|
||||
exec:
|
||||
properties:
|
||||
@@ -693,6 +925,8 @@ spec:
|
||||
type: array
|
||||
containerName:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
namespace:
|
||||
type: string
|
||||
selector:
|
||||
@@ -701,9 +935,12 @@ spec:
|
||||
type: array
|
||||
timeout:
|
||||
type: string
|
||||
when:
|
||||
type: boolean
|
||||
required:
|
||||
- selector
|
||||
- namespace
|
||||
- when
|
||||
type: object
|
||||
http:
|
||||
properties:
|
||||
@@ -722,6 +959,8 @@ spec:
|
||||
required:
|
||||
- url
|
||||
type: object
|
||||
name:
|
||||
type: string
|
||||
post:
|
||||
properties:
|
||||
body:
|
||||
@@ -752,11 +991,19 @@ spec:
|
||||
required:
|
||||
- url
|
||||
type: object
|
||||
when:
|
||||
type: boolean
|
||||
required:
|
||||
- when
|
||||
type: object
|
||||
logs:
|
||||
properties:
|
||||
collectorName:
|
||||
type: string
|
||||
containerNames:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
limits:
|
||||
properties:
|
||||
maxAge:
|
||||
@@ -765,14 +1012,19 @@ spec:
|
||||
format: int64
|
||||
type: integer
|
||||
type: object
|
||||
name:
|
||||
type: string
|
||||
namespace:
|
||||
type: string
|
||||
selector:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
when:
|
||||
type: boolean
|
||||
required:
|
||||
- selector
|
||||
- when
|
||||
type: object
|
||||
run:
|
||||
properties:
|
||||
@@ -790,13 +1042,18 @@ spec:
|
||||
type: string
|
||||
imagePullPolicy:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
namespace:
|
||||
type: string
|
||||
timeout:
|
||||
type: string
|
||||
when:
|
||||
type: boolean
|
||||
required:
|
||||
- namespace
|
||||
- image
|
||||
- when
|
||||
type: object
|
||||
secret:
|
||||
properties:
|
||||
@@ -810,8 +1067,11 @@ spec:
|
||||
type: string
|
||||
namespace:
|
||||
type: string
|
||||
when:
|
||||
type: boolean
|
||||
required:
|
||||
- name
|
||||
- when
|
||||
type: object
|
||||
type: object
|
||||
type: array
|
||||
|
||||
@@ -8,6 +8,31 @@ import (
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *AfterCollection) DeepCopyInto(out *AfterCollection) {
|
||||
*out = *in
|
||||
if in.UploadResultsTo != nil {
|
||||
in, out := &in.UploadResultsTo, &out.UploadResultsTo
|
||||
*out = new(ResultRequest)
|
||||
**out = **in
|
||||
}
|
||||
if in.Callback != nil {
|
||||
in, out := &in.Callback, &out.Callback
|
||||
*out = new(ResultRequest)
|
||||
**out = **in
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AfterCollection.
|
||||
func (in *AfterCollection) DeepCopy() *AfterCollection {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(AfterCollection)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Analyze) DeepCopyInto(out *Analyze) {
|
||||
*out = *in
|
||||
@@ -41,6 +66,26 @@ func (in *Analyze) DeepCopyInto(out *Analyze) {
|
||||
*out = new(ImagePullSecret)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.DeploymentStatus != nil {
|
||||
in, out := &in.DeploymentStatus, &out.DeploymentStatus
|
||||
*out = new(DeploymentStatus)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.StatefulsetStatus != nil {
|
||||
in, out := &in.StatefulsetStatus, &out.StatefulsetStatus
|
||||
*out = new(StatefulsetStatus)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.ContainerRuntime != nil {
|
||||
in, out := &in.ContainerRuntime, &out.ContainerRuntime
|
||||
*out = new(ContainerRuntime)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.Distribution != nil {
|
||||
in, out := &in.Distribution, &out.Distribution
|
||||
*out = new(Distribution)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Analyze.
|
||||
@@ -100,7 +145,7 @@ func (in *Analyzer) DeepCopyInto(out *Analyzer) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
out.Spec = in.Spec
|
||||
in.Spec.DeepCopyInto(&out.Spec)
|
||||
out.Status = in.Status
|
||||
}
|
||||
|
||||
@@ -246,6 +291,17 @@ func (in *AnalyzerList) DeepCopyObject() runtime.Object {
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *AnalyzerSpec) DeepCopyInto(out *AnalyzerSpec) {
|
||||
*out = *in
|
||||
if in.Analyzers != nil {
|
||||
in, out := &in.Analyzers, &out.Analyzers
|
||||
*out = make([]*Analyze, len(*in))
|
||||
for i := range *in {
|
||||
if (*in)[i] != nil {
|
||||
in, out := &(*in)[i], &(*out)[i]
|
||||
*out = new(Analyze)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AnalyzerSpec.
|
||||
@@ -276,6 +332,7 @@ func (in *AnalyzerStatus) DeepCopy() *AnalyzerStatus {
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ClusterInfo) DeepCopyInto(out *ClusterInfo) {
|
||||
*out = *in
|
||||
out.CollectorMeta = in.CollectorMeta
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterInfo.
|
||||
@@ -291,6 +348,7 @@ func (in *ClusterInfo) DeepCopy() *ClusterInfo {
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ClusterResources) DeepCopyInto(out *ClusterResources) {
|
||||
*out = *in
|
||||
out.CollectorMeta = in.CollectorMeta
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterResources.
|
||||
@@ -363,6 +421,11 @@ func (in *Collect) DeepCopyInto(out *Collect) {
|
||||
*out = new(Exec)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.Data != nil {
|
||||
in, out := &in.Data, &out.Data
|
||||
*out = new(Data)
|
||||
**out = **in
|
||||
}
|
||||
if in.Copy != nil {
|
||||
in, out := &in.Copy, &out.Copy
|
||||
*out = new(Copy)
|
||||
@@ -390,17 +453,7 @@ func (in *Collector) DeepCopyInto(out *Collector) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
if in.Spec != nil {
|
||||
in, out := &in.Spec, &out.Spec
|
||||
*out = make([]*Collect, len(*in))
|
||||
for i := range *in {
|
||||
if (*in)[i] != nil {
|
||||
in, out := &(*in)[i], &(*out)[i]
|
||||
*out = new(Collect)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
}
|
||||
in.Spec.DeepCopyInto(&out.Spec)
|
||||
out.Status = in.Status
|
||||
}
|
||||
|
||||
@@ -589,6 +642,43 @@ func (in *CollectorRef) DeepCopy() *CollectorRef {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *CollectorSpec) DeepCopyInto(out *CollectorSpec) {
|
||||
*out = *in
|
||||
if in.Collectors != nil {
|
||||
in, out := &in.Collectors, &out.Collectors
|
||||
*out = make([]*Collect, len(*in))
|
||||
for i := range *in {
|
||||
if (*in)[i] != nil {
|
||||
in, out := &(*in)[i], &(*out)[i]
|
||||
*out = new(Collect)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
}
|
||||
if in.AfterCollection != nil {
|
||||
in, out := &in.AfterCollection, &out.AfterCollection
|
||||
*out = make([]*AfterCollection, len(*in))
|
||||
for i := range *in {
|
||||
if (*in)[i] != nil {
|
||||
in, out := &(*in)[i], &(*out)[i]
|
||||
*out = new(AfterCollection)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CollectorSpec.
|
||||
func (in *CollectorSpec) DeepCopy() *CollectorSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(CollectorSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *CollectorStatus) DeepCopyInto(out *CollectorStatus) {
|
||||
*out = *in
|
||||
@@ -604,6 +694,33 @@ func (in *CollectorStatus) DeepCopy() *CollectorStatus {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ContainerRuntime) DeepCopyInto(out *ContainerRuntime) {
|
||||
*out = *in
|
||||
out.AnalyzeMeta = in.AnalyzeMeta
|
||||
if in.Outcomes != nil {
|
||||
in, out := &in.Outcomes, &out.Outcomes
|
||||
*out = make([]*Outcome, len(*in))
|
||||
for i := range *in {
|
||||
if (*in)[i] != nil {
|
||||
in, out := &(*in)[i], &(*out)[i]
|
||||
*out = new(Outcome)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ContainerRuntime.
|
||||
func (in *ContainerRuntime) DeepCopy() *ContainerRuntime {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ContainerRuntime)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Copy) DeepCopyInto(out *Copy) {
|
||||
*out = *in
|
||||
@@ -652,6 +769,76 @@ func (in *CustomResourceDefinition) DeepCopy() *CustomResourceDefinition {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Data) DeepCopyInto(out *Data) {
|
||||
*out = *in
|
||||
out.CollectorMeta = in.CollectorMeta
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Data.
|
||||
func (in *Data) DeepCopy() *Data {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Data)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *DeploymentStatus) DeepCopyInto(out *DeploymentStatus) {
|
||||
*out = *in
|
||||
out.AnalyzeMeta = in.AnalyzeMeta
|
||||
if in.Outcomes != nil {
|
||||
in, out := &in.Outcomes, &out.Outcomes
|
||||
*out = make([]*Outcome, len(*in))
|
||||
for i := range *in {
|
||||
if (*in)[i] != nil {
|
||||
in, out := &(*in)[i], &(*out)[i]
|
||||
*out = new(Outcome)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeploymentStatus.
|
||||
func (in *DeploymentStatus) DeepCopy() *DeploymentStatus {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(DeploymentStatus)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Distribution) DeepCopyInto(out *Distribution) {
|
||||
*out = *in
|
||||
out.AnalyzeMeta = in.AnalyzeMeta
|
||||
if in.Outcomes != nil {
|
||||
in, out := &in.Outcomes, &out.Outcomes
|
||||
*out = make([]*Outcome, len(*in))
|
||||
for i := range *in {
|
||||
if (*in)[i] != nil {
|
||||
in, out := &(*in)[i], &(*out)[i]
|
||||
*out = new(Outcome)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Distribution.
|
||||
func (in *Distribution) DeepCopy() *Distribution {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Distribution)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Exec) DeepCopyInto(out *Exec) {
|
||||
*out = *in
|
||||
@@ -814,6 +1001,11 @@ func (in *Logs) DeepCopyInto(out *Logs) {
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.ContainerNames != nil {
|
||||
in, out := &in.ContainerNames, &out.ContainerNames
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.Limits != nil {
|
||||
in, out := &in.Limits, &out.Limits
|
||||
*out = new(LogLimits)
|
||||
@@ -1151,6 +1343,21 @@ func (in *Put) DeepCopy() *Put {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ResultRequest) DeepCopyInto(out *ResultRequest) {
|
||||
*out = *in
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResultRequest.
|
||||
func (in *ResultRequest) DeepCopy() *ResultRequest {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ResultRequest)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Run) DeepCopyInto(out *Run) {
|
||||
*out = *in
|
||||
@@ -1208,6 +1415,33 @@ func (in *SingleOutcome) DeepCopy() *SingleOutcome {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *StatefulsetStatus) DeepCopyInto(out *StatefulsetStatus) {
|
||||
*out = *in
|
||||
out.AnalyzeMeta = in.AnalyzeMeta
|
||||
if in.Outcomes != nil {
|
||||
in, out := &in.Outcomes, &out.Outcomes
|
||||
*out = make([]*Outcome, len(*in))
|
||||
for i := range *in {
|
||||
if (*in)[i] != nil {
|
||||
in, out := &(*in)[i], &(*out)[i]
|
||||
*out = new(Outcome)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StatefulsetStatus.
|
||||
func (in *StatefulsetStatus) DeepCopy() *StatefulsetStatus {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(StatefulsetStatus)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *StorageClass) DeepCopyInto(out *StorageClass) {
|
||||
*out = *in
|
||||
|
||||
@@ -3,76 +3,77 @@ kind: Collector
|
||||
metadata:
|
||||
name: collector-sample
|
||||
spec:
|
||||
- clusterInfo: {}
|
||||
- clusterResources: {}
|
||||
# - secret:
|
||||
# name: illmannered-cricket-mysql
|
||||
# namespace: default
|
||||
# key: mysql-password
|
||||
# - logs:
|
||||
# selector:
|
||||
# - name=nginx-ingress-microk8s
|
||||
# namespace: default
|
||||
# limits:
|
||||
# maxAge: 30d
|
||||
# maxLines: 10000
|
||||
# - run:
|
||||
# collectorName: ping-google
|
||||
# namespace: default
|
||||
# image: flungo/netutils
|
||||
# command: ["ping"]
|
||||
# args: ["www.google.com"]
|
||||
# timeout: 5s
|
||||
- exec:
|
||||
collectorName: mysql-vars
|
||||
selector:
|
||||
- app=mysql
|
||||
namespace: default
|
||||
command: ["mysql"]
|
||||
args: ["-ureplicated", "-ppassword", "-e", "show processlist"]
|
||||
timeout: 60m
|
||||
- exec:
|
||||
collectorName: hosts
|
||||
selector:
|
||||
- app=graphql-api
|
||||
namespace: default
|
||||
command: ["cat"]
|
||||
args: ["/etc/hosts"]
|
||||
timeout: 60m
|
||||
- exec:
|
||||
collectorName: broken
|
||||
selector:
|
||||
- app=graphql-api
|
||||
namespace: default
|
||||
command: ["cat"]
|
||||
args: ["/etc/hostdasddsda"]
|
||||
timeout: 60m
|
||||
# - copy:
|
||||
# selector:
|
||||
# - app=illmannered-cricket-mysql
|
||||
# namespace: default
|
||||
# containerPath: /etc/hosts
|
||||
- http:
|
||||
collectorName: test-get
|
||||
get:
|
||||
url: https://api.staging.replicated.com/market/v1/echo/ip
|
||||
insecureSkipVerify: false
|
||||
headers: {}
|
||||
- http:
|
||||
collectorName: test-post
|
||||
post:
|
||||
url: http://httpbin.org/headers
|
||||
insecureSkipVerify: false
|
||||
headers:
|
||||
X-Custom-Header: "post-request"
|
||||
- http:
|
||||
collectorName: test-put
|
||||
put:
|
||||
url: http://httpbin.org/anything
|
||||
insecureSkipVerify: false
|
||||
headers:
|
||||
X-Custom-Header: "put-request"
|
||||
- http:
|
||||
collectorName: test-broken
|
||||
put:
|
||||
url: ""
|
||||
collectors:
|
||||
- clusterInfo: {}
|
||||
- clusterResources: {}
|
||||
# - secret:
|
||||
# name: illmannered-cricket-mysql
|
||||
# namespace: default
|
||||
# key: mysql-password
|
||||
# - logs:
|
||||
# selector:
|
||||
# - name=nginx-ingress-microk8s
|
||||
# namespace: default
|
||||
# limits:
|
||||
# maxAge: 30d
|
||||
# maxLines: 10000
|
||||
# - run:
|
||||
# collectorName: ping-google
|
||||
# namespace: default
|
||||
# image: flungo/netutils
|
||||
# command: ["ping"]
|
||||
# args: ["www.google.com"]
|
||||
# timeout: 5s
|
||||
- exec:
|
||||
collectorName: mysql-vars
|
||||
selector:
|
||||
- app=mysql
|
||||
namespace: default
|
||||
command: ["mysql"]
|
||||
args: ["-ureplicated", "-ppassword", "-e", "show processlist"]
|
||||
timeout: 60m
|
||||
- exec:
|
||||
collectorName: hosts
|
||||
selector:
|
||||
- app=graphql-api
|
||||
namespace: default
|
||||
command: ["cat"]
|
||||
args: ["/etc/hosts"]
|
||||
timeout: 60m
|
||||
- exec:
|
||||
collectorName: broken
|
||||
selector:
|
||||
- app=graphql-api
|
||||
namespace: default
|
||||
command: ["cat"]
|
||||
args: ["/etc/hostdasddsda"]
|
||||
timeout: 60m
|
||||
# - copy:
|
||||
# selector:
|
||||
# - app=illmannered-cricket-mysql
|
||||
# namespace: default
|
||||
# containerPath: /etc/hosts
|
||||
- http:
|
||||
collectorName: test-get
|
||||
get:
|
||||
url: https://api.staging.replicated.com/market/v1/echo/ip
|
||||
insecureSkipVerify: false
|
||||
headers: {}
|
||||
- http:
|
||||
collectorName: test-post
|
||||
post:
|
||||
url: http://httpbin.org/headers
|
||||
insecureSkipVerify: false
|
||||
headers:
|
||||
X-Custom-Header: "post-request"
|
||||
- http:
|
||||
collectorName: test-put
|
||||
put:
|
||||
url: http://httpbin.org/anything
|
||||
insecureSkipVerify: false
|
||||
headers:
|
||||
X-Custom-Header: "put-request"
|
||||
- http:
|
||||
collectorName: test-broken
|
||||
put:
|
||||
url: ""
|
||||
|
||||
@@ -4,22 +4,6 @@ release:
|
||||
owner: replicatedhq
|
||||
name: troubleshoot
|
||||
builds:
|
||||
- id: collector
|
||||
goos:
|
||||
- linux
|
||||
goarch:
|
||||
- amd64
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
main: cmd/collector/main.go
|
||||
ldflags: -s -w
|
||||
-X github.com/replicatedhq/troubleshoot/pkg/version.version={{.Version}}
|
||||
-X github.com/replicatedhq/troubleshoot/pkg/version.gitSHA={{.Commit}}
|
||||
-X github.com/replicatedhq/troubleshoot/pkg/version.buildTime={{.Date}}
|
||||
-extldflags "-static"
|
||||
flags: -tags netgo -installsuffix netgo
|
||||
binary: collector
|
||||
hooks: {}
|
||||
- id: preflight
|
||||
goos:
|
||||
- linux
|
||||
@@ -36,7 +20,7 @@ builds:
|
||||
flags: -tags netgo -installsuffix netgo
|
||||
binary: preflight
|
||||
hooks: {}
|
||||
- id: troubleshoot
|
||||
- id: support-bundle
|
||||
goos:
|
||||
- linux
|
||||
goarch:
|
||||
@@ -50,28 +34,12 @@ builds:
|
||||
-X github.com/replicatedhq/troubleshoot/pkg/version.buildTime={{.Date}}
|
||||
-extldflags "-static"
|
||||
flags: -tags netgo -installsuffix netgo
|
||||
binary: troubleshoot
|
||||
hooks: {}
|
||||
- id: manager
|
||||
goos:
|
||||
- linux
|
||||
goarch:
|
||||
- amd64
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
main: cmd/manager/main.go
|
||||
ldflags: -s -w
|
||||
-X github.com/replicatedhq/troubleshoot/pkg/version.version={{.Version}}
|
||||
-X github.com/replicatedhq/troubleshoot/pkg/version.gitSHA={{.Commit}}
|
||||
-X github.com/replicatedhq/troubleshoot/pkg/version.buildTime={{.Date}}
|
||||
-extldflags "-static"
|
||||
flags: -tags netgo -installsuffix netgo
|
||||
binary: manager
|
||||
binary: support-bundle
|
||||
hooks: {}
|
||||
archives:
|
||||
- id: tar
|
||||
format: tar.gz
|
||||
name_template: '{{ .Binary }}_{{.Version}}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{.Arm }}{{ end }}-alpha'
|
||||
name_template: '{{ .Binary }}_{{ .Os }}_{{ .Arch }}-alpha'
|
||||
files:
|
||||
- licence*
|
||||
- LICENCE*
|
||||
@@ -86,20 +54,13 @@ dockers:
|
||||
image_templates:
|
||||
- "replicated/troubleshoot:alpha"
|
||||
binaries:
|
||||
- collector
|
||||
- troubleshoot
|
||||
- support-bundle
|
||||
- preflight
|
||||
- dockerfile: ./deploy/Dockerfile.troubleshoot
|
||||
image_templates:
|
||||
- "replicated/preflight:alpha"
|
||||
binaries:
|
||||
- collector
|
||||
- troubleshoot
|
||||
- support-bundle
|
||||
- preflight
|
||||
- dockerfile: ./deploy/Dockerfile.manager
|
||||
image_templates:
|
||||
- "replicated/troubleshoot-manager:alpha"
|
||||
binaries:
|
||||
- manager
|
||||
snapshot:
|
||||
name_template: SNAPSHOT-{{ .Commit }}
|
||||
|
||||
@@ -4,21 +4,17 @@ release:
|
||||
owner: replicatedhq
|
||||
name: troubleshoot
|
||||
builds:
|
||||
- id: collector
|
||||
- id: so
|
||||
goos:
|
||||
- linux
|
||||
goarch:
|
||||
- amd64
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
main: cmd/collector/main.go
|
||||
ldflags: -s -w
|
||||
-X github.com/replicatedhq/troubleshoot/pkg/version.version={{.Version}}
|
||||
-X github.com/replicatedhq/troubleshoot/pkg/version.gitSHA={{.Commit}}
|
||||
-X github.com/replicatedhq/troubleshoot/pkg/version.buildTime={{.Date}}
|
||||
-extldflags "-static"
|
||||
flags: -tags netgo -installsuffix netgo
|
||||
binary: collector
|
||||
- CGO_ENABLED=1
|
||||
- GO111MODULE=on
|
||||
main: ffi/main.go
|
||||
flags: -buildmode=c-shared
|
||||
binary: troubleshoot.so
|
||||
hooks: {}
|
||||
- id: preflight
|
||||
goos:
|
||||
@@ -38,7 +34,7 @@ builds:
|
||||
flags: -tags netgo -installsuffix netgo
|
||||
binary: preflight
|
||||
hooks: {}
|
||||
- id: troubleshoot
|
||||
- id: support-bundle
|
||||
goos:
|
||||
- linux
|
||||
- windows
|
||||
@@ -54,28 +50,16 @@ builds:
|
||||
-X github.com/replicatedhq/troubleshoot/pkg/version.buildTime={{.Date}}
|
||||
-extldflags "-static"
|
||||
flags: -tags netgo -installsuffix netgo
|
||||
binary: troubleshoot
|
||||
hooks: {}
|
||||
- id: manager
|
||||
goos:
|
||||
- linux
|
||||
goarch:
|
||||
- amd64
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
main: cmd/manager/main.go
|
||||
ldflags: -s -w
|
||||
-X github.com/replicatedhq/troubleshoot/pkg/version.version={{.Version}}
|
||||
-X github.com/replicatedhq/troubleshoot/pkg/version.gitSHA={{.Commit}}
|
||||
-X github.com/replicatedhq/troubleshoot/pkg/version.buildTime={{.Date}}
|
||||
-extldflags "-static"
|
||||
flags: -tags netgo -installsuffix netgo
|
||||
binary: manager
|
||||
binary: support-bundle
|
||||
hooks: {}
|
||||
archives:
|
||||
- id: so
|
||||
builds:
|
||||
- so
|
||||
format: tar.gz
|
||||
- id: tar
|
||||
format: tar.gz
|
||||
name_template: '{{ .Binary }}_{{.Version}}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{.Arm }}{{ end }}-alpha'
|
||||
name_template: '{{ .Binary }}_{{ .Os }}_{{ .Arch }}-alpha'
|
||||
files:
|
||||
- licence*
|
||||
- LICENCE*
|
||||
@@ -90,20 +74,13 @@ dockers:
|
||||
image_templates:
|
||||
- "replicated/troubleshoot:alpha"
|
||||
binaries:
|
||||
- collector
|
||||
- troubleshoot
|
||||
- support-bundle
|
||||
- preflight
|
||||
- dockerfile: ./deploy/Dockerfile.troubleshoot
|
||||
image_templates:
|
||||
- "replicated/preflight:alpha"
|
||||
binaries:
|
||||
- collector
|
||||
- troubleshoot
|
||||
- support-bundle
|
||||
- preflight
|
||||
- dockerfile: ./deploy/Dockerfile.manager
|
||||
image_templates:
|
||||
- "replicated/troubleshoot-manager:alpha"
|
||||
binaries:
|
||||
- manager
|
||||
snapshot:
|
||||
name_template: SNAPSHOT-{{ .Commit }}
|
||||
|
||||
+19
-44
@@ -4,21 +4,17 @@ release:
|
||||
owner: replicatedhq
|
||||
name: troubleshoot
|
||||
builds:
|
||||
- id: collector
|
||||
- id: so
|
||||
goos:
|
||||
- linux
|
||||
goarch:
|
||||
- amd64
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
main: cmd/collector/main.go
|
||||
ldflags: -s -w
|
||||
-X github.com/replicatedhq/troubleshoot/pkg/version.version={{.Version}}
|
||||
-X github.com/replicatedhq/troubleshoot/pkg/version.gitSHA={{.Commit}}
|
||||
-X github.com/replicatedhq/troubleshoot/pkg/version.buildTime={{.Date}}
|
||||
-extldflags "-static"
|
||||
flags: -tags netgo -installsuffix netgo
|
||||
binary: collector
|
||||
- CGO_ENABLED=1
|
||||
- GO111MODULE=on
|
||||
main: ffi/main.go
|
||||
flags: -buildmode=c-shared
|
||||
binary: troubleshoot.so
|
||||
hooks: {}
|
||||
- id: preflight
|
||||
goos:
|
||||
@@ -38,7 +34,7 @@ builds:
|
||||
flags: -tags netgo -installsuffix netgo
|
||||
binary: preflight
|
||||
hooks: {}
|
||||
- id: troubleshoot
|
||||
- id: support-bundle
|
||||
goos:
|
||||
- linux
|
||||
- darwin
|
||||
@@ -54,25 +50,14 @@ builds:
|
||||
-X github.com/replicatedhq/troubleshoot/pkg/version.buildTime={{.Date}}
|
||||
-extldflags "-static"
|
||||
flags: -tags netgo -installsuffix netgo
|
||||
binary: troubleshoot
|
||||
hooks: {}
|
||||
- id: manager
|
||||
goos:
|
||||
- linux
|
||||
goarch:
|
||||
- amd64
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
main: cmd/manager/main.go
|
||||
ldflags: -s -w
|
||||
-X github.com/replicatedhq/troubleshoot/pkg/version.version={{.Version}}
|
||||
-X github.com/replicatedhq/troubleshoot/pkg/version.gitSHA={{.Commit}}
|
||||
-X github.com/replicatedhq/troubleshoot/pkg/version.buildTime={{.Date}}
|
||||
-extldflags "-static"
|
||||
flags: -tags netgo -installsuffix netgo
|
||||
binary: manager
|
||||
binary: support-bundle
|
||||
hooks: {}
|
||||
archives:
|
||||
- id: so
|
||||
builds:
|
||||
- so
|
||||
format: tar.gz
|
||||
name_template: 'troubleshoot.so_{{ .Os }}_{{ .Arch }}'
|
||||
- id: preflight
|
||||
builds:
|
||||
- preflight
|
||||
@@ -80,7 +65,7 @@ archives:
|
||||
format_overrides:
|
||||
- goos: windows
|
||||
format: zip
|
||||
name_template: '{{ .Binary }}_{{.Version}}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{.Arm }}{{ end }}-{{ .Major }}.{{ .Minor }}.{{ .Patch }}'
|
||||
name_template: '{{ .Binary }}_{{ .Os }}_{{ .Arch }}'
|
||||
files:
|
||||
- licence*
|
||||
- LICENCE*
|
||||
@@ -90,14 +75,14 @@ archives:
|
||||
- README*
|
||||
- changelog*
|
||||
- CHANGELOG*
|
||||
- id: troubleshoot
|
||||
- id: support-bundle
|
||||
builds:
|
||||
- troubleshoot
|
||||
- support-bundle
|
||||
format: tar.gz
|
||||
format_overrides:
|
||||
- goos: windows
|
||||
format: zip
|
||||
name_template: '{{ .Binary }}_{{.Version}}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{.Arm }}{{ end }}-{{ .Major }}.{{ .Minor }}.{{ .Patch }}'
|
||||
name_template: '{{ .Binary }}_{{ .Os }}_{{ .Arch }}'
|
||||
files:
|
||||
- licence*
|
||||
- LICENCE*
|
||||
@@ -115,8 +100,7 @@ dockers:
|
||||
- "replicated/troubleshoot:{{ .Major }}.{{ .Minor }}"
|
||||
- "replicated/troubleshoot:{{ .Major }}.{{ .Minor }}.{{ .Patch }}"
|
||||
binaries:
|
||||
- collector
|
||||
- troubleshoot
|
||||
- support-bundle
|
||||
- preflight
|
||||
- dockerfile: ./deploy/Dockerfile.troubleshoot
|
||||
image_templates:
|
||||
@@ -125,14 +109,5 @@ dockers:
|
||||
- "replicated/preflight:{{ .Major }}.{{ .Minor }}"
|
||||
- "replicated/preflight:{{ .Major }}.{{ .Minor }}.{{ .Patch }}"
|
||||
binaries:
|
||||
- collector
|
||||
- troubleshoot
|
||||
- support-bundle
|
||||
- preflight
|
||||
- dockerfile: ./deploy/Dockerfile.manager
|
||||
image_templates:
|
||||
- "replicated/troubleshoot-manager:latest"
|
||||
- "replicated/troubleshoot-manager:{{ .Major }}"
|
||||
- "replicated/troubleshoot-manager:{{ .Major }}.{{ .Minor }}"
|
||||
- "replicated/troubleshoot-manager:{{ .Major }}.{{ .Minor }}.{{ .Patch }}"
|
||||
binaries:
|
||||
- manager
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
FROM ubuntu:latest
|
||||
WORKDIR /
|
||||
COPY manager /manager
|
||||
ENTRYPOINT ["/manager"]
|
||||
@@ -5,8 +5,7 @@ RUN apt-get -qq update \
|
||||
&& apt-get -qq -y install \
|
||||
ca-certificates
|
||||
|
||||
COPY troubleshoot /troubleshoot/troubleshoot
|
||||
COPY collector /troubleshoot/collector
|
||||
COPY support-bundle /troubleshoot/support-bundle
|
||||
COPY preflight /troubleshoot/preflight
|
||||
|
||||
ENV PATH="/troubleshoot:${PATH}"
|
||||
|
||||
+22
-16
@@ -3,38 +3,44 @@ kind: Plugin
|
||||
metadata:
|
||||
name: preflight
|
||||
spec:
|
||||
version: "v0.9.1"
|
||||
version: "v0.9.11"
|
||||
platforms:
|
||||
- selector:
|
||||
matchLabels:
|
||||
os: linux
|
||||
arch: amd64
|
||||
uri: https://github.com/replicatedhq/troubleshoot/releases/download/v0.9.1/preflight_0.9.1_linux_amd64-0.9.1.tar.gz
|
||||
sha256: "2a50b896b2bd0fdd10ae0bc63ecfe417da1de2ab81255990958fa433d123ef85"
|
||||
uri: https://github.com/replicatedhq/troubleshoot/releases/download/v0.9.11/preflight_0.9.11_linux_amd64-0.9.11.tar.gz
|
||||
sha256: "54c7a692f1c2e90f0f3ee65082ec6e0eebe976414cb20a9343c06a15538e3c8b"
|
||||
files:
|
||||
- from: "./preflight"
|
||||
to: "."
|
||||
bin: "preflight"
|
||||
- from: preflight
|
||||
to: .
|
||||
- from: LICENSE
|
||||
to: .
|
||||
bin: preflight
|
||||
- selector:
|
||||
matchLabels:
|
||||
os: darwin
|
||||
arch: amd64
|
||||
uri: https://github.com/replicatedhq/troubleshoot/releases/download/v0.9.1/preflight_0.9.1_darwin_amd64-0.9.1.tar.gz
|
||||
sha256: "e8d99442c12606f062edce164c7a503aca316928b796708a08f8ae3fc8e3444d"
|
||||
uri: https://github.com/replicatedhq/troubleshoot/releases/download/v0.9.11/preflight_0.9.11_darwin_amd64-0.9.11.tar.gz
|
||||
sha256: "03eeb2f40a4afa326ea139000a6befda854da80cdacc7d92e92af15552e664cb"
|
||||
files:
|
||||
- from: "./preflight"
|
||||
to: "."
|
||||
bin: "preflight"
|
||||
- from: preflight
|
||||
to: .
|
||||
- from: LICENSE
|
||||
to: .
|
||||
bin: preflight
|
||||
- selector:
|
||||
matchLabels:
|
||||
os: windows
|
||||
arch: amd64
|
||||
uri: https://github.com/replicatedhq/troubleshoot/releases/download/v0.9.1/preflight_0.9.1_windows_amd64-0.9.1.zip
|
||||
sha256: "ac1940826bda4025cf2e8be1627f3ba9ef0df0ee259dba388cb7a167c617dfd4"
|
||||
uri: https://github.com/replicatedhq/troubleshoot/releases/download/v0.9.11/preflight_0.9.11_windows_amd64-0.9.11.zip
|
||||
sha256: "e5395b0b68d319233a69b02953e8b09331866ca574fc99cfddebf264b9000a77"
|
||||
files:
|
||||
- from: "/preflight.exe"
|
||||
to: "."
|
||||
bin: "preflight.exe"
|
||||
- from: preflight.exe
|
||||
to: .
|
||||
- from: LICENSE
|
||||
to: .
|
||||
bin: preflight.exe
|
||||
shortDescription: Executes application preflight tests in a cluster
|
||||
homepage: https://github.com/replicatedhq/troubleshoot
|
||||
caveats: |
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
apiVersion: krew.googlecontainertools.github.com/v1alpha2
|
||||
kind: Plugin
|
||||
metadata:
|
||||
name: support-bundle
|
||||
spec:
|
||||
version: "v0.9.11"
|
||||
platforms:
|
||||
- selector:
|
||||
matchLabels:
|
||||
os: linux
|
||||
arch: amd64
|
||||
uri: https://github.com/replicatedhq/troubleshoot/releases/download/v0.9.11/support-bundle_0.9.11_linux_amd64-0.9.11.tar.gz
|
||||
sha256: "601710ec70a9e9fb68f5395c591f80e72060a9c82eaeee969232a99a44663309"
|
||||
files:
|
||||
- from: support-bundle
|
||||
to: .
|
||||
- from: LICENSE
|
||||
to: .
|
||||
bin: support-bundle
|
||||
- selector:
|
||||
matchLabels:
|
||||
os: darwin
|
||||
arch: amd64
|
||||
uri: https://github.com/replicatedhq/troubleshoot/releases/download/v0.9.11/support-bundle_0.9.11_darwin_amd64-0.9.11.tar.gz
|
||||
sha256: "fe02a8f88013a55c012e7560856e7d4c36a4d13dbf17170208f870cff57ff030"
|
||||
files:
|
||||
- from: support-bundle
|
||||
to: .
|
||||
- from: LICENSE
|
||||
to: .
|
||||
bin: support-bundle
|
||||
- selector:
|
||||
matchLabels:
|
||||
os: windows
|
||||
arch: amd64
|
||||
uri: https://github.com/replicatedhq/troubleshoot/releases/download/v0.9.11/support-bundle_0.9.11_windows_amd64-0.9.11.zip
|
||||
sha256: "fded59fe9e46b61efbd6808f9f4124de542f2e32525e2fd992cf233578aaeaef"
|
||||
files:
|
||||
- from: support-bundle.exe
|
||||
to: .
|
||||
- from: LICENSE
|
||||
to: .
|
||||
bin: support-bundle.exe
|
||||
shortDescription: Creates support bundles for off-cluster analysis
|
||||
homepage: https://github.com/replicatedhq/troubleshoot
|
||||
caveats: |
|
||||
Usage:
|
||||
$ kubectl support-bundle <uri>
|
||||
|
||||
where <uri> references a set of application collectors
|
||||
|
||||
For example:
|
||||
|
||||
$ kubectl support-bundle https://troubleshoot.replicated.com
|
||||
|
||||
For additional options:
|
||||
$ kubectl support-bundle --help
|
||||
|
||||
Documentation:
|
||||
Full documentation on this plugin is available at:
|
||||
https://help.replicated.com/docs/troubleshoot/kubernetes/support-bundle/overview/
|
||||
|
||||
For application developers writing collectors and analyzers:
|
||||
https://help.replicated.com/docs/troubleshoot/kubernetes/collectors/defining-collectors/
|
||||
|
||||
description: |
|
||||
This plugin collects information about the cluster, and automatically
|
||||
redacts sensitive data from being collected. This can optionally include
|
||||
application-specific data. The plugin writes the collected files into a
|
||||
single archive named support-bundle.tar.gz. This archive can be manually
|
||||
inspected or uploaded to https://vendor.replicated.com for automated
|
||||
analysis.
|
||||
|
||||
Application developers can create and host a Collector manifest that
|
||||
defines information to be collected.
|
||||
|
||||
For information on creating a Collector manifest, view the documentation
|
||||
at https://help.replicated.com/docs/troubleshoot/kubernetes/collectors/defining-collectors/
|
||||
@@ -1,39 +0,0 @@
|
||||
apiVersion: krew.googlecontainertools.github.com/v1alpha2
|
||||
kind: Plugin
|
||||
metadata:
|
||||
name: troubleshoot
|
||||
spec:
|
||||
version: "v0.1.0"
|
||||
platforms:
|
||||
- selector:
|
||||
matchLabels:
|
||||
os: linux
|
||||
arch: amd64
|
||||
uri: https://github.com/replicatedhq/troubleshoot/releases/download/v0.1.0/troubleshoot_0.1.0_linux_amd64-alpha.tar.gz
|
||||
sha256: "fcfee994959a4924d0f70817a4b40d2b4df9e21a5a2a106d340784ce77fbbc15"
|
||||
files:
|
||||
- from: "/kubectl-troubleshoot"
|
||||
to: "."
|
||||
bin: "./kubectl-troubleshoot"
|
||||
- selector:
|
||||
matchLabels:
|
||||
os: darwin
|
||||
arch: amd64
|
||||
uri: https://github.com/replicatedhq/troubleshoot/releases/download/v0.1.0/troubleshoot_0.1.0_darwin_amd64-alpha.tar.gz
|
||||
sha256: "fcfee994959a4924d0f70817a4b40d2b4df9e21a5a2a106d340784ce77fbbc15"
|
||||
files:
|
||||
- from: "/kubectl-troubleshoot"
|
||||
to: "."
|
||||
bin: "./kubectl-troubleshoot"
|
||||
- selector:
|
||||
matchLabels:
|
||||
os: windows
|
||||
arch: amd64
|
||||
uri: https://github.com/replicatedhq/troubleshoot/releases/download/v0.1.0/troubleshoot_0.1.0_windows_amd64-alpha.tar.gz
|
||||
sha256: "fcfee994959a4924d0f70817a4b40d2b4df9e21a5a2a106d340784ce77fbbc15"
|
||||
files:
|
||||
- from: "/kubectl-troubleshoot.exe"
|
||||
to: "."
|
||||
bin: "./kubectl-troubleshoot.exe"
|
||||
shortDescription: Creates support bundles for off-cluster analysis
|
||||
homepage: https://github.com/replicaetdhq/troubleshoot
|
||||
@@ -3,6 +3,12 @@ kind: Preflight
|
||||
metadata:
|
||||
name: example-preflight-checks
|
||||
spec:
|
||||
collectors:
|
||||
- secret:
|
||||
name: myapp-postgres
|
||||
namespace: default
|
||||
key: uri
|
||||
includeValue: false
|
||||
analyzers:
|
||||
- clusterVersion:
|
||||
outcomes:
|
||||
@@ -15,13 +21,15 @@ spec:
|
||||
message: Your cluster meets the minimum version of Kubernetes, but we recommend you update to 1.15.0 or later.
|
||||
uri: https://kubernetes.io
|
||||
- pass:
|
||||
when: ">= 1.15.0"
|
||||
message: Your cluster meets the recommended and required versions of Kubernetes.
|
||||
- customResourceDefinition:
|
||||
customResourceDefinitionName: constrainttemplates.templates.gatekeeper.sh
|
||||
checkName: Gatekeeper policy runtime is installed
|
||||
checkName: Gatekeeper policy runtime
|
||||
outcomes:
|
||||
- fail:
|
||||
message: Gatekeeper is required for the application, but not found in the cluster.
|
||||
uri: https://enterprise.support.io/installing/gatekeeper
|
||||
- pass:
|
||||
message: Found a supported version of Gatekeeper installed and running in the cluster.
|
||||
- imagePullSecret:
|
||||
@@ -31,5 +39,17 @@ spec:
|
||||
- fail:
|
||||
message: |
|
||||
Cannot pull from quay.io. An image pull secret should be deployed to the cluster that has credentials to pull the images. To obtain this secret, please contact your support rep.
|
||||
uri: https://enterprise.support.io/installing/registry-credentials
|
||||
- pass:
|
||||
message: Found credentials to pull from quay.io
|
||||
- secret:
|
||||
checkName: Postgres connection string
|
||||
secretName: myapp-postgres
|
||||
namespace: default
|
||||
key: uri
|
||||
outcomes:
|
||||
- fail:
|
||||
message: A secret named "myapp-postgres" must be deployed with a "uri" key
|
||||
uri: https://enterprise.support.io/installing/postgres
|
||||
- pass:
|
||||
message: Found a valid postgres connection string
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
apiVersion: troubleshoot.replicated.com/v1beta1
|
||||
kind: Analyzer
|
||||
metadata:
|
||||
name: a
|
||||
spec:
|
||||
analyzers:
|
||||
- distribution:
|
||||
outcomes:
|
||||
- fail:
|
||||
when: "= docker desktop"
|
||||
message: "docker for desktop is not allowed"
|
||||
- fail:
|
||||
when: "microk8s"
|
||||
message: "mickrk8s is not prod"
|
||||
- warn:
|
||||
when: "!= eks"
|
||||
message: "YMMV on not eks"
|
||||
- pass:
|
||||
message: "good work"
|
||||
|
||||
|
||||
@@ -3,29 +3,4 @@ kind: Collector
|
||||
metadata:
|
||||
name: collector-sample
|
||||
spec:
|
||||
- clusterInfo: {}
|
||||
- clusterResources: {}
|
||||
- secret:
|
||||
name: illmannered-cricket-mysql
|
||||
namespace: default
|
||||
key: mysql-password
|
||||
- logs:
|
||||
selector:
|
||||
- name=nginx-ingress-microk8s
|
||||
namespace: default
|
||||
limits:
|
||||
maxAge: 30d
|
||||
maxLines: 10000
|
||||
- run:
|
||||
collectorName: ping-google
|
||||
namespace: default
|
||||
image: flungo/netutils
|
||||
command: ["ping"]
|
||||
args: ["www.google.com"]
|
||||
timeout: 5s
|
||||
- http:
|
||||
collectorName: test-get
|
||||
get:
|
||||
url: https://api.staging.replicated.com/market/v1/echo/ip
|
||||
insecureSkipVerify: false
|
||||
headers: {}
|
||||
collectors: []
|
||||
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
package main
|
||||
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"encoding/json"
|
||||
|
||||
"gopkg.in/yaml.v2"
|
||||
analyzer "github.com/replicatedhq/troubleshoot/pkg/analyze"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/logger"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/convert"
|
||||
|
||||
)
|
||||
|
||||
//export Analyze
|
||||
func Analyze(bundleURL string, analyzers string, outputFormat string, compatibility string) *C.char {
|
||||
logger.SetQuiet(true)
|
||||
|
||||
result, err := analyzer.DownloadAndAnalyze(bundleURL, analyzers)
|
||||
if err != nil {
|
||||
fmt.Printf("error downloading and analyzing: %s\n", err.Error())
|
||||
return C.CString("")
|
||||
}
|
||||
|
||||
var data interface{}
|
||||
switch compatibility {
|
||||
case "support-bundle":
|
||||
data = convert.FromAnalyzerResult(result)
|
||||
default:
|
||||
data = result
|
||||
}
|
||||
|
||||
var formatted []byte
|
||||
switch outputFormat {
|
||||
case "json":
|
||||
formatted, err = json.MarshalIndent(data, "", " ")
|
||||
case "", "yaml":
|
||||
formatted, err = yaml.Marshal(data)
|
||||
default:
|
||||
fmt.Printf("unknown output format: %s\n", outputFormat)
|
||||
return C.CString("")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
fmt.Printf("error formatting output: %#v\n", err)
|
||||
return C.CString("")
|
||||
}
|
||||
|
||||
return C.CString(string(formatted))
|
||||
}
|
||||
|
||||
func main() {}
|
||||
@@ -3,43 +3,56 @@ module github.com/replicatedhq/troubleshoot
|
||||
go 1.12
|
||||
|
||||
require (
|
||||
github.com/Masterminds/goutils v1.1.0 // indirect
|
||||
github.com/Masterminds/semver v1.4.2 // indirect
|
||||
github.com/Masterminds/sprig v2.20.0+incompatible // indirect
|
||||
github.com/andrewchambers/go-jqpipe v0.0.0-20180509223707-2d54cef8cd94 // indirect
|
||||
github.com/ahmetalpbalkan/go-cursor v0.0.0-20131010032410-8136607ea412
|
||||
github.com/aws/aws-sdk-go v1.22.0 // indirect
|
||||
github.com/blang/semver v3.5.1+incompatible
|
||||
github.com/docker/distribution v2.7.1+incompatible // indirect
|
||||
github.com/docker/docker v1.13.1 // indirect
|
||||
github.com/docker/go-connections v0.4.0 // indirect
|
||||
github.com/docker/go-units v0.4.0 // indirect
|
||||
github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c // indirect
|
||||
github.com/dsnet/compress v0.0.1 // indirect
|
||||
github.com/elazarl/goproxy v0.0.0-20191011121108-aa519ddbe484 // indirect
|
||||
github.com/emicklei/go-restful v2.9.6+incompatible // indirect
|
||||
github.com/evanphx/json-patch v4.5.0+incompatible // indirect
|
||||
github.com/fatih/color v1.7.0
|
||||
github.com/frankban/quicktest v1.7.2 // indirect
|
||||
github.com/gin-gonic/gin v1.4.0
|
||||
github.com/gizak/termui/v3 v3.1.0
|
||||
github.com/go-openapi/spec v0.19.2 // indirect
|
||||
github.com/golang/snappy v0.0.1 // indirect
|
||||
github.com/google/uuid v1.1.1 // indirect
|
||||
github.com/hashicorp/go-getter v1.3.0
|
||||
github.com/google/gofuzz v1.0.0 // indirect
|
||||
github.com/googleapis/gnostic v0.3.0 // indirect
|
||||
github.com/hashicorp/go-getter v1.3.1-0.20190627223108-da0323b9545e
|
||||
github.com/hashicorp/go-multierror v1.0.0
|
||||
github.com/huandu/xstrings v1.2.0 // indirect
|
||||
github.com/manifoldco/promptui v0.3.2 // indirect
|
||||
github.com/imdario/mergo v0.3.7 // indirect
|
||||
github.com/mailru/easyjson v0.0.0-20190620125010-da37f6c1e481 // indirect
|
||||
github.com/mattn/go-colorable v0.1.2 // indirect
|
||||
github.com/mholt/archiver v3.1.1+incompatible
|
||||
github.com/mitchellh/go-wordwrap v1.0.0 // indirect
|
||||
github.com/nwaples/rardecode v1.0.0 // indirect
|
||||
github.com/onsi/ginkgo v1.8.0 // indirect
|
||||
github.com/onsi/gomega v1.5.0
|
||||
github.com/opencontainers/go-digest v1.0.0-rc1 // indirect
|
||||
github.com/pierrec/lz4 v2.0.5+incompatible // indirect
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
|
||||
github.com/pierrec/lz4 v2.2.6+incompatible // indirect
|
||||
github.com/pkg/errors v0.8.1
|
||||
github.com/spf13/cobra v0.0.3
|
||||
github.com/spf13/cobra v0.0.5
|
||||
github.com/spf13/viper v1.4.0
|
||||
github.com/stretchr/testify v1.3.0
|
||||
github.com/tj/go-spin v1.1.0
|
||||
github.com/ugorji/go v1.1.7 // indirect
|
||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
|
||||
github.com/xo/dburl v0.0.0-20190203050942-98997a05b24f // indirect
|
||||
golang.org/x/net v0.0.0-20190522155817-f3200d17e092
|
||||
go.opencensus.io v0.20.2 // indirect
|
||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 // indirect
|
||||
golang.org/x/net v0.0.0-20190812203447-cdfb69ac37fc
|
||||
golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a // indirect
|
||||
golang.org/x/sys v0.0.0-20190621203818-d432491b9138 // indirect
|
||||
google.golang.org/appengine v1.5.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.2.2
|
||||
k8s.io/api v0.0.0-20190409021203-6e4e0e4f393b
|
||||
k8s.io/apiextensions-apiserver v0.0.0-20190409022649-727a075fdec8
|
||||
k8s.io/apimachinery v0.0.0-20190404173353-6a84e37a896d
|
||||
k8s.io/cli-runtime v0.0.0-20190314001948-2899ed30580f
|
||||
k8s.io/client-go v11.0.1-0.20190409021438-1a26190bd76a+incompatible
|
||||
k8s.io/klog v0.4.0 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20190815110238-8ff09bc626d6 // indirect
|
||||
k8s.io/utils v0.0.0-20190809000727-6c36bc71fc4a // indirect
|
||||
sigs.k8s.io/controller-runtime v0.2.0-beta.2
|
||||
sigs.k8s.io/controller-tools v0.2.0-beta.2 // indirect
|
||||
sigs.k8s.io/kustomize v2.0.3+incompatible // indirect
|
||||
)
|
||||
|
||||
@@ -5,38 +5,38 @@ cloud.google.com/go v0.34.0 h1:eOI3/cP2VTU6uZLDYAoic+eyzzB9YyGmJ7eIjl8rOPg=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.36.0 h1:+aCSj7tOo2LODWVEuZDZeGCckdt6MlSF+X/rB3wUiS8=
|
||||
cloud.google.com/go v0.36.0/go.mod h1:RUoy9p/M4ge0HzT8L+SDZ8jg+Q6fth0CiBuhFJpSV40=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
cloud.google.com/go v0.41.0 h1:NFvqUTDnSNYPX5oReekmB+D+90jrJIcVImxQ3qrBVgM=
|
||||
cloud.google.com/go v0.41.0/go.mod h1:OauMR7DV8fzvZIl2qg6rkaIhD/vmgk4iwEw/h6ercmg=
|
||||
dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU=
|
||||
dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU=
|
||||
dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4=
|
||||
dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=
|
||||
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/Masterminds/goutils v1.1.0 h1:zukEsf/1JZwCMgHiK3GZftabmxiCw4apj3a28RPBiVg=
|
||||
github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
|
||||
github.com/Masterminds/semver v1.4.2 h1:WBLTQ37jOCzSLtXNdoo8bNM8876KhNqOKvrlGITgsTc=
|
||||
github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
|
||||
github.com/Masterminds/sprig v2.20.0+incompatible h1:dJTKKuUkYW3RMFdQFXPU/s6hg10RgctmTjRcbZ98Ap8=
|
||||
github.com/Masterminds/sprig v2.20.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o=
|
||||
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/PuerkitoBio/purell v1.0.0 h1:0GoNN3taZV6QI81IXgCbxMyEaJDXMSIjArYBCYzVVvs=
|
||||
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
|
||||
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2 h1:JCHLVE3B+kJde7bIEo5N4J+ZbLhp0J1Fs+ulyRws4gE=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||
github.com/alecthomas/gometalinter v2.0.11+incompatible/go.mod h1:qfIpQGGz3d+NmgyPBqv+LSh50emm1pt72EtcX2vKYQk=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
|
||||
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
|
||||
github.com/ahmetalpbalkan/go-cursor v0.0.0-20131010032410-8136607ea412 h1:vOVO0ypMfTt6tZacyI0kp+iCZb1XSNiYDqnzBWYgfe4=
|
||||
github.com/ahmetalpbalkan/go-cursor v0.0.0-20131010032410-8136607ea412/go.mod h1:AI9hp1tkp10pAlK5TCwL+7yWbRgtDm9jhToq6qij2xs=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/andrewchambers/go-jqpipe v0.0.0-20180509223707-2d54cef8cd94 h1:mOmUCSuraMWgFv6Wr3pCkt9UZ2RBsx2XMZoIXg2Oj6w=
|
||||
github.com/andrewchambers/go-jqpipe v0.0.0-20180509223707-2d54cef8cd94/go.mod h1:8KsRln2ynWOaJwxYOUwfPU/ix+H9oh3aCJ6L0DsHW3Q=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
||||
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||
github.com/appscode/jsonpatch v0.0.0-20190108182946-7c0e3b262f30 h1:Kn3rqvbUFqSepE2OqVu0Pn1CbDw9IuMlONapol0zuwk=
|
||||
github.com/appscode/jsonpatch v0.0.0-20190108182946-7c0e3b262f30/go.mod h1:4AJxUpXUhv4N+ziTvIcWWXgeorXpxPZOfk9HdEVr96M=
|
||||
github.com/appscode/jsonpatch v2.0.0+incompatible h1:DEsgcSnA7ui6pICc75uxDpyN8Bx4DLFTS8aRym702nE=
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
github.com/aws/aws-sdk-go v1.15.78 h1:LaXy6lWR0YK7LKyuU0QWy2ws/LWTPfYV/UgfiBu4tvY=
|
||||
github.com/aws/aws-sdk-go v1.15.78/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3ATZkfNZeM=
|
||||
github.com/aws/aws-sdk-go v1.22.0 h1:e88V6+dSEyBibUy0ekOydtTfNWzqG3hrtCR8SF6UqqY=
|
||||
github.com/aws/aws-sdk-go v1.22.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
@@ -47,10 +47,6 @@ github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnweb
|
||||
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/cheggaaa/pb v1.0.27/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
@@ -66,34 +62,34 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug=
|
||||
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/docker v1.13.1 h1:IkZjBSIc8hBjLpqeAbeE5mca5mNgeatLHBy3GO78BWo=
|
||||
github.com/docker/docker v1.13.1/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
|
||||
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
|
||||
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
|
||||
github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c h1:ZfSZ3P3BedhKGUhzj7BQlPSU4OvT6tfOKe3DVHzOA7s=
|
||||
github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
|
||||
github.com/dsnet/compress v0.0.1 h1:PlZu0n3Tuv04TzpfPbrnI0HW/YwodEXDS+oPKahKF0Q=
|
||||
github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo=
|
||||
github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
|
||||
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
|
||||
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
|
||||
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
|
||||
github.com/elazarl/goproxy v0.0.0-20191011121108-aa519ddbe484 h1:pEtiCjIXx3RvGjlUJuCNxNOw0MNblyR9Wi+vJGBFh+8=
|
||||
github.com/elazarl/goproxy v0.0.0-20191011121108-aa519ddbe484/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
|
||||
github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2 h1:dWB6v3RcOy03t/bUadywsbyrQwCqZeNIEX6M1OtSZOM=
|
||||
github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8=
|
||||
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633 h1:H2pdYOb3KQ1/YsqVWoWNLQO+fusocsw354rqGTZtAgw=
|
||||
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||
github.com/emicklei/go-restful v2.9.6+incompatible h1:tfrHha8zJ01ywiOEC1miGY8st1/igzWB8OmvPgoYX7w=
|
||||
github.com/emicklei/go-restful v2.9.6+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||
github.com/evanphx/json-patch v4.0.0+incompatible h1:xregGRMLBeuRcwiOTHRCsPPuzCQlqhxUPbqdw+zNkLc=
|
||||
github.com/evanphx/json-patch v4.0.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/evanphx/json-patch v4.1.0+incompatible h1:K1MDoo4AZ4wU0GIU/fPmtZg7VpzLjCxu+UwBD1FvwOc=
|
||||
github.com/evanphx/json-patch v4.1.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/evanphx/json-patch v4.2.0+incompatible h1:fUDGZCv/7iAN7u0puUVhvKCcsR6vRfwrJatElLBEf0I=
|
||||
github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/evanphx/json-patch v4.5.0+incompatible h1:ouOWdg56aJriqS0huScTkVXPC5IcNrDCXZ6OoTAWu7M=
|
||||
github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||
github.com/frankban/quicktest v1.7.2 h1:2QxQoC1TS09S7fhCPsrvqYdvP1H5M1P1ih5ABm3BTYk=
|
||||
github.com/frankban/quicktest v1.7.2/go.mod h1:jaStnuzAqU1AJdCO0l53JDCJrVDKcS03DbaAcR7Ks/o=
|
||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
@@ -115,71 +111,65 @@ github.com/go-logr/logr v0.1.0 h1:M1Tv3VzNlEHg6uyACnRdtrploV2P7wZqH8BoQMtz0cg=
|
||||
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
|
||||
github.com/go-logr/zapr v0.1.0 h1:h+WVe9j6HAA01niTJPA/kKH0i7e0rLZBCwauQFcRE54=
|
||||
github.com/go-logr/zapr v0.1.0/go.mod h1:tabnROwaDl0UNxkVeFRbY8bwB37GwRv0P8lg6aAiEnk=
|
||||
github.com/go-logr/zapr v0.1.1 h1:qXBXPDdNncunGs7XeEpsJt8wCjYBygluzfdLO0G5baE=
|
||||
github.com/go-logr/zapr v0.1.1/go.mod h1:tabnROwaDl0UNxkVeFRbY8bwB37GwRv0P8lg6aAiEnk=
|
||||
github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1 h1:wSt/4CYxs70xbATrGXhokKF1i0tZjENLOo1ioIO13zk=
|
||||
github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
|
||||
github.com/go-openapi/jsonpointer v0.19.2 h1:A9+F4Dc/MCNB5jibxf6rRvOvR/iFgQdyNx9eIhnGqq0=
|
||||
github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
|
||||
github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9 h1:tF+augKRWlWx0J0B7ZyyKSiTyV6E1zZe+7b3qQlcEf8=
|
||||
github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
|
||||
github.com/go-openapi/jsonreference v0.19.2 h1:o20suLFB4Ri0tuzpWtyHlh7E7HnkqTNLq6aR6WVNS1w=
|
||||
github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
|
||||
github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501 h1:C1JKChikHGpXwT5UQDFaryIpDtyyGL/CR6C2kB7F1oc=
|
||||
github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=
|
||||
github.com/go-openapi/spec v0.19.2 h1:SStNd1jRcYtfKCN7R0laGNs80WYYvn5CbBjM2sOmCrE=
|
||||
github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY=
|
||||
github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87 h1:zP3nY8Tk2E6RTkqGYrarZXuzh+ffyLDljLxCy1iJw80=
|
||||
github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
|
||||
github.com/go-openapi/swag v0.19.2 h1:jvO6bCMBEilGwMfHhrd61zIID4oIFdwb76V17SM88dE=
|
||||
github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/gobuffalo/envy v1.6.5/go.mod h1:N+GkhhZ/93bGZc6ZKhJLP6+m+tCNPKwgSpH9kaifseQ=
|
||||
github.com/gobuffalo/envy v1.6.15 h1:OsV5vOpHYUpP7ZLS6sem1y40/lNX1BZj+ynMiRi21lQ=
|
||||
github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
|
||||
github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
|
||||
github.com/gogo/protobuf v1.0.0 h1:2jyBKDKU/8v3v2xVR2PtiWQviFUyiaGk2rpfyFT8rTM=
|
||||
github.com/gogo/protobuf v1.0.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.1.1 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
|
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903 h1:LbsanbbD6LieFkXbj9YNNBupiGHJgFeLpO0j0Fza1h8=
|
||||
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20180513044358-24b0969c4cb7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef h1:veQD95Isof8w9/WXiA+pa3tz3fJXkt5B7QaRBrM62gk=
|
||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
|
||||
github.com/golang/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||
github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
|
||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
|
||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367 h1:ScAXWS+TR6MZKex+7Z8rneuSJH+FSDqd6ocQyl+ZHo4=
|
||||
github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
|
||||
github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf h1:+RRA9JqSOZFfKrOeqr2z77+8R2RKyh8PG66dcu1V0ck=
|
||||
github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
|
||||
github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf/go.mod h1:RpwtwJQFrIEPstU94h88MWPXP2ektJZ8cZ0YntAmXiE=
|
||||
github.com/google/uuid v1.0.0 h1:b4Gk+7WdP/d3HZH8EJsZpvV7EtDOgaZLtnaNGIu1adA=
|
||||
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go v2.0.0+incompatible h1:j0GKcs05QVmm7yesiZq2+9cxHkNK9YM6zKx4D2qucQU=
|
||||
github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
|
||||
github.com/googleapis/gax-go/v2 v2.0.3 h1:siORttZ36U2R/WjiJuDz8znElWBiAlO9rVt+mqJt0Cc=
|
||||
github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/googleapis/gnostic v0.0.0-20170426233943-68f4ded48ba9/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
|
||||
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d h1:7XGaL1e6bYS1yIonGp9761ExpPPV1ui0SAC59Yube9k=
|
||||
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
|
||||
github.com/googleapis/gnostic v0.2.0 h1:l6N3VoaVzTncYYW+9yOz2LJJammFZGBO13sqgEhpy9g=
|
||||
@@ -187,12 +177,12 @@ github.com/googleapis/gnostic v0.2.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTV
|
||||
github.com/googleapis/gnostic v0.3.0 h1:CcQijm0XKekKjP/YCz28LXVSpgguuB+nCxaSjCe09y0=
|
||||
github.com/googleapis/gnostic v0.3.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gordonklaus/ineffassign v0.0.0-20180909121442-1003c8bd00dc/go.mod h1:cuNKsD1zp2v6XfE/orVX2QE1LC+i254ceGcVeDT3pTU=
|
||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
|
||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM=
|
||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA=
|
||||
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
|
||||
@@ -201,8 +191,8 @@ github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/U
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.0 h1:wvCrVc9TjDls6+YGAF2hAifE1E5U1+b4tH6KdvN3Gig=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-getter v1.3.0 h1:pFMSFlI9l5NaeuzkpE3L7BYk9qQ9juTAgXW/H0cqxcU=
|
||||
github.com/hashicorp/go-getter v1.3.0/go.mod h1:/O1k/AizTN0QmfEKknCYGvICeyKUDqCYA8vvWtGWDeQ=
|
||||
github.com/hashicorp/go-getter v1.3.1-0.20190627223108-da0323b9545e h1:6krcdHPiS+aIP9XKzJzSahfjD7jG7Z+4+opm0z39V1M=
|
||||
github.com/hashicorp/go-getter v1.3.1-0.20190627223108-da0323b9545e/go.mod h1:/O1k/AizTN0QmfEKknCYGvICeyKUDqCYA8vvWtGWDeQ=
|
||||
github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o=
|
||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||
github.com/hashicorp/go-safetemp v1.0.0 h1:2HR189eFNrjHQyENnQMMpCiBAsRxzbTMIgBhEyExpmo=
|
||||
@@ -213,14 +203,10 @@ github.com/hashicorp/golang-lru v0.0.0-20180201235237-0fb14efe8c47 h1:UnszMmmmm5
|
||||
github.com/hashicorp/golang-lru v0.0.0-20180201235237-0fb14efe8c47/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/huandu/xstrings v1.2.0 h1:yPeWdRnmynF7p+lLYz0H2tthW9lqhMJrQV/U7yy4wX0=
|
||||
github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4=
|
||||
github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28=
|
||||
github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/imdario/mergo v0.3.7 h1:Y+UAYTZ7gDEuOfhxKWy+dvb5dRQ6rJjFSdX2HZY1/gI=
|
||||
@@ -230,16 +216,13 @@ github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANyt
|
||||
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8 h1:12VvqtR6Aowv3l/EQUlocDHW2Cp4G9WJVH7uyH8QFJE=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
|
||||
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a h1:FaWFmfWdAUKbSCtOU2QjDaorUexogfaMgbipgYATUMU=
|
||||
github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a/go.mod h1:UJSiEoRfvx3hP73CvoARgeLjaIOjybY9vj8PUPPFGeU=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
@@ -251,22 +234,26 @@ github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a h1:weJVJJRzAJBFRlAiJQROKQs8oC9vOxvm4rZmBBk0ONw=
|
||||
github.com/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
|
||||
github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a h1:TpvdAwDAt1K4ANVOfcihouRdvP+MgAfDWwBuct4l6ZY=
|
||||
github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/manifoldco/promptui v0.3.2 h1:rir7oByTERac6jhpHUPErHuopoRDvO3jxS+FdadEns8=
|
||||
github.com/manifoldco/promptui v0.3.2/go.mod h1:8JU+igZ+eeiiRku4T5BjtKh2ms8sziGpSYl1gN8Bazw=
|
||||
github.com/markbates/inflect v1.0.4 h1:5fh1gzTFhfae06u3hzHYO9xe3l3v3nW5Pwt3naLTP5g=
|
||||
github.com/markbates/inflect v1.0.4/go.mod h1:1fR9+pO2KHEO9ZRtto13gDwwZaAKstQzferVeWqbgNs=
|
||||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63 h1:nTT4s92Dgz2HlrB2NaMgvlfqHH39OgMhA7z3PK7PGD4=
|
||||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190620125010-da37f6c1e481 h1:IaSjLMT6WvkoZZjspGxy3rdaTEmWLoRm49WbtVUi9sA=
|
||||
github.com/mailru/easyjson v0.0.0-20190620125010-da37f6c1e481/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
|
||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc=
|
||||
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-runewidth v0.0.2 h1:UnlwIPBGaTZfPQ6T1IGzPI0EkYAQmT9fAEJ/poFC63o=
|
||||
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
|
||||
@@ -278,11 +265,14 @@ github.com/mholt/archiver v3.1.1+incompatible/go.mod h1:Dh2dOXnSdiLxRiPoVfIr/fI1
|
||||
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
|
||||
github.com/mitchellh/go-homedir v1.0.0 h1:vKb8ShqSby24Yrqr/yDYkuFz8d0WUjys40rvnGC8aR0=
|
||||
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0=
|
||||
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 h1:DpOJ2HYzCv8LZP15IdmG+YdwD2luVPHITV96TkirNBM=
|
||||
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
||||
github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4=
|
||||
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
||||
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
@@ -293,7 +283,6 @@ github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
|
||||
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
|
||||
github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
|
||||
github.com/nsf/termbox-go v0.0.0-20190121233118-02980233997d h1:x3S6kxmy49zXVVyhcnrFqxvNVCBPb2KZ9hV2RBdS840=
|
||||
@@ -303,28 +292,26 @@ github.com/nwaples/rardecode v1.0.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWk
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w=
|
||||
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
||||
github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo=
|
||||
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ=
|
||||
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||
github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
|
||||
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
|
||||
github.com/pborman/uuid v0.0.0-20170612153648-e790cca94e6c h1:MUyE44mTvnI5A0xrxIxaMqoWFzPfQvtE2IWUollMDMs=
|
||||
github.com/pborman/uuid v0.0.0-20170612153648-e790cca94e6c/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34=
|
||||
github.com/pborman/uuid v0.0.0-20180906182336-adf5a7427709 h1:zNBQb37RGLmJybyMcs983HfUfpkw9OTFD9tbBfAViHE=
|
||||
github.com/pborman/uuid v0.0.0-20180906182336-adf5a7427709/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34=
|
||||
github.com/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g=
|
||||
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
|
||||
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/petar/GoLLRB v0.0.0-20190514000832-33fb24c13b99/go.mod h1:HUpKUBZnpzkdx0kD/+Yfuft+uD3zHGtXF/XJB14TUr4=
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
||||
github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I=
|
||||
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||
github.com/pierrec/lz4 v2.2.6+incompatible h1:6aCX4/YZ9v8q69hTyiR7dNLnTA3fgtKHVVW5BCd5Znw=
|
||||
github.com/pierrec/lz4 v2.2.6+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||
github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
@@ -335,37 +322,27 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
|
||||
github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
|
||||
github.com/prometheus/client_golang v0.9.3 h1:9iH4JKXLzFbOAdtqv/a+j8aewx2Y8lAjAydhbaScPF8=
|
||||
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
|
||||
github.com/prometheus/client_golang v0.9.4/go.mod h1:oCXIBxdI62A4cR6aTRJCgetEjecSIYzOEaeAn4iYEpM=
|
||||
github.com/prometheus/client_golang v1.0.0 h1:vrDKnkGzuGvhNAL56c7DBz29ZL+KxnoR0x7enabFceM=
|
||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.4.0 h1:7etb9YClo3a6HjLzfl6rIQaU+FDfi0VSX39io3aQ+DM=
|
||||
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.4.1 h1:K0MGApIoQvMw27RTdJkPbr3JZ7DNbtxQNyi5STVM6Kw=
|
||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.6.0 h1:kRhiuYSXR3+uv2IbVbZhUxK5zVD/2pp3Gd2PpvPkpEo=
|
||||
github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
|
||||
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084 h1:sofwID9zm4tzrgykg80hfFph1mryUeLRsUfoocVVmRY=
|
||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.0.2 h1:6LJUbpNm42llc4HRCuvApCSWB/WfhuNo9K98Q9sNGfs=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.0.3 h1:CTwfnzjQ+8dS6MhHHu4YswVAD99sL2wjPqP+VkURmKE=
|
||||
github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
|
||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.2.2 h1:J7U/N7eRtzjhs26d6GqMh2HBuXP8/Z64Densiiieafo=
|
||||
github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
@@ -401,8 +378,7 @@ github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc=
|
||||
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
||||
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8=
|
||||
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||
github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s=
|
||||
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
||||
github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
@@ -415,6 +391,7 @@ github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU=
|
||||
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||
@@ -423,23 +400,27 @@ github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cb
|
||||
github.com/tj/go-spin v1.1.0 h1:lhdWZsvImxvZ3q1C5OIB7d72DuOwP4O2NdBg9PyzNds=
|
||||
github.com/tj/go-spin v1.1.0/go.mod h1:Mg1mzmePZm4dva8Qz60H2lHwmJ2loum4VIrLgVnKwh4=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/tsenart/deadcode v0.0.0-20160724212837-210d2dc333e9/go.mod h1:q+QjxYvZ+fpjMXqs+XEriussHjSYqeXVnAdSV1tkMYk=
|
||||
github.com/ugorji/go v1.1.4 h1:j4s+tAvLfL3bZyefP2SEWmhBzmuIlH/eqNuPdFPgngw=
|
||||
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
|
||||
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
|
||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8 h1:3SVOIvH7Ae1KRYyQWRjXWJEA9sS/c/pjvH++55Gr648=
|
||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
|
||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
||||
github.com/ulikunitz/xz v0.5.5/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
|
||||
github.com/ulikunitz/xz v0.5.6 h1:jGHAfXawEGZQ3blwU5wnWKQJvAraT7Ftq9EXjnXYgt8=
|
||||
github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
|
||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo=
|
||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/xo/dburl v0.0.0-20190203050942-98997a05b24f/go.mod h1:g6rdekR8vgfVZrkLWfobLTm0kVez7GAN23mWtkGCJ14=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.opencensus.io v0.18.0 h1:Mk5rgZcggtbvtAun5aJzAtjKKN/t0R3jJPlWILlv938=
|
||||
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
||||
go.opencensus.io v0.20.2 h1:NAfh7zF0/3/HqtMvJNZ/RFrSlCE6ZTlHmKfhL/Dm1Jk=
|
||||
go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
@@ -456,21 +437,16 @@ golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnf
|
||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8 h1:1wopBVtVdWnn03fZelqdXTqk7U7zPQCb+T4rbU9ZEoU=
|
||||
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc=
|
||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -480,27 +456,25 @@ golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73r
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190522155817-f3200d17e092 h1:4QSRKanuywn15aTZvI/mIDEgPQpswuFndXpOj3rKEco=
|
||||
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980 h1:dfGZHvZk057jK2MCeWus/TowKpJ8y4AmooUzdBSR9GU=
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190628185345-da137c7871d7 h1:rTIdg5QFRR7XCaK4LCjBiPbx8j4DQRpdYMnGn/bJUEU=
|
||||
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190812203447-cdfb69ac37fc h1:gkKoSkUmnU6bpS/VhkuO27bzQeSA51uaEfbOW5dNb68=
|
||||
golang.org/x/net v0.0.0-20190812203447-cdfb69ac37fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890 h1:uESlIz09WIHT2I+pasSXcpLYqYK8wHcdCetU3VuMBJE=
|
||||
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a h1:tImsplftrFpALCYumobsd0K86vlAs/eXGFms2txfJfA=
|
||||
golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -520,21 +494,15 @@ golang.org/x/sys v0.0.0-20181213200352-4d1cda033e06/go.mod h1:STP8DvDyc/dI5b8T5h
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190429190828-d89cdac9e872 h1:cGjJzUd8RgBw428LXP65YXni0aiGNA4Bl+ls8SmLOm8=
|
||||
golang.org/x/sys v0.0.0-20190429190828-d89cdac9e872/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f h1:25KHgbfyiSm6vwQLbM3zZIe1v9p/3ea4Rz+nnM5K/i4=
|
||||
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb h1:fgwFCsaw9buMuxNd6+DQfAuSFqbNiQZpcgJQAgJsK6k=
|
||||
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190621203818-d432491b9138 h1:t8BZD9RDjkm9/h7yYN6kE8oaeov5r9aztkB7zKA5Tkg=
|
||||
golang.org/x/sys v0.0.0-20190621203818-d432491b9138/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2 h1:z99zHgr7hKfrUcX/KsoJk5FJfjTceCKIp96+biqP4To=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
@@ -547,71 +515,52 @@ golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGm
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181122213734-04b5d21e00f1/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd h1:/e+gpKk9r3dJobndpTytxS2gOy6m5uvpg+ISQoEcusQ=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190501045030-23463209683d h1:D7DVZUZEUgsSIDTivnUtVeGfN5AvhDIKtdIZAqx0ieE=
|
||||
golang.org/x/tools v0.0.0-20190501045030-23463209683d/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59 h1:QjA/9ArTfVTLfEhClDCG7SGrZkZixxWpwNCDiwJfh88=
|
||||
golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190624190245-7f2218787638/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190703212419-2214986f1668/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
||||
gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0=
|
||||
gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
|
||||
gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ=
|
||||
google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
||||
google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
||||
google.golang.org/api v0.1.0 h1:K6z2u68e86TPdSdefXdzvXgR1zEMa+459vBSfWYAZkI=
|
||||
google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
google.golang.org/api v0.3.1 h1:oJra/lMfmtm13/rgY/8i3MzjFWYXvQIAKjQ3HqofMk8=
|
||||
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
|
||||
google.golang.org/appengine v1.1.0 h1:igQkv0AAhEIvTEpD5LIpAfav2eeVO9HBTjvKHVJPRSs=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.3.0 h1:FBSsiFRMz3LBeXIomRnVzrQwSDj4ibvcRexLG0LZGQk=
|
||||
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
|
||||
google.golang.org/genproto v0.0.0-20190201180003-4b09977fb922 h1:mBVYJnbrXLA/ZCBTCe7PtEgAUP+1bg92qTaFoPHdz+8=
|
||||
google.golang.org/genproto v0.0.0-20190201180003-4b09977fb922/go.mod h1:L3J43x8/uS+qIUoksaLKe6OS3nUKxOKuIFz1sl2/jx4=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19 h1:Lj2SnHtxkRGJDqnGaSjo+CCdIieEnwVazbOXILwQemk=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190626174449-989357319d63/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
|
||||
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
|
||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.0 h1:G+97AoqBnmZIT91cLG/EkCoK9NSelj64P8bOHHNmGn0=
|
||||
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/cheggaaa/pb.v1 v1.0.27/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM=
|
||||
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
|
||||
gopkg.in/go-playground/validator.v8 v8.18.2 h1:lFB4DoMU6B626w8ny76MV7VX6W2VHct2GVOI3xgiMrQ=
|
||||
gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
|
||||
gopkg.in/inf.v0 v0.9.0 h1:3zYtXIO92bvsdS3ggAdA8Gb4Azj0YU+TVY1uGYNFA8o=
|
||||
gopkg.in/inf.v0 v0.9.0/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||
@@ -625,68 +574,34 @@ grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJd
|
||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
k8s.io/api v0.0.0-20190222213804-5cb15d344471 h1:MzQGt8qWQCR+39kbYRd0uQqsvSidpYqJLFeWiJ9l4OE=
|
||||
k8s.io/api v0.0.0-20190222213804-5cb15d344471/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA=
|
||||
k8s.io/api v0.0.0-20190409021203-6e4e0e4f393b h1:aBGgKJUM9Hk/3AE8WaZIApnTxG35kbuQba2w+SXqezo=
|
||||
k8s.io/api v0.0.0-20190409021203-6e4e0e4f393b/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA=
|
||||
k8s.io/api v0.0.0-20190703205437-39734b2a72fe h1:MFaHtAyhZcfBZocN91muHSqnwiF5yfXx7yGoehneNYg=
|
||||
k8s.io/api v0.0.0-20190703205437-39734b2a72fe/go.mod h1:J5EZ0KSEjvyKOBy5BDHSF3zn82madLLWg7nUKaOHZKU=
|
||||
k8s.io/api v0.0.0-20190717022910-653c86b0609b h1:WiE134uexhvhHw4DjJuYsghv792UCe2xN5SHQOayf28=
|
||||
k8s.io/apiextensions-apiserver v0.0.0-20190228180357-d002e88f6236 h1:JfFtjaElBIgYKCWEtYQkcNrTpW+lMO4GJy8NP6SVQmM=
|
||||
k8s.io/apiextensions-apiserver v0.0.0-20190228180357-d002e88f6236/go.mod h1:IxkesAMoaCRoLrPJdZNZUQp9NfZnzqaVzLhb2VEQzXE=
|
||||
k8s.io/apiextensions-apiserver v0.0.0-20190409022649-727a075fdec8 h1:q1Qvjzs/iEdXF6A1a8H3AKVFDzJNcJn3nXMs6R6qFtA=
|
||||
k8s.io/apiextensions-apiserver v0.0.0-20190409022649-727a075fdec8/go.mod h1:IxkesAMoaCRoLrPJdZNZUQp9NfZnzqaVzLhb2VEQzXE=
|
||||
k8s.io/apimachinery v0.0.0-20190221213512-86fb29eff628 h1:UYfHH+KEF88OTg+GojQUwFTNxbxwmoktLwutUzR0GPg=
|
||||
k8s.io/apimachinery v0.0.0-20190221213512-86fb29eff628/go.mod h1:ccL7Eh7zubPUSh9A3USN90/OzHNSVN6zxzde07TDCL0=
|
||||
k8s.io/apimachinery v0.0.0-20190404173353-6a84e37a896d h1:Jmdtdt1ZnoGfWWIIik61Z7nKYgO3J+swQJtPYsP9wHA=
|
||||
k8s.io/apimachinery v0.0.0-20190404173353-6a84e37a896d/go.mod h1:ccL7Eh7zubPUSh9A3USN90/OzHNSVN6zxzde07TDCL0=
|
||||
k8s.io/apimachinery v0.0.0-20190703205208-4cfb76a8bf76 h1:vxMYBaJgczGAIpJAOBco2eHuFYIyDdNIebt60jxLauA=
|
||||
k8s.io/apimachinery v0.0.0-20190703205208-4cfb76a8bf76/go.mod h1:M2fZgZL9DbLfeJaPBCDqSqNsdsmLN+V29knYJnIXlMA=
|
||||
k8s.io/client-go v0.0.0-20190228174230-b40b2a5939e4 h1:aE8wOCKuoRs2aU0OP/Rz8SXiAB0FTTku3VtGhhrkSmc=
|
||||
k8s.io/client-go v0.0.0-20190228174230-b40b2a5939e4/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s=
|
||||
k8s.io/client-go v11.0.0+incompatible h1:LBbX2+lOwY9flffWlJM7f1Ct8V2SRNiMRDFeiwnJo9o=
|
||||
k8s.io/client-go v11.0.0+incompatible/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s=
|
||||
k8s.io/cli-runtime v0.0.0-20190314001948-2899ed30580f h1:gRAqn9Z3rp62UwLU3PdC7Lhmsvd3e9PXLsq7EG+bq1s=
|
||||
k8s.io/cli-runtime v0.0.0-20190314001948-2899ed30580f/go.mod h1:qWnH3/b8sp/l7EvlDh7ulDU3UWA4P4N1NFbEEP791tM=
|
||||
k8s.io/client-go v11.0.1-0.20190409021438-1a26190bd76a+incompatible h1:U5Bt+dab9K8qaUmXINrkXO135kA11/i5Kg1RUydgaMQ=
|
||||
k8s.io/client-go v11.0.1-0.20190409021438-1a26190bd76a+incompatible/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s=
|
||||
k8s.io/code-generator v0.0.0-20181117043124-c2090bec4d9b h1:KH0fUlgdFZH8UMxJ/FDCYHpczfSQKefetq5NjL6BVF0=
|
||||
k8s.io/code-generator v0.0.0-20181117043124-c2090bec4d9b/go.mod h1:MYiN+ZJZ9HkETbgVZdWw2AsuAi9PZ4V80cwfuf2axe8=
|
||||
k8s.io/code-generator v0.0.0-20190703204957-583809a49343/go.mod h1:gZ0mjdzfPz3P+cILxibAJ25xFoZ1Kf0xQrFUHy66AEs=
|
||||
k8s.io/gengo v0.0.0-20190116091435-f8a0810f38af/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
|
||||
k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
|
||||
k8s.io/gengo v0.0.0-20190327210449-e17681d19d3a h1:QoHVuRquf80YZ+/bovwxoMO3Q/A3nt3yTgS0/0nejuk=
|
||||
k8s.io/gengo v0.0.0-20190327210449-e17681d19d3a/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
|
||||
k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
|
||||
k8s.io/klog v0.2.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
|
||||
k8s.io/klog v0.3.0 h1:0VPpR+sizsiivjIfIAQH/rl8tan6jvWkS7lU+0di3lE=
|
||||
k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
|
||||
k8s.io/klog v0.3.1 h1:RVgyDHY/kFKtLqh67NvEWIgkMneNoIrdkN0CxDSQc68=
|
||||
k8s.io/klog v0.3.1/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
|
||||
k8s.io/klog v0.3.3 h1:niceAagH1tzskmaie/icWd7ci1wbG7Bf2c6YGcQv+3c=
|
||||
k8s.io/klog v0.3.3/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
|
||||
k8s.io/klog v0.4.0 h1:lCJCxf/LIowc2IGS9TPjWDyXY4nOmdGdfcwwDQCOURQ=
|
||||
k8s.io/klog v0.4.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
|
||||
k8s.io/kube-openapi v0.0.0-20180731170545-e3762e86a74c h1:3KSCztE7gPitlZmWbNwue/2U0YruD65DqX3INopDAQM=
|
||||
k8s.io/kube-openapi v0.0.0-20180731170545-e3762e86a74c/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc=
|
||||
k8s.io/kube-openapi v0.0.0-20190228160746-b3a7cee44a30 h1:TRb4wNWoBVrH9plmkp2q86FIDppkbrEXdXlxU3a3BMI=
|
||||
k8s.io/kube-openapi v0.0.0-20190228160746-b3a7cee44a30/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc=
|
||||
k8s.io/kube-openapi v0.0.0-20190603182131-db7b694dc208 h1:5sW+fEHvlJI3Ngolx30CmubFulwH28DhKjGf70Xmtco=
|
||||
k8s.io/kube-openapi v0.0.0-20190603182131-db7b694dc208/go.mod h1:nfDlWeOsu3pUf4yWGL+ERqohP4YsZcBJXWMK+gkzOA4=
|
||||
k8s.io/kube-openapi v0.0.0-20190815110238-8ff09bc626d6 h1:3zEq2jkpzwO71j00zmrJAokMnI6KQRS3dzRl2ATTkUY=
|
||||
k8s.io/kube-openapi v0.0.0-20190815110238-8ff09bc626d6/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E=
|
||||
k8s.io/utils v0.0.0-20190506122338-8fab8cb257d5 h1:VBM/0P5TWxwk+Nw6Z+lAw3DKgO76g90ETOiA6rfLV1Y=
|
||||
k8s.io/utils v0.0.0-20190506122338-8fab8cb257d5/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
|
||||
k8s.io/utils v0.0.0-20190607212802-c55fbcfc754a h1:2jUDc9gJja832Ftp+QbDV0tVhQHMISFn01els+2ZAcw=
|
||||
k8s.io/utils v0.0.0-20190607212802-c55fbcfc754a/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
|
||||
modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw=
|
||||
modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk=
|
||||
modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k=
|
||||
modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs=
|
||||
modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
sigs.k8s.io/controller-runtime v0.1.12 h1:ovDq28E64PeY1yR+6H7DthakIC09soiDCrKvfP2tPYo=
|
||||
sigs.k8s.io/controller-runtime v0.1.12/go.mod h1:HFAYoOh6XMV+jKF1UjFwrknPbowfyHEHHRdJMf2jMX8=
|
||||
k8s.io/utils v0.0.0-20190809000727-6c36bc71fc4a h1:uy5HAgt4Ha5rEMbhZA+aM1j2cq5LmR6LQ71EYC2sVH4=
|
||||
k8s.io/utils v0.0.0-20190809000727-6c36bc71fc4a/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
|
||||
sigs.k8s.io/controller-runtime v0.2.0-beta.2 h1:hOWldx1qmGI9TsU+uUsq1xTgVmUV7AZo08VAYX0dwGI=
|
||||
sigs.k8s.io/controller-runtime v0.2.0-beta.2/go.mod h1:TSH2R0nSz4WAlUUlNnOFcOR/VUhfwBLlmtq2X6AiQCA=
|
||||
sigs.k8s.io/controller-tools v0.1.11/go.mod h1:6g08p9m9G/So3sBc1AOQifHfhxH/mb6Sc4z0LMI8XMw=
|
||||
sigs.k8s.io/controller-tools v0.2.0-beta.2 h1:ucniFzEuW7PFfFDuUxacdY4Fy4q065wPguVl+BE2/t0=
|
||||
sigs.k8s.io/controller-tools v0.2.0-beta.2/go.mod h1:gC5UAnK1jbxWnDaqTi0yxKIsRsRwshzeRtTUGbM9vos=
|
||||
sigs.k8s.io/kustomize v2.0.3+incompatible h1:JUufWFNlI44MdtnjUqVnvh29rR37PQFzPbLXqhyOyX0=
|
||||
sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU=
|
||||
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
|
||||
sigs.k8s.io/testing_frameworks v0.1.1 h1:cP2l8fkA3O9vekpy5Ks8mmA0NW/F7yBdXf8brkWhVrs=
|
||||
sigs.k8s.io/testing_frameworks v0.1.1/go.mod h1:VVBKrHmJ6Ekkfz284YKhQePcdycOzNH9qL6ht1zEr/U=
|
||||
|
||||
@@ -20,23 +20,65 @@ type getChildCollectedFileContents func(string) (map[string][]byte, error)
|
||||
|
||||
func Analyze(analyzer *troubleshootv1beta1.Analyze, getFile getCollectedFileContents, findFiles getChildCollectedFileContents) (*AnalyzeResult, error) {
|
||||
if analyzer.ClusterVersion != nil {
|
||||
if analyzer.ClusterVersion.Exclude {
|
||||
return nil, nil
|
||||
}
|
||||
return analyzeClusterVersion(analyzer.ClusterVersion, getFile)
|
||||
}
|
||||
if analyzer.StorageClass != nil {
|
||||
if analyzer.StorageClass.Exclude {
|
||||
return nil, nil
|
||||
}
|
||||
return analyzeStorageClass(analyzer.StorageClass, getFile)
|
||||
}
|
||||
if analyzer.CustomResourceDefinition != nil {
|
||||
if analyzer.CustomResourceDefinition.Exclude {
|
||||
return nil, nil
|
||||
}
|
||||
return analyzeCustomResourceDefinition(analyzer.CustomResourceDefinition, getFile)
|
||||
}
|
||||
if analyzer.Ingress != nil {
|
||||
if analyzer.Ingress.Exclude {
|
||||
return nil, nil
|
||||
}
|
||||
return analyzeIngress(analyzer.Ingress, getFile)
|
||||
}
|
||||
if analyzer.Secret != nil {
|
||||
if analyzer.Secret.Exclude {
|
||||
return nil, nil
|
||||
}
|
||||
return analyzeSecret(analyzer.Secret, getFile)
|
||||
}
|
||||
if analyzer.ImagePullSecret != nil {
|
||||
if analyzer.ImagePullSecret.Exclude {
|
||||
return nil, nil
|
||||
}
|
||||
return analyzeImagePullSecret(analyzer.ImagePullSecret, findFiles)
|
||||
}
|
||||
if analyzer.DeploymentStatus != nil {
|
||||
if analyzer.DeploymentStatus.Exclude {
|
||||
return nil, nil
|
||||
}
|
||||
return analyzeDeploymentStatus(analyzer.DeploymentStatus, getFile)
|
||||
}
|
||||
if analyzer.StatefulsetStatus != nil {
|
||||
if analyzer.StatefulsetStatus.Exclude {
|
||||
return nil, nil
|
||||
}
|
||||
return analyzeStatefulsetStatus(analyzer.StatefulsetStatus, getFile)
|
||||
}
|
||||
if analyzer.ContainerRuntime != nil {
|
||||
if analyzer.ContainerRuntime.Exclude {
|
||||
return nil, nil
|
||||
}
|
||||
return analyzeContainerRuntime(analyzer.ContainerRuntime, getFile)
|
||||
}
|
||||
if analyzer.Distribution != nil {
|
||||
if analyzer.Distribution.Exclude {
|
||||
return nil, nil
|
||||
}
|
||||
return analyzeDistribution(analyzer.Distribution, getFile)
|
||||
}
|
||||
|
||||
return nil, errors.New("invalid analyzer")
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
func analyzeClusterVersion(analyzer *troubleshootv1beta1.ClusterVersion, getCollectedFileContents func(string) ([]byte, error)) (*AnalyzeResult, error) {
|
||||
clusterInfo, err := getCollectedFileContents("cluster-info/cluster_version.json")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed top get contents of cluster_version.json")
|
||||
return nil, errors.Wrap(err, "failed to get contents of cluster_version.json")
|
||||
}
|
||||
|
||||
collectorClusterVersion := collect.ClusterVersion{}
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
package analyzer
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
|
||||
)
|
||||
|
||||
func commonStatus(outcomes []*troubleshootv1beta1.Outcome, title string, readyReplicas int) (*AnalyzeResult, error) {
|
||||
result := &AnalyzeResult{
|
||||
Title: title,
|
||||
}
|
||||
|
||||
// ordering from the spec is important, the first one that matches returns
|
||||
for _, outcome := range outcomes {
|
||||
if outcome.Fail != nil {
|
||||
if outcome.Fail.When == "" {
|
||||
result.IsFail = true
|
||||
result.Message = outcome.Fail.Message
|
||||
result.URI = outcome.Fail.URI
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
match, err := compareActualToWhen(outcome.Fail.When, readyReplicas)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to parse fail range")
|
||||
}
|
||||
|
||||
if match {
|
||||
result.IsFail = true
|
||||
result.Message = outcome.Fail.Message
|
||||
result.URI = outcome.Fail.URI
|
||||
|
||||
return result, nil
|
||||
}
|
||||
} else if outcome.Warn != nil {
|
||||
if outcome.Warn.When == "" {
|
||||
result.IsWarn = true
|
||||
result.Message = outcome.Warn.Message
|
||||
result.URI = outcome.Warn.URI
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
match, err := compareActualToWhen(outcome.Warn.When, readyReplicas)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to parse warn range")
|
||||
}
|
||||
|
||||
if match {
|
||||
result.IsWarn = true
|
||||
result.Message = outcome.Warn.Message
|
||||
result.URI = outcome.Warn.URI
|
||||
|
||||
return result, nil
|
||||
}
|
||||
} else if outcome.Pass != nil {
|
||||
if outcome.Pass.When == "" {
|
||||
result.IsPass = true
|
||||
result.Message = outcome.Pass.Message
|
||||
result.URI = outcome.Pass.URI
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
match, err := compareActualToWhen(outcome.Pass.When, readyReplicas)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to parse pass range")
|
||||
}
|
||||
|
||||
if match {
|
||||
result.IsPass = true
|
||||
result.Message = outcome.Pass.Message
|
||||
result.URI = outcome.Pass.URI
|
||||
|
||||
return result, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func compareActualToWhen(when string, actual int) (bool, error) {
|
||||
parts := strings.Split(strings.TrimSpace(when), " ")
|
||||
|
||||
// we can make this a lot more flexible
|
||||
if len(parts) != 2 {
|
||||
return false, errors.New("unable to parse when range")
|
||||
}
|
||||
|
||||
value, err := strconv.Atoi(parts[1])
|
||||
if err != nil {
|
||||
return false, errors.New("unable to parse when value")
|
||||
}
|
||||
|
||||
switch parts[0] {
|
||||
case "=":
|
||||
fallthrough
|
||||
case "==":
|
||||
fallthrough
|
||||
case "===":
|
||||
return actual == value, nil
|
||||
|
||||
case "<":
|
||||
return actual < value, nil
|
||||
|
||||
case ">":
|
||||
return actual > value, nil
|
||||
|
||||
case "<=":
|
||||
return actual <= value, nil
|
||||
|
||||
case ">=":
|
||||
return actual >= value, nil
|
||||
}
|
||||
|
||||
return false, errors.Errorf("unknown comparator: %q", parts[0])
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
package analyzer
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
func analyzeContainerRuntime(analyzer *troubleshootv1beta1.ContainerRuntime, getCollectedFileContents func(string) ([]byte, error)) (*AnalyzeResult, error) {
|
||||
collected, err := getCollectedFileContents("cluster-resources/nodes.json")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get contents of nodes.json")
|
||||
}
|
||||
|
||||
var nodes []corev1.Node
|
||||
if err := json.Unmarshal(collected, &nodes); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to unmarshal node list")
|
||||
}
|
||||
|
||||
foundRuntimes := []string{}
|
||||
for _, node := range nodes {
|
||||
foundRuntimes = append(foundRuntimes, node.Status.NodeInfo.ContainerRuntimeVersion)
|
||||
}
|
||||
|
||||
result := &AnalyzeResult{
|
||||
Title: "Container Runtime",
|
||||
}
|
||||
|
||||
// ordering is important for passthrough
|
||||
for _, outcome := range analyzer.Outcomes {
|
||||
if outcome.Fail != nil {
|
||||
if outcome.Fail.When == "" {
|
||||
result.IsFail = true
|
||||
result.Message = outcome.Fail.Message
|
||||
result.URI = outcome.Fail.URI
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
for _, foundRuntime := range foundRuntimes {
|
||||
isMatch, err := compareRuntimeConditionalToActual(outcome.Fail.When, foundRuntime)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to compare runtime conditional")
|
||||
}
|
||||
|
||||
if isMatch {
|
||||
result.IsFail = true
|
||||
result.Message = outcome.Fail.Message
|
||||
result.URI = outcome.Fail.URI
|
||||
|
||||
return result, nil
|
||||
}
|
||||
}
|
||||
} else if outcome.Warn != nil {
|
||||
if outcome.Warn.When == "" {
|
||||
result.IsWarn = true
|
||||
result.Message = outcome.Warn.Message
|
||||
result.URI = outcome.Warn.URI
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
for _, foundRuntime := range foundRuntimes {
|
||||
isMatch, err := compareRuntimeConditionalToActual(outcome.Warn.When, foundRuntime)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to compare runtime conditional")
|
||||
}
|
||||
|
||||
if isMatch {
|
||||
result.IsWarn = true
|
||||
result.Message = outcome.Warn.Message
|
||||
result.URI = outcome.Warn.URI
|
||||
|
||||
return result, nil
|
||||
}
|
||||
}
|
||||
} else if outcome.Pass != nil {
|
||||
if outcome.Pass.When == "" {
|
||||
result.IsPass = true
|
||||
result.Message = outcome.Pass.Message
|
||||
result.URI = outcome.Pass.URI
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
for _, foundRuntime := range foundRuntimes {
|
||||
isMatch, err := compareRuntimeConditionalToActual(outcome.Pass.When, foundRuntime)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to compare runtime conditional")
|
||||
}
|
||||
|
||||
if isMatch {
|
||||
result.IsPass = true
|
||||
result.Message = outcome.Pass.Message
|
||||
result.URI = outcome.Pass.URI
|
||||
|
||||
return result, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func compareRuntimeConditionalToActual(conditional string, actual string) (bool, error) {
|
||||
parts := strings.Split(strings.TrimSpace(conditional), " ")
|
||||
|
||||
// we can make this a lot more flexible
|
||||
if len(parts) != 2 {
|
||||
return false, errors.New("unable to parse conditional")
|
||||
}
|
||||
|
||||
parsedRuntime, err := url.Parse(actual)
|
||||
if err != nil {
|
||||
return false, errors.New("unable to parse url")
|
||||
}
|
||||
|
||||
switch parts[0] {
|
||||
case "=":
|
||||
fallthrough
|
||||
case "==":
|
||||
fallthrough
|
||||
case "===":
|
||||
return parts[1] == parsedRuntime.Scheme, nil
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
package analyzer
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_compareRuntimeConditionalToActual(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
conditional string
|
||||
actual string
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
name: "containerd://1.2.5 = containerd",
|
||||
conditional: "= containerd",
|
||||
actual: "containerd://1.2.5",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "containerd://1.2.5 == containerd",
|
||||
conditional: "== containerd",
|
||||
actual: "containerd://1.2.5",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "containerd://1.2.5 === containerd",
|
||||
conditional: "=== containerd",
|
||||
actual: "containerd://1.2.5",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "containerd://1.2.5 != containerd",
|
||||
conditional: "!= containerd",
|
||||
actual: "containerd://1.2.5",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "containerd://1.2.5 !== containerd",
|
||||
conditional: "!== containerd",
|
||||
actual: "containerd://1.2.5",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "containerd://1.2.5 !== containerd",
|
||||
conditional: "!=== containerd",
|
||||
actual: "containerd://1.2.5",
|
||||
expected: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
req := require.New(t)
|
||||
|
||||
actual, err := compareRuntimeConditionalToActual(test.conditional, test.actual)
|
||||
req.NoError(err)
|
||||
|
||||
assert.Equal(t, test.expected, actual)
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_containerRuntime(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
analyzer troubleshootv1beta1.ContainerRuntime
|
||||
expectResult AnalyzeResult
|
||||
files map[string][]byte
|
||||
}{
|
||||
{
|
||||
name: "no containerd, when it's containerd",
|
||||
analyzer: troubleshootv1beta1.ContainerRuntime{
|
||||
Outcomes: []*troubleshootv1beta1.Outcome{
|
||||
{
|
||||
Pass: &troubleshootv1beta1.SingleOutcome{
|
||||
When: "!= containerd",
|
||||
Message: "pass",
|
||||
},
|
||||
},
|
||||
{
|
||||
Fail: &troubleshootv1beta1.SingleOutcome{
|
||||
Message: "containerd detected",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectResult: AnalyzeResult{
|
||||
IsPass: false,
|
||||
IsWarn: false,
|
||||
IsFail: true,
|
||||
Title: "Container Runtime",
|
||||
Message: "containerd detected",
|
||||
},
|
||||
files: map[string][]byte{
|
||||
"cluster-resources/nodes.json": []byte(collectedNodes),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
req := require.New(t)
|
||||
|
||||
getFiles := func(n string) ([]byte, error) {
|
||||
return test.files[n], nil
|
||||
}
|
||||
|
||||
actual, err := analyzeContainerRuntime(&test.analyzer, getFiles)
|
||||
req.NoError(err)
|
||||
|
||||
assert.Equal(t, &test.expectResult, actual)
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,574 @@
|
||||
package analyzer
|
||||
|
||||
var collectedDeployments = `[
|
||||
{
|
||||
"metadata": {
|
||||
"name": "kotsadm-api",
|
||||
"namespace": "default",
|
||||
"selfLink": "/apis/apps/v1/namespaces/default/deployments/kotsadm-api",
|
||||
"uid": "56526035-cd29-4d08-8375-291503b1a006",
|
||||
"resourceVersion": "1583068",
|
||||
"generation": 1,
|
||||
"creationTimestamp": "2019-11-07T00:34:32Z",
|
||||
"labels": {
|
||||
"app.kubernetes.io/managed-by": "skaffold-v0.41.0"
|
||||
},
|
||||
"annotations": {
|
||||
"deployment.kubernetes.io/revision": "1",
|
||||
"kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"annotations\":{},\"labels\":{\"app.kubernetes.io/managed-by\":\"skaffold-v0.41.0\",\"skaffold.dev/builder\":\"local\",\"skaffold.dev/cleanup\":\"true\",\"skaffold.dev/deployer\":\"kustomize\",\"skaffold.dev/docker-api-version\":\"1.40\",\"skaffold.dev/run-id\":\"98f0a02b-9739-4d94-ba11-3e4d273c743e\",\"skaffold.dev/tag-policy\":\"git-commit\",\"skaffold.dev/tail\":\"true\"},\"name\":\"kotsadm-api\",\"namespace\":\"default\"},\"spec\":{\"selector\":{\"matchLabels\":{\"app\":\"kotsadm-api\"}},\"template\":{\"metadata\":{\"labels\":{\"app\":\"kotsadm-api\",\"app.kubernetes.io/managed-by\":\"skaffold-v0.41.0\",\"skaffold.dev/builder\":\"local\",\"skaffold.dev/cleanup\":\"true\",\"skaffold.dev/deployer\":\"kustomize\",\"skaffold.dev/docker-api-version\":\"1.40\",\"skaffold.dev/run-id\":\"98f0a02b-9739-4d94-ba11-3e4d273c743e\",\"skaffold.dev/tag-policy\":\"git-commit\",\"skaffold.dev/tail\":\"true\"}},\"spec\":{\"affinity\":{\"podAffinity\":{\"preferredDuringSchedulingIgnoredDuringExecution\":[{\"podAffinityTerm\":{\"labelSelector\":{\"matchExpressions\":[{\"key\":\"app\",\"operator\":\"In\",\"values\":[\"ship-www\"]}]},\"topologyKey\":\"kubernetes.io/hostname\"},\"weight\":1}]},\"podAntiAffinity\":{\"preferredDuringSchedulingIgnoredDuringExecution\":[{\"podAffinityTerm\":{\"labelSelector\":{\"matchExpressions\":[{\"key\":\"app\",\"operator\":\"In\",\"values\":[\"kotsadm-api\"]}]},\"topologyKey\":\"kubernetes.io/hostname\"},\"weight\":2}]}},\"containers\":[{\"env\":[{\"name\":\"DEV_NAMESPACE\",\"value\":\"test\"},{\"name\":\"LOG_LEVEL\",\"value\":\"debug\"},{\"name\":\"SESSION_KEY\",\"valueFrom\":{\"secretKeyRef\":{\"key\":\"key\",\"name\":\"session\"}}},{\"name\":\"POSTGRES_URI\",\"valueFrom\":{\"secretKeyRef\":{\"key\":\"uri\",\"name\":\"ship-postgres\"}}},{\"name\":\"API_ENCRYPTION_KEY\",\"value\":\"IvWItkB8+ezMisPjSMBknT1PdKjBx7Xc/txZqOP8Y2Oe7+Jy\"},{\"name\":\"INIT_SERVER_URI\",\"value\":\"http://init-server:3000\"},{\"name\":\"WATCH_SERVER_URI\",\"value\":\"http://watch-server:3000\"},{\"name\":\"PINO_LOG_PRETTY\",\"value\":\"1\"},{\"name\":\"S3_BUCKET_NAME\",\"value\":\"shipbucket\"},{\"name\":\"AIRGAP_BUNDLE_S3_BUCKET\",\"value\":\"airgap\"},{\"name\":\"S3_ENDPOINT\",\"value\":\"http://kotsadm-s3.default.svc.cluster.local:4569/\"},{\"name\":\"S3_ACCESS_KEY_ID\",\"value\":\"***HIDDEN***\"},{\"name\":\"S3_SECRET_ACCESS_KEY\",\"value\":\"***HIDDEN***\"},{\"name\":\"S3_BUCKET_ENDPOINT\",\"value\":\"true\"},{\"name\":\"GITHUB_CLIENT_ID\",\"valueFrom\":{\"secretKeyRef\":{\"key\":\"client-id\",\"name\":\"github-app\"}}},{\"name\":\"GITHUB_CLIENT_SECRET\",\"valueFrom\":{\"secretKeyRef\":{\"key\":\"client-secret\",\"name\":\"github-app\"}}},{\"name\":\"GITHUB_INTEGRATION_ID\",\"valueFrom\":{\"secretKeyRef\":{\"key\":\"integration-id\",\"name\":\"github-app\"}}},{\"name\":\"GITHUB_PRIVATE_KEY_FILE\",\"value\":\"/keys/github/private-key.pem\"},{\"name\":\"SHIP_API_ENDPOINT\",\"value\":\"http://kotsadm-api.default.svc.cluster.local:3000\"},{\"name\":\"SHIP_API_ADVERTISE_ENDPOINT\",\"value\":\"http://localhost:30065\"},{\"name\":\"GRAPHQL_PREM_ENDPOINT\",\"value\":\"http://graphql-api-prem:3000/graphql\"},{\"name\":\"AUTO_CREATE_CLUSTER\",\"value\":\"1\"},{\"name\":\"AUTO_CREATE_CLUSTER_NAME\",\"value\":\"microk8s\"},{\"name\":\"AUTO_CREATE_CLUSTER_TOKEN\",\"value\":\"***HIDDEN***\"},{\"name\":\"ENABLE_SHIP\",\"value\":\"1\"},{\"name\":\"ENABLE_KOTS\",\"value\":\"1\"},{\"name\":\"ENABLE_KURL\",\"value\":\"1\"},{\"name\":\"POD_NAMESPACE\",\"valueFrom\":{\"fieldRef\":{\"fieldPath\":\"metadata.namespace\"}}}],\"image\":\"localhost:32000/kotsadm-api:v1.0.1-30-g8fa13e34-dirty@sha256:4a0ca1a2eae46472bd2d454f9e763a458ddd172689e179b17054262507bb4fc8\",\"imagePullPolicy\":\"IfNotPresent\",\"name\":\"kotsadm-api\",\"ports\":[{\"containerPort\":3000,\"name\":\"http\"},{\"containerPort\":9229,\"name\":\"debug\"}],\"readinessProbe\":{\"httpGet\":{\"path\":\"/healthz\",\"port\":3000},\"initialDelaySeconds\":2,\"periodSeconds\":2},\"volumeMounts\":[{\"mountPath\":\"/keys/github\",\"name\":\"github-app-private-key\",\"readOnly\":true}]}],\"restartPolicy\":\"Always\",\"securityContext\":{\"runAsUser\":0},\"serviceAccount\":\"kotsadm-api\",\"volumes\":[{\"name\":\"github-app-private-key\",\"secret\":{\"secretName\":\"github-app-private-key\"}}]}}}}\n"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"replicas": 1,
|
||||
"selector": {
|
||||
"matchLabels": {
|
||||
"app": "kotsadm-api"
|
||||
}
|
||||
},
|
||||
"template": {
|
||||
"metadata": {
|
||||
"creationTimestamp": null,
|
||||
"labels": {
|
||||
"app": "kotsadm-api"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"containers": [
|
||||
{
|
||||
"name": "kotsadm-api",
|
||||
"image": "localhost:32000/kotsadm-api:v1.0.1-30-g8fa13e34-dirty@sha256:4a0ca1a2eae46472bd2d454f9e763a458ddd172689e179b17054262507bb4fc8",
|
||||
"ports": [
|
||||
{
|
||||
"name": "http",
|
||||
"containerPort": 3000,
|
||||
"protocol": "TCP"
|
||||
}
|
||||
],
|
||||
"env": [
|
||||
{
|
||||
"name": "DEV_NAMESPACE",
|
||||
"value": "test"
|
||||
},
|
||||
{
|
||||
"name": "POD_NAMESPACE",
|
||||
"valueFrom": {
|
||||
"fieldRef": {
|
||||
"apiVersion": "v1",
|
||||
"fieldPath": "metadata.namespace"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"resources": {},
|
||||
"readinessProbe": {
|
||||
"httpGet": {
|
||||
"path": "/healthz",
|
||||
"port": 3000,
|
||||
"scheme": "HTTP"
|
||||
},
|
||||
"initialDelaySeconds": 2,
|
||||
"timeoutSeconds": 1,
|
||||
"periodSeconds": 2,
|
||||
"successThreshold": 1,
|
||||
"failureThreshold": 3
|
||||
},
|
||||
"terminationMessagePath": "/dev/termination-log",
|
||||
"terminationMessagePolicy": "File",
|
||||
"imagePullPolicy": "IfNotPresent"
|
||||
}
|
||||
],
|
||||
"restartPolicy": "Always",
|
||||
"terminationGracePeriodSeconds": 30,
|
||||
"dnsPolicy": "ClusterFirst",
|
||||
"serviceAccountName": "kotsadm-api",
|
||||
"serviceAccount": "kotsadm-api",
|
||||
"schedulerName": "default-scheduler"
|
||||
}
|
||||
},
|
||||
"strategy": {
|
||||
"type": "RollingUpdate",
|
||||
"rollingUpdate": {
|
||||
"maxUnavailable": "25%",
|
||||
"maxSurge": "25%"
|
||||
}
|
||||
},
|
||||
"revisionHistoryLimit": 10,
|
||||
"progressDeadlineSeconds": 600
|
||||
},
|
||||
"status": {
|
||||
"observedGeneration": 1,
|
||||
"replicas": 1,
|
||||
"updatedReplicas": 1,
|
||||
"readyReplicas": 1,
|
||||
"availableReplicas": 1,
|
||||
"conditions": [
|
||||
{
|
||||
"type": "Available",
|
||||
"status": "True",
|
||||
"lastUpdateTime": "2019-11-07T00:35:23Z",
|
||||
"lastTransitionTime": "2019-11-07T00:35:23Z",
|
||||
"reason": "MinimumReplicasAvailable",
|
||||
"message": "Deployment has minimum availability."
|
||||
},
|
||||
{
|
||||
"type": "Progressing",
|
||||
"status": "True",
|
||||
"lastUpdateTime": "2019-11-07T00:35:23Z",
|
||||
"lastTransitionTime": "2019-11-07T00:34:32Z",
|
||||
"reason": "NewReplicaSetAvailable",
|
||||
"message": "ReplicaSet \"kotsadm-api-6f4b994bd5\" has successfully progressed."
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"metadata": {
|
||||
"name": "kotsadm-operator",
|
||||
"namespace": "default",
|
||||
"selfLink": "/apis/apps/v1/namespaces/default/deployments/kotsadm-operator",
|
||||
"uid": "cfae9877-eef4-44c9-acac-0bf0d1aa547e",
|
||||
"resourceVersion": "1583379",
|
||||
"generation": 2,
|
||||
"creationTimestamp": "2019-11-07T00:34:32Z",
|
||||
"labels": {
|
||||
"app.kubernetes.io/managed-by": "skaffold-v0.41.0"
|
||||
},
|
||||
"annotations": {
|
||||
"deployment.kubernetes.io/revision": "2",
|
||||
"kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"annotations\":{},\"labels\":{\"app.kubernetes.io/managed-by\":\"skaffold-v0.41.0\",\"skaffold.dev/builder\":\"local\",\"skaffold.dev/cleanup\":\"true\",\"skaffold.dev/deployer\":\"kustomize\",\"skaffold.dev/docker-api-version\":\"1.40\",\"skaffold.dev/run-id\":\"98f0a02b-9739-4d94-ba11-3e4d273c743e\",\"skaffold.dev/tag-policy\":\"git-commit\",\"skaffold.dev/tail\":\"true\"},\"name\":\"kotsadm-operator\",\"namespace\":\"default\"},\"spec\":{\"selector\":{\"matchLabels\":{\"app\":\"kotsadm-operator\"}},\"template\":{\"metadata\":{\"labels\":{\"app\":\"kotsadm-operator\",\"app.kubernetes.io/managed-by\":\"skaffold-v0.41.0\",\"skaffold.dev/builder\":\"local\",\"skaffold.dev/cleanup\":\"true\",\"skaffold.dev/deployer\":\"kustomize\",\"skaffold.dev/docker-api-version\":\"1.40\",\"skaffold.dev/run-id\":\"98f0a02b-9739-4d94-ba11-3e4d273c743e\",\"skaffold.dev/tag-policy\":\"git-commit\",\"skaffold.dev/tail\":\"true\"}},\"spec\":{\"containers\":[{\"env\":[{\"name\":\"KOTSADM_API_ENDPOINT\",\"value\":\"http://kotsadm-api:3000\"},{\"name\":\"KOTSADM_TOKEN\",\"value\":\"***HIDDEN***\"},{\"name\":\"KOTSADM_TARGET_NAMESPACE\",\"value\":\"test\"}],\"image\":\"localhost:32000/kotsadm-operator:v1.0.1-30-g8fa13e34-dirty@sha256:177c15b6399717048e9355bc8fd8b8ed213be90615c7c6ee7b7fdcee50aca6c5\",\"imagePullPolicy\":\"Always\",\"name\":\"kotsadm-operator\",\"resources\":{\"limits\":{\"cpu\":\"200m\",\"memory\":\"1000Mi\"},\"requests\":{\"cpu\":\"100m\",\"memory\":\"500Mi\"}}}],\"restartPolicy\":\"Always\"}}}}\n"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"replicas": 1,
|
||||
"selector": {
|
||||
"matchLabels": {
|
||||
"app": "kotsadm-operator"
|
||||
}
|
||||
},
|
||||
"template": {
|
||||
"metadata": {
|
||||
"creationTimestamp": null,
|
||||
"labels": {
|
||||
"app": "kotsadm-operator"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"containers": [
|
||||
{
|
||||
"name": "kotsadm-operator",
|
||||
"image": "localhost:32000/kotsadm-operator:v1.0.1-30-g8fa13e34-dirty@sha256:177c15b6399717048e9355bc8fd8b8ed213be90615c7c6ee7b7fdcee50aca6c5",
|
||||
"env": [
|
||||
{
|
||||
"name": "KOTSADM_API_ENDPOINT",
|
||||
"value": "http://kotsadm-api:3000"
|
||||
},
|
||||
{
|
||||
"name": "KOTSADM_TOKEN",
|
||||
"value": "***HIDDEN***"
|
||||
},
|
||||
{
|
||||
"name": "KOTSADM_TARGET_NAMESPACE",
|
||||
"value": "test"
|
||||
}
|
||||
],
|
||||
"resources": {
|
||||
"limits": {
|
||||
"cpu": "200m",
|
||||
"memory": "1000Mi"
|
||||
},
|
||||
"requests": {
|
||||
"cpu": "100m",
|
||||
"memory": "500Mi"
|
||||
}
|
||||
},
|
||||
"terminationMessagePath": "/dev/termination-log",
|
||||
"terminationMessagePolicy": "File",
|
||||
"imagePullPolicy": "Always"
|
||||
}
|
||||
],
|
||||
"restartPolicy": "Always",
|
||||
"terminationGracePeriodSeconds": 30,
|
||||
"dnsPolicy": "ClusterFirst",
|
||||
"securityContext": {},
|
||||
"schedulerName": "default-scheduler"
|
||||
}
|
||||
},
|
||||
"strategy": {
|
||||
"type": "RollingUpdate",
|
||||
"rollingUpdate": {
|
||||
"maxUnavailable": "25%",
|
||||
"maxSurge": "25%"
|
||||
}
|
||||
},
|
||||
"revisionHistoryLimit": 10,
|
||||
"progressDeadlineSeconds": 600
|
||||
},
|
||||
"status": {
|
||||
"observedGeneration": 2,
|
||||
"replicas": 1,
|
||||
"updatedReplicas": 1,
|
||||
"readyReplicas": 1,
|
||||
"availableReplicas": 1,
|
||||
"conditions": [
|
||||
{
|
||||
"type": "Available",
|
||||
"status": "True",
|
||||
"lastUpdateTime": "2019-11-07T00:34:37Z",
|
||||
"lastTransitionTime": "2019-11-07T00:34:37Z",
|
||||
"reason": "MinimumReplicasAvailable",
|
||||
"message": "Deployment has minimum availability."
|
||||
},
|
||||
{
|
||||
"type": "Progressing",
|
||||
"status": "True",
|
||||
"lastUpdateTime": "2019-11-07T00:36:34Z",
|
||||
"lastTransitionTime": "2019-11-07T00:34:32Z",
|
||||
"reason": "NewReplicaSetAvailable",
|
||||
"message": "ReplicaSet \"kotsadm-operator-5b5c977699\" has successfully progressed."
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"metadata": {
|
||||
"name": "kotsadm-postgres-watch",
|
||||
"namespace": "default",
|
||||
"selfLink": "/apis/apps/v1/namespaces/default/deployments/kotsadm-postgres-watch",
|
||||
"uid": "ec195b07-4fd2-4bbe-8b01-4cbdfbe0e79d",
|
||||
"resourceVersion": "1582762",
|
||||
"generation": 1,
|
||||
"creationTimestamp": "2019-11-07T00:34:39Z",
|
||||
"annotations": {
|
||||
"deployment.kubernetes.io/revision": "1"
|
||||
},
|
||||
"ownerReferences": [
|
||||
{
|
||||
"apiVersion": "databases.schemahero.io/v1alpha2",
|
||||
"kind": "Database",
|
||||
"name": "kotsadm-postgres",
|
||||
"uid": "e6d51ce6-c4c1-428f-9e8e-56f1a3a43588",
|
||||
"controller": true,
|
||||
"blockOwnerDeletion": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"spec": {
|
||||
"replicas": 1,
|
||||
"selector": {
|
||||
"matchLabels": {
|
||||
"deployment": "kotsadm-postgreswatch"
|
||||
}
|
||||
},
|
||||
"template": {
|
||||
"metadata": {
|
||||
"creationTimestamp": null,
|
||||
"labels": {
|
||||
"deployment": "kotsadm-postgreswatch"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"containers": [
|
||||
{
|
||||
"name": "schemahero",
|
||||
"image": "schemahero/schemahero:alpha",
|
||||
"args": [
|
||||
"watch",
|
||||
"--driver",
|
||||
"postgres",
|
||||
"--uri",
|
||||
"postgres://shipcloud:password@postgres.default.svc.cluster.local:5432/shipcloud?sslmode=disable",
|
||||
"--namespace",
|
||||
"default",
|
||||
"--instance",
|
||||
"kotsadm-postgres"
|
||||
],
|
||||
"resources": {},
|
||||
"terminationMessagePath": "/dev/termination-log",
|
||||
"terminationMessagePolicy": "File",
|
||||
"imagePullPolicy": "Always"
|
||||
}
|
||||
],
|
||||
"restartPolicy": "Always",
|
||||
"terminationGracePeriodSeconds": 30,
|
||||
"dnsPolicy": "ClusterFirst",
|
||||
"serviceAccountName": "kotsadm-postgres",
|
||||
"serviceAccount": "kotsadm-postgres",
|
||||
"securityContext": {},
|
||||
"schedulerName": "default-scheduler"
|
||||
}
|
||||
},
|
||||
"strategy": {
|
||||
"type": "RollingUpdate",
|
||||
"rollingUpdate": {
|
||||
"maxUnavailable": "25%",
|
||||
"maxSurge": "25%"
|
||||
}
|
||||
},
|
||||
"revisionHistoryLimit": 10,
|
||||
"progressDeadlineSeconds": 600
|
||||
},
|
||||
"status": {
|
||||
"observedGeneration": 1,
|
||||
"replicas": 1,
|
||||
"updatedReplicas": 1,
|
||||
"readyReplicas": 1,
|
||||
"availableReplicas": 1,
|
||||
"conditions": [
|
||||
{
|
||||
"type": "Available",
|
||||
"status": "True",
|
||||
"lastUpdateTime": "2019-11-07T00:35:00Z",
|
||||
"lastTransitionTime": "2019-11-07T00:35:00Z",
|
||||
"reason": "MinimumReplicasAvailable",
|
||||
"message": "Deployment has minimum availability."
|
||||
},
|
||||
{
|
||||
"type": "Progressing",
|
||||
"status": "True",
|
||||
"lastUpdateTime": "2019-11-07T00:35:00Z",
|
||||
"lastTransitionTime": "2019-11-07T00:34:39Z",
|
||||
"reason": "NewReplicaSetAvailable",
|
||||
"message": "ReplicaSet \"kotsadm-postgres-watch-5cf76f4c45\" has successfully progressed."
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"metadata": {
|
||||
"name": "kotsadm-web",
|
||||
"namespace": "default",
|
||||
"selfLink": "/apis/apps/v1/namespaces/default/deployments/kotsadm-web",
|
||||
"uid": "4e657f8a-5edb-498b-9402-1f93f51f5dda",
|
||||
"resourceVersion": "1582354",
|
||||
"generation": 1,
|
||||
"creationTimestamp": "2019-11-07T00:34:32Z",
|
||||
"labels": {
|
||||
"app.kubernetes.io/managed-by": "skaffold-v0.41.0",
|
||||
"skaffold.dev/builder": "local",
|
||||
"skaffold.dev/cleanup": "true",
|
||||
"skaffold.dev/deployer": "kustomize",
|
||||
"skaffold.dev/docker-api-version": "1.40",
|
||||
"skaffold.dev/run-id": "98f0a02b-9739-4d94-ba11-3e4d273c743e",
|
||||
"skaffold.dev/tag-policy": "git-commit",
|
||||
"skaffold.dev/tail": "true"
|
||||
},
|
||||
"annotations": {
|
||||
"deployment.kubernetes.io/revision": "1",
|
||||
"kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"annotations\":{},\"labels\":{\"app.kubernetes.io/managed-by\":\"skaffold-v0.41.0\",\"skaffold.dev/builder\":\"local\",\"skaffold.dev/cleanup\":\"true\",\"skaffold.dev/deployer\":\"kustomize\",\"skaffold.dev/docker-api-version\":\"1.40\",\"skaffold.dev/run-id\":\"98f0a02b-9739-4d94-ba11-3e4d273c743e\",\"skaffold.dev/tag-policy\":\"git-commit\",\"skaffold.dev/tail\":\"true\"},\"name\":\"kotsadm-web\",\"namespace\":\"default\"},\"spec\":{\"selector\":{\"matchLabels\":{\"app\":\"kotsadm-web\"}},\"template\":{\"metadata\":{\"labels\":{\"app\":\"kotsadm-web\",\"app.kubernetes.io/managed-by\":\"skaffold-v0.41.0\",\"skaffold.dev/builder\":\"local\",\"skaffold.dev/cleanup\":\"true\",\"skaffold.dev/deployer\":\"kustomize\",\"skaffold.dev/docker-api-version\":\"1.40\",\"skaffold.dev/run-id\":\"98f0a02b-9739-4d94-ba11-3e4d273c743e\",\"skaffold.dev/tag-policy\":\"git-commit\",\"skaffold.dev/tail\":\"true\"}},\"spec\":{\"containers\":[{\"env\":[{\"name\":\"GITHUB_CLIENT_ID\",\"valueFrom\":{\"secretKeyRef\":{\"key\":\"client-id\",\"name\":\"github-app\"}}},{\"name\":\"GITHUB_INSTALL_URL\",\"valueFrom\":{\"secretKeyRef\":{\"key\":\"install-url\",\"name\":\"github-app\"}}},{\"name\":\"SHIP_CLUSTER_API_SERVER\",\"value\":\"http://localhost:30065\"},{\"name\":\"SHIP_CLUSTER_WEB_URI\",\"value\":\"http://localhost:8000\"}],\"image\":\"localhost:32000/kotsadm-web:v1.0.1-30-g8fa13e34@sha256:5b5b5b640b6e09d8b3185d4ae15ac4dc558d4e2ea034ac3e567d8cce04eadb9c\",\"imagePullPolicy\":\"IfNotPresent\",\"name\":\"kotsadm-web\",\"ports\":[{\"containerPort\":8000,\"name\":\"http\"}]}]}}}}\n"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"replicas": 1,
|
||||
"selector": {
|
||||
"matchLabels": {
|
||||
"app": "kotsadm-web"
|
||||
}
|
||||
},
|
||||
"template": {
|
||||
"metadata": {
|
||||
"creationTimestamp": null,
|
||||
"labels": {
|
||||
"app": "kotsadm-web",
|
||||
"app.kubernetes.io/managed-by": "skaffold-v0.41.0",
|
||||
"skaffold.dev/builder": "local",
|
||||
"skaffold.dev/cleanup": "true",
|
||||
"skaffold.dev/deployer": "kustomize",
|
||||
"skaffold.dev/docker-api-version": "1.40",
|
||||
"skaffold.dev/run-id": "98f0a02b-9739-4d94-ba11-3e4d273c743e",
|
||||
"skaffold.dev/tag-policy": "git-commit",
|
||||
"skaffold.dev/tail": "true"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"containers": [
|
||||
{
|
||||
"name": "kotsadm-web",
|
||||
"image": "localhost:32000/kotsadm-web:v1.0.1-30-g8fa13e34@sha256:5b5b5b640b6e09d8b3185d4ae15ac4dc558d4e2ea034ac3e567d8cce04eadb9c",
|
||||
"ports": [
|
||||
{
|
||||
"name": "http",
|
||||
"containerPort": 8000,
|
||||
"protocol": "TCP"
|
||||
}
|
||||
],
|
||||
"env": [
|
||||
{
|
||||
"name": "GITHUB_CLIENT_ID",
|
||||
"valueFrom": {
|
||||
"secretKeyRef": {
|
||||
"name": "github-app",
|
||||
"key": "client-id"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"resources": {},
|
||||
"terminationMessagePath": "/dev/termination-log",
|
||||
"terminationMessagePolicy": "File",
|
||||
"imagePullPolicy": "IfNotPresent"
|
||||
}
|
||||
],
|
||||
"restartPolicy": "Always",
|
||||
"terminationGracePeriodSeconds": 30,
|
||||
"dnsPolicy": "ClusterFirst",
|
||||
"securityContext": {},
|
||||
"schedulerName": "default-scheduler"
|
||||
}
|
||||
},
|
||||
"strategy": {
|
||||
"type": "RollingUpdate",
|
||||
"rollingUpdate": {
|
||||
"maxUnavailable": "25%",
|
||||
"maxSurge": "25%"
|
||||
}
|
||||
},
|
||||
"revisionHistoryLimit": 10,
|
||||
"progressDeadlineSeconds": 600
|
||||
},
|
||||
"status": {
|
||||
"observedGeneration": 1,
|
||||
"replicas": 1,
|
||||
"updatedReplicas": 1,
|
||||
"readyReplicas": 1,
|
||||
"availableReplicas": 1,
|
||||
"conditions": [
|
||||
{
|
||||
"type": "Available",
|
||||
"status": "True",
|
||||
"lastUpdateTime": "2019-11-07T00:34:39Z",
|
||||
"lastTransitionTime": "2019-11-07T00:34:39Z",
|
||||
"reason": "MinimumReplicasAvailable",
|
||||
"message": "Deployment has minimum availability."
|
||||
},
|
||||
{
|
||||
"type": "Progressing",
|
||||
"status": "True",
|
||||
"lastUpdateTime": "2019-11-07T00:34:39Z",
|
||||
"lastTransitionTime": "2019-11-07T00:34:32Z",
|
||||
"reason": "NewReplicaSetAvailable",
|
||||
"message": "ReplicaSet \"kotsadm-web-79bfb95c48\" has successfully progressed."
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]`
|
||||
|
||||
var collectedNodes = `[
|
||||
{
|
||||
"apiVersion": "v1",
|
||||
"kind": "Node",
|
||||
"metadata": {
|
||||
"annotations": {
|
||||
"node.alpha.kubernetes.io/ttl": "0",
|
||||
"volumes.kubernetes.io/controller-managed-attach-detach": "true"
|
||||
},
|
||||
"creationTimestamp": "2019-10-23T18:16:43Z",
|
||||
"labels": {
|
||||
"beta.kubernetes.io/arch": "amd64",
|
||||
"beta.kubernetes.io/os": "linux",
|
||||
"kubernetes.io/arch": "amd64",
|
||||
"kubernetes.io/hostname": "repldev-marc",
|
||||
"kubernetes.io/os": "linux",
|
||||
"microk8s.io/cluster": "true"
|
||||
},
|
||||
"name": "repldev-marc",
|
||||
"resourceVersion": "1769699",
|
||||
"selfLink": "/api/v1/nodes/repldev-marc",
|
||||
"uid": "cd30c57f-b445-437f-9473-f13343124030"
|
||||
},
|
||||
"spec": {},
|
||||
"status": {
|
||||
"addresses": [
|
||||
{
|
||||
"address": "10.168.0.26",
|
||||
"type": "InternalIP"
|
||||
},
|
||||
{
|
||||
"address": "repldev-marc",
|
||||
"type": "Hostname"
|
||||
}
|
||||
],
|
||||
"allocatable": {
|
||||
"cpu": "8",
|
||||
"ephemeral-storage": "1015018628Ki",
|
||||
"hugepages-1Gi": "0",
|
||||
"hugepages-2Mi": "0",
|
||||
"memory": "30770604Ki",
|
||||
"pods": "110"
|
||||
},
|
||||
"capacity": {
|
||||
"cpu": "8",
|
||||
"ephemeral-storage": "1016067204Ki",
|
||||
"hugepages-1Gi": "0",
|
||||
"hugepages-2Mi": "0",
|
||||
"memory": "30873004Ki",
|
||||
"pods": "110"
|
||||
},
|
||||
"conditions": [
|
||||
{
|
||||
"lastHeartbeatTime": "2019-11-08T17:03:39Z",
|
||||
"lastTransitionTime": "2019-10-31T21:28:36Z",
|
||||
"message": "kubelet has sufficient memory available",
|
||||
"reason": "KubeletHasSufficientMemory",
|
||||
"status": "False",
|
||||
"type": "MemoryPressure"
|
||||
},
|
||||
{
|
||||
"lastHeartbeatTime": "2019-11-08T17:03:39Z",
|
||||
"lastTransitionTime": "2019-10-31T21:28:36Z",
|
||||
"message": "kubelet has no disk pressure",
|
||||
"reason": "KubeletHasNoDiskPressure",
|
||||
"status": "False",
|
||||
"type": "DiskPressure"
|
||||
},
|
||||
{
|
||||
"lastHeartbeatTime": "2019-11-08T17:03:39Z",
|
||||
"lastTransitionTime": "2019-10-31T21:28:36Z",
|
||||
"message": "kubelet has sufficient PID available",
|
||||
"reason": "KubeletHasSufficientPID",
|
||||
"status": "False",
|
||||
"type": "PIDPressure"
|
||||
},
|
||||
{
|
||||
"lastHeartbeatTime": "2019-11-08T17:03:39Z",
|
||||
"lastTransitionTime": "2019-10-31T21:28:36Z",
|
||||
"message": "kubelet is posting ready status. AppArmor enabled",
|
||||
"reason": "KubeletReady",
|
||||
"status": "True",
|
||||
"type": "Ready"
|
||||
}
|
||||
],
|
||||
"daemonEndpoints": {
|
||||
"kubeletEndpoint": {
|
||||
"Port": 10250
|
||||
}
|
||||
},
|
||||
"images": [
|
||||
{
|
||||
"names": [
|
||||
"localhost:32000/kotsadm-api@sha256:d4821b65869454dfac53ad01f295740df6fcd52711f0dcf6aa9d7e515f7ebe3c"
|
||||
],
|
||||
"sizeBytes": 755312372
|
||||
},
|
||||
{
|
||||
"names": [
|
||||
"localhost:32000/kotsadm-api@sha256:fc3c971facc9dbd1b07e19c1ebb33c6361dd219af8efed0616afd1278f81fa4e"
|
||||
],
|
||||
"sizeBytes": 755312032
|
||||
}
|
||||
],
|
||||
"nodeInfo": {
|
||||
"architecture": "amd64",
|
||||
"bootID": "3401cdf2-129c-473d-a50c-723afd7378d3",
|
||||
"containerRuntimeVersion": "containerd://1.2.5",
|
||||
"kernelVersion": "5.0.0-1021-gcp",
|
||||
"kubeProxyVersion": "v1.16.2",
|
||||
"kubeletVersion": "v1.16.2",
|
||||
"machineID": "97f4a34d2aa9e26785177a6b64fb9108",
|
||||
"operatingSystem": "linux",
|
||||
"osImage": "Ubuntu 18.04.2 LTS",
|
||||
"systemUUID": "9dc594e5-ac7b-c649-e61f-cad715a28f79"
|
||||
}
|
||||
}
|
||||
}
|
||||
]`
|
||||
@@ -0,0 +1,41 @@
|
||||
package analyzer
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"path"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
)
|
||||
|
||||
func analyzeDeploymentStatus(analyzer *troubleshootv1beta1.DeploymentStatus, getCollectedFileContents func(string) ([]byte, error)) (*AnalyzeResult, error) {
|
||||
collected, err := getCollectedFileContents(path.Join("cluster-resources", "deployments", fmt.Sprintf("%s.json", analyzer.Namespace)))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to read collected deployments from namespace")
|
||||
}
|
||||
|
||||
var deployments []appsv1.Deployment
|
||||
if err := json.Unmarshal(collected, &deployments); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to unmarshal deployment list")
|
||||
}
|
||||
|
||||
var status *appsv1.DeploymentStatus
|
||||
for _, deployment := range deployments {
|
||||
if deployment.Name == analyzer.Name {
|
||||
status = &deployment.Status
|
||||
}
|
||||
}
|
||||
|
||||
if status == nil {
|
||||
// there's not an error, but maybe the requested deployment is not even deployed
|
||||
return &AnalyzeResult{
|
||||
Title: fmt.Sprintf("%s Deployment Status", analyzer.Name),
|
||||
IsFail: true,
|
||||
Message: fmt.Sprintf("The deployment %q was not found", analyzer.Name),
|
||||
}, nil
|
||||
}
|
||||
|
||||
return commonStatus(analyzer.Outcomes, fmt.Sprintf("%s Status", analyzer.Name), int(status.ReadyReplicas))
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
package analyzer
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_deploymentStatus(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
analyzer troubleshootv1beta1.DeploymentStatus
|
||||
expectResult AnalyzeResult
|
||||
files map[string][]byte
|
||||
}{
|
||||
{
|
||||
name: "1/1, pass when = 1",
|
||||
analyzer: troubleshootv1beta1.DeploymentStatus{
|
||||
Outcomes: []*troubleshootv1beta1.Outcome{
|
||||
{
|
||||
Pass: &troubleshootv1beta1.SingleOutcome{
|
||||
When: "= 1",
|
||||
Message: "pass",
|
||||
},
|
||||
},
|
||||
{
|
||||
Fail: &troubleshootv1beta1.SingleOutcome{
|
||||
Message: "fail",
|
||||
},
|
||||
},
|
||||
},
|
||||
Namespace: "default",
|
||||
Name: "kotsadm-api",
|
||||
},
|
||||
expectResult: AnalyzeResult{
|
||||
IsPass: true,
|
||||
IsWarn: false,
|
||||
IsFail: false,
|
||||
Title: "kotsadm-api Status",
|
||||
Message: "pass",
|
||||
},
|
||||
files: map[string][]byte{
|
||||
"cluster-resources/deployments/default.json": []byte(collectedDeployments),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "1/1, pass when = 2",
|
||||
analyzer: troubleshootv1beta1.DeploymentStatus{
|
||||
Outcomes: []*troubleshootv1beta1.Outcome{
|
||||
{
|
||||
Pass: &troubleshootv1beta1.SingleOutcome{
|
||||
When: "= 2",
|
||||
Message: "pass",
|
||||
},
|
||||
},
|
||||
{
|
||||
Fail: &troubleshootv1beta1.SingleOutcome{
|
||||
Message: "fail",
|
||||
},
|
||||
},
|
||||
},
|
||||
Namespace: "default",
|
||||
Name: "kotsadm-api",
|
||||
},
|
||||
expectResult: AnalyzeResult{
|
||||
IsPass: false,
|
||||
IsWarn: false,
|
||||
IsFail: true,
|
||||
Title: "kotsadm-api Status",
|
||||
Message: "fail",
|
||||
},
|
||||
files: map[string][]byte{
|
||||
"cluster-resources/deployments/default.json": []byte(collectedDeployments),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "1/1, pass when >= 2, warn when = 1, fail when 0",
|
||||
analyzer: troubleshootv1beta1.DeploymentStatus{
|
||||
Outcomes: []*troubleshootv1beta1.Outcome{
|
||||
{
|
||||
Pass: &troubleshootv1beta1.SingleOutcome{
|
||||
When: ">= 2",
|
||||
Message: "pass",
|
||||
},
|
||||
},
|
||||
{
|
||||
Warn: &troubleshootv1beta1.SingleOutcome{
|
||||
When: "= 1",
|
||||
Message: "warn",
|
||||
},
|
||||
},
|
||||
{
|
||||
Fail: &troubleshootv1beta1.SingleOutcome{
|
||||
Message: "fail",
|
||||
},
|
||||
},
|
||||
},
|
||||
Namespace: "default",
|
||||
Name: "kotsadm-api",
|
||||
},
|
||||
expectResult: AnalyzeResult{
|
||||
IsPass: false,
|
||||
IsWarn: true,
|
||||
IsFail: false,
|
||||
Title: "kotsadm-api Status",
|
||||
Message: "warn",
|
||||
},
|
||||
files: map[string][]byte{
|
||||
"cluster-resources/deployments/default.json": []byte(collectedDeployments),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
req := require.New(t)
|
||||
|
||||
getFiles := func(n string) ([]byte, error) {
|
||||
return test.files[n], nil
|
||||
}
|
||||
|
||||
actual, err := analyzeDeploymentStatus(&test.analyzer, getFiles)
|
||||
req.NoError(err)
|
||||
|
||||
assert.Equal(t, &test.expectResult, actual)
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,202 @@
|
||||
package analyzer
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
type providers struct {
|
||||
microk8s bool
|
||||
dockerDesktop bool
|
||||
eks bool
|
||||
gke bool
|
||||
digitalOcean bool
|
||||
}
|
||||
|
||||
type Provider int
|
||||
|
||||
const (
|
||||
unknown Provider = iota
|
||||
microk8s Provider = iota
|
||||
dockerDesktop Provider = iota
|
||||
eks Provider = iota
|
||||
gke Provider = iota
|
||||
digitalOcean Provider = iota
|
||||
)
|
||||
|
||||
func analyzeDistribution(analyzer *troubleshootv1beta1.Distribution, getCollectedFileContents func(string) ([]byte, error)) (*AnalyzeResult, error) {
|
||||
collected, err := getCollectedFileContents("cluster-resources/nodes.json")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get contents of nodes.json")
|
||||
}
|
||||
|
||||
var nodes []corev1.Node
|
||||
if err := json.Unmarshal(collected, &nodes); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to unmarshal node list")
|
||||
}
|
||||
|
||||
foundProviders := providers{}
|
||||
|
||||
for _, node := range nodes {
|
||||
for k, v := range node.ObjectMeta.Labels {
|
||||
if k == "microk8s.io/cluster" && v == "true" {
|
||||
foundProviders.microk8s = true
|
||||
}
|
||||
}
|
||||
|
||||
if node.Status.NodeInfo.OSImage == "Docker Desktop" {
|
||||
foundProviders.dockerDesktop = true
|
||||
}
|
||||
|
||||
if strings.HasPrefix(node.Spec.ProviderID, "digitalocean:") {
|
||||
foundProviders.digitalOcean = true
|
||||
}
|
||||
if strings.HasPrefix(node.Spec.ProviderID, "aws:") {
|
||||
foundProviders.eks = true
|
||||
}
|
||||
if strings.HasPrefix(node.Spec.ProviderID, "gce:") {
|
||||
foundProviders.gke = true
|
||||
}
|
||||
}
|
||||
|
||||
result := &AnalyzeResult{
|
||||
Title: "Kubernetes Distribution",
|
||||
}
|
||||
|
||||
// ordering is important for passthrough
|
||||
for _, outcome := range analyzer.Outcomes {
|
||||
if outcome.Fail != nil {
|
||||
if outcome.Fail.When == "" {
|
||||
result.IsFail = true
|
||||
result.Message = outcome.Fail.Message
|
||||
result.URI = outcome.Fail.URI
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
isMatch, err := compareDistributionConditionalToActual(outcome.Fail.When, foundProviders)
|
||||
if err != nil {
|
||||
return result, errors.Wrap(err, "failed to compare distribution conditional")
|
||||
}
|
||||
|
||||
if isMatch {
|
||||
result.IsFail = true
|
||||
result.Message = outcome.Fail.Message
|
||||
result.URI = outcome.Fail.URI
|
||||
|
||||
return result, nil
|
||||
}
|
||||
} else if outcome.Warn != nil {
|
||||
if outcome.Warn.When == "" {
|
||||
result.IsWarn = true
|
||||
result.Message = outcome.Warn.Message
|
||||
result.URI = outcome.Warn.URI
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
isMatch, err := compareDistributionConditionalToActual(outcome.Warn.When, foundProviders)
|
||||
if err != nil {
|
||||
return result, errors.Wrap(err, "failed to compare distribution conditional")
|
||||
}
|
||||
|
||||
if isMatch {
|
||||
result.IsWarn = true
|
||||
result.Message = outcome.Warn.Message
|
||||
result.URI = outcome.Warn.URI
|
||||
|
||||
return result, nil
|
||||
}
|
||||
} else if outcome.Pass != nil {
|
||||
if outcome.Pass.When == "" {
|
||||
result.IsPass = true
|
||||
result.Message = outcome.Pass.Message
|
||||
result.URI = outcome.Pass.URI
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
isMatch, err := compareDistributionConditionalToActual(outcome.Pass.When, foundProviders)
|
||||
if err != nil {
|
||||
return result, errors.Wrap(err, "failed to compare distribution conditional")
|
||||
}
|
||||
|
||||
if isMatch {
|
||||
result.IsPass = true
|
||||
result.Message = outcome.Pass.Message
|
||||
result.URI = outcome.Pass.URI
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func compareDistributionConditionalToActual(conditional string, actual providers) (bool, error) {
|
||||
parts := strings.Split(strings.TrimSpace(conditional), " ")
|
||||
|
||||
// we can make this a lot more flexible
|
||||
if len(parts) == 1 {
|
||||
parts = []string{
|
||||
"=",
|
||||
parts[0],
|
||||
}
|
||||
}
|
||||
|
||||
if len(parts) != 2 {
|
||||
return false, errors.New("unable to parse conditional")
|
||||
}
|
||||
|
||||
normalizedName := mustNormalizeDistributionName(parts[1])
|
||||
|
||||
if normalizedName == unknown {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
isMatch := false
|
||||
switch normalizedName {
|
||||
case microk8s:
|
||||
isMatch = actual.microk8s
|
||||
case dockerDesktop:
|
||||
isMatch = actual.dockerDesktop
|
||||
case eks:
|
||||
isMatch = actual.eks
|
||||
case gke:
|
||||
isMatch = actual.gke
|
||||
case digitalOcean:
|
||||
isMatch = actual.digitalOcean
|
||||
}
|
||||
|
||||
switch parts[0] {
|
||||
case "=", "==", "===":
|
||||
return isMatch, nil
|
||||
case "!=", "!==":
|
||||
return !isMatch, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func mustNormalizeDistributionName(raw string) Provider {
|
||||
switch strings.ReplaceAll(strings.TrimSpace(strings.ToLower(raw)), "-", "") {
|
||||
case "microk8s":
|
||||
return microk8s
|
||||
case "dockerdesktop", "docker desktop", "docker-desktop":
|
||||
return dockerDesktop
|
||||
case "eks":
|
||||
return eks
|
||||
case "gke":
|
||||
return gke
|
||||
case "digitalocean":
|
||||
return digitalOcean
|
||||
}
|
||||
|
||||
return unknown
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package analyzer
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_compareDistributionConditionalToActual(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
conditional string
|
||||
input providers
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
name: "== microk8s when microk8s is found",
|
||||
conditional: "== microk8s",
|
||||
input: providers{
|
||||
microk8s: true,
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "!= microk8s when microk8s is found",
|
||||
conditional: "!= microk8s",
|
||||
input: providers{
|
||||
microk8s: true,
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "!== eks when gke is found",
|
||||
conditional: "!== eks",
|
||||
input: providers{
|
||||
gke: true,
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
req := require.New(t)
|
||||
|
||||
actual, err := compareDistributionConditionalToActual(test.conditional, test.input)
|
||||
req.NoError(err)
|
||||
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func Test_mustNormalizeDistributionName(t *testing.T) {
|
||||
tests := []struct {
|
||||
raw string
|
||||
expected Provider
|
||||
}{
|
||||
{
|
||||
raw: "microk8s",
|
||||
expected: microk8s,
|
||||
},
|
||||
{
|
||||
raw: "MICROK8S",
|
||||
expected: microk8s,
|
||||
},
|
||||
{
|
||||
raw: " microk8s ",
|
||||
expected: microk8s,
|
||||
},
|
||||
{
|
||||
raw: "Docker-Desktop",
|
||||
expected: dockerDesktop,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.raw, func(t *testing.T) {
|
||||
actual := mustNormalizeDistributionName(test.raw)
|
||||
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
+76
-40
@@ -3,43 +3,53 @@ package analyzer
|
||||
import (
|
||||
"archive/tar"
|
||||
"compress/gzip"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
getter "github.com/hashicorp/go-getter"
|
||||
"github.com/pkg/errors"
|
||||
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
|
||||
troubleshootscheme "github.com/replicatedhq/troubleshoot/pkg/client/troubleshootclientset/scheme"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/logger"
|
||||
"gopkg.in/yaml.v2"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
)
|
||||
|
||||
type fileContentProvider struct {
|
||||
rootDir string
|
||||
}
|
||||
|
||||
func DownloadAndAnalyze(ctx context.Context, bundleURL string) ([]*AnalyzeResult, error) {
|
||||
func DownloadAndAnalyze(bundleURL string, analyzersSpec string) ([]*AnalyzeResult, error) {
|
||||
tmpDir, err := ioutil.TempDir("", "troubleshoot-k8s")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, errors.Wrap(err, "failed to create temp dir")
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
if err := downLoadTroubleshootBundle(bundleURL, tmpDir); err != nil {
|
||||
return nil, err
|
||||
if err := downloadTroubleshootBundle(bundleURL, tmpDir); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to download bundle")
|
||||
}
|
||||
|
||||
_, err = os.Stat(filepath.Join(tmpDir, "version.yaml"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, errors.Wrap(err, "failed to read version.yaml")
|
||||
}
|
||||
|
||||
analyzers, err := getTroubleshootAnalyzers()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
analyzers := []*troubleshootv1beta1.Analyze{}
|
||||
|
||||
if analyzersSpec == "" {
|
||||
defaultAnalyzers, err := getDefaultAnalyzers()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get default analyzers")
|
||||
}
|
||||
analyzers = defaultAnalyzers
|
||||
} else {
|
||||
parsedAnalyzers, err := parseAnalyzers(analyzersSpec)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to parse analyzers")
|
||||
}
|
||||
analyzers = parsedAnalyzers
|
||||
}
|
||||
|
||||
fcp := fileContentProvider{rootDir: tmpDir}
|
||||
@@ -52,21 +62,32 @@ func DownloadAndAnalyze(ctx context.Context, bundleURL string) ([]*AnalyzeResult
|
||||
continue
|
||||
}
|
||||
|
||||
analyzeResults = append(analyzeResults, analyzeResult)
|
||||
if analyzeResult != nil {
|
||||
analyzeResults = append(analyzeResults, analyzeResult)
|
||||
}
|
||||
}
|
||||
|
||||
return analyzeResults, nil
|
||||
}
|
||||
|
||||
func downLoadTroubleshootBundle(bundleURL, destDir string) error {
|
||||
func downloadTroubleshootBundle(bundleURL string, destDir string) error {
|
||||
if bundleURL[0] == os.PathSeparator {
|
||||
f, err := os.Open(bundleURL)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to open support bundle")
|
||||
}
|
||||
defer f.Close()
|
||||
return extractTroubleshootBundle(f, destDir)
|
||||
}
|
||||
|
||||
pwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "failed to get workdir")
|
||||
}
|
||||
|
||||
tmpDir, err := ioutil.TempDir("", "getter")
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "failed to create tmp dir")
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
@@ -77,12 +98,12 @@ func downLoadTroubleshootBundle(bundleURL, destDir string) error {
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "failed to read support bundle file")
|
||||
}
|
||||
|
||||
f, err := os.Open(dst)
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "failed to open support bundle")
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
@@ -92,7 +113,7 @@ func downLoadTroubleshootBundle(bundleURL, destDir string) error {
|
||||
func extractTroubleshootBundle(reader io.Reader, destDir string) error {
|
||||
gzReader, err := gzip.NewReader(reader)
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "failed to create gzip reader")
|
||||
}
|
||||
|
||||
tarReader := tar.NewReader(gzReader)
|
||||
@@ -102,22 +123,25 @@ func extractTroubleshootBundle(reader io.Reader, destDir string) error {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "failed to read header from tar")
|
||||
}
|
||||
|
||||
switch header.Typeflag {
|
||||
case tar.TypeDir:
|
||||
name := filepath.Join(destDir, header.Name)
|
||||
if err := os.MkdirAll(name, os.FileMode(header.Mode)); err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "failed to mkdir")
|
||||
}
|
||||
case tar.TypeReg:
|
||||
name := filepath.Join(destDir, header.Name)
|
||||
file, err := os.OpenFile(name, os.O_RDWR|os.O_CREATE, os.FileMode(header.Mode))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to open tar file")
|
||||
}
|
||||
_, err = io.Copy(file, tarReader)
|
||||
file.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "failed to extract file")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -125,29 +149,41 @@ func extractTroubleshootBundle(reader io.Reader, destDir string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func getTroubleshootAnalyzers() ([]*troubleshootv1beta1.Analyze, error) {
|
||||
specURL := `https://gist.githubusercontent.com/divolgin/92b512ad4697c7255f383a7c1b56fd83/raw`
|
||||
resp, err := http.Get(specURL)
|
||||
func parseAnalyzers(spec string) ([]*troubleshootv1beta1.Analyze, error) {
|
||||
troubleshootscheme.AddToScheme(scheme.Scheme)
|
||||
decode := scheme.Codecs.UniversalDeserializer().Decode
|
||||
|
||||
obj, _, err := decode([]byte(spec), nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("could not download analyzer spec, status code: %v", resp.StatusCode)
|
||||
return nil, errors.Wrap(err, "failed to decode analyzers")
|
||||
}
|
||||
|
||||
spec, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
analyzer := obj.(*troubleshootv1beta1.Analyzer)
|
||||
return analyzer.Spec.Analyzers, nil
|
||||
}
|
||||
|
||||
preflight := troubleshootv1beta1.Preflight{}
|
||||
if err := yaml.Unmarshal([]byte(spec), &preflight); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
func getDefaultAnalyzers() ([]*troubleshootv1beta1.Analyze, error) {
|
||||
spec := `apiVersion: troubleshoot.replicated.com/v1beta1
|
||||
kind: Analyzer
|
||||
metadata:
|
||||
name: defaultAnalyzers
|
||||
spec:
|
||||
analyzers:
|
||||
- clusterVersion:
|
||||
outcomes:
|
||||
- fail:
|
||||
when: "< 1.13.0"
|
||||
message: The application requires at Kubernetes 1.13.0 or later, and recommends 1.15.0.
|
||||
uri: https://www.kubernetes.io
|
||||
- warn:
|
||||
when: "< 1.15.0"
|
||||
message: Your cluster meets the minimum version of Kubernetes, but we recommend you update to 1.15.0 or later.
|
||||
uri: https://kubernetes.io
|
||||
- pass:
|
||||
when: ">= 1.15.0"
|
||||
message: Your cluster meets the recommended and required versions of Kubernetes.`
|
||||
|
||||
return preflight.Spec.Analyzers, nil
|
||||
return parseAnalyzers(spec)
|
||||
}
|
||||
|
||||
func (f fileContentProvider) getFileContents(fileName string) ([]byte, error) {
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
package analyzer
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"path"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
)
|
||||
|
||||
func analyzeStatefulsetStatus(analyzer *troubleshootv1beta1.StatefulsetStatus, getCollectedFileContents func(string) ([]byte, error)) (*AnalyzeResult, error) {
|
||||
collected, err := getCollectedFileContents(path.Join("cluster-resources", "statefulsets", fmt.Sprintf("%s.json", analyzer.Namespace)))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to read collected deployments from namespace")
|
||||
}
|
||||
|
||||
var statefulsets []appsv1.StatefulSet
|
||||
if err := json.Unmarshal(collected, &statefulsets); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to unmarshal statefulset list")
|
||||
}
|
||||
|
||||
var status *appsv1.StatefulSetStatus
|
||||
for _, statefulset := range statefulsets {
|
||||
if statefulset.Name == analyzer.Name {
|
||||
status = &statefulset.Status
|
||||
}
|
||||
}
|
||||
|
||||
if status == nil {
|
||||
// there's not an error, but maybe the requested statefulset is not even deployed
|
||||
return &AnalyzeResult{
|
||||
Title: fmt.Sprintf("%s Statefulset Status", analyzer.Name),
|
||||
IsFail: true,
|
||||
Message: fmt.Sprintf("The statefulset %q was not found", analyzer.Name),
|
||||
}, nil
|
||||
}
|
||||
|
||||
return commonStatus(analyzer.Outcomes, fmt.Sprintf("%s Status", analyzer.Name), int(status.ReadyReplicas))
|
||||
}
|
||||
@@ -50,8 +50,33 @@ type ImagePullSecret struct {
|
||||
RegistryName string `json:"registryName" yaml:"registryName"`
|
||||
}
|
||||
|
||||
type DeploymentStatus struct {
|
||||
AnalyzeMeta `json:",inline" yaml:",inline"`
|
||||
Outcomes []*Outcome `json:"outcomes" yaml:"outcomes"`
|
||||
Namespace string `json:"namespace" yaml:"namespace"`
|
||||
Name string `json:"name" yaml:"name"`
|
||||
}
|
||||
|
||||
type StatefulsetStatus struct {
|
||||
AnalyzeMeta `json:",inline" yaml:",inline"`
|
||||
Outcomes []*Outcome `json:"outcomes" yaml:"outcomes"`
|
||||
Namespace string `json:"namespace" yaml:"namespace"`
|
||||
Name string `json:"name" yaml:"name"`
|
||||
}
|
||||
|
||||
type ContainerRuntime struct {
|
||||
AnalyzeMeta `json:",inline" yaml:",inline"`
|
||||
Outcomes []*Outcome `json:"outcomes" yaml:"outcomes"`
|
||||
}
|
||||
|
||||
type Distribution struct {
|
||||
AnalyzeMeta `json:",inline" yaml:",inline"`
|
||||
Outcomes []*Outcome `json:"outcomes" yaml:"outcomes"`
|
||||
}
|
||||
|
||||
type AnalyzeMeta struct {
|
||||
CheckName string `json:"checkName,omitempty" yaml:"checkName,omitempty"`
|
||||
Exclude bool `json:"exclude,omitempty" yaml:"exclude,omitempty"`
|
||||
}
|
||||
|
||||
type Analyze struct {
|
||||
@@ -61,4 +86,8 @@ type Analyze struct {
|
||||
Ingress *Ingress `json:"ingress,omitempty" yaml:"ingress,omitempty"`
|
||||
Secret *AnalyzeSecret `json:"secret,omitempty" yaml:"secret,omitempty"`
|
||||
ImagePullSecret *ImagePullSecret `json:"imagePullSecret,omitempty" yaml:"imagePullSecret,omitempty"`
|
||||
DeploymentStatus *DeploymentStatus `json:"deploymentStatus,omitempty" yaml:"deploymentStatus,omitempty"`
|
||||
StatefulsetStatus *StatefulsetStatus `json:"statefulsetStatus,omitempty" yaml:"statefulsetStatus,omitempty"`
|
||||
ContainerRuntime *ContainerRuntime `json:"containerRuntime,omitempty" yaml:"containerRuntime,omitempty"`
|
||||
Distribution *Distribution `json:"distribution,omitempty" yaml:"distribution,omitempty"`
|
||||
}
|
||||
|
||||
@@ -20,19 +20,13 @@ import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
|
||||
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
|
||||
|
||||
// AnalyzerSpec defines the desired state of Analyzer
|
||||
type AnalyzerSpec struct {
|
||||
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
|
||||
// Important: Run "make" to regenerate code after modifying this file
|
||||
Analyzers []*Analyze `json:"analyzers,omitempty"`
|
||||
}
|
||||
|
||||
// AnalyzerStatus defines the observed state of Analyzer
|
||||
type AnalyzerStatus struct {
|
||||
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
|
||||
// Important: Run "make" to regenerate code after modifying this file
|
||||
}
|
||||
|
||||
// +genclient
|
||||
|
||||
@@ -2,17 +2,20 @@ package v1beta1
|
||||
|
||||
type CollectorMeta struct {
|
||||
CollectorName string `json:"collectorName,omitempty" yaml:"collectorName,omitempty"`
|
||||
Exclude bool `json:"when,omitmempty" yaml:"when,omitempty"`
|
||||
}
|
||||
|
||||
type ClusterInfo struct {
|
||||
CollectorMeta `json:",inline" yaml:",inline"`
|
||||
}
|
||||
|
||||
type ClusterResources struct {
|
||||
CollectorMeta `json:",inline" yaml:",inline"`
|
||||
}
|
||||
|
||||
type Secret struct {
|
||||
CollectorMeta `json:",inline" yaml:",inline"`
|
||||
Name string `json:"name" yaml:"name"`
|
||||
SecretName string `json:"name" yaml:"name"`
|
||||
Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"`
|
||||
Key string `json:"key,omitempty" yaml:"key,omitempty"`
|
||||
IncludeValue bool `json:"includeValue,omitempty" yaml:"includeValue,omitempty"`
|
||||
@@ -24,14 +27,23 @@ type LogLimits struct {
|
||||
}
|
||||
|
||||
type Logs struct {
|
||||
CollectorMeta `json:",inline" yaml:",inline"`
|
||||
Name string `json:"name,omitempty" yaml:"name,omitempty"`
|
||||
Selector []string `json:"selector" yaml:"selector"`
|
||||
Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"`
|
||||
ContainerNames []string `json:"containerNames,omitempty" yaml:"containerNames,omitempty"`
|
||||
Limits *LogLimits `json:"limits,omitempty" yaml:"omitempty"`
|
||||
}
|
||||
|
||||
type Data struct {
|
||||
CollectorMeta `json:",inline" yaml:",inline"`
|
||||
Selector []string `json:"selector" yaml:"selector"`
|
||||
Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"`
|
||||
Limits *LogLimits `json:"limits,omitempty" yaml:"omitempty"`
|
||||
Name string `json:"name,omitempty" yaml:"name,omitempty"`
|
||||
Data string `json:"data" yaml:"data"`
|
||||
}
|
||||
|
||||
type Run struct {
|
||||
CollectorMeta `json:",inline" yaml:",inline"`
|
||||
Name string `json:"name,omitempty" yaml:"name,omitempty"`
|
||||
Namespace string `json:"namespace" yaml:"namespace"`
|
||||
Image string `json:"image" yaml:"image"`
|
||||
Command []string `json:"command,omitempty" yaml:"command,omitempty"`
|
||||
@@ -42,6 +54,7 @@ type Run struct {
|
||||
|
||||
type Exec struct {
|
||||
CollectorMeta `json:",inline" yaml:",inline"`
|
||||
Name string `json:"name,omitempty" yaml:"name,omitempty"`
|
||||
Selector []string `json:"selector" yaml:"selector"`
|
||||
Namespace string `json:"namespace" yaml:"namespace"`
|
||||
ContainerName string `json:"containerName,omitempty" yaml:"containerName,omitempty"`
|
||||
@@ -52,6 +65,7 @@ type Exec struct {
|
||||
|
||||
type Copy struct {
|
||||
CollectorMeta `json:",inline" yaml:",inline"`
|
||||
Name string `json:"name,omitempty" yaml:"name,omitempty"`
|
||||
Selector []string `json:"selector" yaml:"selector"`
|
||||
Namespace string `json:"namespace" yaml:"namespace"`
|
||||
ContainerPath string `json:"containerPath" yaml:"containerPath"`
|
||||
@@ -60,9 +74,10 @@ type Copy struct {
|
||||
|
||||
type HTTP struct {
|
||||
CollectorMeta `json:",inline" yaml:",inline"`
|
||||
Get *Get `json:"get,omitempty" yaml:"get,omitempty"`
|
||||
Post *Post `json:"post,omitempty" yaml:"post,omitempty"`
|
||||
Put *Put `json:"put,omitempty" yaml:"put,omitempty"`
|
||||
Name string `json:"name,omitempty" yaml:"name,omitempty"`
|
||||
Get *Get `json:"get,omitempty" yaml:"get,omitempty"`
|
||||
Post *Post `json:"post,omitempty" yaml:"post,omitempty"`
|
||||
Put *Put `json:"put,omitempty" yaml:"put,omitempty"`
|
||||
}
|
||||
|
||||
type Get struct {
|
||||
@@ -92,6 +107,7 @@ type Collect struct {
|
||||
Logs *Logs `json:"logs,omitempty" yaml:"logs,omitempty"`
|
||||
Run *Run `json:"run,omitempty" yaml:"run,omitempty"`
|
||||
Exec *Exec `json:"exec,omitempty" yaml:"exec,omitempty"`
|
||||
Data *Data `json:"data,omitempty" yaml:"data,omitempty"`
|
||||
Copy *Copy `json:"copy,omitempty" yaml:"copy,omitempty"`
|
||||
HTTP *HTTP `json:"http,omitempty" yaml:"http,omitempty"`
|
||||
}
|
||||
|
||||
@@ -20,6 +20,22 @@ import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
type ResultRequest struct {
|
||||
URI string `json:"uri" yaml:"uri"`
|
||||
Method string `json:"method" yaml:"method"`
|
||||
}
|
||||
|
||||
type AfterCollection struct {
|
||||
UploadResultsTo *ResultRequest `json:"uploadResultsTo,omitempty" yaml:"uploadResultsTo,omitempty"`
|
||||
Callback *ResultRequest `json:"callback,omitempty" yaml:"callback,omitempty"`
|
||||
}
|
||||
|
||||
// CollectorSpec defines the desired state of Collector
|
||||
type CollectorSpec struct {
|
||||
Collectors []*Collect `json:"collectors,omitempty" yaml:"collectors,omitempty"`
|
||||
AfterCollection []*AfterCollection `json:"afterCollection,omitempty" yaml:"afterCollection,omitempty"`
|
||||
}
|
||||
|
||||
// CollectorStatus defines the observed state of Collector
|
||||
type CollectorStatus struct {
|
||||
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
|
||||
@@ -35,7 +51,7 @@ type Collector struct {
|
||||
metav1.TypeMeta `json:",inline" yaml:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty"`
|
||||
|
||||
Spec []*Collect `json:"spec,omitempty" yaml:"spec,omitempty"`
|
||||
Spec CollectorSpec `json:"spec,omitempty" yaml:"spec,omitempty"`
|
||||
Status CollectorStatus `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,31 @@ import (
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *AfterCollection) DeepCopyInto(out *AfterCollection) {
|
||||
*out = *in
|
||||
if in.UploadResultsTo != nil {
|
||||
in, out := &in.UploadResultsTo, &out.UploadResultsTo
|
||||
*out = new(ResultRequest)
|
||||
**out = **in
|
||||
}
|
||||
if in.Callback != nil {
|
||||
in, out := &in.Callback, &out.Callback
|
||||
*out = new(ResultRequest)
|
||||
**out = **in
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AfterCollection.
|
||||
func (in *AfterCollection) DeepCopy() *AfterCollection {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(AfterCollection)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Analyze) DeepCopyInto(out *Analyze) {
|
||||
*out = *in
|
||||
@@ -57,6 +82,26 @@ func (in *Analyze) DeepCopyInto(out *Analyze) {
|
||||
*out = new(ImagePullSecret)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.DeploymentStatus != nil {
|
||||
in, out := &in.DeploymentStatus, &out.DeploymentStatus
|
||||
*out = new(DeploymentStatus)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.StatefulsetStatus != nil {
|
||||
in, out := &in.StatefulsetStatus, &out.StatefulsetStatus
|
||||
*out = new(StatefulsetStatus)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.ContainerRuntime != nil {
|
||||
in, out := &in.ContainerRuntime, &out.ContainerRuntime
|
||||
*out = new(ContainerRuntime)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.Distribution != nil {
|
||||
in, out := &in.Distribution, &out.Distribution
|
||||
*out = new(Distribution)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Analyze.
|
||||
@@ -116,7 +161,7 @@ func (in *Analyzer) DeepCopyInto(out *Analyzer) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
out.Spec = in.Spec
|
||||
in.Spec.DeepCopyInto(&out.Spec)
|
||||
out.Status = in.Status
|
||||
}
|
||||
|
||||
@@ -262,6 +307,17 @@ func (in *AnalyzerList) DeepCopyObject() runtime.Object {
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *AnalyzerSpec) DeepCopyInto(out *AnalyzerSpec) {
|
||||
*out = *in
|
||||
if in.Analyzers != nil {
|
||||
in, out := &in.Analyzers, &out.Analyzers
|
||||
*out = make([]*Analyze, len(*in))
|
||||
for i := range *in {
|
||||
if (*in)[i] != nil {
|
||||
in, out := &(*in)[i], &(*out)[i]
|
||||
*out = new(Analyze)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AnalyzerSpec.
|
||||
@@ -292,6 +348,7 @@ func (in *AnalyzerStatus) DeepCopy() *AnalyzerStatus {
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ClusterInfo) DeepCopyInto(out *ClusterInfo) {
|
||||
*out = *in
|
||||
out.CollectorMeta = in.CollectorMeta
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterInfo.
|
||||
@@ -307,6 +364,7 @@ func (in *ClusterInfo) DeepCopy() *ClusterInfo {
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ClusterResources) DeepCopyInto(out *ClusterResources) {
|
||||
*out = *in
|
||||
out.CollectorMeta = in.CollectorMeta
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterResources.
|
||||
@@ -379,6 +437,11 @@ func (in *Collect) DeepCopyInto(out *Collect) {
|
||||
*out = new(Exec)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.Data != nil {
|
||||
in, out := &in.Data, &out.Data
|
||||
*out = new(Data)
|
||||
**out = **in
|
||||
}
|
||||
if in.Copy != nil {
|
||||
in, out := &in.Copy, &out.Copy
|
||||
*out = new(Copy)
|
||||
@@ -406,17 +469,7 @@ func (in *Collector) DeepCopyInto(out *Collector) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
if in.Spec != nil {
|
||||
in, out := &in.Spec, &out.Spec
|
||||
*out = make([]*Collect, len(*in))
|
||||
for i := range *in {
|
||||
if (*in)[i] != nil {
|
||||
in, out := &(*in)[i], &(*out)[i]
|
||||
*out = new(Collect)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
}
|
||||
in.Spec.DeepCopyInto(&out.Spec)
|
||||
out.Status = in.Status
|
||||
}
|
||||
|
||||
@@ -605,6 +658,43 @@ func (in *CollectorRef) DeepCopy() *CollectorRef {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *CollectorSpec) DeepCopyInto(out *CollectorSpec) {
|
||||
*out = *in
|
||||
if in.Collectors != nil {
|
||||
in, out := &in.Collectors, &out.Collectors
|
||||
*out = make([]*Collect, len(*in))
|
||||
for i := range *in {
|
||||
if (*in)[i] != nil {
|
||||
in, out := &(*in)[i], &(*out)[i]
|
||||
*out = new(Collect)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
}
|
||||
if in.AfterCollection != nil {
|
||||
in, out := &in.AfterCollection, &out.AfterCollection
|
||||
*out = make([]*AfterCollection, len(*in))
|
||||
for i := range *in {
|
||||
if (*in)[i] != nil {
|
||||
in, out := &(*in)[i], &(*out)[i]
|
||||
*out = new(AfterCollection)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CollectorSpec.
|
||||
func (in *CollectorSpec) DeepCopy() *CollectorSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(CollectorSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *CollectorStatus) DeepCopyInto(out *CollectorStatus) {
|
||||
*out = *in
|
||||
@@ -620,6 +710,33 @@ func (in *CollectorStatus) DeepCopy() *CollectorStatus {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ContainerRuntime) DeepCopyInto(out *ContainerRuntime) {
|
||||
*out = *in
|
||||
out.AnalyzeMeta = in.AnalyzeMeta
|
||||
if in.Outcomes != nil {
|
||||
in, out := &in.Outcomes, &out.Outcomes
|
||||
*out = make([]*Outcome, len(*in))
|
||||
for i := range *in {
|
||||
if (*in)[i] != nil {
|
||||
in, out := &(*in)[i], &(*out)[i]
|
||||
*out = new(Outcome)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ContainerRuntime.
|
||||
func (in *ContainerRuntime) DeepCopy() *ContainerRuntime {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ContainerRuntime)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Copy) DeepCopyInto(out *Copy) {
|
||||
*out = *in
|
||||
@@ -668,6 +785,76 @@ func (in *CustomResourceDefinition) DeepCopy() *CustomResourceDefinition {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Data) DeepCopyInto(out *Data) {
|
||||
*out = *in
|
||||
out.CollectorMeta = in.CollectorMeta
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Data.
|
||||
func (in *Data) DeepCopy() *Data {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Data)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *DeploymentStatus) DeepCopyInto(out *DeploymentStatus) {
|
||||
*out = *in
|
||||
out.AnalyzeMeta = in.AnalyzeMeta
|
||||
if in.Outcomes != nil {
|
||||
in, out := &in.Outcomes, &out.Outcomes
|
||||
*out = make([]*Outcome, len(*in))
|
||||
for i := range *in {
|
||||
if (*in)[i] != nil {
|
||||
in, out := &(*in)[i], &(*out)[i]
|
||||
*out = new(Outcome)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeploymentStatus.
|
||||
func (in *DeploymentStatus) DeepCopy() *DeploymentStatus {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(DeploymentStatus)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Distribution) DeepCopyInto(out *Distribution) {
|
||||
*out = *in
|
||||
out.AnalyzeMeta = in.AnalyzeMeta
|
||||
if in.Outcomes != nil {
|
||||
in, out := &in.Outcomes, &out.Outcomes
|
||||
*out = make([]*Outcome, len(*in))
|
||||
for i := range *in {
|
||||
if (*in)[i] != nil {
|
||||
in, out := &(*in)[i], &(*out)[i]
|
||||
*out = new(Outcome)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Distribution.
|
||||
func (in *Distribution) DeepCopy() *Distribution {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Distribution)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Exec) DeepCopyInto(out *Exec) {
|
||||
*out = *in
|
||||
@@ -830,6 +1017,11 @@ func (in *Logs) DeepCopyInto(out *Logs) {
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.ContainerNames != nil {
|
||||
in, out := &in.ContainerNames, &out.ContainerNames
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.Limits != nil {
|
||||
in, out := &in.Limits, &out.Limits
|
||||
*out = new(LogLimits)
|
||||
@@ -1167,6 +1359,21 @@ func (in *Put) DeepCopy() *Put {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ResultRequest) DeepCopyInto(out *ResultRequest) {
|
||||
*out = *in
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResultRequest.
|
||||
func (in *ResultRequest) DeepCopy() *ResultRequest {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ResultRequest)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Run) DeepCopyInto(out *Run) {
|
||||
*out = *in
|
||||
@@ -1224,6 +1431,33 @@ func (in *SingleOutcome) DeepCopy() *SingleOutcome {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *StatefulsetStatus) DeepCopyInto(out *StatefulsetStatus) {
|
||||
*out = *in
|
||||
out.AnalyzeMeta = in.AnalyzeMeta
|
||||
if in.Outcomes != nil {
|
||||
in, out := &in.Outcomes, &out.Outcomes
|
||||
*out = make([]*Outcome, len(*in))
|
||||
for i := range *in {
|
||||
if (*in)[i] != nil {
|
||||
in, out := &(*in)[i], &(*out)[i]
|
||||
*out = new(Outcome)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StatefulsetStatus.
|
||||
func (in *StatefulsetStatus) DeepCopy() *StatefulsetStatus {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(StatefulsetStatus)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *StorageClass) DeepCopyInto(out *StorageClass) {
|
||||
*out = *in
|
||||
|
||||
@@ -2,11 +2,10 @@ package collect
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"k8s.io/apimachinery/pkg/version"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/config"
|
||||
)
|
||||
|
||||
type ClusterVersion struct {
|
||||
@@ -19,15 +18,10 @@ type ClusterInfoOutput struct {
|
||||
Errors []byte `json:"cluster-info/errors.json,omitempty"`
|
||||
}
|
||||
|
||||
func ClusterInfo() error {
|
||||
cfg, err := config.GetConfig()
|
||||
func ClusterInfo(ctx *Context) ([]byte, error) {
|
||||
client, err := kubernetes.NewForConfig(ctx.ClientConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client, err := kubernetes.NewForConfig(cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, errors.Wrap(err, "Failed to create kubernetes clientset")
|
||||
}
|
||||
|
||||
clusterInfoOutput := ClusterInfoOutput{}
|
||||
@@ -37,17 +31,15 @@ func ClusterInfo() error {
|
||||
clusterInfoOutput.ClusterVersion = clusterVersion
|
||||
clusterInfoOutput.Errors, err = marshalNonNil(clusterErrors)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, errors.Wrap(err, "failed to marshal errors")
|
||||
}
|
||||
|
||||
b, err := json.MarshalIndent(clusterInfoOutput, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, errors.Wrap(err, "failed to marshal cluster info")
|
||||
}
|
||||
|
||||
fmt.Printf("%s\n", b)
|
||||
|
||||
return nil
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func clusterVersion(client *kubernetes.Clientset) ([]byte, []string) {
|
||||
|
||||
@@ -11,7 +11,6 @@ import (
|
||||
apiextensionsv1beta1clientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1beta1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/config"
|
||||
)
|
||||
|
||||
type ClusterResourcesOutput struct {
|
||||
@@ -31,39 +30,45 @@ type ClusterResourcesOutput struct {
|
||||
CustomResourceDefinitionsErrors []byte `json:"cluster-resources/custom-resource-definitions-errors.json,omitempty"`
|
||||
ImagePullSecrets map[string][]byte `json:"cluster-resources/image-pull-secrets,omitempty"`
|
||||
ImagePullSecretsErrors []byte `json:"cluster-resources/image-pull-secrets-errors.json,omitempty"`
|
||||
Nodes []byte `json:"cluster-resources/nodes.json,omitempty"`
|
||||
NodesErrors []byte `json:"cluster-resources/nodes-errors.json,omitempty"`
|
||||
}
|
||||
|
||||
func ClusterResources(redact bool) error {
|
||||
cfg, err := config.GetConfig()
|
||||
func ClusterResources(ctx *Context) ([]byte, error) {
|
||||
client, err := kubernetes.NewForConfig(ctx.ClientConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client, err := kubernetes.NewForConfig(cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
clusterResourcesOutput := &ClusterResourcesOutput{}
|
||||
|
||||
// namespaces
|
||||
namespaces, namespaceList, nsErrors := namespaces(client)
|
||||
clusterResourcesOutput.Namespaces = namespaces
|
||||
clusterResourcesOutput.NamespacesErrors, err = marshalNonNil(nsErrors)
|
||||
if err != nil {
|
||||
return err
|
||||
var namespaceNames []string
|
||||
if ctx.Namespace == "" {
|
||||
namespaces, namespaceList, namespaceErrors := namespaces(client)
|
||||
clusterResourcesOutput.Namespaces = namespaces
|
||||
clusterResourcesOutput.NamespacesErrors, err = marshalNonNil(namespaceErrors)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if namespaceList != nil {
|
||||
for _, namespace := range namespaceList.Items {
|
||||
namespaceNames = append(namespaceNames, namespace.Name)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
namespaces, namespaceErrors := getNamespace(client, ctx.Namespace)
|
||||
clusterResourcesOutput.Namespaces = namespaces
|
||||
clusterResourcesOutput.NamespacesErrors, err = marshalNonNil(namespaceErrors)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
namespaceNames = append(namespaceNames, ctx.Namespace)
|
||||
}
|
||||
|
||||
namespaceNames := make([]string, 0, 0)
|
||||
for _, namespace := range namespaceList.Items {
|
||||
namespaceNames = append(namespaceNames, namespace.Name)
|
||||
}
|
||||
|
||||
pods, podErrors := pods(client, namespaceNames)
|
||||
clusterResourcesOutput.Pods = pods
|
||||
clusterResourcesOutput.PodsErrors, err = marshalNonNil(podErrors)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// services
|
||||
@@ -71,7 +76,7 @@ func ClusterResources(redact bool) error {
|
||||
clusterResourcesOutput.Services = services
|
||||
clusterResourcesOutput.ServicesErrors, err = marshalNonNil(servicesErrors)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// deployments
|
||||
@@ -79,7 +84,7 @@ func ClusterResources(redact bool) error {
|
||||
clusterResourcesOutput.Deployments = deployments
|
||||
clusterResourcesOutput.DeploymentsErrors, err = marshalNonNil(deploymentsErrors)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// ingress
|
||||
@@ -87,7 +92,7 @@ func ClusterResources(redact bool) error {
|
||||
clusterResourcesOutput.Ingress = ingress
|
||||
clusterResourcesOutput.IngressErrors, err = marshalNonNil(ingressErrors)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// storage classes
|
||||
@@ -95,19 +100,19 @@ func ClusterResources(redact bool) error {
|
||||
clusterResourcesOutput.StorageClasses = storageClasses
|
||||
clusterResourcesOutput.StorageErrors, err = marshalNonNil(storageErrors)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// crds
|
||||
crdClient, err := apiextensionsv1beta1clientset.NewForConfig(cfg)
|
||||
crdClient, err := apiextensionsv1beta1clientset.NewForConfig(ctx.ClientConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
customResourceDefinitions, crdErrors := crds(crdClient)
|
||||
clusterResourcesOutput.CustomResourceDefinitions = customResourceDefinitions
|
||||
clusterResourcesOutput.CustomResourceDefinitionsErrors, err = marshalNonNil(crdErrors)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// imagepullsecrets
|
||||
@@ -115,24 +120,30 @@ func ClusterResources(redact bool) error {
|
||||
clusterResourcesOutput.ImagePullSecrets = imagePullSecrets
|
||||
clusterResourcesOutput.ImagePullSecretsErrors, err = marshalNonNil(pullSecretsErrors)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if redact {
|
||||
// nodes
|
||||
nodes, nodeErrors := nodes(client)
|
||||
clusterResourcesOutput.Nodes = nodes
|
||||
clusterResourcesOutput.NodesErrors, err = marshalNonNil(nodeErrors)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if ctx.Redact {
|
||||
clusterResourcesOutput, err = clusterResourcesOutput.Redact()
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
b, err := json.MarshalIndent(clusterResourcesOutput, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fmt.Printf("%s\n", b)
|
||||
|
||||
return nil
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func namespaces(client *kubernetes.Clientset) ([]byte, *corev1.NamespaceList, []string) {
|
||||
@@ -149,6 +160,20 @@ func namespaces(client *kubernetes.Clientset) ([]byte, *corev1.NamespaceList, []
|
||||
return b, namespaces, nil
|
||||
}
|
||||
|
||||
func getNamespace(client *kubernetes.Clientset, namespace string) ([]byte, []string) {
|
||||
namespaces, err := client.CoreV1().Namespaces().Get(namespace, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, []string{err.Error()}
|
||||
}
|
||||
|
||||
b, err := json.MarshalIndent(namespaces, "", " ")
|
||||
if err != nil {
|
||||
return nil, []string{err.Error()}
|
||||
}
|
||||
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func pods(client *kubernetes.Clientset, namespaces []string) (map[string][]byte, map[string]string) {
|
||||
podsByNamespace := make(map[string][]byte)
|
||||
errorsByNamespace := make(map[string]string)
|
||||
@@ -321,11 +346,29 @@ func imagePullSecrets(client *kubernetes.Clientset, namespaces []string) (map[st
|
||||
return imagePullSecrets, errors
|
||||
}
|
||||
|
||||
func nodes(client *kubernetes.Clientset) ([]byte, []string) {
|
||||
nodes, err := client.CoreV1().Nodes().List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, []string{err.Error()}
|
||||
}
|
||||
|
||||
b, err := json.MarshalIndent(nodes.Items, "", " ")
|
||||
if err != nil {
|
||||
return nil, []string{err.Error()}
|
||||
}
|
||||
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (c *ClusterResourcesOutput) Redact() (*ClusterResourcesOutput, error) {
|
||||
namespaces, err := redact.Redact(c.Namespaces)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nodes, err := redact.Redact(c.Nodes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pods, err := redactMap(c.Pods)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -353,6 +396,8 @@ func (c *ClusterResourcesOutput) Redact() (*ClusterResourcesOutput, error) {
|
||||
return &ClusterResourcesOutput{
|
||||
Namespaces: namespaces,
|
||||
NamespacesErrors: c.NamespacesErrors,
|
||||
Nodes: nodes,
|
||||
NodesErrors: c.NodesErrors,
|
||||
Pods: pods,
|
||||
PodsErrors: c.PodsErrors,
|
||||
Services: services,
|
||||
|
||||
+127
-35
@@ -2,51 +2,143 @@ package collect
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
|
||||
"gopkg.in/yaml.v2"
|
||||
"k8s.io/client-go/rest"
|
||||
)
|
||||
|
||||
type Collector struct {
|
||||
Spec string
|
||||
Redact bool
|
||||
Collect *troubleshootv1beta1.Collect
|
||||
Redact bool
|
||||
ClientConfig *rest.Config
|
||||
Namespace string
|
||||
}
|
||||
|
||||
func (c *Collector) RunCollectorSync() error {
|
||||
collect, err := parseSpec(c.Spec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if collect.ClusterInfo != nil {
|
||||
return ClusterInfo()
|
||||
}
|
||||
if collect.ClusterResources != nil {
|
||||
return ClusterResources(c.Redact)
|
||||
}
|
||||
if collect.Secret != nil {
|
||||
return Secret(collect.Secret, c.Redact)
|
||||
}
|
||||
if collect.Logs != nil {
|
||||
return Logs(collect.Logs, c.Redact)
|
||||
}
|
||||
if collect.Run != nil {
|
||||
return Run(collect.Run, c.Redact)
|
||||
}
|
||||
if collect.Exec != nil {
|
||||
return Exec(collect.Exec, c.Redact)
|
||||
}
|
||||
if collect.Copy != nil {
|
||||
return Copy(collect.Copy, c.Redact)
|
||||
}
|
||||
if collect.HTTP != nil {
|
||||
return HTTP(collect.HTTP, c.Redact)
|
||||
}
|
||||
|
||||
return errors.New("no spec found to run")
|
||||
type Context struct {
|
||||
Redact bool
|
||||
ClientConfig *rest.Config
|
||||
Namespace string
|
||||
}
|
||||
|
||||
func parseSpec(specContents string) (*troubleshootv1beta1.Collect, error) {
|
||||
func (c *Collector) RunCollectorSync() ([]byte, error) {
|
||||
if c.Collect.ClusterInfo != nil {
|
||||
if c.Collect.ClusterInfo.Exclude {
|
||||
return nil, nil
|
||||
}
|
||||
return ClusterInfo(c.GetContext())
|
||||
}
|
||||
if c.Collect.ClusterResources != nil {
|
||||
if c.Collect.ClusterResources.Exclude {
|
||||
return nil, nil
|
||||
}
|
||||
return ClusterResources(c.GetContext())
|
||||
}
|
||||
if c.Collect.Secret != nil {
|
||||
if c.Collect.Secret.Exclude {
|
||||
return nil, nil
|
||||
}
|
||||
return Secret(c.GetContext(), c.Collect.Secret)
|
||||
}
|
||||
if c.Collect.Logs != nil {
|
||||
if c.Collect.Logs.Exclude {
|
||||
return nil, nil
|
||||
}
|
||||
return Logs(c.GetContext(), c.Collect.Logs)
|
||||
}
|
||||
if c.Collect.Run != nil {
|
||||
if c.Collect.Run.Exclude {
|
||||
return nil, nil
|
||||
}
|
||||
return Run(c.GetContext(), c.Collect.Run)
|
||||
}
|
||||
if c.Collect.Exec != nil {
|
||||
if c.Collect.Exec.Exclude {
|
||||
return nil, nil
|
||||
}
|
||||
return Exec(c.GetContext(), c.Collect.Exec)
|
||||
}
|
||||
if c.Collect.Data != nil {
|
||||
if c.Collect.Data.Exclude {
|
||||
return nil, nil
|
||||
}
|
||||
return Data(c.GetContext(), c.Collect.Data)
|
||||
}
|
||||
if c.Collect.Copy != nil {
|
||||
if c.Collect.Copy.Exclude {
|
||||
return nil, nil
|
||||
}
|
||||
return Copy(c.GetContext(), c.Collect.Copy)
|
||||
}
|
||||
if c.Collect.HTTP != nil {
|
||||
if c.Collect.HTTP.Exclude {
|
||||
return nil, nil
|
||||
}
|
||||
return HTTP(c.GetContext(), c.Collect.HTTP)
|
||||
}
|
||||
|
||||
return nil, errors.New("no spec found to run")
|
||||
}
|
||||
|
||||
func (c *Collector) GetDisplayName() string {
|
||||
var collector, name, selector string
|
||||
if c.Collect.ClusterInfo != nil {
|
||||
collector = "cluster-info"
|
||||
}
|
||||
if c.Collect.ClusterResources != nil {
|
||||
collector = "cluster-resources"
|
||||
}
|
||||
if c.Collect.Secret != nil {
|
||||
collector = "secret"
|
||||
name = c.Collect.Secret.CollectorName
|
||||
}
|
||||
if c.Collect.Logs != nil {
|
||||
collector = "logs"
|
||||
name = c.Collect.Logs.CollectorName
|
||||
selector = strings.Join(c.Collect.Logs.Selector, ",")
|
||||
}
|
||||
if c.Collect.Run != nil {
|
||||
collector = "run"
|
||||
name = c.Collect.Run.CollectorName
|
||||
}
|
||||
if c.Collect.Exec != nil {
|
||||
collector = "exec"
|
||||
name = c.Collect.Exec.CollectorName
|
||||
selector = strings.Join(c.Collect.Exec.Selector, ",")
|
||||
}
|
||||
if c.Collect.Copy != nil {
|
||||
collector = "copy"
|
||||
name = c.Collect.Copy.CollectorName
|
||||
selector = strings.Join(c.Collect.Copy.Selector, ",")
|
||||
}
|
||||
if c.Collect.HTTP != nil {
|
||||
collector = "http"
|
||||
name = c.Collect.HTTP.CollectorName
|
||||
}
|
||||
|
||||
if collector == "" {
|
||||
return "<none>"
|
||||
}
|
||||
if name != "" {
|
||||
return fmt.Sprintf("%s/%s", collector, name)
|
||||
}
|
||||
if selector != "" {
|
||||
return fmt.Sprintf("%s/%s", collector, selector)
|
||||
}
|
||||
return collector
|
||||
}
|
||||
|
||||
func (c *Collector) GetContext() *Context {
|
||||
return &Context{
|
||||
Redact: c.Redact,
|
||||
ClientConfig: c.ClientConfig,
|
||||
Namespace: c.Namespace,
|
||||
}
|
||||
}
|
||||
|
||||
func ParseSpec(specContents string) (*troubleshootv1beta1.Collect, error) {
|
||||
collect := troubleshootv1beta1.Collect{}
|
||||
|
||||
if err := yaml.Unmarshal([]byte(specContents), &collect); err != nil {
|
||||
|
||||
@@ -26,7 +26,7 @@ func Test_ParseSpec(t *testing.T) {
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
c, err := parseSpec(test.spec)
|
||||
c, err := ParseSpec(test.spec)
|
||||
|
||||
if test.expectError {
|
||||
assert.Error(t, err)
|
||||
|
||||
+53
-45
@@ -4,83 +4,70 @@ import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/tools/remotecommand"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/config"
|
||||
)
|
||||
|
||||
type CopyOutput struct {
|
||||
Files map[string][]byte `json:"copy/,omitempty"`
|
||||
Errors map[string][]byte `json:"copy-errors/,omitempty"`
|
||||
}
|
||||
type CopyOutput map[string][]byte
|
||||
|
||||
func Copy(copyCollector *troubleshootv1beta1.Copy, redact bool) error {
|
||||
cfg, err := config.GetConfig()
|
||||
func Copy(ctx *Context, copyCollector *troubleshootv1beta1.Copy) ([]byte, error) {
|
||||
client, err := kubernetes.NewForConfig(ctx.ClientConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client, err := kubernetes.NewForConfig(cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
copyOutput := &CopyOutput{
|
||||
Files: make(map[string][]byte),
|
||||
Errors: make(map[string][]byte),
|
||||
}
|
||||
copyOutput := CopyOutput{}
|
||||
|
||||
pods, podsErrors := listPodsInSelectors(client, copyCollector.Namespace, copyCollector.Selector)
|
||||
if len(podsErrors) > 0 {
|
||||
errorBytes, err := marshalNonNil(podsErrors)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
copyOutput.Errors[getCopyErrosFileName(copyCollector)] = errorBytes
|
||||
copyOutput[getCopyErrosFileName(copyCollector)] = errorBytes
|
||||
}
|
||||
|
||||
if len(pods) > 0 {
|
||||
for _, pod := range pods {
|
||||
files, copyErrors := copyFiles(client, pod, copyCollector)
|
||||
bundlePath := filepath.Join(copyCollector.Name, pod.Namespace, pod.Name)
|
||||
|
||||
files, copyErrors := copyFiles(ctx, client, pod, copyCollector)
|
||||
if len(copyErrors) > 0 {
|
||||
key := fmt.Sprintf("%s/%s/%s-errors.json", pod.Namespace, pod.Name, copyCollector.ContainerPath)
|
||||
copyOutput.Errors[key], err = marshalNonNil(copyErrors)
|
||||
key := filepath.Join(bundlePath, copyCollector.ContainerPath+"-errors.json")
|
||||
copyOutput[key], err = marshalNonNil(copyErrors)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
for k, v := range files {
|
||||
copyOutput.Files[k] = v
|
||||
copyOutput[filepath.Join(bundlePath, k)] = v
|
||||
}
|
||||
}
|
||||
|
||||
if redact {
|
||||
// TODO
|
||||
if ctx.Redact {
|
||||
copyOutput, err = copyOutput.Redact()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
b, err := json.MarshalIndent(copyOutput, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fmt.Printf("%s\n", b)
|
||||
|
||||
return nil
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func copyFiles(client *kubernetes.Clientset, pod corev1.Pod, copyCollector *troubleshootv1beta1.Copy) (map[string][]byte, map[string]string) {
|
||||
cfg, err := config.GetConfig()
|
||||
if err != nil {
|
||||
return nil, map[string]string{"error": err.Error()}
|
||||
}
|
||||
|
||||
func copyFiles(ctx *Context, client *kubernetes.Clientset, pod corev1.Pod, copyCollector *troubleshootv1beta1.Copy) (map[string][]byte, map[string]string) {
|
||||
container := pod.Spec.Containers[0].Name
|
||||
if copyCollector.ContainerName != "" {
|
||||
container = copyCollector.ContainerName
|
||||
@@ -91,7 +78,9 @@ func copyFiles(client *kubernetes.Clientset, pod corev1.Pod, copyCollector *trou
|
||||
req := client.CoreV1().RESTClient().Post().Resource("pods").Name(pod.Name).Namespace(pod.Namespace).SubResource("exec")
|
||||
scheme := runtime.NewScheme()
|
||||
if err := corev1.AddToScheme(scheme); err != nil {
|
||||
return nil, map[string]string{"error": err.Error()}
|
||||
return nil, map[string]string{
|
||||
filepath.Join(copyCollector.ContainerPath, "error"): err.Error(),
|
||||
}
|
||||
}
|
||||
|
||||
parameterCodec := runtime.NewParameterCodec(scheme)
|
||||
@@ -104,9 +93,11 @@ func copyFiles(client *kubernetes.Clientset, pod corev1.Pod, copyCollector *trou
|
||||
TTY: false,
|
||||
}, parameterCodec)
|
||||
|
||||
exec, err := remotecommand.NewSPDYExecutor(cfg, "POST", req.URL())
|
||||
exec, err := remotecommand.NewSPDYExecutor(ctx.ClientConfig, "POST", req.URL())
|
||||
if err != nil {
|
||||
return nil, map[string]string{"error": err.Error()}
|
||||
return nil, map[string]string{
|
||||
filepath.Join(copyCollector.ContainerPath, "error"): err.Error(),
|
||||
}
|
||||
}
|
||||
|
||||
output := new(bytes.Buffer)
|
||||
@@ -118,21 +109,38 @@ func copyFiles(client *kubernetes.Clientset, pod corev1.Pod, copyCollector *trou
|
||||
Tty: false,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, map[string]string{
|
||||
"stdout": output.String(),
|
||||
"stderr": stderr.String(),
|
||||
"error": err.Error(),
|
||||
errors := map[string]string{
|
||||
filepath.Join(copyCollector.ContainerPath, "error"): err.Error(),
|
||||
}
|
||||
if s := output.String(); len(s) > 0 {
|
||||
errors[filepath.Join(copyCollector.ContainerPath, "stdout")] = s
|
||||
}
|
||||
if s := stderr.String(); len(s) > 0 {
|
||||
errors[filepath.Join(copyCollector.ContainerPath, "stderr")] = s
|
||||
}
|
||||
return nil, errors
|
||||
}
|
||||
|
||||
return map[string][]byte{
|
||||
fmt.Sprintf("%s/%s/%s", pod.Namespace, pod.Name, copyCollector.ContainerPath): output.Bytes(),
|
||||
copyCollector.ContainerPath: output.Bytes(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c CopyOutput) Redact() (CopyOutput, error) {
|
||||
results, err := redactMap(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func getCopyErrosFileName(copyCollector *troubleshootv1beta1.Copy) string {
|
||||
if len(copyCollector.Name) > 0 {
|
||||
return fmt.Sprintf("%s-errors.json", copyCollector.Name)
|
||||
}
|
||||
if len(copyCollector.CollectorName) > 0 {
|
||||
return fmt.Sprintf("%s.json", copyCollector.CollectorName)
|
||||
return fmt.Sprintf("%s-errors.json", copyCollector.CollectorName)
|
||||
}
|
||||
// TODO: random part
|
||||
return "errors.json"
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package collect
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"path/filepath"
|
||||
|
||||
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
|
||||
)
|
||||
|
||||
type DataOutput map[string][]byte
|
||||
|
||||
func Data(ctx *Context, dataCollector *troubleshootv1beta1.Data) ([]byte, error) {
|
||||
bundlePath := filepath.Join(dataCollector.Name, dataCollector.CollectorName)
|
||||
dataOutput := DataOutput{
|
||||
bundlePath: []byte(dataCollector.Data),
|
||||
}
|
||||
|
||||
b, err := json.MarshalIndent(dataOutput, "", " ")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return b, nil
|
||||
}
|
||||
+50
-52
@@ -5,6 +5,7 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
|
||||
@@ -12,101 +13,98 @@ import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/tools/remotecommand"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/config"
|
||||
)
|
||||
|
||||
type ExecOutput struct {
|
||||
Results map[string][]byte `json:"exec/,omitempty"`
|
||||
Errors map[string][]byte `json:"exec-errors/,omitempty"`
|
||||
}
|
||||
type ExecOutput map[string][]byte
|
||||
|
||||
func Exec(execCollector *troubleshootv1beta1.Exec, redact bool) error {
|
||||
func Exec(ctx *Context, execCollector *troubleshootv1beta1.Exec) ([]byte, error) {
|
||||
if execCollector.Timeout == "" {
|
||||
return execWithoutTimeout(execCollector, redact)
|
||||
return execWithoutTimeout(ctx, execCollector)
|
||||
}
|
||||
|
||||
timeout, err := time.ParseDuration(execCollector.Timeout)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
execChan := make(chan error, 1)
|
||||
errCh := make(chan error, 1)
|
||||
resultCh := make(chan []byte, 1)
|
||||
|
||||
go func() {
|
||||
execChan <- execWithoutTimeout(execCollector, redact)
|
||||
b, err := execWithoutTimeout(ctx, execCollector)
|
||||
if err != nil {
|
||||
errCh <- err
|
||||
} else {
|
||||
resultCh <- b
|
||||
}
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-time.After(timeout):
|
||||
return errors.New("timeout")
|
||||
case err := <-execChan:
|
||||
return err
|
||||
return nil, errors.New("timeout")
|
||||
case result := <-resultCh:
|
||||
return result, nil
|
||||
case err := <-errCh:
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
func execWithoutTimeout(execCollector *troubleshootv1beta1.Exec, redact bool) error {
|
||||
cfg, err := config.GetConfig()
|
||||
func execWithoutTimeout(ctx *Context, execCollector *troubleshootv1beta1.Exec) ([]byte, error) {
|
||||
client, err := kubernetes.NewForConfig(ctx.ClientConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client, err := kubernetes.NewForConfig(cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
execOutput := &ExecOutput{
|
||||
Results: make(map[string][]byte),
|
||||
Errors: make(map[string][]byte),
|
||||
}
|
||||
execOutput := ExecOutput{}
|
||||
|
||||
pods, podsErrors := listPodsInSelectors(client, execCollector.Namespace, execCollector.Selector)
|
||||
if len(podsErrors) > 0 {
|
||||
errorBytes, err := marshalNonNil(podsErrors)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
execOutput.Errors[getExecErrosFileName(execCollector)] = errorBytes
|
||||
execOutput[getExecErrosFileName(execCollector)] = errorBytes
|
||||
}
|
||||
|
||||
if len(pods) > 0 {
|
||||
for _, pod := range pods {
|
||||
stdout, stderr, execErrors := getExecOutputs(client, pod, execCollector, redact)
|
||||
execOutput.Results[fmt.Sprintf("%s/%s/%s-stdout.txt", pod.Namespace, pod.Name, execCollector.CollectorName)] = stdout
|
||||
execOutput.Results[fmt.Sprintf("%s/%s/%s-stderr.txt", pod.Namespace, pod.Name, execCollector.CollectorName)] = stderr
|
||||
stdout, stderr, execErrors := getExecOutputs(ctx, client, pod, execCollector)
|
||||
|
||||
bundlePath := filepath.Join(execCollector.Name, pod.Namespace, pod.Name)
|
||||
if len(stdout) > 0 {
|
||||
execOutput[filepath.Join(bundlePath, execCollector.CollectorName+"-stdout.txt")] = stdout
|
||||
}
|
||||
if len(stderr) > 0 {
|
||||
execOutput[filepath.Join(bundlePath, execCollector.CollectorName+"-stderr.txt")] = stderr
|
||||
}
|
||||
|
||||
if len(execErrors) > 0 {
|
||||
errorBytes, err := marshalNonNil(execErrors)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
execOutput.Results[fmt.Sprintf("%s/%s/%s-errors.json", pod.Namespace, pod.Name, execCollector.CollectorName)] = errorBytes
|
||||
execOutput[filepath.Join(bundlePath, execCollector.CollectorName+"-errors.json")] = errorBytes
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if redact {
|
||||
if ctx.Redact {
|
||||
execOutput, err = execOutput.Redact()
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
b, err := json.MarshalIndent(execOutput, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fmt.Printf("%s\n", b)
|
||||
|
||||
return nil
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func getExecOutputs(client *kubernetes.Clientset, pod corev1.Pod, execCollector *troubleshootv1beta1.Exec, doRedact bool) ([]byte, []byte, []string) {
|
||||
cfg, err := config.GetConfig()
|
||||
if err != nil {
|
||||
return nil, nil, []string{err.Error()}
|
||||
}
|
||||
|
||||
func getExecOutputs(ctx *Context, client *kubernetes.Clientset, pod corev1.Pod, execCollector *troubleshootv1beta1.Exec) ([]byte, []byte, []string) {
|
||||
container := pod.Spec.Containers[0].Name
|
||||
if execCollector.ContainerName != "" {
|
||||
container = execCollector.ContainerName
|
||||
@@ -128,7 +126,7 @@ func getExecOutputs(client *kubernetes.Clientset, pod corev1.Pod, execCollector
|
||||
TTY: false,
|
||||
}, parameterCodec)
|
||||
|
||||
exec, err := remotecommand.NewSPDYExecutor(cfg, "POST", req.URL())
|
||||
exec, err := remotecommand.NewSPDYExecutor(ctx.ClientConfig, "POST", req.URL())
|
||||
if err != nil {
|
||||
return nil, nil, []string{err.Error()}
|
||||
}
|
||||
@@ -150,21 +148,21 @@ func getExecOutputs(client *kubernetes.Clientset, pod corev1.Pod, execCollector
|
||||
return stdout.Bytes(), stderr.Bytes(), nil
|
||||
}
|
||||
|
||||
func (r *ExecOutput) Redact() (*ExecOutput, error) {
|
||||
results, err := redactMap(r.Results)
|
||||
func (r ExecOutput) Redact() (ExecOutput, error) {
|
||||
results, err := redactMap(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &ExecOutput{
|
||||
Results: results,
|
||||
Errors: r.Errors,
|
||||
}, nil
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func getExecErrosFileName(execCollector *troubleshootv1beta1.Exec) string {
|
||||
if len(execCollector.Name) > 0 {
|
||||
return fmt.Sprintf("%s-errors.json", execCollector.Name)
|
||||
}
|
||||
if len(execCollector.CollectorName) > 0 {
|
||||
return fmt.Sprintf("%s.json", execCollector.CollectorName)
|
||||
return fmt.Sprintf("%s-errors.json", execCollector.CollectorName)
|
||||
}
|
||||
// TODO: random part
|
||||
return "errors.json"
|
||||
|
||||
+14
-16
@@ -4,18 +4,16 @@ import (
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/redact"
|
||||
)
|
||||
|
||||
type HTTPOutput struct {
|
||||
Response map[string][]byte `json:"http/,omitempty"`
|
||||
}
|
||||
type HTTPOutput map[string][]byte
|
||||
|
||||
type httpResponse struct {
|
||||
Status int `json:"status"`
|
||||
@@ -27,7 +25,7 @@ type httpError struct {
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
func HTTP(httpCollector *troubleshootv1beta1.HTTP, redact bool) error {
|
||||
func HTTP(ctx *Context, httpCollector *troubleshootv1beta1.HTTP) ([]byte, error) {
|
||||
var response *http.Response
|
||||
var err error
|
||||
|
||||
@@ -38,28 +36,28 @@ func HTTP(httpCollector *troubleshootv1beta1.HTTP, redact bool) error {
|
||||
} else if httpCollector.Put != nil {
|
||||
response, err = doPut(httpCollector.Put)
|
||||
} else {
|
||||
return errors.New("no supported http request type")
|
||||
return nil, errors.New("no supported http request type")
|
||||
}
|
||||
|
||||
output, err := responseToOutput(response, err, redact)
|
||||
output, err := responseToOutput(response, err, ctx.Redact)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
httpOutput := &HTTPOutput{
|
||||
Response: map[string][]byte{
|
||||
httpCollector.CollectorName + ".json": output,
|
||||
},
|
||||
fileName := "result.json"
|
||||
if httpCollector.CollectorName != "" {
|
||||
fileName = httpCollector.CollectorName + ".json"
|
||||
}
|
||||
httpOutput := HTTPOutput{
|
||||
filepath.Join(httpCollector.Name, fileName): output,
|
||||
}
|
||||
|
||||
b, err := json.MarshalIndent(httpOutput, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fmt.Printf("%s\n", b)
|
||||
|
||||
return nil
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func doGet(get *troubleshootv1beta1.Get) (*http.Response, error) {
|
||||
|
||||
+54
-46
@@ -13,71 +13,74 @@ import (
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/config"
|
||||
)
|
||||
|
||||
type LogsOutput struct {
|
||||
PodLogs map[string][]byte `json:"logs/,omitempty"`
|
||||
Errors map[string][]byte `json:"logs-errors/,omitempty"`
|
||||
}
|
||||
type LogsOutput map[string][]byte
|
||||
|
||||
func Logs(logsCollector *troubleshootv1beta1.Logs, redact bool) error {
|
||||
cfg, err := config.GetConfig()
|
||||
func Logs(ctx *Context, logsCollector *troubleshootv1beta1.Logs) ([]byte, error) {
|
||||
client, err := kubernetes.NewForConfig(ctx.ClientConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client, err := kubernetes.NewForConfig(cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logsOutput := &LogsOutput{
|
||||
PodLogs: make(map[string][]byte),
|
||||
}
|
||||
logsOutput := LogsOutput{}
|
||||
|
||||
pods, podsErrors := listPodsInSelectors(client, logsCollector.Namespace, logsCollector.Selector)
|
||||
if len(podsErrors) > 0 {
|
||||
errorBytes, err := marshalNonNil(podsErrors)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
logsOutput.Errors[getLogsErrosFileName(logsCollector)] = errorBytes
|
||||
logsOutput[getLogsErrorsFileName(logsCollector)] = errorBytes
|
||||
}
|
||||
|
||||
if len(pods) > 0 {
|
||||
for _, pod := range pods {
|
||||
podLogs, err := getPodLogs(client, pod, logsCollector.Limits, false)
|
||||
if err != nil {
|
||||
key := fmt.Sprintf("%s/%s-errors.json", pod.Namespace, pod.Name)
|
||||
logsOutput.Errors[key], err = marshalNonNil([]string{err.Error()})
|
||||
if len(logsCollector.ContainerNames) == 0 {
|
||||
podLogs, err := getPodLogs(client, pod, logsCollector.Name, "", logsCollector.Limits, false)
|
||||
if err != nil {
|
||||
return err
|
||||
key := fmt.Sprintf("%s/%s-errors.json", logsCollector.Name, pod.Name)
|
||||
logsOutput[key], err = marshalNonNil([]string{err.Error()})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
for k, v := range podLogs {
|
||||
logsOutput[k] = v
|
||||
}
|
||||
} else {
|
||||
for _, container := range logsCollector.ContainerNames {
|
||||
containerLogs, err := getPodLogs(client, pod, logsCollector.Name, container, logsCollector.Limits, false)
|
||||
if err != nil {
|
||||
key := fmt.Sprintf("%s/%s/%s-errors.json", logsCollector.Name, pod.Name, container)
|
||||
logsOutput[key], err = marshalNonNil([]string{err.Error()})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
for k, v := range containerLogs {
|
||||
logsOutput[k] = v
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
for k, v := range podLogs {
|
||||
logsOutput.PodLogs[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
if redact {
|
||||
if ctx.Redact {
|
||||
logsOutput, err = logsOutput.Redact()
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
b, err := json.MarshalIndent(logsOutput, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fmt.Printf("%s\n", b)
|
||||
|
||||
return nil
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func listPodsInSelectors(client *kubernetes.Clientset, namespace string, selector []string) ([]corev1.Pod, []string) {
|
||||
@@ -95,9 +98,10 @@ func listPodsInSelectors(client *kubernetes.Clientset, namespace string, selecto
|
||||
return pods.Items, nil
|
||||
}
|
||||
|
||||
func getPodLogs(client *kubernetes.Clientset, pod corev1.Pod, limits *troubleshootv1beta1.LogLimits, follow bool) (map[string][]byte, error) {
|
||||
func getPodLogs(client *kubernetes.Clientset, pod corev1.Pod, name, container string, limits *troubleshootv1beta1.LogLimits, follow bool) (map[string][]byte, error) {
|
||||
podLogOpts := corev1.PodLogOptions{
|
||||
Follow: follow,
|
||||
Follow: follow,
|
||||
Container: container,
|
||||
}
|
||||
|
||||
defaultMaxLines := int64(10000)
|
||||
@@ -133,26 +137,30 @@ func getPodLogs(client *kubernetes.Clientset, pod corev1.Pod, limits *troublesho
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fileKey := fmt.Sprintf("%s/%s.txt", name, pod.Name)
|
||||
if container != "" {
|
||||
fileKey = fmt.Sprintf("%s/%s/%s.txt", name, pod.Name, container)
|
||||
}
|
||||
|
||||
return map[string][]byte{
|
||||
fmt.Sprintf("%s/%s.txt", pod.Namespace, pod.Name): buf.Bytes(),
|
||||
fileKey: buf.Bytes(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (l *LogsOutput) Redact() (*LogsOutput, error) {
|
||||
podLogs, err := redactMap(l.PodLogs)
|
||||
func (l LogsOutput) Redact() (LogsOutput, error) {
|
||||
podLogs, err := redactMap(l)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &LogsOutput{
|
||||
PodLogs: podLogs,
|
||||
Errors: l.Errors,
|
||||
}, nil
|
||||
return podLogs, nil
|
||||
}
|
||||
|
||||
func getLogsErrosFileName(logsCollector *troubleshootv1beta1.Logs) string {
|
||||
if len(logsCollector.CollectorName) > 0 {
|
||||
return fmt.Sprintf("%s.json", logsCollector.CollectorName)
|
||||
func getLogsErrorsFileName(logsCollector *troubleshootv1beta1.Logs) string {
|
||||
if len(logsCollector.Name) > 0 {
|
||||
return fmt.Sprintf("%s/errors.json", logsCollector.Name)
|
||||
} else if len(logsCollector.CollectorName) > 0 {
|
||||
return fmt.Sprintf("%s/errors.json", logsCollector.CollectorName)
|
||||
}
|
||||
// TODO: random part
|
||||
return "errors.json"
|
||||
|
||||
+37
-47
@@ -3,7 +3,6 @@ package collect
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
|
||||
@@ -11,27 +10,19 @@ import (
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/config"
|
||||
)
|
||||
|
||||
type RunOutput struct {
|
||||
PodLogs map[string][]byte `json:"run/,omitempty"`
|
||||
}
|
||||
type RunOutput map[string][]byte
|
||||
|
||||
func Run(runCollector *troubleshootv1beta1.Run, redact bool) error {
|
||||
cfg, err := config.GetConfig()
|
||||
func Run(ctx *Context, runCollector *troubleshootv1beta1.Run) ([]byte, error) {
|
||||
client, err := kubernetes.NewForConfig(ctx.ClientConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client, err := kubernetes.NewForConfig(cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pod, err := runPod(client, runCollector)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
@@ -41,77 +32,78 @@ func Run(runCollector *troubleshootv1beta1.Run, redact bool) error {
|
||||
}()
|
||||
|
||||
if runCollector.Timeout == "" {
|
||||
return runWithoutTimeout(pod, runCollector, redact)
|
||||
return runWithoutTimeout(ctx, pod, runCollector)
|
||||
}
|
||||
|
||||
timeout, err := time.ParseDuration(runCollector.Timeout)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
runChan := make(chan error, 1)
|
||||
errCh := make(chan error, 1)
|
||||
resultCh := make(chan []byte, 1)
|
||||
go func() {
|
||||
runChan <- runWithoutTimeout(pod, runCollector, redact)
|
||||
b, err := runWithoutTimeout(ctx, pod, runCollector)
|
||||
if err != nil {
|
||||
errCh <- err
|
||||
} else {
|
||||
resultCh <- b
|
||||
}
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-time.After(timeout):
|
||||
return errors.New("timeout")
|
||||
case err := <-runChan:
|
||||
return err
|
||||
return nil, errors.New("timeout")
|
||||
case result := <-resultCh:
|
||||
return result, nil
|
||||
case err := <-errCh:
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
func runWithoutTimeout(pod *corev1.Pod, runCollector *troubleshootv1beta1.Run, redact bool) error {
|
||||
cfg, err := config.GetConfig()
|
||||
func runWithoutTimeout(ctx *Context, pod *corev1.Pod, runCollector *troubleshootv1beta1.Run) ([]byte, error) {
|
||||
client, err := kubernetes.NewForConfig(ctx.ClientConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client, err := kubernetes.NewForConfig(cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for {
|
||||
status, err := client.CoreV1().Pods(pod.Namespace).Get(pod.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
if status.Status.Phase == "Running" {
|
||||
if status.Status.Phase == corev1.PodRunning ||
|
||||
status.Status.Phase == corev1.PodFailed ||
|
||||
status.Status.Phase == corev1.PodSucceeded {
|
||||
break
|
||||
}
|
||||
time.Sleep(time.Second * 1)
|
||||
}
|
||||
|
||||
runOutput := &RunOutput{
|
||||
PodLogs: make(map[string][]byte),
|
||||
}
|
||||
runOutput := RunOutput{}
|
||||
|
||||
limits := troubleshootv1beta1.LogLimits{
|
||||
MaxLines: 10000,
|
||||
}
|
||||
podLogs, err := getPodLogs(client, *pod, &limits, true)
|
||||
podLogs, err := getPodLogs(client, *pod, runCollector.Name, "", &limits, true)
|
||||
|
||||
for k, v := range podLogs {
|
||||
runOutput.PodLogs[k] = v
|
||||
runOutput[k] = v
|
||||
}
|
||||
|
||||
if redact {
|
||||
if ctx.Redact {
|
||||
runOutput, err = runOutput.Redact()
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
b, err := json.MarshalIndent(runOutput, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fmt.Printf("%s\n", b)
|
||||
|
||||
return nil
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func runPod(client *kubernetes.Clientset, runCollector *troubleshootv1beta1.Run) (*corev1.Pod, error) {
|
||||
@@ -155,13 +147,11 @@ func runPod(client *kubernetes.Clientset, runCollector *troubleshootv1beta1.Run)
|
||||
return created, nil
|
||||
}
|
||||
|
||||
func (r *RunOutput) Redact() (*RunOutput, error) {
|
||||
podLogs, err := redactMap(r.PodLogs)
|
||||
func (r RunOutput) Redact() (RunOutput, error) {
|
||||
podLogs, err := redactMap(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &RunOutput{
|
||||
PodLogs: podLogs,
|
||||
}, nil
|
||||
return podLogs, nil
|
||||
}
|
||||
|
||||
+12
-20
@@ -7,7 +7,6 @@ import (
|
||||
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/config"
|
||||
)
|
||||
|
||||
type FoundSecret struct {
|
||||
@@ -23,15 +22,10 @@ type SecretOutput struct {
|
||||
Errors map[string][]byte `json:"secrets-errors/,omitempty"`
|
||||
}
|
||||
|
||||
func Secret(secretCollector *troubleshootv1beta1.Secret, redact bool) error {
|
||||
cfg, err := config.GetConfig()
|
||||
func Secret(ctx *Context, secretCollector *troubleshootv1beta1.Secret) ([]byte, error) {
|
||||
client, err := kubernetes.NewForConfig(ctx.ClientConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client, err := kubernetes.NewForConfig(cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
secretOutput := &SecretOutput{
|
||||
@@ -43,36 +37,34 @@ func Secret(secretCollector *troubleshootv1beta1.Secret, redact bool) error {
|
||||
if err != nil {
|
||||
errorBytes, err := marshalNonNil([]string{err.Error()})
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
secretOutput.Errors[fmt.Sprintf("%s/%s.json", secret.Namespace, secret.Name)] = errorBytes
|
||||
secretOutput.Errors[fmt.Sprintf("%s.json", secret.Name)] = errorBytes
|
||||
}
|
||||
if encoded != nil {
|
||||
secretOutput.FoundSecret[fmt.Sprintf("%s/%s.json", secret.Namespace, secret.Name)] = encoded
|
||||
if redact {
|
||||
secretOutput.FoundSecret[fmt.Sprintf("%s.json", secret.Name)] = encoded
|
||||
if ctx.Redact {
|
||||
secretOutput, err = secretOutput.Redact()
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
b, err := json.MarshalIndent(secretOutput, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fmt.Printf("%s\n", b)
|
||||
|
||||
return nil
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func secret(client *kubernetes.Clientset, secretCollector *troubleshootv1beta1.Secret) (*FoundSecret, []byte, error) {
|
||||
found, err := client.CoreV1().Secrets(secretCollector.Namespace).Get(secretCollector.Name, metav1.GetOptions{})
|
||||
found, err := client.CoreV1().Secrets(secretCollector.Namespace).Get(secretCollector.SecretName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
missingSecret := FoundSecret{
|
||||
Namespace: secretCollector.Namespace,
|
||||
Name: secretCollector.Name,
|
||||
Name: secretCollector.SecretName,
|
||||
SecretExists: false,
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ func DeterministicIDForCollector(collector *troubleshootv1beta1.Collect) string
|
||||
}
|
||||
|
||||
if collector.Secret != nil {
|
||||
unsafeID = fmt.Sprintf("secret-%s-%s", collector.Secret.Namespace, collector.Secret.Name)
|
||||
unsafeID = fmt.Sprintf("secret-%s-%s", collector.Secret.Namespace, collector.Secret.SecretName)
|
||||
}
|
||||
|
||||
if collector.Logs != nil {
|
||||
|
||||
@@ -52,8 +52,8 @@ func Test_DeterministicIDForCollector(t *testing.T) {
|
||||
name: "secret",
|
||||
collector: &troubleshootv1beta1.Collect{
|
||||
Secret: &troubleshootv1beta1.Secret{
|
||||
Name: "secret-agent-woman",
|
||||
Namespace: "top-secret",
|
||||
SecretName: "secret-agent-woman",
|
||||
Namespace: "top-secret",
|
||||
},
|
||||
},
|
||||
expect: "secret-top-secret-secret-agent-woman",
|
||||
|
||||
@@ -25,7 +25,6 @@ import (
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
|
||||
@@ -40,8 +39,8 @@ import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/rest"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/config"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
"sigs.k8s.io/controller-runtime/pkg/handler"
|
||||
@@ -61,7 +60,11 @@ func Add(mgr manager.Manager) error {
|
||||
|
||||
// newReconciler returns a new reconcile.Reconciler
|
||||
func newReconciler(mgr manager.Manager) reconcile.Reconciler {
|
||||
return &ReconcileCollectorJob{Client: mgr.GetClient(), scheme: mgr.GetScheme()}
|
||||
return &ReconcileCollectorJob{
|
||||
Client: mgr.GetClient(),
|
||||
config: mgr.GetConfig(),
|
||||
scheme: mgr.GetScheme(),
|
||||
}
|
||||
}
|
||||
|
||||
// add adds a new Controller to mgr with r as the reconcile.Reconciler
|
||||
@@ -94,6 +97,7 @@ var _ reconcile.Reconciler = &ReconcileCollectorJob{}
|
||||
// ReconcileCollectorJob reconciles a CollectorJob object
|
||||
type ReconcileCollectorJob struct {
|
||||
client.Client
|
||||
config *rest.Config
|
||||
scheme *runtime.Scheme
|
||||
}
|
||||
|
||||
@@ -137,7 +141,7 @@ func (r *ReconcileCollectorJob) Reconcile(request reconcile.Request) (reconcile.
|
||||
return reconcile.Result{}, err
|
||||
}
|
||||
|
||||
for _, collector := range collectorSpec.Spec {
|
||||
for _, collector := range collectorSpec.Spec.Collectors {
|
||||
if err := r.reconileOneCollectorJob(instance, collector); err != nil {
|
||||
return reconcile.Result{}, err
|
||||
}
|
||||
@@ -147,12 +151,7 @@ func (r *ReconcileCollectorJob) Reconcile(request reconcile.Request) (reconcile.
|
||||
}
|
||||
|
||||
func (r *ReconcileCollectorJob) getCollectorSpec(namespace string, name string) (*troubleshootv1beta1.Collector, error) {
|
||||
cfg, err := config.GetConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
troubleshootClient, err := troubleshootclientv1beta1.NewForConfig(cfg)
|
||||
troubleshootClient, err := troubleshootclientv1beta1.NewForConfig(r.config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -190,12 +189,7 @@ func (r *ReconcileCollectorJob) reconileOneCollectorJob(instance *troubleshootv1
|
||||
// Get the logs
|
||||
podLogOpts := corev1.PodLogOptions{}
|
||||
|
||||
cfg, err := config.GetConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
k8sClient, err := kubernetes.NewForConfig(cfg)
|
||||
k8sClient, err := kubernetes.NewForConfig(r.config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -223,15 +217,9 @@ func (r *ReconcileCollectorJob) reconileOneCollectorJob(instance *troubleshootv1
|
||||
logger.Printf("setting up port forwarding because the manager is not running in the cluster\n")
|
||||
|
||||
// this isn't likely to be very solid
|
||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
localPort := 3000 + r.Intn(999)
|
||||
localPort := 3000 + rand.New(rand.NewSource(time.Now().UnixNano())).Intn(999)
|
||||
|
||||
homeDir := os.Getenv("HOME")
|
||||
if homeDir == "" {
|
||||
homeDir = os.Getenv("USERPROFILE")
|
||||
}
|
||||
kubeContext := filepath.Join(homeDir, ".kube", "config")
|
||||
ch, err := k8sutil.PortForward(kubeContext, localPort, 8000, instance.Namespace, instance.Name+"-collector")
|
||||
ch, err := k8sutil.PortForward(r.config, localPort, 8000, instance.Namespace, instance.Name+"-collector")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -25,8 +25,8 @@ import (
|
||||
kuberneteserrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/client-go/rest"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/config"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller"
|
||||
"sigs.k8s.io/controller-runtime/pkg/handler"
|
||||
"sigs.k8s.io/controller-runtime/pkg/manager"
|
||||
@@ -45,7 +45,11 @@ func Add(mgr manager.Manager) error {
|
||||
|
||||
// newReconciler returns a new reconcile.Reconciler
|
||||
func newReconciler(mgr manager.Manager) reconcile.Reconciler {
|
||||
return &ReconcilePreflightJob{Client: mgr.GetClient(), scheme: mgr.GetScheme()}
|
||||
return &ReconcilePreflightJob{
|
||||
Client: mgr.GetClient(),
|
||||
config: mgr.GetConfig(),
|
||||
scheme: mgr.GetScheme(),
|
||||
}
|
||||
}
|
||||
|
||||
// add adds a new Controller to mgr with r as the reconcile.Reconciler
|
||||
@@ -70,6 +74,7 @@ var _ reconcile.Reconciler = &ReconcilePreflightJob{}
|
||||
// ReconcilePreflightJob reconciles a PreflightJob object
|
||||
type ReconcilePreflightJob struct {
|
||||
client.Client
|
||||
config *rest.Config
|
||||
scheme *runtime.Scheme
|
||||
}
|
||||
|
||||
@@ -155,12 +160,7 @@ func (r *ReconcilePreflightJob) Reconcile(request reconcile.Request) (reconcile.
|
||||
}
|
||||
|
||||
func (r *ReconcilePreflightJob) getPreflightSpec(namespace string, name string) (*troubleshootv1beta1.Preflight, error) {
|
||||
cfg, err := config.GetConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
troubleshootClient, err := troubleshootclientv1beta1.NewForConfig(cfg)
|
||||
troubleshootClient, err := troubleshootclientv1beta1.NewForConfig(r.config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -8,21 +8,17 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/portforward"
|
||||
"k8s.io/client-go/transport/spdy"
|
||||
)
|
||||
|
||||
func PortForward(kubeContext string, localPort int, remotePort int, namespace string, podName string) (chan struct{}, error) {
|
||||
// port forward
|
||||
config, err := clientcmd.BuildConfigFromFlags("", kubeContext)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
func PortForward(config *restclient.Config, localPort int, remotePort int, namespace string, podName string) (chan struct{}, error) {
|
||||
roundTripper, upgrader, err := spdy.RoundTripperFor(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
path := fmt.Sprintf("/api/v1/namespaces/%s/pods/%s/portforward", namespace, podName)
|
||||
hostIP := strings.TrimLeft(config.Host, "htps:/")
|
||||
serverURL := url.URL{Scheme: "http", Path: path, Host: hostIP}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package version
|
||||
|
||||
// NOTE: these variables are injected at build time
|
||||
|
||||
var (
|
||||
version, gitSHA, buildTime string
|
||||
)
|
||||
@@ -0,0 +1,12 @@
|
||||
package version
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
var RunAt time.Time
|
||||
|
||||
func init() {
|
||||
RunAt = time.Now().UTC()
|
||||
initBuild()
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package version
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
build Build
|
||||
)
|
||||
|
||||
// Build holds details about this build of the binary
|
||||
type Build struct {
|
||||
Version string `json:"version,omitempty"`
|
||||
GitSHA string `json:"git,omitempty"`
|
||||
BuildTime time.Time `json:"buildTime,omitempty"`
|
||||
TimeFallback string `json:"buildTimeFallback,omitempty"`
|
||||
GoInfo GoInfo `json:"go,omitempty"`
|
||||
RunAt *time.Time `json:"runAt,omitempty"`
|
||||
}
|
||||
|
||||
type GoInfo struct {
|
||||
Version string `json:"version,omitempty"`
|
||||
Compiler string `json:"compiler,omitempty"`
|
||||
OS string `json:"os,omitempty"`
|
||||
Arch string `json:"arch,omitempty"`
|
||||
}
|
||||
|
||||
// initBuild sets up the version info from build args
|
||||
func initBuild() {
|
||||
build.Version = version
|
||||
if len(gitSHA) >= 7 {
|
||||
build.GitSHA = gitSHA[:7]
|
||||
}
|
||||
var err error
|
||||
build.BuildTime, err = time.Parse(time.RFC3339, buildTime)
|
||||
if err != nil {
|
||||
build.TimeFallback = buildTime
|
||||
}
|
||||
|
||||
build.GoInfo = getGoInfo()
|
||||
build.RunAt = &RunAt
|
||||
}
|
||||
|
||||
// GetBuild gets the build
|
||||
func GetBuild() Build {
|
||||
return build
|
||||
}
|
||||
|
||||
// Version gets the version
|
||||
func Version() string {
|
||||
return build.Version
|
||||
}
|
||||
|
||||
// GitSHA gets the gitsha
|
||||
func GitSHA() string {
|
||||
return build.GitSHA
|
||||
}
|
||||
|
||||
// BuildTime gets the build time
|
||||
func BuildTime() time.Time {
|
||||
return build.BuildTime
|
||||
}
|
||||
|
||||
func getGoInfo() GoInfo {
|
||||
return GoInfo{
|
||||
Version: runtime.Version(),
|
||||
Compiler: runtime.Compiler,
|
||||
OS: runtime.GOOS,
|
||||
Arch: runtime.GOARCH,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user