-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 1e8716f
Showing
13 changed files
with
1,238 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
name: goreleaser | ||
|
||
on: | ||
pull_request: | ||
push: | ||
|
||
jobs: | ||
goreleaser: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- name: Checkout | ||
uses: actions/checkout@v2 | ||
with: | ||
fetch-depth: 0 | ||
|
||
- name: Set up Go | ||
uses: actions/setup-go@v2 | ||
with: | ||
go-version: 1.17 | ||
|
||
- name: Run GoReleaser | ||
uses: goreleaser/goreleaser-action@v2 | ||
with: | ||
version: latest | ||
args: release --rm-dist | ||
env: | ||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
|
||
- name: Upload artifacts | ||
uses: actions/upload-artifact@v2 | ||
with: | ||
name: snapshot | ||
path: dist/* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
dist/ | ||
.idea/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
project_name: cloud-z | ||
before: | ||
hooks: | ||
- go mod tidy | ||
builds: | ||
- env: | ||
- CGO_ENABLED=0 | ||
goos: | ||
- linux | ||
- windows | ||
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 | ||
archives: | ||
- replacements: | ||
linux: Linux | ||
windows: Windows | ||
amd64: x86_64 | ||
checksum: | ||
name_template: 'checksums.txt' | ||
snapshot: | ||
name_template: "{{ incpatch .Version }}-next" | ||
changelog: | ||
use: github | ||
sort: asc | ||
groups: | ||
- title: Features | ||
regexp: "^.*feat[(\\w)]*:+.*$" | ||
order: 0 | ||
- title: 'Bug fixes' | ||
regexp: "^.*fix[(\\w)]*:+.*$" | ||
order: 1 | ||
- title: Others | ||
order: 999 | ||
filters: | ||
exclude: | ||
- '^docs:' | ||
- '^test:' | ||
- '^ci:' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
# Cloud-Z | ||
|
||
Cloud-Z gathers information and perform benchmarks on cloud instances in multiple cloud providers. | ||
|
||
- [x] Cloud type, instance id, and type | ||
- [ ] CPU information including type, number of available cores, and cache sizes | ||
- [ ] Benchmark CPU | ||
- [ ] Storage devices information | ||
- [ ] Benchmark storage | ||
- [ ] Network devices information | ||
- [ ] Benchmark network | ||
|
||
### 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). | ||
|
||
```bash | ||
$ ./cloud-z | ||
+---------------+-----------------------+ | ||
| Cloud | AWS | | ||
| AMI | ami-0712c70d31ba14f8a | | ||
| Instance ID | i-12345678900112344 | | ||
| Instance type | t4g.nano | | ||
+---------------+-----------------------+ | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
package cmd | ||
|
||
import ( | ||
"cloud-z/providers" | ||
"fmt" | ||
"github.com/olekukonko/tablewriter" | ||
"github.com/spf13/cobra" | ||
"log" | ||
"os" | ||
) | ||
|
||
var ( | ||
version = "dev" | ||
commit = "none" | ||
date = "unknown" | ||
builtBy = "unknown" | ||
) | ||
|
||
var rootCmd = &cobra.Command{ | ||
Use: "cloud-z", | ||
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) { | ||
allProviders := []providers.Provider{ | ||
&providers.AwsProvider{}, | ||
&providers.GcpProvider{}, | ||
&providers.AzureProvider{}, | ||
} | ||
|
||
for _, provider := range allProviders { | ||
// TODO detect faster with goroutines? | ||
if provider.Detect() { | ||
data, err := provider.GetData() | ||
if err != nil { | ||
log.Fatalln(err) | ||
} | ||
|
||
table := tablewriter.NewWriter(os.Stdout) | ||
for _, v := range data { | ||
table.Append(v) | ||
} | ||
table.Render() | ||
|
||
return | ||
} | ||
} | ||
|
||
println("Unable to detect cloud provider") | ||
}, | ||
} | ||
|
||
func Execute() { | ||
if err := rootCmd.Execute(); err != nil { | ||
_, _ = fmt.Fprintln(os.Stderr, err) | ||
os.Exit(1) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
module cloud-z | ||
|
||
go 1.17 | ||
|
||
require ( | ||
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/mattn/go-runewidth v0.0.13 // indirect | ||
github.com/rivo/uniseg v0.2.0 // indirect | ||
github.com/spf13/pflag v1.0.5 // indirect | ||
) |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package main | ||
|
||
import ( | ||
"cloud-z/cmd" | ||
) | ||
|
||
func main() { | ||
cmd.Execute() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
package metadata | ||
|
||
import ( | ||
"encoding/json" | ||
"errors" | ||
"io" | ||
"net/http" | ||
) | ||
|
||
func GetMetadataHeader(header string) (string, error) { | ||
resp, err := http.Get("http://169.254.169.254/") | ||
|
||
if err != nil { | ||
return "", err | ||
} | ||
|
||
resp.Body.Close() | ||
|
||
return resp.Header.Get(header), nil | ||
} | ||
|
||
var UnauthorizedError = errors.New("metadata server returned 401") | ||
|
||
func requestMetadata(action string, url string, headerName string, headerValue string) (*http.Response, error) { | ||
req, err := http.NewRequest(action, "http://169.254.169.254"+url, nil) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
if headerName != "" && headerValue != "" { | ||
req.Header.Add(headerName, headerValue) | ||
} | ||
|
||
resp, err := http.DefaultClient.Do(req) | ||
if err != nil { | ||
return resp, err | ||
} | ||
|
||
if resp.StatusCode == 401 { | ||
defer resp.Body.Close() | ||
return resp, UnauthorizedError | ||
} else if resp.StatusCode != 200 { | ||
defer resp.Body.Close() | ||
return resp, errors.New(resp.Status) | ||
} | ||
|
||
return resp, nil | ||
} | ||
|
||
func GetMetadataJson(url string, target interface{}, headerName string, headerValue string) error { | ||
resp, err := requestMetadata("GET", url, headerName, headerValue) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
defer resp.Body.Close() | ||
return json.NewDecoder(resp.Body).Decode(target) | ||
} | ||
|
||
func GetMetadataText(url string, headerName string, headerValue string) (string, error) { | ||
resp, err := requestMetadata("GET", url, headerName, headerValue) | ||
if err != nil { | ||
return "", err | ||
} | ||
|
||
defer resp.Body.Close() | ||
|
||
body, err := io.ReadAll(resp.Body) | ||
if err != nil { | ||
return "", err | ||
} | ||
|
||
return string(body), nil | ||
} | ||
|
||
func PutMetadata(url string, headerName string, headerValue string) (string, error) { | ||
resp, err := requestMetadata("PUT", url, headerName, headerValue) | ||
if err != nil { | ||
return "", err | ||
} | ||
|
||
defer resp.Body.Close() | ||
|
||
body, err := io.ReadAll(resp.Body) | ||
if err != nil { | ||
return "", err | ||
} | ||
|
||
return string(body), nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
package providers | ||
|
||
import ( | ||
"cloud-z/metadata" | ||
"errors" | ||
"log" | ||
) | ||
|
||
type AwsProvider struct { | ||
token *string | ||
instanceIdentityDocument *instanceIdentityDocumentType | ||
} | ||
|
||
func (provider *AwsProvider) Detect() bool { | ||
server, err := metadata.GetMetadataHeader("Server") | ||
|
||
if err != nil { | ||
log.Println(err) | ||
} | ||
|
||
return server == "EC2ws" | ||
} | ||
|
||
func (provider *AwsProvider) getMetadataWithPossibleToken(url string, target interface{}) error { | ||
if provider.token != nil { | ||
return metadata.GetMetadataJson(url, target, "X-aws-ec2-metadata-token", *provider.token) | ||
} | ||
|
||
err := metadata.GetMetadataJson(url, target, "", "") | ||
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 | ||
} else { | ||
return err | ||
} | ||
|
||
return metadata.GetMetadataJson(url, target, "X-aws-ec2-metadata-token", *provider.token) | ||
} | ||
|
||
type instanceIdentityDocumentType struct { | ||
MarketplaceProductCodes *[]string `json:"marketplaceProductCodes"` | ||
AvailabilityZone string `json:"availabilityZone"` | ||
PrivateIp string `json:"privateIp"` | ||
Version string `json:"version"` | ||
InstanceId string `json:"instanceId"` | ||
BillingProducts *[]string `json:"billingProducts"` | ||
InstanceType string `json:"instanceType"` | ||
AccountId string `json:"accountId"` | ||
ImageId string `json:"imageId"` | ||
PendingTime string `json:"pendingTime"` | ||
Architecture string `json:"architecture"` | ||
KernelId *string `json:"kernelId"` | ||
RamdiskId *string `json:"ramdiskId"` | ||
Region string `json:"region"` | ||
} | ||
|
||
func (provider *AwsProvider) getInstanceIdentity() error { | ||
if provider.instanceIdentityDocument != nil { | ||
return nil | ||
} | ||
|
||
provider.instanceIdentityDocument = &instanceIdentityDocumentType{} | ||
err := provider.getMetadataWithPossibleToken("/2021-07-15/dynamic/instance-identity/document", provider.instanceIdentityDocument) | ||
if err != nil { | ||
provider.instanceIdentityDocument = nil | ||
return err | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func (provider *AwsProvider) GetData() ([][]string, error) { | ||
// https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-identity-documents.html | ||
err := provider.getInstanceIdentity() | ||
if err != nil { | ||
return [][]string{}, err | ||
} | ||
|
||
return [][]string{ | ||
{"Cloud", "AWS"}, | ||
{"AMI", provider.instanceIdentityDocument.ImageId}, | ||
{"Instance ID", provider.instanceIdentityDocument.InstanceId}, | ||
{"Instance type", provider.instanceIdentityDocument.InstanceType}, | ||
}, nil | ||
} |
Oops, something went wrong.