From 80253a1eb30d60716c0bf8952d07d999c24af8fd Mon Sep 17 00:00:00 2001 From: Marc Campbell Date: Wed, 17 Jul 2019 21:04:07 +0000 Subject: [PATCH 1/4] UI --- cmd/preflight/cli/interactive_results.go | 104 ++++++++++++++++++ cmd/preflight/cli/run.go | 3 + cmd/preflight/cli/run_nocrd.go | 9 +- .../troubleshoot_v1beta1_preflight.yaml | 1 + go.mod | 2 + go.sum | 28 +++++ pkg/analyze/analyzer.go | 1 + pkg/analyze/cluster_version.go | 10 +- .../troubleshoot/v1beta1/analyzer_shared.go | 12 +- .../v1beta1/zz_generated.deepcopy.go | 17 +++ 10 files changed, 182 insertions(+), 5 deletions(-) create mode 100644 cmd/preflight/cli/interactive_results.go diff --git a/cmd/preflight/cli/interactive_results.go b/cmd/preflight/cli/interactive_results.go new file mode 100644 index 000000000..8b133897d --- /dev/null +++ b/cmd/preflight/cli/interactive_results.go @@ -0,0 +1,104 @@ +package cli + +import ( + "fmt" + + ui "github.com/gizak/termui/v3" + "github.com/gizak/termui/v3/widgets" + analyzerunner "github.com/replicatedhq/troubleshoot/pkg/analyze" +) + +type nodeValue string + +func (nv nodeValue) String() string { + return string(nv) +} + +func showInteractiveResults(analyzeResults []*analyzerunner.AnalyzeResult) error { + if err := ui.Init(); err != nil { + return err + } + defer ui.Close() + + selectedResult := 0 + + preflightTable := getPreflightTable(analyzeResults) + details := getDetails(analyzeResults[selectedResult]) + + grid := ui.NewGrid() + termWidth, termHeight := ui.TerminalDimensions() + grid.SetRect(0, 0, termWidth, termHeight) + + grid.Set( + ui.NewRow(1.0, + ui.NewCol(1.0/2, preflightTable), + ui.NewCol(1.0/2, details), + ), + ) + + ui.Render(grid) + + uiEvents := ui.PollEvents() + for { + select { + case e := <-uiEvents: + switch e.ID { + case "q", "": + return nil + case "": + payload := e.Payload.(ui.Resize) + grid.SetRect(0, 0, payload.Width, payload.Height) + ui.Clear() + ui.Render(grid) + } + } + } +} + +func getPreflightTable(analyzeResults []*analyzerunner.AnalyzeResult) *widgets.Table { + table := widgets.NewTable() + table.Border = true + table.Rows = [][]string{} + + for i, analyzeResult := range analyzeResults { + table.Rows = append(table.Rows, []string{ + analyzeResult.Title, + }) + + if analyzeResult.IsPass { + table.RowStyles[i] = ui.NewStyle(ui.ColorGreen, ui.ColorClear, ui.ModifierBold) + } else if analyzeResult.IsWarn { + table.RowStyles[i] = ui.NewStyle(ui.ColorYellow, ui.ColorClear, ui.ModifierBold) + } else if analyzeResult.IsFail { + table.RowStyles[i] = ui.NewStyle(ui.ColorRed, ui.ColorClear) + } + } + + return table +} + +func getDetails(analysisResult *analyzerunner.AnalyzeResult) *ui.Grid { + grid := ui.NewGrid() + + entries := []interface{}{} + + title := widgets.NewParagraph() + title.Text = analysisResult.Title + title.Border = false + entries = append(entries, ui.NewRow(0.2, ui.NewCol(1.0, title))) + + message := widgets.NewParagraph() + message.Text = analysisResult.Message + message.Border = false + entries = append(entries, ui.NewRow(0.2, ui.NewCol(1.0, message))) + + if analysisResult.URI != "" { + uri := widgets.NewParagraph() + uri.Text = fmt.Sprintf("For more information: %s", analysisResult.URI) + uri.Border = false + entries = append(entries, ui.NewRow(0.2, ui.NewCol(1.0, uri))) + } + + grid.Set(entries...) + return grid +} diff --git a/cmd/preflight/cli/run.go b/cmd/preflight/cli/run.go index cb72388e0..507c522d7 100644 --- a/cmd/preflight/cli/run.go +++ b/cmd/preflight/cli/run.go @@ -27,6 +27,9 @@ func Run() *cobra.Command { }, } + 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") diff --git a/cmd/preflight/cli/run_nocrd.go b/cmd/preflight/cli/run_nocrd.go index e692ae0d1..e5ae6618e 100644 --- a/cmd/preflight/cli/run_nocrd.go +++ b/cmd/preflight/cli/run_nocrd.go @@ -77,14 +77,21 @@ func runPreflightsNoCRD(v *viper.Viper, arg string) error { return contents, nil } + analyzeResults := []*analyzerunner.AnalyzeResult{} for _, analyzer := range preflight.Spec.Analyzers { analyzeResult, err := analyzerunner.Analyze(analyzer, getCollectedFileContents) if err != nil { return err } - fmt.Printf("%#v\n", analyzeResult) + analyzeResults = append(analyzeResults, analyzeResult) } + + if v.GetBool("interactive") { + return showInteractiveResults(analyzeResults) + } + + fmt.Printf("only interactive results are supported\n") return nil } diff --git a/config/samples/troubleshoot_v1beta1_preflight.yaml b/config/samples/troubleshoot_v1beta1_preflight.yaml index 5ff553596..e738e9068 100644 --- a/config/samples/troubleshoot_v1beta1_preflight.yaml +++ b/config/samples/troubleshoot_v1beta1_preflight.yaml @@ -9,6 +9,7 @@ spec: - fail: when: "< 1.14.0" message: You need more kubernetes + uri: - warn: when: "< 1.15.0" message: You have barely enough kubernetes diff --git a/go.mod b/go.mod index 8064a3e44..d45d44d36 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,9 @@ require ( github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c // indirect github.com/dsnet/compress v0.0.1 // indirect github.com/gin-gonic/gin v1.4.0 + github.com/gizak/termui/v3 v3.1.0 github.com/golang/snappy v0.0.1 // indirect + github.com/manifoldco/promptui v0.3.2 github.com/mholt/archiver v3.1.1+incompatible github.com/nwaples/rardecode v1.0.0 // indirect github.com/onsi/gomega v1.5.0 diff --git a/go.sum b/go.sum index 8a61c5c53..a54bb8fb0 100644 --- a/go.sum +++ b/go.sum @@ -11,6 +11,7 @@ github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb0 github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= 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/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/appscode/jsonpatch v0.0.0-20190108182946-7c0e3b262f30 h1:Kn3rqvbUFqSepE2OqVu0Pn1CbDw9IuMlONapol0zuwk= @@ -23,6 +24,10 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +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= @@ -63,6 +68,8 @@ github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3 h1:t8FVkw33L+wilf2 github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= github.com/gin-gonic/gin v1.4.0 h1:3tMoCCfM7ppqsR0ptz/wi1impNpT7/9wQtMZ8lr1mCQ= github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM= +github.com/gizak/termui/v3 v3.1.0 h1:ZZmVDgwHl7gR7elfKf1xc4IudXZ5qqfDh4wExk4Iajc= +github.com/gizak/termui/v3 v3.1.0/go.mod h1:bXQEBkJpzxUAKf0+xq9MSWAvWZlE7c+aidmyFlkYTrY= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= @@ -94,6 +101,7 @@ github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef h1:veQD95Isof8w9 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-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= @@ -118,6 +126,7 @@ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/ 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= @@ -131,6 +140,7 @@ github.com/googleapis/gnostic v0.2.0 h1:l6N3VoaVzTncYYW+9yOz2LJJammFZGBO13sqgEhp github.com/googleapis/gnostic v0.2.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/googleapis/gnostic v0.3.0 h1:CcQijm0XKekKjP/YCz28LXVSpgguuB+nCxaSjCe09y0= github.com/googleapis/gnostic v0.3.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= +github.com/gordonklaus/ineffassign v0.0.0-20180909121442-1003c8bd00dc/go.mod h1:cuNKsD1zp2v6XfE/orVX2QE1LC+i254ceGcVeDT3pTU= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= 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= @@ -159,6 +169,8 @@ github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCV 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= @@ -171,17 +183,28 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 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/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/go.mod h1:1fR9+pO2KHEO9ZRtto13gDwwZaAKstQzferVeWqbgNs= +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-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-runewidth v0.0.2 h1:UnlwIPBGaTZfPQ6T1IGzPI0EkYAQmT9fAEJ/poFC63o= +github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mholt/archiver v3.1.1+incompatible h1:1dCVxuqs0dJseYEhi5pl7MYPH9zDa1wBi7mF09cbNkU= github.com/mholt/archiver v3.1.1+incompatible/go.mod h1:Dh2dOXnSdiLxRiPoVfIr/fI1TwETms9B8CTWfeh7ROU= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +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/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= @@ -193,6 +216,8 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb 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/nsf/termbox-go v0.0.0-20190121233118-02980233997d h1:x3S6kxmy49zXVVyhcnrFqxvNVCBPb2KZ9hV2RBdS840= +github.com/nsf/termbox-go v0.0.0-20190121233118-02980233997d/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ= github.com/nwaples/rardecode v1.0.0 h1:r7vGuS5akxOnR4JQSkko62RJ1ReCMXxQRPtxsiFMBOs= github.com/nwaples/rardecode v1.0.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= @@ -284,6 +309,7 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 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/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= @@ -360,6 +386,7 @@ golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -389,6 +416,7 @@ golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 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-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= diff --git a/pkg/analyze/analyzer.go b/pkg/analyze/analyzer.go index 9a2ea7d0e..371f7dd33 100644 --- a/pkg/analyze/analyzer.go +++ b/pkg/analyze/analyzer.go @@ -11,6 +11,7 @@ type AnalyzeResult struct { IsFail bool IsWarn bool + Title string Message string URI string } diff --git a/pkg/analyze/cluster_version.go b/pkg/analyze/cluster_version.go index a9bd8d466..ec1d450f5 100644 --- a/pkg/analyze/cluster_version.go +++ b/pkg/analyze/cluster_version.go @@ -32,7 +32,15 @@ func analyzeClusterVersion(analyzer *troubleshootv1beta1.ClusterVersion, getColl message := "" uri := "" - result = AnalyzeResult{} + title := analyzer.Name + if title == "" { + title = "Required Kubernetes Version" + } + + result = AnalyzeResult{ + Title: title, + } + if outcome.Fail != nil { result.IsFail = true when = outcome.Fail.When diff --git a/pkg/apis/troubleshoot/v1beta1/analyzer_shared.go b/pkg/apis/troubleshoot/v1beta1/analyzer_shared.go index 7f58623d7..34a2a8744 100644 --- a/pkg/apis/troubleshoot/v1beta1/analyzer_shared.go +++ b/pkg/apis/troubleshoot/v1beta1/analyzer_shared.go @@ -13,12 +13,18 @@ type Outcome struct { } type ClusterVersion struct { - Outcomes []*Outcome `json:"outcomes" yaml:"outcomes"` + AnalyzeMeta `json:",inline" yaml:",inline"` + Outcomes []*Outcome `json:"outcomes" yaml:"outcomes"` } type StorageClass struct { - Outcome []*Outcome `json:"outcomes" yaml:"outcomes"` - Name string `json:"name" yaml:"name"` + AnalyzeMeta `json:",inline" yaml:",inline"` + Outcome []*Outcome `json:"outcomes" yaml:"outcomes"` + Name string `json:"name" yaml:"name"` +} + +type AnalyzeMeta struct { + Name string `json:"name,omitempty" yaml:"name,omitempty"` } type Analyze struct { diff --git a/pkg/apis/troubleshoot/v1beta1/zz_generated.deepcopy.go b/pkg/apis/troubleshoot/v1beta1/zz_generated.deepcopy.go index a92bdde68..895931ba6 100644 --- a/pkg/apis/troubleshoot/v1beta1/zz_generated.deepcopy.go +++ b/pkg/apis/troubleshoot/v1beta1/zz_generated.deepcopy.go @@ -49,6 +49,21 @@ func (in *Analyze) DeepCopy() *Analyze { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AnalyzeMeta) DeepCopyInto(out *AnalyzeMeta) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AnalyzeMeta. +func (in *AnalyzeMeta) DeepCopy() *AnalyzeMeta { + if in == nil { + return nil + } + out := new(AnalyzeMeta) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Analyzer) DeepCopyInto(out *Analyzer) { *out = *in @@ -260,6 +275,7 @@ func (in *ClusterResources) DeepCopy() *ClusterResources { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ClusterVersion) DeepCopyInto(out *ClusterVersion) { *out = *in + out.AnalyzeMeta = in.AnalyzeMeta if in.Outcomes != nil { in, out := &in.Outcomes, &out.Outcomes *out = make([]*Outcome, len(*in)) @@ -806,6 +822,7 @@ func (in *SingleOutcome) DeepCopy() *SingleOutcome { // 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 + out.AnalyzeMeta = in.AnalyzeMeta if in.Outcome != nil { in, out := &in.Outcome, &out.Outcome *out = make([]*Outcome, len(*in)) From f693073aaf33e07e2b913481a067831114dba8b4 Mon Sep 17 00:00:00 2001 From: Marc Campbell Date: Wed, 17 Jul 2019 21:30:01 +0000 Subject: [PATCH 2/4] Storage class preflight --- ...roubleshoot.replicated.com_preflights.yaml | 8 ++- config/crds/zz_generated.deepcopy.go | 17 ++++++ .../troubleshoot_v1beta1_preflight.yaml | 17 +++--- pkg/analyze/analyzer.go | 5 +- pkg/analyze/cluster_version.go | 2 +- pkg/analyze/storage_class.go | 55 +++++++++++++++++++ .../troubleshoot/v1beta1/analyzer_shared.go | 10 ++-- .../v1beta1/zz_generated.deepcopy.go | 4 +- 8 files changed, 100 insertions(+), 18 deletions(-) create mode 100644 pkg/analyze/storage_class.go diff --git a/config/crds/troubleshoot.replicated.com_preflights.yaml b/config/crds/troubleshoot.replicated.com_preflights.yaml index 78d3db5c9..9dd97fb39 100644 --- a/config/crds/troubleshoot.replicated.com_preflights.yaml +++ b/config/crds/troubleshoot.replicated.com_preflights.yaml @@ -396,6 +396,8 @@ spec: properties: clusterVersion: properties: + checkName: + type: string outcomes: items: properties: @@ -433,7 +435,7 @@ spec: type: object storageClass: properties: - name: + checkName: type: string outcomes: items: @@ -467,9 +469,11 @@ spec: type: object type: object type: array + storageClassName: + type: string required: - outcomes - - name + - storageClassName type: object type: object type: array diff --git a/config/crds/zz_generated.deepcopy.go b/config/crds/zz_generated.deepcopy.go index c0bafc2da..099c72ed3 100644 --- a/config/crds/zz_generated.deepcopy.go +++ b/config/crds/zz_generated.deepcopy.go @@ -33,6 +33,21 @@ func (in *Analyze) DeepCopy() *Analyze { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AnalyzeMeta) DeepCopyInto(out *AnalyzeMeta) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AnalyzeMeta. +func (in *AnalyzeMeta) DeepCopy() *AnalyzeMeta { + if in == nil { + return nil + } + out := new(AnalyzeMeta) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Analyzer) DeepCopyInto(out *Analyzer) { *out = *in @@ -244,6 +259,7 @@ func (in *ClusterResources) DeepCopy() *ClusterResources { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ClusterVersion) DeepCopyInto(out *ClusterVersion) { *out = *in + out.AnalyzeMeta = in.AnalyzeMeta if in.Outcomes != nil { in, out := &in.Outcomes, &out.Outcomes *out = make([]*Outcome, len(*in)) @@ -790,6 +806,7 @@ func (in *SingleOutcome) DeepCopy() *SingleOutcome { // 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 + out.AnalyzeMeta = in.AnalyzeMeta if in.Outcome != nil { in, out := &in.Outcome, &out.Outcome *out = make([]*Outcome, len(*in)) diff --git a/config/samples/troubleshoot_v1beta1_preflight.yaml b/config/samples/troubleshoot_v1beta1_preflight.yaml index e738e9068..ddc49b777 100644 --- a/config/samples/troubleshoot_v1beta1_preflight.yaml +++ b/config/samples/troubleshoot_v1beta1_preflight.yaml @@ -9,18 +9,21 @@ spec: - fail: when: "< 1.14.0" message: You need more kubernetes - uri: + uri: https://help.replicated.com/kubernetes-version - warn: when: "< 1.15.0" message: You have barely enough kubernetes + uri: https://help.replicated.com/kubernetes-version - pass: message: Good job keeping k8s current - # - storageClass: - # name: "my-custom-storage-class" - # fail: - # message: The custom storage class thing was not found - # pass: - # message: All good on storage classes + - storageClass: + checkName: Required storage classes + storageClassName: "microk8s-hostpath" + outcomes: + - fail: + message: The micr0k8s storage class thing was not found + - pass: + message: All good on storage classes # - manifests: # - secret: # namespace: default diff --git a/pkg/analyze/analyzer.go b/pkg/analyze/analyzer.go index 371f7dd33..b9d088cee 100644 --- a/pkg/analyze/analyzer.go +++ b/pkg/analyze/analyzer.go @@ -20,6 +20,9 @@ func Analyze(analyzer *troubleshootv1beta1.Analyze, getCollectedFileContents fun if analyzer.ClusterVersion != nil { return analyzeClusterVersion(analyzer.ClusterVersion, getCollectedFileContents) } + if analyzer.StorageClass != nil { + return analyzeStorageClass(analyzer.StorageClass, getCollectedFileContents) + } - return nil, errors.New("invalid analyer") + return nil, errors.New("invalid analyzer") } diff --git a/pkg/analyze/cluster_version.go b/pkg/analyze/cluster_version.go index ec1d450f5..21645af13 100644 --- a/pkg/analyze/cluster_version.go +++ b/pkg/analyze/cluster_version.go @@ -32,7 +32,7 @@ func analyzeClusterVersion(analyzer *troubleshootv1beta1.ClusterVersion, getColl message := "" uri := "" - title := analyzer.Name + title := analyzer.CheckName if title == "" { title = "Required Kubernetes Version" } diff --git a/pkg/analyze/storage_class.go b/pkg/analyze/storage_class.go new file mode 100644 index 000000000..ee21bda09 --- /dev/null +++ b/pkg/analyze/storage_class.go @@ -0,0 +1,55 @@ +package analyzer + +import ( + "encoding/json" + "fmt" + + troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1" + storagev1beta1 "k8s.io/api/storage/v1beta1" + // "github.com/replicatedhq/troubleshoot/pkg/collect" +) + +func analyzeStorageClass(analyzer *troubleshootv1beta1.StorageClass, getCollectedFileContents func(string) ([]byte, error)) (*AnalyzeResult, error) { + storageClassesData, err := getCollectedFileContents("cluster-resources/storage-classes.json") + if err != nil { + return nil, err + } + + var storageClasses []storagev1beta1.StorageClass + if err := json.Unmarshal(storageClassesData, &storageClasses); err != nil { + return nil, err + } + + title := analyzer.CheckName + if title == "" { + title = fmt.Sprintf("Storage class %s", analyzer.StorageClassName) + } + + result := AnalyzeResult{ + Title: title, + } + + for _, storageClass := range storageClasses { + if storageClass.Name == analyzer.StorageClassName { + result.IsPass = true + for _, outcome := range analyzer.Outcomes { + if outcome.Pass != nil { + result.Message = outcome.Pass.Message + result.URI = outcome.Pass.URI + } + } + + return &result, nil + } + } + + result.IsFail = true + for _, outcome := range analyzer.Outcomes { + if outcome.Fail != nil { + result.Message = outcome.Fail.Message + result.URI = outcome.Fail.URI + } + } + + return &result, nil +} diff --git a/pkg/apis/troubleshoot/v1beta1/analyzer_shared.go b/pkg/apis/troubleshoot/v1beta1/analyzer_shared.go index 34a2a8744..6666d1bd8 100644 --- a/pkg/apis/troubleshoot/v1beta1/analyzer_shared.go +++ b/pkg/apis/troubleshoot/v1beta1/analyzer_shared.go @@ -18,16 +18,16 @@ type ClusterVersion struct { } type StorageClass struct { - AnalyzeMeta `json:",inline" yaml:",inline"` - Outcome []*Outcome `json:"outcomes" yaml:"outcomes"` - Name string `json:"name" yaml:"name"` + AnalyzeMeta `json:",inline" yaml:",inline"` + Outcomes []*Outcome `json:"outcomes" yaml:"outcomes"` + StorageClassName string `json:"storageClassName" yaml:"storageClassName"` } type AnalyzeMeta struct { - Name string `json:"name,omitempty" yaml:"name,omitempty"` + CheckName string `json:"checkName,omitempty" yaml:"checkName,omitempty"` } type Analyze struct { ClusterVersion *ClusterVersion `json:"clusterVersion,omitempty" yaml:"clusterVersion,omitempty"` - StorageClass *StorageClass `json:"storageClass,omitempty" yaml:"supportBundle,omitempty"` + StorageClass *StorageClass `json:"storageClass,omitempty" yaml:"storageClass,omitempty"` } diff --git a/pkg/apis/troubleshoot/v1beta1/zz_generated.deepcopy.go b/pkg/apis/troubleshoot/v1beta1/zz_generated.deepcopy.go index 895931ba6..773dbc831 100644 --- a/pkg/apis/troubleshoot/v1beta1/zz_generated.deepcopy.go +++ b/pkg/apis/troubleshoot/v1beta1/zz_generated.deepcopy.go @@ -823,8 +823,8 @@ func (in *SingleOutcome) DeepCopy() *SingleOutcome { func (in *StorageClass) DeepCopyInto(out *StorageClass) { *out = *in out.AnalyzeMeta = in.AnalyzeMeta - if in.Outcome != nil { - in, out := &in.Outcome, &out.Outcome + if in.Outcomes != nil { + in, out := &in.Outcomes, &out.Outcomes *out = make([]*Outcome, len(*in)) for i := range *in { if (*in)[i] != nil { From 6fc6db0549eccdca668c06f933f650327b653e85 Mon Sep 17 00:00:00 2001 From: Marc Campbell Date: Wed, 17 Jul 2019 22:09:26 +0000 Subject: [PATCH 3/4] crd client --- pkg/collect/cluster_resources.go | 40 +++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/pkg/collect/cluster_resources.go b/pkg/collect/cluster_resources.go index ed29b3dbb..3e122232b 100644 --- a/pkg/collect/cluster_resources.go +++ b/pkg/collect/cluster_resources.go @@ -5,18 +5,21 @@ import ( "fmt" corev1 "k8s.io/api/core/v1" + apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" + 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 { - Namespaces []byte `json:"cluster-resources/namespaces.json,omitempty"` - Pods map[string][]byte `json:"cluster-resources/pods,omitempty"` - Services map[string][]byte `json:"cluster-resources/services,omitempty"` - Deployments map[string][]byte `json:"cluster-resources/deployments,omitempty"` - Ingress map[string][]byte `json:"cluster-resources/ingress,omitempty"` - StorageClasses []byte `json:"cluster-resources/storage-classes.json,omitempty"` + Namespaces []byte `json:"cluster-resources/namespaces.json,omitempty"` + Pods map[string][]byte `json:"cluster-resources/pods,omitempty"` + Services map[string][]byte `json:"cluster-resources/services,omitempty"` + Deployments map[string][]byte `json:"cluster-resources/deployments,omitempty"` + Ingress map[string][]byte `json:"cluster-resources/ingress,omitempty"` + StorageClasses []byte `json:"cluster-resources/storage-classes.json,omitempty"` + CustomResourceDefinitions []byte `json:"cluster-resources/custom-resource-definitions.json,omitempty"` } func ClusterResources() error { @@ -78,6 +81,17 @@ func ClusterResources() error { } clusterResourcesOutput.StorageClasses = storageClasses + // crds + crdClient, err := apiextensionsv1beta1.NewForConfig(cfg) + if err != nil { + return err + } + customResourceDefinitions, err := crds(crdClient) + if err != nil { + return err + } + clusterResourcesOutput.CustomResourceDefinitions = customResourceDefinitions + b, err := json.MarshalIndent(clusterResourcesOutput, "", " ") if err != nil { return err @@ -195,3 +209,17 @@ func storageClasses(client *kubernetes.Clientset) ([]byte, error) { return b, nil } + +func crds(client *apiextensionsv1beta1clientset.ApiextensionsV1beta1Client) ([]byte, error) { + crds, err := client.CustomResourceDefinitions().List(metav1.ListOptions{}) + if err != nil { + return nil, err + } + + b, err := json.MarshalIndent(crds.Items, "", " ") + if err != nil { + return nil, err + } + + return b, nil +} From 92862fff2c3787d7d49bb6d34f31c31ba145cfc1 Mon Sep 17 00:00:00 2001 From: Marc Campbell Date: Wed, 17 Jul 2019 22:22:56 +0000 Subject: [PATCH 4/4] CRDs --- ...roubleshoot.replicated.com_preflights.yaml | 42 +++++++++++++++ config/crds/zz_generated.deepcopy.go | 36 ++++++++++++- .../troubleshoot_v1beta1_preflight.yaml | 13 ++--- go.mod | 1 + pkg/analyze/analyzer.go | 3 ++ pkg/analyze/crd.go | 54 +++++++++++++++++++ pkg/analyze/storage_class.go | 1 - .../troubleshoot/v1beta1/analyzer_shared.go | 11 +++- .../v1beta1/zz_generated.deepcopy.go | 32 +++++++++++ pkg/collect/cluster_resources.go | 3 +- 10 files changed, 183 insertions(+), 13 deletions(-) create mode 100644 pkg/analyze/crd.go diff --git a/config/crds/troubleshoot.replicated.com_preflights.yaml b/config/crds/troubleshoot.replicated.com_preflights.yaml index 9dd97fb39..46057225d 100644 --- a/config/crds/troubleshoot.replicated.com_preflights.yaml +++ b/config/crds/troubleshoot.replicated.com_preflights.yaml @@ -433,6 +433,48 @@ spec: required: - outcomes type: object + customResourceDefinition: + properties: + checkName: + type: string + customResourceDefinitionName: + 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 + - customResourceDefinitionName + type: object storageClass: properties: checkName: diff --git a/config/crds/zz_generated.deepcopy.go b/config/crds/zz_generated.deepcopy.go index 099c72ed3..f5aee5092 100644 --- a/config/crds/zz_generated.deepcopy.go +++ b/config/crds/zz_generated.deepcopy.go @@ -21,6 +21,11 @@ func (in *Analyze) DeepCopyInto(out *Analyze) { *out = new(StorageClass) (*in).DeepCopyInto(*out) } + if in.CustomResourceDefinition != nil { + in, out := &in.CustomResourceDefinition, &out.CustomResourceDefinition + *out = new(CustomResourceDefinition) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Analyze. @@ -512,6 +517,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 *CustomResourceDefinition) DeepCopyInto(out *CustomResourceDefinition) { + *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 CustomResourceDefinition. +func (in *CustomResourceDefinition) DeepCopy() *CustomResourceDefinition { + if in == nil { + return nil + } + out := new(CustomResourceDefinition) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Outcome) DeepCopyInto(out *Outcome) { *out = *in @@ -807,8 +839,8 @@ func (in *SingleOutcome) DeepCopy() *SingleOutcome { func (in *StorageClass) DeepCopyInto(out *StorageClass) { *out = *in out.AnalyzeMeta = in.AnalyzeMeta - if in.Outcome != nil { - in, out := &in.Outcome, &out.Outcome + if in.Outcomes != nil { + in, out := &in.Outcomes, &out.Outcomes *out = make([]*Outcome, len(*in)) for i := range *in { if (*in)[i] != nil { diff --git a/config/samples/troubleshoot_v1beta1_preflight.yaml b/config/samples/troubleshoot_v1beta1_preflight.yaml index ddc49b777..81dab2108 100644 --- a/config/samples/troubleshoot_v1beta1_preflight.yaml +++ b/config/samples/troubleshoot_v1beta1_preflight.yaml @@ -47,9 +47,10 @@ spec: # message: Can't pull the images # pass: # message: Connected to docker registry - # - customResourceDefinitions: - # name: rook - # fail: - # message: You don't have rook installed - # pass: - # message: Found rook! + - customResourceDefinition: + customResourceDefinitionName: rook + outcomes: + - fail: + message: You don't have rook installed + - pass: + message: Found rook! diff --git a/go.mod b/go.mod index d45d44d36..c4236351d 100644 --- a/go.mod +++ b/go.mod @@ -23,6 +23,7 @@ require ( golang.org/x/net v0.0.0-20190522155817-f3200d17e092 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/client-go v11.0.1-0.20190409021438-1a26190bd76a+incompatible sigs.k8s.io/controller-runtime v0.2.0-beta.2 diff --git a/pkg/analyze/analyzer.go b/pkg/analyze/analyzer.go index b9d088cee..c0d2fbdd1 100644 --- a/pkg/analyze/analyzer.go +++ b/pkg/analyze/analyzer.go @@ -23,6 +23,9 @@ func Analyze(analyzer *troubleshootv1beta1.Analyze, getCollectedFileContents fun if analyzer.StorageClass != nil { return analyzeStorageClass(analyzer.StorageClass, getCollectedFileContents) } + if analyzer.CustomResourceDefinition != nil { + return analyzeCustomResourceDefinition(analyzer.CustomResourceDefinition, getCollectedFileContents) + } return nil, errors.New("invalid analyzer") } diff --git a/pkg/analyze/crd.go b/pkg/analyze/crd.go new file mode 100644 index 000000000..c1e4f3009 --- /dev/null +++ b/pkg/analyze/crd.go @@ -0,0 +1,54 @@ +package analyzer + +import ( + "encoding/json" + "fmt" + + troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1" + apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" +) + +func analyzeCustomResourceDefinition(analyzer *troubleshootv1beta1.CustomResourceDefinition, getCollectedFileContents func(string) ([]byte, error)) (*AnalyzeResult, error) { + crdData, err := getCollectedFileContents("cluster-resources/custom-resource-definitions.json") + if err != nil { + return nil, err + } + + var crds []apiextensionsv1beta1.CustomResourceDefinition + if err := json.Unmarshal(crdData, &crds); err != nil { + return nil, err + } + + title := analyzer.CheckName + if title == "" { + title = fmt.Sprintf("Custom resource definition %s", analyzer.CustomResourceDefinitionName) + } + + result := AnalyzeResult{ + Title: title, + } + + for _, storageClass := range crds { + if storageClass.Name == analyzer.CustomResourceDefinitionName { + result.IsPass = true + for _, outcome := range analyzer.Outcomes { + if outcome.Pass != nil { + result.Message = outcome.Pass.Message + result.URI = outcome.Pass.URI + } + } + + return &result, nil + } + } + + result.IsFail = true + for _, outcome := range analyzer.Outcomes { + if outcome.Fail != nil { + result.Message = outcome.Fail.Message + result.URI = outcome.Fail.URI + } + } + + return &result, nil +} diff --git a/pkg/analyze/storage_class.go b/pkg/analyze/storage_class.go index ee21bda09..9ae831260 100644 --- a/pkg/analyze/storage_class.go +++ b/pkg/analyze/storage_class.go @@ -6,7 +6,6 @@ import ( troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1" storagev1beta1 "k8s.io/api/storage/v1beta1" - // "github.com/replicatedhq/troubleshoot/pkg/collect" ) func analyzeStorageClass(analyzer *troubleshootv1beta1.StorageClass, getCollectedFileContents func(string) ([]byte, error)) (*AnalyzeResult, error) { diff --git a/pkg/apis/troubleshoot/v1beta1/analyzer_shared.go b/pkg/apis/troubleshoot/v1beta1/analyzer_shared.go index 6666d1bd8..b4cdfecc9 100644 --- a/pkg/apis/troubleshoot/v1beta1/analyzer_shared.go +++ b/pkg/apis/troubleshoot/v1beta1/analyzer_shared.go @@ -23,11 +23,18 @@ type StorageClass struct { StorageClassName string `json:"storageClassName" yaml:"storageClassName"` } +type CustomResourceDefinition struct { + AnalyzeMeta `json:",inline" yaml:",inline"` + Outcomes []*Outcome `json:"outcomes" yaml:"outcomes"` + CustomResourceDefinitionName string `json:"customResourceDefinitionName" yaml:"customResourceDefinitionName"` +} + type AnalyzeMeta struct { CheckName string `json:"checkName,omitempty" yaml:"checkName,omitempty"` } type Analyze struct { - ClusterVersion *ClusterVersion `json:"clusterVersion,omitempty" yaml:"clusterVersion,omitempty"` - StorageClass *StorageClass `json:"storageClass,omitempty" yaml:"storageClass,omitempty"` + ClusterVersion *ClusterVersion `json:"clusterVersion,omitempty" yaml:"clusterVersion,omitempty"` + StorageClass *StorageClass `json:"storageClass,omitempty" yaml:"storageClass,omitempty"` + CustomResourceDefinition *CustomResourceDefinition `json:"customResourceDefinition,omitempty" yaml:"customResourceDefinition,omitempty"` } diff --git a/pkg/apis/troubleshoot/v1beta1/zz_generated.deepcopy.go b/pkg/apis/troubleshoot/v1beta1/zz_generated.deepcopy.go index 773dbc831..c8bdd98d2 100644 --- a/pkg/apis/troubleshoot/v1beta1/zz_generated.deepcopy.go +++ b/pkg/apis/troubleshoot/v1beta1/zz_generated.deepcopy.go @@ -37,6 +37,11 @@ func (in *Analyze) DeepCopyInto(out *Analyze) { *out = new(StorageClass) (*in).DeepCopyInto(*out) } + if in.CustomResourceDefinition != nil { + in, out := &in.CustomResourceDefinition, &out.CustomResourceDefinition + *out = new(CustomResourceDefinition) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Analyze. @@ -528,6 +533,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 *CustomResourceDefinition) DeepCopyInto(out *CustomResourceDefinition) { + *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 CustomResourceDefinition. +func (in *CustomResourceDefinition) DeepCopy() *CustomResourceDefinition { + if in == nil { + return nil + } + out := new(CustomResourceDefinition) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Outcome) DeepCopyInto(out *Outcome) { *out = *in diff --git a/pkg/collect/cluster_resources.go b/pkg/collect/cluster_resources.go index 3e122232b..f1b99fc64 100644 --- a/pkg/collect/cluster_resources.go +++ b/pkg/collect/cluster_resources.go @@ -5,7 +5,6 @@ import ( "fmt" corev1 "k8s.io/api/core/v1" - apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" 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" @@ -82,7 +81,7 @@ func ClusterResources() error { clusterResourcesOutput.StorageClasses = storageClasses // crds - crdClient, err := apiextensionsv1beta1.NewForConfig(cfg) + crdClient, err := apiextensionsv1beta1clientset.NewForConfig(cfg) if err != nil { return err }