Skip to content

Commit

Permalink
Detect the managed instance group that created a VM (#897)
Browse files Browse the repository at this point in the history
This will be used to populate new resource attributes for the instance group manager.

Tested manually on a non-MIG and a MIG instance using the new `TestDetectorPrint` test.
  • Loading branch information
quentinmit committed Sep 30, 2024
1 parent bb4687e commit 40a2c3c
Show file tree
Hide file tree
Showing 15 changed files with 511 additions and 261 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ generate: $(STRINGER) $(PROTOC)

.PHONY: fieldalignment
fieldalignment: $(FIELDALIGNMENT)
$(MAKE) for-all-package CMD="$(FIELDALIGNMENT) -fix ."
$(MAKE) for-all-package CMD="$(FIELDALIGNMENT) -fix -test=false ."


.PHONY: for-all-mod
Expand Down
4 changes: 3 additions & 1 deletion detectors/gcp/app_engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@

package gcp

import "context"

const (
// See https://cloud.google.com/appengine/docs/flexible/python/migrating#modules
// for the environment variables available in GAE environments.
Expand Down Expand Up @@ -67,7 +69,7 @@ func (d *Detector) AppEngineFlexAvailabilityZoneAndRegion() (string, string, err

// AppEngineStandardAvailabilityZone returns the zone the app engine service is running in.
func (d *Detector) AppEngineStandardAvailabilityZone() (string, error) {
return d.metadata.Zone()
return d.metadata.ZoneWithContext(context.TODO())
}

// AppEngineStandardCloudRegion returns the region the app engine service is running in.
Expand Down
82 changes: 40 additions & 42 deletions detectors/gcp/app_engine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,137 +22,135 @@ import (
)

func TestAppEngineServiceName(t *testing.T) {
d := NewTestDetector(&FakeMetadataProvider{}, &FakeOSProvider{
d := NewTestDetector(newFakeMetadataTransport(t), &FakeOSProvider{
Vars: map[string]string{
gaeServiceEnv: "my-service",
},
})
serviceName, err := d.AppEngineServiceName()
assert.NoError(t, err)
assert.Equal(t, serviceName, "my-service")
assert.Equal(t, "my-service", serviceName)
}

func TestAppEngineServiceNameErr(t *testing.T) {
d := NewTestDetector(&FakeMetadataProvider{}, &FakeOSProvider{
d := NewTestDetector(newFakeMetadataTransport(t), &FakeOSProvider{
Vars: map[string]string{},
})
name, err := d.AppEngineServiceName()
assert.Error(t, err)
assert.Equal(t, name, "")
assert.Equal(t, "", name)
}

func TestAppEngineServiceVersion(t *testing.T) {
d := NewTestDetector(&FakeMetadataProvider{}, &FakeOSProvider{
d := NewTestDetector(newFakeMetadataTransport(t), &FakeOSProvider{
Vars: map[string]string{
gaeVersionEnv: "my-version",
},
})
version, err := d.AppEngineServiceVersion()
assert.NoError(t, err)
assert.Equal(t, version, "my-version")
assert.Equal(t, "my-version", version)
}

func TestAppEngineServiceVersionErr(t *testing.T) {
d := NewTestDetector(&FakeMetadataProvider{}, &FakeOSProvider{
d := NewTestDetector(newFakeMetadataTransport(t), &FakeOSProvider{
Vars: map[string]string{},
})
version, err := d.AppEngineServiceVersion()
assert.Error(t, err)
assert.Equal(t, version, "")
assert.Equal(t, "", version)
}

func TestAppEngineServiceInstance(t *testing.T) {
d := NewTestDetector(&FakeMetadataProvider{}, &FakeOSProvider{
d := NewTestDetector(newFakeMetadataTransport(t), &FakeOSProvider{
Vars: map[string]string{
gaeInstanceEnv: "instance-123",
},
})
instance, err := d.AppEngineServiceInstance()
assert.NoError(t, err)
assert.Equal(t, instance, "instance-123")
assert.Equal(t, "instance-123", instance)
}

func TestAppEngineServiceInstanceErr(t *testing.T) {
d := NewTestDetector(&FakeMetadataProvider{}, &FakeOSProvider{
d := NewTestDetector(newFakeMetadataTransport(t), &FakeOSProvider{
Vars: map[string]string{},
})
instance, err := d.AppEngineServiceInstance()
assert.Error(t, err)
assert.Equal(t, instance, "")
assert.Equal(t, "", instance)
}

func TestAppEngineStandardAvailabilityZone(t *testing.T) {
d := NewTestDetector(&FakeMetadataProvider{
FakeZone: "us16",
}, &FakeOSProvider{})
d := NewTestDetector(newFakeMetadataTransport(t,
"instance/zone", "us16",
), &FakeOSProvider{})
zone, err := d.AppEngineStandardAvailabilityZone()
assert.NoError(t, err)
assert.Equal(t, zone, "us16")
assert.Equal(t, "us16", zone)
}

func TestAppEngineStandardAvailabilityZoneErr(t *testing.T) {
d := NewTestDetector(&FakeMetadataProvider{
d := NewTestDetector(&FakeMetadataTransport{
Err: fmt.Errorf("fake error"),
}, &FakeOSProvider{})
zone, err := d.AppEngineStandardAvailabilityZone()
assert.Error(t, err)
assert.Equal(t, zone, "")
assert.Equal(t, "", zone)
}

func TestAppEngineStandardCloudRegion(t *testing.T) {
d := NewTestDetector(&FakeMetadataProvider{
Attributes: map[string]string{regionMetadataAttr: "/projects/123/regions/us-central1"},
}, &FakeOSProvider{})
d := NewTestDetector(newFakeMetadataTransport(t,
regionMetadataAttr, "/projects/123/regions/us-central1",
), &FakeOSProvider{})
instance, err := d.AppEngineStandardCloudRegion()
assert.NoError(t, err)
assert.Equal(t, instance, "us-central1")
assert.Equal(t, "us-central1", instance)
}

func TestAppEngineStandardCloudRegionErr(t *testing.T) {
d := NewTestDetector(&FakeMetadataProvider{
d := NewTestDetector(&FakeMetadataTransport{
Err: fmt.Errorf("fake error"),
}, &FakeOSProvider{})
instance, err := d.AppEngineStandardCloudRegion()
assert.Error(t, err)
assert.Equal(t, instance, "")
assert.Equal(t, "", instance)
}

func TestAppEngineFlexAvailabilityZoneAndRegion(t *testing.T) {
d := NewTestDetector(&FakeMetadataProvider{
FakeZone: "us-central1-c",
}, &FakeOSProvider{})
d := NewTestDetector(newFakeMetadataTransport(t), &FakeOSProvider{})
zone, region, err := d.AppEngineFlexAvailabilityZoneAndRegion()
assert.NoError(t, err)
assert.Equal(t, zone, "us-central1-c")
assert.Equal(t, region, "us-central1")
assert.Equal(t, fakeZone, zone)
assert.Equal(t, fakeRegion, region)
}

func TestAppEngineFlexAvailabilityZoneAndRegionMalformedZone(t *testing.T) {
d := NewTestDetector(&FakeMetadataProvider{
FakeZone: "us-central1",
}, &FakeOSProvider{})
d := NewTestDetector(newFakeMetadataTransport(t,
"instance/zone", "us-central1",
), &FakeOSProvider{})
zone, region, err := d.AppEngineFlexAvailabilityZoneAndRegion()
assert.Error(t, err)
assert.Equal(t, zone, "")
assert.Equal(t, region, "")
assert.Equal(t, "", zone)
assert.Equal(t, "", region)
}

func TestAppEngineFlexAvailabilityZoneAndRegionNoZone(t *testing.T) {
d := NewTestDetector(&FakeMetadataProvider{
FakeZone: "",
}, &FakeOSProvider{})
d := NewTestDetector(newFakeMetadataTransport(t,
"instance/zone", "",
), &FakeOSProvider{})
zone, region, err := d.AppEngineFlexAvailabilityZoneAndRegion()
assert.Error(t, err)
assert.Equal(t, zone, "")
assert.Equal(t, region, "")
assert.Equal(t, "", zone)
assert.Equal(t, "", region)
}

func TestAppEngineFlexAvailabilityZoneAndRegionErr(t *testing.T) {
d := NewTestDetector(&FakeMetadataProvider{
d := NewTestDetector(&FakeMetadataTransport{
Err: fmt.Errorf("fake error"),
}, &FakeOSProvider{})
zone, region, err := d.AppEngineFlexAvailabilityZoneAndRegion()
assert.Error(t, err)
assert.Equal(t, zone, "")
assert.Equal(t, region, "")
assert.Equal(t, "", zone)
assert.Equal(t, "", region)
}
24 changes: 12 additions & 12 deletions detectors/gcp/bms_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,61 +21,61 @@ import (
)

func TestBareMetalSolutionInstanceID(t *testing.T) {
d := NewTestDetector(&FakeMetadataProvider{}, &FakeOSProvider{
d := NewTestDetector(&FakeMetadataTransport{}, &FakeOSProvider{
Vars: map[string]string{
bmsInstanceIDEnv: "my-host-123",
},
})
instanceID, err := d.BareMetalSolutionInstanceID()
assert.NoError(t, err)
assert.Equal(t, instanceID, "my-host-123")
assert.Equal(t, "my-host-123", instanceID)
}

func TestBareMetalSolutionInstanceIDErr(t *testing.T) {
d := NewTestDetector(&FakeMetadataProvider{}, &FakeOSProvider{
d := NewTestDetector(&FakeMetadataTransport{}, &FakeOSProvider{
Vars: map[string]string{},
})
instanceID, err := d.BareMetalSolutionInstanceID()
assert.Error(t, err)
assert.Equal(t, instanceID, "")
assert.Equal(t, "", instanceID)
}

func TestBareMetalSolutionCloudRegion(t *testing.T) {
d := NewTestDetector(&FakeMetadataProvider{}, &FakeOSProvider{
d := NewTestDetector(&FakeMetadataTransport{}, &FakeOSProvider{
Vars: map[string]string{
bmsRegionEnv: "us-central1",
},
})
region, err := d.BareMetalSolutionCloudRegion()
assert.NoError(t, err)
assert.Equal(t, region, "us-central1")
assert.Equal(t, "us-central1", region)
}

func TestBareMetalSolutionCloudRegionErr(t *testing.T) {
d := NewTestDetector(&FakeMetadataProvider{}, &FakeOSProvider{
d := NewTestDetector(&FakeMetadataTransport{}, &FakeOSProvider{
Vars: map[string]string{},
})
region, err := d.BareMetalSolutionCloudRegion()
assert.Error(t, err)
assert.Equal(t, region, "")
assert.Equal(t, "", region)
}

func TestBareMetalSolutionProjectID(t *testing.T) {
d := NewTestDetector(&FakeMetadataProvider{}, &FakeOSProvider{
d := NewTestDetector(&FakeMetadataTransport{}, &FakeOSProvider{
Vars: map[string]string{
bmsProjectIDEnv: "my-test-project",
},
})
projectID, err := d.BareMetalSolutionProjectID()
assert.NoError(t, err)
assert.Equal(t, projectID, "my-test-project")
assert.Equal(t, "my-test-project", projectID)
}

func TestBareMetalSolutionProjectIDErr(t *testing.T) {
d := NewTestDetector(&FakeMetadataProvider{}, &FakeOSProvider{
d := NewTestDetector(&FakeMetadataTransport{}, &FakeOSProvider{
Vars: map[string]string{},
})
projectID, err := d.BareMetalSolutionProjectID()
assert.Error(t, err)
assert.Equal(t, projectID, "")
assert.Equal(t, "", projectID)
}
27 changes: 13 additions & 14 deletions detectors/gcp/detector.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@
package gcp

import (
"context"
"errors"
"os"
"strings"

"cloud.google.com/go/compute/metadata"
)
Expand Down Expand Up @@ -68,27 +70,24 @@ func (d *Detector) CloudPlatform() Platform {

// ProjectID returns the ID of the project in which this program is running.
func (d *Detector) ProjectID() (string, error) {
return d.metadata.ProjectID()
// N.B. d.metadata.ProjectIDWithContext(context.TODO()) is cached globally, so if we use it here it's untestable.
s, err := d.metadata.GetWithContext(context.TODO(), "project/project-id")
return strings.TrimSpace(s), err
}

// instanceID returns the ID of the project in which this program is running.
func (d *Detector) instanceID() (string, error) {
// N.B. d.metadata.InstanceIDWithContext(context.TODO()) is cached globally, so if we use it here it's untestable.
s, err := d.metadata.GetWithContext(context.TODO(), "instance/id")
return strings.TrimSpace(s), err
}

// Detector collects resource information for all GCP platforms.
type Detector struct {
metadata metadataProvider
metadata *metadata.Client
os osProvider
}

// metadataProvider contains the subset of the metadata.Client functions used
// by this resource Detector to allow testing with a fake implementation.
type metadataProvider interface {
ProjectID() (string, error)
InstanceID() (string, error)
Get(string) (string, error)
InstanceName() (string, error)
Hostname() (string, error)
Zone() (string, error)
InstanceAttributeValue(string) (string, error)
}

// osProvider contains the subset of the os package functions used by.
type osProvider interface {
LookupEnv(string) (string, bool)
Expand Down
Loading

0 comments on commit 40a2c3c

Please sign in to comment.