diff --git a/internal/cloud/metadata.go b/internal/cloud/metadata.go index 97b1e7902..94ffe5941 100644 --- a/internal/cloud/metadata.go +++ b/internal/cloud/metadata.go @@ -2,6 +2,7 @@ package cloud import ( "os/exec" + "regexp" "strings" log "github.com/sirupsen/logrus" @@ -9,6 +10,8 @@ import ( const ( Azure = "azure" + Aws = "aws" + Gcp = "gcp" // DMI chassis asset tag for Azure machines, needed to identify wether or not we are running on Azure // This is actually ASCII-encoded, the decoding into a string results in "MSFT AZURE VM" azureDmiTag = "7783-7084-3265-9085-8269-3286-77" @@ -23,25 +26,74 @@ type CustomCommand func(name string, arg ...string) *exec.Cmd var customExecCommand CustomCommand = exec.Command -func IdentifyCloudProvider() (string, error) { - log.Info("Identifying if the VM is running in a cloud environment...") +// All these detection methods are based in crmsh code, which has been refined over the years +// https://github.com/ClusterLabs/crmsh/blob/master/crmsh/utils.py#L2009 + +func identifyAzure() (bool, error) { + log.Debug("Checking if the VM is running on Azure...") output, err := customExecCommand("dmidecode", "-s", "chassis-asset-tag").Output() if err != nil { - return "", err + return false, err + } + + provider := strings.TrimSpace(string(output)) + log.Debugf("dmidecode output: %s", provider) + + return provider == azureDmiTag, nil +} + +func identifyAws() (bool, error) { + log.Debug("Checking if the VM is running on Aws...") + output, err := customExecCommand("dmidecode", "-s", "system-version").Output() + if err != nil { + return false, err + } + + provider := strings.TrimSpace(string(output)) + log.Debugf("dmidecode output: %s", provider) + + return regexp.MatchString(".*amazon.*", provider) +} + +func identifyGcp() (bool, error) { + log.Debug("Checking if the VM is running on Gcp...") + output, err := customExecCommand("dmidecode", "-s", "bios-vendor").Output() + if err != nil { + return false, err } provider := strings.TrimSpace(string(output)) log.Debugf("dmidecode output: %s", provider) - switch string(provider) { - case azureDmiTag: + return regexp.MatchString(".*Google.*", provider) +} + +func IdentifyCloudProvider() (string, error) { + log.Info("Identifying if the VM is running in a cloud environment...") + + if result, err := identifyAzure(); err != nil { + return "", err + } else if result { log.Infof("VM is running on %s", Azure) return Azure, nil - default: - log.Info("VM is not running in any recognized cloud provider") - return "", nil } + if result, err := identifyAws(); err != nil { + return "", err + } else if result { + log.Infof("VM is running on %s", Aws) + return Aws, nil + } + + if result, err := identifyGcp(); err != nil { + return "", err + } else if result { + log.Infof("VM is running on %s", Gcp) + return Gcp, nil + } + + log.Info("VM is not running in any recognized cloud provider") + return "", nil } func NewCloudInstance() (*CloudInstance, error) { diff --git a/internal/cloud/metadata_test.go b/internal/cloud/metadata_test.go index 967224433..ead0e3e10 100644 --- a/internal/cloud/metadata_test.go +++ b/internal/cloud/metadata_test.go @@ -50,6 +50,56 @@ func TestIdentifyCloudProviderAzure(t *testing.T) { assert.NoError(t, err) } +func mockDmidecodeAws() *exec.Cmd { + return exec.Command("echo", "4.11.amazon") +} + +func TestIdentifyCloudProviderAws(t *testing.T) { + mockCommand := new(mocks.CustomCommand) + + customExecCommand = mockCommand.Execute + + mockCommand.On("Execute", "dmidecode", "-s", "chassis-asset-tag").Return( + mockDmidecodeNoCloud(), + ) + + mockCommand.On("Execute", "dmidecode", "-s", "system-version").Return( + mockDmidecodeAws(), + ) + + provider, err := IdentifyCloudProvider() + + assert.Equal(t, "aws", provider) + assert.NoError(t, err) +} + +func mockDmidecodeGcp() *exec.Cmd { + return exec.Command("echo", "Google") +} + +func TestIdentifyCloudProviderGcp(t *testing.T) { + mockCommand := new(mocks.CustomCommand) + + customExecCommand = mockCommand.Execute + + mockCommand.On("Execute", "dmidecode", "-s", "chassis-asset-tag").Return( + mockDmidecodeNoCloud(), + ) + + mockCommand.On("Execute", "dmidecode", "-s", "system-version").Return( + mockDmidecodeNoCloud(), + ) + + mockCommand.On("Execute", "dmidecode", "-s", "bios-vendor").Return( + mockDmidecodeGcp(), + ) + + provider, err := IdentifyCloudProvider() + + assert.Equal(t, "gcp", provider) + assert.NoError(t, err) +} + func mockDmidecodeNoCloud() *exec.Cmd { return exec.Command("echo", "") } @@ -63,6 +113,14 @@ func TestIdentifyCloudProviderNoCloud(t *testing.T) { mockDmidecodeNoCloud(), ) + mockCommand.On("Execute", "dmidecode", "-s", "system-version").Return( + mockDmidecodeNoCloud(), + ) + + mockCommand.On("Execute", "dmidecode", "-s", "bios-vendor").Return( + mockDmidecodeNoCloud(), + ) + provider, err := IdentifyCloudProvider() assert.Equal(t, "", provider) @@ -110,6 +168,14 @@ func TestNewCloudInstanceNoCloud(t *testing.T) { mockDmidecodeNoCloud(), ) + mockCommand.On("Execute", "dmidecode", "-s", "system-version").Return( + mockDmidecodeNoCloud(), + ) + + mockCommand.On("Execute", "dmidecode", "-s", "bios-vendor").Return( + mockDmidecodeNoCloud(), + ) + c, err := NewCloudInstance() assert.NoError(t, err)