From 79a7eb8fa31487dda686b8ace245981c68eff19e Mon Sep 17 00:00:00 2001 From: divolgin Date: Wed, 24 Jul 2019 17:43:46 +0000 Subject: [PATCH] Adding version file, analyzer result format conversions --- cmd/troubleshoot/cli/receive.go | 10 +- cmd/troubleshoot/cli/run_nocrd.go | 10 +- cmd/troubleshoot/cli/version.go | 28 +++++ config/crds/zz_generated.deepcopy.go | 15 +++ go.mod | 14 +++ go.sum | 27 +++++ pkg/analyze/cluster_version.go | 1 + pkg/apis/troubleshoot/v1beta1/version.go | 7 ++ .../v1beta1/zz_generated.deepcopy.go | 15 +++ pkg/convert/errors.go | 32 ++++++ pkg/convert/supportbundle.go | 107 ++++++++++++++++++ pkg/convert/templates.go | 63 +++++++++++ 12 files changed, 327 insertions(+), 2 deletions(-) create mode 100644 cmd/troubleshoot/cli/version.go create mode 100644 pkg/apis/troubleshoot/v1beta1/version.go create mode 100644 pkg/convert/errors.go create mode 100644 pkg/convert/supportbundle.go create mode 100644 pkg/convert/templates.go diff --git a/cmd/troubleshoot/cli/receive.go b/cmd/troubleshoot/cli/receive.go index 247add67..1ecee722 100644 --- a/cmd/troubleshoot/cli/receive.go +++ b/cmd/troubleshoot/cli/receive.go @@ -27,6 +27,11 @@ func receiveSupportBundle(collectorJobNamespace string, collectorJobName string) } defer os.RemoveAll(bundlePath) + versionFilename, err := writeVersionFile(bundlePath) + if err != nil { + return err + } + receivedCollectors := []string{} for { job, err := troubleshootClient.CollectorJobs(collectorJobNamespace).Get(collectorJobName, metav1.GetOptions{}) @@ -117,7 +122,10 @@ func receiveSupportBundle(collectorJobNamespace string, collectorJobName string) }, } - paths := make([]string, 0, 0) + // 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)) } diff --git a/cmd/troubleshoot/cli/run_nocrd.go b/cmd/troubleshoot/cli/run_nocrd.go index bd8d7c76..29fb3a46 100644 --- a/cmd/troubleshoot/cli/run_nocrd.go +++ b/cmd/troubleshoot/cli/run_nocrd.go @@ -144,6 +144,11 @@ func runCollectors(v *viper.Viper, collector troubleshootv1beta1.Collector) (str } 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()) @@ -257,7 +262,10 @@ func runCollectors(v *viper.Viper, collector troubleshootv1beta1.Collector) (str }, } - paths := make([]string, 0, 0) + // version file should be first in tar archive for quick extraction + paths := []string{ + versionFilename, + } for _, collectorDir := range collectorDirs { paths = append(paths, collectorDir) } diff --git a/cmd/troubleshoot/cli/version.go b/cmd/troubleshoot/cli/version.go new file mode 100644 index 00000000..88836a26 --- /dev/null +++ b/cmd/troubleshoot/cli/version.go @@ -0,0 +1,28 @@ +package cli + +import ( + troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1" + "gopkg.in/yaml.v2" + "io/ioutil" + "path/filepath" +) + +func writeVersionFile(path string) (string, error) { + version := troubleshootv1beta1.SupportBundleVersion{ + ApiVersion: "troubleshoot.replicated.com/v1beta1", + Kind: "SupportBundle", + LayoutVersion: "0.0.1", + } + b, err := yaml.Marshal(version) + if err != nil { + return "", err + } + + filename := filepath.Join(path, "version.yaml") + err = ioutil.WriteFile(filename, b, 0644) + if err != nil { + return "", err + } + + return filename, nil +} diff --git a/config/crds/zz_generated.deepcopy.go b/config/crds/zz_generated.deepcopy.go index 2958be35..bea7701b 100644 --- a/config/crds/zz_generated.deepcopy.go +++ b/config/crds/zz_generated.deepcopy.go @@ -1234,3 +1234,18 @@ func (in *StorageClass) DeepCopy() *StorageClass { in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SupportBundleVersion) DeepCopyInto(out *SupportBundleVersion) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SupportBundleVersion. +func (in *SupportBundleVersion) DeepCopy() *SupportBundleVersion { + if in == nil { + return nil + } + out := new(SupportBundleVersion) + in.DeepCopyInto(out) + return out +} diff --git a/go.mod b/go.mod index 26ead2b3..171f5d26 100644 --- a/go.mod +++ b/go.mod @@ -3,17 +3,31 @@ 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/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/gin-gonic/gin v1.4.0 github.com/gizak/termui/v3 v3.1.0 github.com/golang/snappy v0.0.1 // indirect + github.com/google/uuid v1.1.1 // indirect + 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/mholt/archiver v3.1.1+incompatible github.com/nwaples/rardecode v1.0.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/pkg/errors v0.8.1 + github.com/replicatedcom/support-bundle v0.27.1 // indirect github.com/sergi/go-diff v1.0.0 // indirect github.com/spf13/cobra v0.0.3 github.com/spf13/viper v1.4.0 diff --git a/go.sum b/go.sum index 0052eabe..ef928416 100644 --- a/go.sum +++ b/go.sum @@ -7,6 +7,12 @@ cloud.google.com/go v0.41.0 h1:NFvqUTDnSNYPX5oReekmB+D+90jrJIcVImxQ3qrBVgM= cloud.google.com/go v0.41.0/go.mod h1:OauMR7DV8fzvZIl2qg6rkaIhD/vmgk4iwEw/h6ercmg= 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/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= @@ -14,6 +20,8 @@ github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdko 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/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/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= @@ -42,6 +50,14 @@ 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= @@ -70,8 +86,10 @@ 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 h1:Wz+5lgoB0kkuqLEc6NVmwRknTKP6dTGbSqvhZtBI/j0= 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 h1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80nA= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logr/logr v0.1.0 h1:M1Tv3VzNlEHg6uyACnRdtrploV2P7wZqH8BoQMtz0cg= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= @@ -149,6 +167,10 @@ github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:Fecb 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.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +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/golang-lru v0.0.0-20180201235237-0fb14efe8c47 h1:UnszMmmmm5vLwWzDjTFVIkfhvWF1NdrmChl8L2NUDCw= github.com/hashicorp/golang-lru v0.0.0-20180201235237-0fb14efe8c47/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo= @@ -159,6 +181,8 @@ 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= @@ -234,6 +258,8 @@ github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGV github.com/onsi/gomega v1.4.2/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/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= @@ -282,6 +308,7 @@ github.com/prometheus/procfs v0.0.3 h1:CTwfnzjQ+8dS6MhHHu4YswVAD99sL2wjPqP+VkURm 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/replicatedcom/support-bundle v0.27.1/go.mod h1:oOJp4t6vM75MBxkBcsGAuwEWRIhq+7wSI05qU/fajPY= 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= diff --git a/pkg/analyze/cluster_version.go b/pkg/analyze/cluster_version.go index 21645af1..f4a591c2 100644 --- a/pkg/analyze/cluster_version.go +++ b/pkg/analyze/cluster_version.go @@ -11,6 +11,7 @@ import ( ) func analyzeClusterVersion(analyzer *troubleshootv1beta1.ClusterVersion, getCollectedFileContents func(string) ([]byte, error)) (*AnalyzeResult, error) { + // TODO: ++++++++ clusterInfo, err := getCollectedFileContents("cluster-info/cluster_version.json") if err != nil { return nil, err diff --git a/pkg/apis/troubleshoot/v1beta1/version.go b/pkg/apis/troubleshoot/v1beta1/version.go new file mode 100644 index 00000000..7331f0b0 --- /dev/null +++ b/pkg/apis/troubleshoot/v1beta1/version.go @@ -0,0 +1,7 @@ +package v1beta1 + +type SupportBundleVersion struct { + ApiVersion string `json:"apiVersion" yaml:"apiVersion"` + Kind string `json:"kind" yaml:"kind"` + LayoutVersion string `json:"layoutVersion" yaml:"layoutVersion"` +} diff --git a/pkg/apis/troubleshoot/v1beta1/zz_generated.deepcopy.go b/pkg/apis/troubleshoot/v1beta1/zz_generated.deepcopy.go index 120cc808..e3beac3f 100644 --- a/pkg/apis/troubleshoot/v1beta1/zz_generated.deepcopy.go +++ b/pkg/apis/troubleshoot/v1beta1/zz_generated.deepcopy.go @@ -1250,3 +1250,18 @@ func (in *StorageClass) DeepCopy() *StorageClass { in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SupportBundleVersion) DeepCopyInto(out *SupportBundleVersion) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SupportBundleVersion. +func (in *SupportBundleVersion) DeepCopy() *SupportBundleVersion { + if in == nil { + return nil + } + out := new(SupportBundleVersion) + in.DeepCopyInto(out) + return out +} diff --git a/pkg/convert/errors.go b/pkg/convert/errors.go new file mode 100644 index 00000000..2d06f7e4 --- /dev/null +++ b/pkg/convert/errors.go @@ -0,0 +1,32 @@ +package convert + +import ( + "github.com/pkg/errors" +) + +type FuncError struct { + Name string // Name of function. + Err error // Pre-formatted error. +} + +func (e FuncError) Error() string { + return e.Err.Error() +} + +// Panic will panic with a recoverable error. +func Panic(name string, err error) error { + panic(Error(name, err)) +} + +// Error will wrap a template error in an ExecError causing template.Execute to recover. +func Error(name string, err error) error { + return FuncError{ + Name: name, + Err: err, + } +} + +func IsFuncError(err error) bool { + _, ok := errors.Cause(err).(FuncError) + return ok +} diff --git a/pkg/convert/supportbundle.go b/pkg/convert/supportbundle.go new file mode 100644 index 00000000..a377ad63 --- /dev/null +++ b/pkg/convert/supportbundle.go @@ -0,0 +1,107 @@ +package convert + +import ( + "fmt" + "regexp" + "strings" + + multierror "github.com/hashicorp/go-multierror" + analyze "github.com/replicatedhq/troubleshoot/pkg/analyze" +) + +type Meta struct { + Name string `json:"name,omitempty" yaml:"name,omitempty" hcl:"name,omitempty"` + Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty" hcl:"labels,omitempty"` +} + +const ( + SeverityError Severity = "error" + SeverityWarn Severity = "warn" + SeverityInfo Severity = "info" + SeverityDebug Severity = "debug" +) + +type Severity string + +type Insight struct { + Meta `json:",inline" yaml:",inline" hcl:",inline"` + + Primary string `json:"primary" yaml:"primary" hcl:"primary"` + Detail string `json:"detail" yaml:"detail" hcl:"detail"` + Severity Severity `json:"severity,omitempty" yaml:"severity,omitempty" hcl:"severity,omitempty"` +} + +type Result struct { + Meta `json:",inline" yaml:",inline" hcl:",inline"` + + Insight *Insight `json:"insight" yaml:"insight" hcl:"insight"` + Severity Severity `json:"severity" yaml:"severity" hcl:"severity"` + AnalyzerSpec string `json:"analyzerSpec" yaml:"analyzerSpec" hcl:"analyzerSpec"` + Variables map[string]interface{} `json:"variables,omitempty" yaml:"variables,omitempty" hcl:"variables,omitempty"` + Error string `json:"error,omitempty" yaml:"error,omitempty" hcl:"error,omitempty"` +} + +func (m *Insight) Render(data interface{}) (*Insight, error) { + var multiErr *multierror.Error + var err error + built := &Insight{ + Meta: m.Meta, + Severity: m.Severity, + } + built.Primary, err = String(m.Primary, data) + multiErr = multierror.Append(multiErr, errWrap(err, "build primary")) + built.Detail, err = String(m.Detail, data) + multiErr = multierror.Append(multiErr, errWrap(err, "build detail")) + return built, multiErr.ErrorOrNil() +} + +func errWrap(err error, text string) error { + if err == nil { + return nil + } + return fmt.Errorf("%s: %v", text, err) +} + +func FromAnalyzerResult(input []*analyze.AnalyzeResult) []*Result { + reg := regexp.MustCompile("[^a-zA-Z0-9]+") + + result := make([]*Result, 0) + for _, i := range input { + name := reg.ReplaceAllString(strings.ToLower(i.Title), ".") + r := &Result{ + Meta: Meta{ + Name: name, + Labels: map[string]string{ + "desiredPosition": "1", + "iconKey": "gray_checkmark", + }, + }, + Insight: &Insight{ + Meta: Meta{ + Name: name, + Labels: map[string]string{ + "iconKey": "gray_checkmark", + }, + }, + Primary: i.Title, + Detail: i.Message, + }, + AnalyzerSpec: "", + Variables: map[string]interface{}{}, + } + if i.IsFail { + r.Severity = SeverityError + r.Insight.Severity = SeverityError + r.Error = i.Message + } else if i.IsWarn { + r.Severity = SeverityWarn + r.Insight.Severity = SeverityWarn + } else if i.IsPass { + r.Severity = SeverityDebug + r.Insight.Severity = SeverityDebug + } + result = append(result, r) + } + + return result +} diff --git a/pkg/convert/templates.go b/pkg/convert/templates.go new file mode 100644 index 00000000..d921b2cc --- /dev/null +++ b/pkg/convert/templates.go @@ -0,0 +1,63 @@ +package convert + +import ( + "bytes" + "strconv" + "sync" + "text/template" +) + +var ( + funcMap = template.FuncMap{} + funcMapMu sync.Mutex +) + +func String(text string, data interface{}) (string, error) { + return Execute(text, data) +} + +func Bool(text string, data interface{}) (bool, error) { + str, err := Execute(text, data) + if err != nil { + return false, err + } + return strconv.ParseBool(str) +} + +func Execute(text string, data interface{}) (string, error) { + tmpl, err := template.New(text). + Delims("{{repl", "}}"). + Funcs(funcMap). + Parse(text) + if err != nil { + return "", err + } + var buf bytes.Buffer + err = func() (err error) { + defer errRecover(&err) + err = tmpl.Execute(&buf, data) + return + }() + return buf.String(), err +} + +func RegisterFunc(key string, fn interface{}) { + funcMapMu.Lock() + funcMap[key] = fn + funcMapMu.Unlock() +} + +// errRecover is the handler that turns panics into returns from FuncMaps. +func errRecover(errp *error) { + e := recover() + if e != nil { + switch err := e.(type) { + case FuncError: + *errp = err // Keep the wrapper. + case error: + *errp = err // Catch panics from template functions + default: + panic(e) + } + } +}