From e069a4e972116023bcc1b434ed2438e8a8643998 Mon Sep 17 00:00:00 2001 From: Amir Szekely Date: Sat, 8 Jan 2022 18:29:58 -0800 Subject: [PATCH] feat: Submit report to central DB --- .github/workflows/goreleaser.yml | 1 + .goreleaser.yaml | 2 +- README.md | 24 ++++---- benchmarks/all.go | 16 +++-- cmd/root.go | 40 ++++++------ go.mod | 11 +++- go.sum | 31 +++++++++- providers/aws.go | 49 ++++++++++++--- providers/azure.go | 26 ++++---- providers/cpuid.go | 43 ++++++------- providers/gcp.go | 41 ++++++++----- providers/memory.go | 40 ++++++------ providers/provider.go | 4 +- reporting/db.go | 39 ++++++++++++ reporting/print.go | 102 +++++++++++++++++++++++++++++++ reporting/structs.go | 62 +++++++++++++++++++ 16 files changed, 403 insertions(+), 128 deletions(-) create mode 100644 reporting/db.go create mode 100644 reporting/print.go create mode 100644 reporting/structs.go diff --git a/.github/workflows/goreleaser.yml b/.github/workflows/goreleaser.yml index 3b3714d..921e5cc 100644 --- a/.github/workflows/goreleaser.yml +++ b/.github/workflows/goreleaser.yml @@ -25,6 +25,7 @@ jobs: args: release --rm-dist env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + API_KEY: ${{ secrets.API_KEY }} - name: Upload artifacts uses: actions/upload-artifact@v2 diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 060ce63..0322968 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -11,7 +11,7 @@ builds: goarch: - amd64 - arm64 - ldflags: -s -w -X cloud-z/cmd.version={{.Version}} -X cloud-z/cmd.commit={{.Commit}} -X cloud-z/cmd.date={{.Date}} -X cloud-z/cmd.builtBy=goreleaser + ldflags: -s -w -X cloud-z/cmd.version={{.Version}} -X cloud-z/cmd.commit={{.Commit}} -X cloud-z/cmd.date={{.Date}} -X cloud-z/cmd.builtBy=goreleaser -X cloud-z/reporting.apiKey={{ if index .Env "API_KEY" }}{{ .Env.API_KEY }}{{ else }}no-env{{ end }} archives: - replacements: linux: Linux diff --git a/README.md b/README.md index 70ed3d7..cb577d6 100644 --- a/README.md +++ b/README.md @@ -6,40 +6,40 @@ Cloud-Z gathers information and perform benchmarks on cloud instances in multipl - [x] CPU information including type, number of available cores, and cache sizes - [x] RAM information - [x] Benchmark CPU -- [ ] Optionally contribute data to central DB +- [x] Optionally contribute data to central DB - [ ] Storage devices information - [ ] Benchmark storage - [ ] Network devices information - [ ] Benchmark network -### Supported clouds: +### Supported Clouds * Amazon Web Services (AWS) * Google Cloud Platform (GCP) * Microsoft Azure -### Supported platforms: - -* Windows - * x86_64 - * arm64 -* Linux - * x86_64 - * arm64 - [![CI](https://github.com/CloudSnorkel/cloud-z/actions/workflows/goreleaser.yml/badge.svg)](https://github.com/CloudSnorkel/cloud-z/actions/workflows/goreleaser.yml) ## Usage Cloud-Z is provided as a single binary that can be downloaded from the [releases page](https://github.com/CloudSnorkel/cloud-z/releases). +### Download Links + +* [Linux x64](https://z.cloudsnorkel.com/cloud-z/download/linux/x64) +* [Linux arm64 (Graviton)](https://z.cloudsnorkel.com/cloud-z/download/linux/arm64) +* [Windows x64](https://z.cloudsnorkel.com/cloud-z/download/windows/x64) +* [Windows arm64 (Graviton)](https://z.cloudsnorkel.com/cloud-z/download/windows/arm64) + ``` +$ curl -sLo cloud-z.tar.gz https://z.cloudsnorkel.com/cloud-z/download/linux/x64 +$ tar xzf cloud-z.tar.gz $ sudo ./cloud-z +---------------+-----------------------+ | Cloud | AWS | | AMI | ami-0712c70d31ba14f8a | | Instance ID | i-12345678900112344 | -| Instance type | t4g.nano | +| Instance type | t4.nano | +---------------+-----------------------+ +-----------------+--------------------------------+ | CPU | Intel(R) Xeon(R) CPU @ 2.20GHz | diff --git a/benchmarks/all.go b/benchmarks/all.go index 20e1a9f..89bc846 100644 --- a/benchmarks/all.go +++ b/benchmarks/all.go @@ -1,10 +1,16 @@ package benchmarks -import "fmt" +import ( + "cloud-z/reporting" +) -func AllBenchmarks() [][]string { - return [][]string{ - // TODO single and multi thread - {"fbench", fmt.Sprintf("%v seconds (lower is better)", fbench())}, +func AllBenchmarks(report *reporting.Report) { + // TODO single and multi thread + report.Benchmarks = map[string]reporting.BenchmarkReport{ + "fbench": { + Version: 1, + Result: fbench(), + Unit: reporting.Seconds, + }, } } diff --git a/cmd/root.go b/cmd/root.go index 8fce5e0..1bfb28b 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -3,10 +3,10 @@ package cmd import ( "cloud-z/benchmarks" "cloud-z/providers" + "cloud-z/reporting" "fmt" - "github.com/olekukonko/tablewriter" + "github.com/AlecAivazis/survey/v2" "github.com/spf13/cobra" - "log" "os" ) @@ -22,6 +22,8 @@ var rootCmd = &cobra.Command{ Short: "Cloud-Z gathers information on cloud instances", Version: fmt.Sprintf("%s, commit %s, built at %s by %s", version, commit, date, builtBy), Run: func(cmd *cobra.Command, args []string) { + report := &reporting.Report{} + allCloudProviders := []providers.CloudProvider{ &providers.AwsProvider{}, &providers.GcpProvider{}, @@ -32,33 +34,29 @@ var rootCmd = &cobra.Command{ for _, provider := range allCloudProviders { // TODO detect faster with goroutines? if provider.Detect() { - data, err := provider.GetData() - if err != nil { - log.Fatalln(err) - } - - printTable(data) - + provider.GetData(report) detectedCloud = true } } if !detectedCloud { - println("Unable to detect cloud provider") + report.AddError("Unable to detect cloud provider") } - printTable(providers.GetCPUInfo()) - printTable(providers.GetMemoryInfo()) - printTable(benchmarks.AllBenchmarks()) - }, -} + providers.GetCPUInfo(report) + providers.GetMemoryInfo(report) + benchmarks.AllBenchmarks(report) -func printTable(data [][]string) { - table := tablewriter.NewWriter(os.Stdout) - for _, v := range data { - table.Append(v) - } - table.Render() + report.Print() + + submit := false + // TODO three options - yes, no, show json + survey.AskOne(&survey.Confirm{Message: "Would you like to anonymously contribute this data to https://z.cloudsnorkel.com/? Your IP address may be logged, but instance id and other PII will not be sent."}, &submit) + if submit { + //report.PrintJson() + report.Send() + } + }, } func Execute() { diff --git a/go.mod b/go.mod index f50fece..5c229c1 100644 --- a/go.mod +++ b/go.mod @@ -3,19 +3,26 @@ module cloud-z go 1.17 require ( + github.com/AlecAivazis/survey/v2 v2.3.2 github.com/cloudfoundry/gosigar v1.3.2 github.com/digitalocean/go-smbios v0.0.0-20180907143718-390a4f403a8e github.com/inhies/go-bytesize v0.0.0-20210819104631-275770b98743 + github.com/jedib0t/go-pretty/v6 v6.2.4 github.com/klauspost/cpuid/v2 v2.0.9 - github.com/olekukonko/tablewriter v0.0.5 github.com/spf13/cobra v1.3.0 ) require ( github.com/inconshreveable/mousetrap v1.0.0 // indirect + github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect + github.com/mattn/go-colorable v0.1.12 // indirect + github.com/mattn/go-isatty v0.0.14 // indirect github.com/mattn/go-runewidth v0.0.13 // indirect + github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect github.com/pkg/errors v0.8.1 // indirect github.com/rivo/uniseg v0.2.0 // indirect github.com/spf13/pflag v1.0.5 // indirect - golang.org/x/sys v0.0.0-20211205182925-97ca703d548d // indirect + golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect + golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect + golang.org/x/text v0.3.7 // indirect ) diff --git a/go.sum b/go.sum index 4cc10c3..104cbf2 100644 --- a/go.sum +++ b/go.sum @@ -46,9 +46,13 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/AlecAivazis/survey/v2 v2.3.2 h1:TqTB+aDDCLYhf9/bD2TwSO8u8jDSmMUd2SUVO4gCnU8= +github.com/AlecAivazis/survey/v2 v2.3.2/go.mod h1:TH2kPCDU3Kqq7pLbnCWwZXDBjnhZtmsCle5EiYDJ2fg= 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/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= +github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8 h1:xzYJEypr/85nBpB11F9br+3HUrpgb+fcm5iADzXXYEw= +github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -110,6 +114,7 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= +github.com/fzipp/gocyclo v0.3.1/go.mod h1:DJHO6AUmbdqj2ET4Z9iArSuwWgYDRryYt2wASxc7x3E= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -225,6 +230,8 @@ github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOn github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4= +github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174 h1:WlZsjVhE8Af9IcZDGgJGQpNflI3+MJSBhsgT5PCtzBQ= +github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A= github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= @@ -232,6 +239,8 @@ github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NH github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inhies/go-bytesize v0.0.0-20210819104631-275770b98743 h1:X3Xxno5Ji8idrNiUoFc7QyXpqhSYlDRYQmc7mlpMBzU= github.com/inhies/go-bytesize v0.0.0-20210819104631-275770b98743/go.mod h1:KrtyD5PFj++GKkFS/7/RRrfnRhAMGQwy75GLCHWrCNs= +github.com/jedib0t/go-pretty/v6 v6.2.4 h1:wdaj2KHD2W+mz8JgJ/Q6L/T5dB7kyqEFI16eLq7GEmk= +github.com/jedib0t/go-pretty/v6 v6.2.4/go.mod h1:+nE9fyyHGil+PuISTCrp7avEdo6bqoMwqZnuiK2r2a0= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -239,6 +248,8 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= @@ -250,24 +261,32 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.4 h1:5Myjjh3JY/NaAi4IsUbHADytDyl1VE1Y9PXDlL+P/VQ= +github.com/kr/pty v1.1.4/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= +github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI= +github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= @@ -283,8 +302,6 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= -github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo v1.2.1-0.20160409220416-2c2e9bb47b4e h1:IMj5WMpYeLyqT/sJnXJJ3Ze/7qoDXc/TJbKaIY8RRBA= github.com/onsi/ginkgo v1.2.1-0.20160409220416-2c2e9bb47b4e/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v1.2.0 h1:tQjc4uvqBp0z424R9V/S2L18penoUiwZftoY0t48IZ4= @@ -295,6 +312,7 @@ github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCko github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -335,6 +353,7 @@ github.com/spf13/viper v1.10.0/go.mod h1:SoyBPwAtKDzypXNDFKN5kzH7ppppbGZtls1UpIy 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/testify v1.1.4/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -367,6 +386,7 @@ golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= @@ -481,6 +501,7 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20171114162044-bf42f188b9bc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180816055513-1c9583448a9c/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -545,9 +566,13 @@ golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211205182925-97ca703d548d h1:FjkYO/PPp4Wi0EAUOVLxePm7qVW4r4ctbWpURyuOD0E= golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.1.1-0.20171102192421-88f656faf3f3/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/providers/aws.go b/providers/aws.go index 04d74b2..0142059 100644 --- a/providers/aws.go +++ b/providers/aws.go @@ -2,7 +2,9 @@ package providers import ( "cloud-z/metadata" + "cloud-z/reporting" "errors" + "fmt" ) type AwsProvider struct { @@ -20,7 +22,7 @@ func (provider *AwsProvider) Detect() bool { return server == "EC2ws" } -func (provider *AwsProvider) getMetadataWithPossibleToken(url string, target interface{}) error { +func (provider *AwsProvider) getMetadataJsonWithPossibleToken(url string, target interface{}) error { if provider.token != nil { return metadata.GetMetadataJson(url, target, "X-aws-ec2-metadata-token", *provider.token) } @@ -39,6 +41,28 @@ func (provider *AwsProvider) getMetadataWithPossibleToken(url string, target int return metadata.GetMetadataJson(url, target, "X-aws-ec2-metadata-token", *provider.token) } +func (provider *AwsProvider) getMetadataTextWithPossibleToken(url string) (string, error) { + // TODO refactor to share code with JSON function + if provider.token != nil { + return metadata.GetMetadataText(url, "X-aws-ec2-metadata-token", *provider.token) + } + + result, err := metadata.GetMetadataText(url, "", "") + if errors.Is(err, metadata.UnauthorizedError) { + tokenValue, err := metadata.PutMetadata("/latest/api/token", "X-aws-ec2-metadata-token-ttl-seconds", "120") + if err != nil { + return "", err + } + provider.token = &tokenValue + + return metadata.GetMetadataText(url, "X-aws-ec2-metadata-token", *provider.token) + } else if err != nil { + return "", err + } else { + return result, nil + } +} + type instanceIdentityDocumentType struct { MarketplaceProductCodes *[]string `json:"marketplaceProductCodes"` AvailabilityZone string `json:"availabilityZone"` @@ -62,7 +86,7 @@ func (provider *AwsProvider) getInstanceIdentity() error { } provider.instanceIdentityDocument = &instanceIdentityDocumentType{} - err := provider.getMetadataWithPossibleToken("/2021-07-15/dynamic/instance-identity/document", provider.instanceIdentityDocument) + err := provider.getMetadataJsonWithPossibleToken("/2021-07-15/dynamic/instance-identity/document", provider.instanceIdentityDocument) if err != nil { provider.instanceIdentityDocument = nil return err @@ -71,17 +95,22 @@ func (provider *AwsProvider) getInstanceIdentity() error { return nil } -func (provider *AwsProvider) GetData() ([][]string, error) { +func (provider *AwsProvider) GetData(report *reporting.Report) { + report.Cloud = "AWS" + // https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-identity-documents.html err := provider.getInstanceIdentity() if err != nil { - return [][]string{}, err + report.AddError(fmt.Sprintf("Unable to get metadata: %v", err)) } - return [][]string{ - {"Cloud", "AWS"}, - {"AMI", provider.instanceIdentityDocument.ImageId}, - {"Instance ID", provider.instanceIdentityDocument.InstanceId}, - {"Instance type", provider.instanceIdentityDocument.InstanceType}, - }, nil + report.ImageId = provider.instanceIdentityDocument.ImageId + report.InstanceId = provider.instanceIdentityDocument.InstanceId + report.InstanceType = provider.instanceIdentityDocument.InstanceType + report.Region = provider.instanceIdentityDocument.Region + + report.AvailabilityZone, err = provider.getMetadataTextWithPossibleToken("/2021-07-15/meta-data/placement/availability-zone-id") + if err != nil { + report.AddError(fmt.Sprintf("Unable to get az: %v", err)) + } } diff --git a/providers/azure.go b/providers/azure.go index b44c66b..c79bc04 100644 --- a/providers/azure.go +++ b/providers/azure.go @@ -2,6 +2,8 @@ package providers import ( "cloud-z/metadata" + "cloud-z/reporting" + "fmt" "strings" ) @@ -22,23 +24,25 @@ func (provider *AzureProvider) getMetadata(url string) (string, error) { return metadata.GetMetadataText(url, "Metadata", "true") } -func (provider *AzureProvider) GetData() ([][]string, error) { +func (provider *AzureProvider) GetData(report *reporting.Report) { + report.Cloud = "Azure" + var attributes [][]string attributes = append(attributes, []string{"Cloud", "Azure"}) // https://docs.microsoft.com/en-us/azure/virtual-machines/windows/instance-metadata-service?tabs=linux#instance-metadata - urls := [][]string{ - {"Instance id", "/metadata/instance/compute/vmId?api-version=2017-08-01&format=text"}, - {"Instance type", "/metadata/instance/compute/vmSize?api-version=2017-08-01&format=text"}, - {"Zone", "/metadata/instance/compute/zone?api-version=2017-08-01&format=text"}, + urls := map[*string]string{ + &report.InstanceId: "/metadata/instance/compute/vmId?api-version=2017-08-01&format=text", + &report.InstanceType: "/metadata/instance/compute/vmSize?api-version=2017-08-01&format=text", + &report.AvailabilityZone: "/metadata/instance/compute/zone?api-version=2017-08-01&format=text", } - for _, url := range urls { - data, err := provider.getMetadata(url[1]) + + for target, url := range urls { + data, err := provider.getMetadata(url) if err != nil { - return [][]string{}, err + report.AddError(fmt.Sprintf("Failed to download: %v", url)) + continue } - attributes = append(attributes, []string{url[0], data}) + *target = data } - - return attributes, nil } diff --git a/providers/cpuid.go b/providers/cpuid.go index 55426e3..10d3ac1 100644 --- a/providers/cpuid.go +++ b/providers/cpuid.go @@ -1,33 +1,24 @@ package providers import ( - "fmt" - "github.com/inhies/go-bytesize" + "cloud-z/reporting" "github.com/klauspost/cpuid/v2" - "strings" ) -func int2bytes(b int) string { - //return bytesize.New(float64(b)).Format("%d", "", false) - return bytesize.New(float64(b)).String() -} - -func GetCPUInfo() [][]string { - return [][]string{ - {"CPU", cpuid.CPU.BrandName}, - {"Vendor", cpuid.CPU.VendorString}, - {"Vendor ID", fmt.Sprintf("%v", cpuid.CPU.VendorID.String())}, - {"Family", fmt.Sprintf("%v", cpuid.CPU.Family)}, - {"MHz", fmt.Sprintf("%v", cpuid.CPU.Hz/1_000_000)}, - {"Logical cores", fmt.Sprintf("%v", cpuid.CPU.LogicalCores)}, - {"Physical cores", fmt.Sprintf("%v", cpuid.CPU.PhysicalCores)}, - {"Thread per core", fmt.Sprintf("%v", cpuid.CPU.ThreadsPerCore)}, - {"Boost frequency", fmt.Sprintf("%v", cpuid.CPU.BoostFreq)}, - {"L1 Cache", fmt.Sprintf("%v instruction, %v data", int2bytes(cpuid.CPU.Cache.L1I), int2bytes(cpuid.CPU.Cache.L1D))}, - {"L2 Cache", int2bytes(cpuid.CPU.Cache.L2)}, - {"L2 Cache", int2bytes(cpuid.CPU.Cache.L2)}, - {"L3 Cache", int2bytes(cpuid.CPU.Cache.L3)}, - {"Cache line", fmt.Sprintf("%v", cpuid.CPU.CacheLine)}, - {"Features", strings.Join(cpuid.CPU.FeatureSet(), ", ")}, - } +func GetCPUInfo(report *reporting.Report) { + report.CPU.Description = cpuid.CPU.BrandName + report.CPU.Vendor = cpuid.CPU.VendorString + report.CPU.VendorId = cpuid.CPU.VendorID.String() + report.CPU.Family = cpuid.CPU.Family + report.CPU.MHz = int(cpuid.CPU.Hz / 1_000_000) + report.CPU.LogicalCores = cpuid.CPU.LogicalCores + report.CPU.PhysicalCores = cpuid.CPU.PhysicalCores + report.CPU.ThreadsPerCore = cpuid.CPU.ThreadsPerCore + report.CPU.BoostFrequency = int(cpuid.CPU.BoostFreq) + report.CPU.CacheL1Instruction = cpuid.CPU.Cache.L1I + report.CPU.CacheL1Data = cpuid.CPU.Cache.L1D + report.CPU.CacheL2 = cpuid.CPU.Cache.L2 + report.CPU.CacheL3 = cpuid.CPU.Cache.L3 + report.CPU.CacheLine = cpuid.CPU.CacheLine + report.CPU.Features = cpuid.CPU.FeatureSet() } diff --git a/providers/gcp.go b/providers/gcp.go index 389fca0..02e05bf 100644 --- a/providers/gcp.go +++ b/providers/gcp.go @@ -2,6 +2,9 @@ package providers import ( "cloud-z/metadata" + "cloud-z/reporting" + "fmt" + "strings" ) type GcpProvider struct { @@ -21,25 +24,35 @@ func (provider *GcpProvider) getMetadata(url string) (string, error) { return metadata.GetMetadataText(url, "Metadata-Flavor", "Google") } -func (provider *GcpProvider) GetData() ([][]string, error) { - var attributes [][]string - attributes = append(attributes, []string{"Cloud", "GCP"}) +func lastPartOfString(s string) string { + parts := strings.Split(s, "/") + if len(parts) > 1 { + return parts[len(parts)-1] + } + return s +} + +func (provider *GcpProvider) GetData(report *reporting.Report) { + report.Cloud = "GCP" // https://cloud.google.com/appengine/docs/standard/java/accessing-instance-metadata - urls := [][]string{ - {"Instance id", "/computeMetadata/v1/instance/id"}, - {"Instance type", "/computeMetadata/v1/instance/machine-type"}, - {"CPU platform", "/computeMetadata/v1/instance/cpu-platform"}, - {"Zone", "/computeMetadata/v1/instance/zone"}, - {"Image", "/computeMetadata/v1/instance/image"}, + urls := map[*string]string{ + &report.InstanceId: "/computeMetadata/v1/instance/id", + &report.InstanceType: "/computeMetadata/v1/instance/machine-type", + //"CPU platform": "/computeMetadata/v1/instance/cpu-platform", + &report.AvailabilityZone: "/computeMetadata/v1/instance/zone", + &report.ImageId: "/computeMetadata/v1/instance/image", } - for _, url := range urls { - data, err := provider.getMetadata(url[1]) + for target, url := range urls { + data, err := provider.getMetadata(url) if err != nil { - return [][]string{}, err + report.AddError(fmt.Sprintf("Failed to download: %v", url)) + continue } - attributes = append(attributes, []string{url[0], data}) + *target = data } - return attributes, nil + // remove project id which is PII + report.InstanceType = lastPartOfString(report.InstanceType) + report.AvailabilityZone = lastPartOfString(report.AvailabilityZone) } diff --git a/providers/memory.go b/providers/memory.go index ece36a2..3b49e8e 100644 --- a/providers/memory.go +++ b/providers/memory.go @@ -2,12 +2,12 @@ package providers import ( "bytes" + "cloud-z/reporting" "encoding/binary" "fmt" "github.com/cloudfoundry/gosigar" "github.com/digitalocean/go-smbios/smbios" "io" - "log" ) // https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.1.1.pdf @@ -126,18 +126,19 @@ func readString(reader io.Reader, strings []string) string { return "" } -func GetMemoryInfo() [][]string { +func GetMemoryInfo(report *reporting.Report) { mem := sigar.Mem{} - mem.Get() - result := [][]string{ - {"Total RAM", sigar.FormatSize(mem.Total) + "B"}, + if err := mem.Get(); err != nil { + report.AddError(fmt.Sprintf("Unable to get total RAM: %v", err)) } + report.Memory.Total = mem.Total + // Find SMBIOS data in operating system-specific location. rc, _, err := smbios.Stream() if err != nil { - log.Printf("Failed to open SMBIOS stream, try sudo: %v\n", err) - return result + report.AddError(fmt.Sprintf("Failed to open SMBIOS stream, try sudo: %v\n", err)) + return } // Be sure to close the stream! defer rc.Close() @@ -146,28 +147,23 @@ func GetMemoryInfo() [][]string { d := smbios.NewDecoder(rc) records, err := d.Decode() if err != nil { - log.Printf("Failed to decode SMBIOS structures: %v\n", err) - return result + report.AddError(fmt.Sprintf("Failed to decode SMBIOS structures: %v\n", err)) + return } - i := 1 for _, record := range records { if record.Header.Type == 17 { memDevice := readMemoryDevice(record) - prefix := fmt.Sprintf("Stick #%v: ", i) - - result = append(result, []string{prefix + "location", memDevice.Location}) - result = append(result, []string{prefix + "type", memDevice.memoryType() + " " + memDevice.formFactor()}) - result = append(result, []string{prefix + "size", memDevice.size()}) - result = append(result, []string{prefix + "data width", fmt.Sprintf("%v-bit", memDevice.DataWidth)}) - result = append(result, []string{prefix + "total width", fmt.Sprintf("%v-bit", memDevice.TotalWidth)}) - result = append(result, []string{prefix + "speed", fmt.Sprintf("%v MHz", memDevice.Speed)}) - - i += 1 + report.Memory.Sticks = append(report.Memory.Sticks, reporting.MemoryStickReport{ + Location: memDevice.Location, + Type: memDevice.memoryType() + " " + memDevice.formFactor(), + Size: memDevice.Size, + DataWidth: memDevice.DataWidth, + TotalWidth: memDevice.TotalWidth, + MHz: memDevice.Speed, + }) } } - - return result } func readMemoryDevice(record *smbios.Structure) MemoryDevice { diff --git a/providers/provider.go b/providers/provider.go index 8ca3549..aa6c0bf 100644 --- a/providers/provider.go +++ b/providers/provider.go @@ -1,6 +1,8 @@ package providers +import "cloud-z/reporting" + type CloudProvider interface { Detect() bool - GetData() ([][]string, error) + GetData(*reporting.Report) } diff --git a/reporting/db.go b/reporting/db.go new file mode 100644 index 0000000..37b1042 --- /dev/null +++ b/reporting/db.go @@ -0,0 +1,39 @@ +package reporting + +import ( + "bytes" + "encoding/json" + "fmt" + "net/http" +) + +var apiKey = "not-set" + +func (report *Report) Send() { + if apiKey == "not-set" { + return + } + + reportJson, err := json.Marshal(report) + if err != nil { + panic(err) + } + + req, err := http.NewRequest("POST", "https://z.cloudsnorkel.com/submit/", bytes.NewReader(reportJson)) + if err != nil { + panic(err) + } + + req.Header.Add("X-API-Key", apiKey) + + resp, err := http.DefaultClient.Do(req) + if err != nil { + panic(err) + } + + defer resp.Body.Close() + + if resp.StatusCode != 200 { + fmt.Printf("Bad status code: %v", resp.Status) + } +} diff --git a/reporting/print.go b/reporting/print.go new file mode 100644 index 0000000..ab0ebfc --- /dev/null +++ b/reporting/print.go @@ -0,0 +1,102 @@ +package reporting + +import ( + "encoding/json" + "fmt" + sigar "github.com/cloudfoundry/gosigar" + "github.com/inhies/go-bytesize" + "github.com/jedib0t/go-pretty/v6/table" + "github.com/jedib0t/go-pretty/v6/text" + "github.com/klauspost/cpuid/v2" + "os" + "strings" +) + +func int2bytes(b int) string { + //return bytesize.New(float64(b)).Format("%d", "", false) + return bytesize.New(float64(b)).String() +} + +func (report *Report) Print() { + t := table.NewWriter() + t.SetOutputMirror(os.Stdout) + t.SetAllowedRowLength(120) + t.SetTitle("Instance Data") + t.AppendRow(table.Row{"Cloud", report.Cloud}) + t.AppendRow(table.Row{"Instance type", report.InstanceType}) + t.AppendRow(table.Row{"Region", report.Region}) + t.AppendRow(table.Row{"Availability zone", report.AvailabilityZone}) + t.AppendRow(table.Row{"Instance id", report.InstanceId}) + t.AppendRow(table.Row{"Image id", report.ImageId}) + t.SetStyle(table.StyleColoredMagentaWhiteOnBlack) + t.Render() + + report.printCPU() + report.printMemory() + report.printErrors() +} + +func (report *Report) printCPU() { + t := table.NewWriter() + t.SetOutputMirror(os.Stdout) + t.SetAllowedRowLength(120) + t.SetTitle("CPU") + t.AppendRow(table.Row{"CPU", cpuid.CPU.BrandName}) + t.AppendRow(table.Row{"Vendor", cpuid.CPU.VendorString}) + t.AppendRow(table.Row{"Vendor ID", fmt.Sprintf("%v", cpuid.CPU.VendorID.String())}) + t.AppendRow(table.Row{"Family", fmt.Sprintf("%v", cpuid.CPU.Family)}) + t.AppendRow(table.Row{"MHz", fmt.Sprintf("%v", cpuid.CPU.Hz/1_000_000)}) + t.AppendRow(table.Row{"Logical cores", fmt.Sprintf("%v", cpuid.CPU.LogicalCores)}) + t.AppendRow(table.Row{"Physical cores", fmt.Sprintf("%v", cpuid.CPU.PhysicalCores)}) + t.AppendRow(table.Row{"Thread per core", fmt.Sprintf("%v", cpuid.CPU.ThreadsPerCore)}) + t.AppendRow(table.Row{"Boost frequency", fmt.Sprintf("%v", cpuid.CPU.BoostFreq)}) + t.AppendRow(table.Row{"L1 Cache", fmt.Sprintf("%v instruction, %v data", int2bytes(cpuid.CPU.Cache.L1I), int2bytes(cpuid.CPU.Cache.L1D))}) + t.AppendRow(table.Row{"L2 Cache", int2bytes(cpuid.CPU.Cache.L2)}) + t.AppendRow(table.Row{"L2 Cache", int2bytes(cpuid.CPU.Cache.L2)}) + t.AppendRow(table.Row{"L3 Cache", int2bytes(cpuid.CPU.Cache.L3)}) + t.AppendRow(table.Row{"Cache line", fmt.Sprintf("%v", cpuid.CPU.CacheLine)}) + t.AppendRow(table.Row{"Features", text.WrapSoft(strings.Join(cpuid.CPU.FeatureSet(), ", "), 80)}) + t.SetStyle(table.StyleColoredMagentaWhiteOnBlack) + t.Render() +} + +func (report *Report) printMemory() { + t := table.NewWriter() + t.SetOutputMirror(os.Stdout) + t.SetAllowedRowLength(120) + t.SetTitle("Memory") + t.AppendRow(table.Row{"Total RAM", sigar.FormatSize(report.Memory.Total) + "B"}) + rowConfigAutoMerge := table.RowConfig{AutoMerge: true} + t.SetColumnConfigs([]table.ColumnConfig{ + {Number: 1, AutoMerge: true}, + }) + for i, stick := range report.Memory.Sticks { + stickCol := fmt.Sprintf("Stick #%v", i+1) + t.AppendRow(table.Row{stickCol, "Location", stick.Location}, rowConfigAutoMerge) + t.AppendRow(table.Row{stickCol, "Type", stick.Type}, rowConfigAutoMerge) + t.AppendRow(table.Row{stickCol, "Size", stick.Size}, rowConfigAutoMerge) + t.AppendRow(table.Row{stickCol, "Data width", fmt.Sprintf("%v-bit", stick.DataWidth)}, rowConfigAutoMerge) + t.AppendRow(table.Row{stickCol, "Total width", fmt.Sprintf("%v-bit", stick.TotalWidth)}, rowConfigAutoMerge) + t.AppendRow(table.Row{stickCol, "Speed", fmt.Sprintf("%v MHz", stick.MHz)}, rowConfigAutoMerge) + } + t.SetStyle(table.StyleColoredMagentaWhiteOnBlack) + t.Render() +} + +func (report *Report) printErrors() { + if len(report.Errors) == 0 { + return + } + + fmt.Println(text.Bold.Sprint("Errors:")) + + for _, err := range report.Errors { + fmt.Println(text.FgRed.Sprintf(" %v", err)) + } +} + +func (report *Report) PrintJson() { + // TODO colors? + result, _ := json.MarshalIndent(report, "", " ") + fmt.Print(string(result)) +} diff --git a/reporting/structs.go b/reporting/structs.go new file mode 100644 index 0000000..632dd1f --- /dev/null +++ b/reporting/structs.go @@ -0,0 +1,62 @@ +package reporting + +type Report struct { + Cloud string `json:"cloud"` + InstanceId string `json:"-"` + InstanceType string `json:"instanceType"` + ImageId string `json:"-"` + Region string `json:"region"` + AvailabilityZone string `json:"availabilityZone"` + CPU CpuReport `json:"cpu"` + Memory MemoryReport `json:"memory"` + Benchmarks map[string]BenchmarkReport `json:"benchmarks"` + Errors []string `json:"errors,omitempty"` +} + +type CpuReport struct { + Description string `json:"description"` + Vendor string `json:"vendor"` + VendorId string `json:"vendorId"` + Family int `json:"family"` + MHz int `json:"mhz"` + LogicalCores int `json:"logicalCores"` + PhysicalCores int `json:"physicalCores"` + ThreadsPerCore int `json:"threadsPerCore"` + BoostFrequency int `json:"boostFrequency"` + CacheL1Instruction int `json:"cacheL1Instruction"` + CacheL1Data int `json:"cacheL1Data"` + CacheL2 int `json:"cacheL2"` + CacheL3 int `json:"cacheL3"` + CacheLine int `json:"cacheLine"` + Features []string `json:"features"` +} + +type MemoryReport struct { + Total uint64 `json:"total"` + Sticks []MemoryStickReport `json:"sticks"` +} + +type MemoryStickReport struct { + Location string `json:"location"` + Type string `json:"type"` + Size uint16 `json:"size"` + DataWidth uint16 `json:"dataWidth"` + TotalWidth uint16 `json:"totalWidth"` + MHz uint16 `json:"mhz"` +} + +type UnitType string + +const ( + Seconds UnitType = "seconds" +) + +type BenchmarkReport struct { + Version int `json:"version"` + Result float64 `json:"result"` + Unit UnitType `json:"unit"` +} + +func (report *Report) AddError(error string) { + report.Errors = append(report.Errors, error) +}