Add ability to override GCE zone and instance.

Fixes b/126593993.

Change-Id: I564c236772fa9c90e45cccd11c8d1b7a91c705f2
Reviewed-on: https://code-review.googlesource.com/c/38612
Reviewed-by: Jianqiao Li <jianqiaoli@google.com>
diff --git a/profiler/profiler.go b/profiler/profiler.go
index 86a7089..546d8d6 100644
--- a/profiler/profiler.go
+++ b/profiler/profiler.go
@@ -156,8 +156,17 @@
 	// for testing.
 	APIAddr string
 
-	instance string
-	zone     string
+	// Instance is the name of Compute Engine instance the profiler agent runs
+	// on. This is normally determined from the Compute Engine metadata server
+	// and doesn't need to be initialized. It needs to be set in rare cases where
+	// the metadata server is present but is flaky or otherwise misbehave.
+	Instance string
+
+	// Zone is the zone of Compute Engine instance the profiler agent runs
+	// on. This is normally determined from the Compute Engine metadata server
+	// and doesn't need to be initialized. It needs to be set in rare cases where
+	// the metadata server is present but is flaky or otherwise misbehave.
+	Zone string
 }
 
 // startError represents the error occurred during the
@@ -421,8 +430,8 @@
 
 func initializeAgent(c pb.ProfilerServiceClient) *agent {
 	labels := map[string]string{languageLabel: "go"}
-	if config.zone != "" {
-		labels[zoneNameLabel] = config.zone
+	if config.Zone != "" {
+		labels[zoneNameLabel] = config.Zone
 	}
 	if config.ServiceVersion != "" {
 		labels[versionLabel] = config.ServiceVersion
@@ -435,8 +444,8 @@
 
 	profileLabels := map[string]string{}
 
-	if config.instance != "" {
-		profileLabels[instanceLabel] = config.instance
+	if config.Instance != "" {
+		profileLabels[instanceLabel] = config.Instance
 	}
 
 	profileTypes := []pb.ProfileType{pb.ProfileType_CPU}
@@ -495,17 +504,20 @@
 			}
 		}
 
-		if config.zone, err = getZone(); err != nil {
-			return fmt.Errorf("failed to get zone from Compute Engine metadata: %v", err)
-		}
-
-		if config.instance, err = getInstanceName(); err != nil {
-			if _, ok := err.(gcemd.NotDefinedError); !ok {
-				return fmt.Errorf("failed to get instance name from Compute Engine metadata: %v", err)
+		if config.Zone == "" {
+			if config.Zone, err = getZone(); err != nil {
+				return fmt.Errorf("failed to get zone from Compute Engine metadata: %v", err)
 			}
-			debugLog("failed to get instance name from Compute Engine metadata, will use empty name: %v", err)
 		}
 
+		if config.Instance == "" {
+			if config.Instance, err = getInstanceName(); err != nil {
+				if _, ok := err.(gcemd.NotDefinedError); !ok {
+					return fmt.Errorf("failed to get instance name from Compute Engine metadata: %v", err)
+				}
+				debugLog("failed to get instance name from Compute Engine metadata, will use empty name: %v", err)
+			}
+		}
 	} else {
 		if config.ProjectID == "" {
 			return fmt.Errorf("project ID must be specified in the configuration if running outside of GCP")
diff --git a/profiler/profiler_test.go b/profiler/profiler_test.go
index 261b5ef..82d803e 100644
--- a/profiler/profiler_test.go
+++ b/profiler/profiler_test.go
@@ -375,13 +375,13 @@
 		wantProfileLabels    map[string]string
 	}{
 		{
-			config:               Config{ServiceVersion: testSvcVersion, zone: testZone},
+			config:               Config{ServiceVersion: testSvcVersion, Zone: testZone},
 			wantProfileTypes:     []pb.ProfileType{pb.ProfileType_CPU, pb.ProfileType_HEAP, pb.ProfileType_THREADS, pb.ProfileType_HEAP_ALLOC},
 			wantDeploymentLabels: map[string]string{zoneNameLabel: testZone, versionLabel: testSvcVersion, languageLabel: "go"},
 			wantProfileLabels:    map[string]string{},
 		},
 		{
-			config:               Config{zone: testZone},
+			config:               Config{Zone: testZone},
 			wantProfileTypes:     []pb.ProfileType{pb.ProfileType_CPU, pb.ProfileType_HEAP, pb.ProfileType_THREADS, pb.ProfileType_HEAP_ALLOC},
 			wantDeploymentLabels: map[string]string{zoneNameLabel: testZone, languageLabel: "go"},
 			wantProfileLabels:    map[string]string{},
@@ -393,13 +393,13 @@
 			wantProfileLabels:    map[string]string{},
 		},
 		{
-			config:               Config{instance: testInstance},
+			config:               Config{Instance: testInstance},
 			wantProfileTypes:     []pb.ProfileType{pb.ProfileType_CPU, pb.ProfileType_HEAP, pb.ProfileType_THREADS, pb.ProfileType_HEAP_ALLOC},
 			wantDeploymentLabels: map[string]string{languageLabel: "go"},
 			wantProfileLabels:    map[string]string{instanceLabel: testInstance},
 		},
 		{
-			config:               Config{instance: testInstance},
+			config:               Config{Instance: testInstance},
 			enableMutex:          true,
 			wantProfileTypes:     []pb.ProfileType{pb.ProfileType_CPU, pb.ProfileType_HEAP, pb.ProfileType_THREADS, pb.ProfileType_HEAP_ALLOC, pb.ProfileType_CONTENTION},
 			wantDeploymentLabels: map[string]string{languageLabel: "go"},
@@ -474,7 +474,7 @@
 		{
 			"accepts service name",
 			Config{Service: testService},
-			Config{Service: testService, ProjectID: testGCEProjectID, zone: testZone, instance: testInstance},
+			Config{Service: testService, ProjectID: testGCEProjectID, Zone: testZone, Instance: testInstance},
 			"",
 			false,
 			true,
@@ -483,7 +483,7 @@
 		{
 			"env project overrides GCE project",
 			Config{Service: testService},
-			Config{Service: testService, ProjectID: testEnvProjectID, zone: testZone, instance: testInstance},
+			Config{Service: testService, ProjectID: testEnvProjectID, Zone: testZone, Instance: testInstance},
 			"",
 			false,
 			true,
@@ -510,7 +510,7 @@
 		{
 			"accepts service name from config and service version from GAE",
 			Config{Service: testService},
-			Config{Service: testService, ServiceVersion: testGAEVersion, ProjectID: testGCEProjectID, zone: testZone, instance: testInstance},
+			Config{Service: testService, ServiceVersion: testGAEVersion, ProjectID: testGCEProjectID, Zone: testZone, Instance: testInstance},
 			"",
 			true,
 			true,
@@ -519,7 +519,7 @@
 		{
 			"reads both service name and version from GAE env vars",
 			Config{},
-			Config{Service: testGAEService, ServiceVersion: testGAEVersion, ProjectID: testGCEProjectID, zone: testZone, instance: testInstance},
+			Config{Service: testGAEService, ServiceVersion: testGAEVersion, ProjectID: testGCEProjectID, Zone: testZone, Instance: testInstance},
 			"",
 			true,
 			true,
@@ -528,7 +528,7 @@
 		{
 			"accepts service version from config",
 			Config{Service: testService, ServiceVersion: testSvcVersion},
-			Config{Service: testService, ServiceVersion: testSvcVersion, ProjectID: testGCEProjectID, zone: testZone, instance: testInstance},
+			Config{Service: testService, ServiceVersion: testSvcVersion, ProjectID: testGCEProjectID, Zone: testZone, Instance: testInstance},
 			"",
 			false,
 			true,
@@ -537,7 +537,7 @@
 		{
 			"configured version has priority over GAE-provided version",
 			Config{Service: testService, ServiceVersion: testSvcVersion},
-			Config{Service: testService, ServiceVersion: testSvcVersion, ProjectID: testGCEProjectID, zone: testZone, instance: testInstance},
+			Config{Service: testService, ServiceVersion: testSvcVersion, ProjectID: testGCEProjectID, Zone: testZone, Instance: testInstance},
 			"",
 			true,
 			true,
@@ -546,7 +546,7 @@
 		{
 			"configured project ID has priority over metadata-provided project ID",
 			Config{Service: testService, ProjectID: testProjectID},
-			Config{Service: testService, ProjectID: testProjectID, zone: testZone, instance: testInstance},
+			Config{Service: testService, ProjectID: testProjectID, Zone: testZone, Instance: testInstance},
 			"",
 			false,
 			true,
@@ -570,6 +570,24 @@
 			false,
 			false,
 		},
+		{
+			"configured zone has priority over metadata-provided zone",
+			Config{Service: testService, ProjectID: testProjectID, Zone: testZone + "-override"},
+			Config{Service: testService, ProjectID: testProjectID, Zone: testZone + "-override", Instance: testInstance},
+			"",
+			false,
+			true,
+			false,
+		},
+		{
+			"configured instance has priority over metadata-provided instance",
+			Config{Service: testService, ProjectID: testProjectID, Instance: testInstance + "-override"},
+			Config{Service: testService, ProjectID: testProjectID, Zone: testZone, Instance: testInstance + "-override"},
+			"",
+			false,
+			true,
+			false,
+		},
 	} {
 		t.Logf("Running test: %s", tt.desc)
 		envService, envVersion := "", ""
@@ -858,8 +876,8 @@
 		Service:   testService,
 		ProjectID: testProjectID,
 		APIAddr:   srv.Addr,
-		instance:  testInstance,
-		zone:      testZone,
+		Instance:  testInstance,
+		Zone:      testZone,
 	}); err != nil {
 		t.Fatalf("Start(): %v", err)
 	}