diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2e31a8b --- /dev/null +++ b/.gitignore @@ -0,0 +1,31 @@ +Gopkg.lock +*.log +# vendor/ + +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, build with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Ignore vendor dir and glide stuff +*.lock +#vendor/ + +# ignore binary (oops) +fit-client + +.firstrun +client.key +modules/ +*.log +middleware/ + +detectcloud \ No newline at end of file diff --git a/README.md b/README.md index 6881389..0ee86c7 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,21 @@ # detectcloud +http.Client timeout is set to `120ms`. Sometimes hitting the metadata service to fast will return unknown instead of the cloud provider detected. + + package main + + import ( + "fmt" + "gitlab.com/taskfitio/lib/detectcloud" + ) + + func main() { + + // detectcloud.Detect() will return a string + // Amazon Web Services, Microsoft Azure, Digital Ocean + // Google Compute Engine, OpenStack, SoftLayer, Vultr + // unknown + + fmt.Println(detectcloud.Detect()) + + } diff --git a/aws.go b/aws.go new file mode 100644 index 0000000..0183df4 --- /dev/null +++ b/aws.go @@ -0,0 +1,13 @@ +package detectcloud + +import ( + "net/http" +) + +func detectAWS() string { + resp, err := hc.Get("http://169.254.169.254/latest/") + if err == nil && resp.StatusCode == http.StatusOK { + return "Amazon Web Services" + } + return "" +} diff --git a/azure.go b/azure.go new file mode 100644 index 0000000..c5c2ced --- /dev/null +++ b/azure.go @@ -0,0 +1,14 @@ +package detectcloud + +import ( + "net/http" +) + +// https://azure.microsoft.com/en-us/blog/what-just-happened-to-my-vm-in-vm-metadata-service/ +func detectAzure() string { + resp, err := hc.Get("http://169.254.169.254/metadata/v1/InstanceInfo") + if err == nil && resp.StatusCode == http.StatusOK { + return "Microsoft Azure" + } + return "" +} diff --git a/detect.go b/detect.go new file mode 100644 index 0000000..fd05c41 --- /dev/null +++ b/detect.go @@ -0,0 +1,79 @@ +package detectcloud + +import ( + "net/http" + "runtime" + "sync" + "time" +) + +var hc = &http.Client{Timeout: 120 * time.Millisecond} + +type Clouds struct { + Aws string + Azure string + Do string + Gce string + Ost string + Sl string + Vr string +} + +func Detect() string { + if runtime.GOOS != "darwin" { + var c Clouds + var wg sync.WaitGroup + wg.Add(7) + go func() { + defer wg.Done() + c.Aws = detectAWS() + }() + go func() { + defer wg.Done() + c.Azure = detectAzure() + }() + go func() { + defer wg.Done() + c.Do = detectDigitalOcean() + }() + go func() { + defer wg.Done() + c.Gce = detectGCE() + }() + go func() { + defer wg.Done() + c.Ost = detectOpenStack() + }() + go func() { + defer wg.Done() + c.Sl = detectSoftlayer() + }() + go func() { + defer wg.Done() + c.Vr = detectVultr() + }() + wg.Wait() + if c.Aws != "" { + return c.Aws + } + if c.Azure != "" { + return c.Azure + } + if c.Do != "" { + return c.Do + } + if c.Gce != "" { + return c.Gce + } + if c.Ost != "" { + return c.Ost + } + if c.Sl != "" { + return c.Sl + } + if c.Vr != "" { + return c.Vr + } + } + return "unknown" +} diff --git a/digitalocean.go b/digitalocean.go new file mode 100644 index 0000000..d06946c --- /dev/null +++ b/digitalocean.go @@ -0,0 +1,14 @@ +package detectcloud + +import ( + "net/http" +) + +// https://developers.digitalocean.com/documentation/metadata/#metadata-in-json +func detectDigitalOcean() string { + resp, err := hc.Get("http://169.254.169.254/metadata/v1.json") + if err == nil && resp.StatusCode == http.StatusOK { + return "Digital Ocean" + } + return "" +} diff --git a/gce.go b/gce.go new file mode 100644 index 0000000..2e3f599 --- /dev/null +++ b/gce.go @@ -0,0 +1,23 @@ +package detectcloud + +import ( + "net/http" +) + +// https://cloud.google.com/compute/docs/storing-retrieving-metadata#endpoints +// curl "http://metadata.google.internal/computeMetadata/v1/instance/tags" -H "Metadata-Flavor: Google" +func detectGCE() string { + r, err := http.NewRequest("GET", "http://metadata.google.internal/computeMetadata/v1/instance/tags", nil) + if err != nil { + return "" + } + r.Header.Add("Metadata-Flavor", "Google") + resp, err := hc.Do(r) + if err != nil { + return "" + } + if resp.StatusCode == http.StatusOK { + return "Google Compute Engine" + } + return "" +} diff --git a/openstack.go b/openstack.go new file mode 100644 index 0000000..18cd847 --- /dev/null +++ b/openstack.go @@ -0,0 +1,21 @@ +package detectcloud + +import ( + "io/ioutil" + "runtime" + "strings" +) + +func detectOpenStack() string { + if runtime.GOOS != "windows" { + data, err := ioutil.ReadFile("/sys/class/dmi/id/sys_vendor") + if err != nil { + return "" + } + if strings.Contains(string(data), "OpenStack Foundation") { + return "OpenStack" + } + return "" + } + return "" +} diff --git a/softlayer.go b/softlayer.go new file mode 100644 index 0000000..ec22ef4 --- /dev/null +++ b/softlayer.go @@ -0,0 +1,15 @@ +package detectcloud + +import ( + "net/http" +) + +// https://sldn.softlayer.com/blog/jarteche/getting-started-user-data-and-post-provisioning-scripts +// https://github.com/bodenr/cci/wiki/SL-user-metadata +func detectSoftlayer() string { + resp, err := hc.Get("https://api.service.softlayer.com/rest/v3/SoftLayer_Resource_Metadata/UserMetadata.txt") + if err == nil && (resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusNotFound) { + return "SoftLayer" + } + return "" +} diff --git a/vultr.go b/vultr.go new file mode 100644 index 0000000..89a5d44 --- /dev/null +++ b/vultr.go @@ -0,0 +1,14 @@ +package detectcloud + +import ( + "net/http" +) + +// https://www.vultr.com/metadata/ +func detectVultr() string { + resp, err := hc.Get("http://169.254.169.254/v1.json") + if err == nil && resp.StatusCode == http.StatusOK { + return "Vultr" + } + return "" +}