[tilo][entity] Create entity ID structs

An entity's ID-related fields are not necessary when sending RPCs to
update the entity (as opposed to create or finish), only its name is
required.  Including the entity's ID causes an error if the value
doesn't match the ID that is set on the request by resultstore/service.go.
These values *should* always be the same, but since they're allowed to be
empty we'll leave them empty since that's easier.

Adding structs to hold the IDs makes it easier to check whether the IDs are
set or unset, and pass a nil value when necessary.

IN-699 #comment

Change-Id: I024676e21e691d167cb17e82cfebf6347c3b08f7
diff --git a/go.sum b/go.sum
index b46351d..9253ef2 100644
--- a/go.sum
+++ b/go.sum
@@ -49,6 +49,7 @@
 golang.org/x/oauth2 v0.0.0-20181003184128-c57b0facaced/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181004145325-8469e314837c h1:SJ7JoQNVl3mC7EWkkONgBWgCno8LcABIJwFMkWBC+EY=
 golang.org/x/sys v0.0.0-20181004145325-8469e314837c/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
diff --git a/tilo/logger.go b/tilo/logger.go
index e9df40b..eb4ceb1 100644
--- a/tilo/logger.go
+++ b/tilo/logger.go
@@ -304,9 +304,11 @@
 	}
 
 	target := &resultstore.Target{
-		ID:           e.Name,
-		InvocationID: invocationID,
-		StartTime:    dummyStartTime,
+		ID: &resultstore.TargetID{
+			ID:           e.Name,
+			InvocationID: invocationID,
+		},
+		StartTime: dummyStartTime,
 	}
 
 	target, err = l.svc.CreateTarget(ctx, target, invocationName)
@@ -333,10 +335,12 @@
 	}
 
 	configuredTarget := &resultstore.ConfiguredTarget{
-		InvocationID: invocationID,
-		ConfigID:     e.EnvName,
-		TargetID:     e.TestName,
-		StartTime:    e.StartTime,
+		ID: &resultstore.ConfiguredTargetID{
+			InvocationID: invocationID,
+			ConfigID:     e.EnvName,
+			TargetID:     e.TestName,
+		},
+		StartTime: e.StartTime,
 	}
 
 	configuredTarget, err = l.svc.CreateConfiguredTarget(ctx, configuredTarget, targetName)
@@ -363,12 +367,14 @@
 	}
 
 	action := &resultstore.TestAction{
-		ID:           "test",
-		InvocationID: invocationID,
-		TestSuite:    e.TestName,
-		TargetID:     e.TestName,
-		ConfigID:     e.EnvName,
-		StartTime:    e.StartTime,
+		TestSuite: e.TestName,
+		ID: &resultstore.TestActionID{
+			ID:           "test",
+			InvocationID: invocationID,
+			TargetID:     e.TestName,
+			ConfigID:     e.EnvName,
+		},
+		StartTime: e.StartTime,
 	}
 
 	action, err = l.svc.CreateTestAction(ctx, action, configuredTargetName)
diff --git a/tilo/logger_test.go b/tilo/logger_test.go
index 14a3af3..9715ae1 100644
--- a/tilo/logger_test.go
+++ b/tilo/logger_test.go
@@ -30,8 +30,8 @@
 	bed.ctrl = gomock.NewController(t)
 	bed.tester = &loggerTester{
 		MockUploadClient: resultstore.NewMockUploadClient(bed.ctrl),
-		T:           fxtesting.T{t},
-		Context:     resultstore.NewContext(),
+		T:                fxtesting.T{t},
+		Context:          resultstore.NewContext(),
 	}
 }
 
@@ -96,8 +96,10 @@
 					Name: "test_a",
 				}
 				expectedTarget := &resultstore.Target{
-					ID:           "test_a",
-					InvocationID: "inv_id",
+					ID: &resultstore.TargetID{
+						ID:           "test_a",
+						InvocationID: "inv_id",
+					},
 					// dummy start time
 					StartTime: time.Date(2018, time.December, 30, 0, 0, 0, 0, time.UTC),
 				}
@@ -132,21 +134,25 @@
 					StartTime: time.Date(1, 2, 3, 4, 5, 6, 7, time.UTC),
 				}
 				expectedConfiguredTarget := &resultstore.ConfiguredTarget{
-					InvocationID: "inv_id",
-					ConfigID:     "env_a",
-					TargetID:     "test_a",
-					StartTime:    time.Date(1, 2, 3, 4, 5, 6, 7, time.UTC),
+					ID: &resultstore.ConfiguredTargetID{
+						InvocationID: "inv_id",
+						ConfigID:     "env_a",
+						TargetID:     "test_a",
+					},
+					StartTime: time.Date(1, 2, 3, 4, 5, 6, 7, time.UTC),
 				}
 				outputConfiguredTarget := *expectedConfiguredTarget
 				outputConfiguredTarget.Name = "cfg_tgt_name"
 
 				expectedTestAction := &resultstore.TestAction{
-					ID:           "test",
-					InvocationID: "inv_id",
-					TestSuite:    "test_a",
-					TargetID:     "test_a",
-					ConfigID:     "env_a",
-					StartTime:    time.Date(1, 2, 3, 4, 5, 6, 7, time.UTC),
+					ID: &resultstore.TestActionID{
+						ID:           "test",
+						InvocationID: "inv_id",
+						TargetID:     "test_a",
+						ConfigID:     "env_a",
+					},
+					TestSuite: "test_a",
+					StartTime: time.Date(1, 2, 3, 4, 5, 6, 7, time.UTC),
 				}
 
 				ctx := context.Background()
diff --git a/tilo/resultstore/entity.go b/tilo/resultstore/entity.go
index 4a1768b..4ceef65 100644
--- a/tilo/resultstore/entity.go
+++ b/tilo/resultstore/entity.go
@@ -126,21 +126,31 @@
 // Target maps to a ResultStore Target.  See full docs at:
 // https://github.com/googleapis/googleapis/blob/master/google/devtools/resultstore/v2/target.proto
 type Target struct {
-	Name         string
+	Name       string
+	ID         *TargetID
+	Properties map[string]string
+	Status     Status
+	StartTime  time.Time
+	Duration   time.Duration
+	TestLogURI string
+}
+type TargetID struct {
 	ID           string
 	InvocationID string
-	Properties   map[string]string
-	Status       Status
-	StartTime    time.Time
-	Duration     time.Duration
 }
 
 func (t Target) ToResultStoreTarget() *api.Target {
+	var id *api.Target_Id
+	if t.ID != nil {
+		id = &api.Target_Id{
+			InvocationId: t.ID.InvocationID,
+			TargetId:     t.ID.ID,
+		}
+	}
+
 	return &api.Target{
-		Id: &api.Target_Id{
-			InvocationId: t.InvocationID,
-			TargetId:     t.ID,
-		},
+		Name: t.Name,
+		Id:   id,
 		Timing: &api.Timing{
 			StartTime: timeToProtoTimestamp(t.StartTime),
 			Duration:  durationToProtoDuration(t.Duration),
@@ -150,37 +160,54 @@
 		},
 		Properties: mapToProperties(t.Properties),
 		Visible:    true,
+		Files: []*api.File{
+			&api.File{
+				Uid: "test.log",
+				Uri: t.TestLogURI,
+			},
+		},
 	}
 }
 
 func (t *Target) FromResultStoreTarget(input *api.Target) {
 	*t = Target{
-		Name:         input.GetName(),
-		ID:           input.GetId().TargetId,
-		InvocationID: input.GetId().InvocationId,
+		Name: input.GetName(),
+		ID: &TargetID{
+			ID:           input.GetId().TargetId,
+			InvocationID: input.GetId().InvocationId,
+		},
 	}
 }
 
 // ConfiguredTarget maps to a ResultStore ConfiguredTarget.  See full docs at:
 // https://github.com/googleapis/googleapis/blob/master/google/devtools/resultstore/v2/configured_target.proto
 type ConfiguredTarget struct {
-	Name         string
-	ConfigID     string
+	Name       string
+	ID         *ConfiguredTargetID
+	Properties map[string]string
+	Status     Status
+	StartTime  time.Time
+	Duration   time.Duration
+}
+type ConfiguredTargetID struct {
 	InvocationID string
 	TargetID     string
-	Properties   map[string]string
-	Status       Status
-	StartTime    time.Time
-	Duration     time.Duration
+	ConfigID     string
 }
 
 func (t ConfiguredTarget) ToResultStoreConfiguredTarget() *api.ConfiguredTarget {
+	var id *api.ConfiguredTarget_Id
+	if t.ID != nil {
+		id = &api.ConfiguredTarget_Id{
+			InvocationId:    t.ID.InvocationID,
+			TargetId:        t.ID.TargetID,
+			ConfigurationId: t.ID.ConfigID,
+		}
+	}
+
 	return &api.ConfiguredTarget{
-		Id: &api.ConfiguredTarget_Id{
-			InvocationId:    t.InvocationID,
-			TargetId:        t.TargetID,
-			ConfigurationId: t.ConfigID,
-		},
+		Name: t.Name,
+		Id:   id,
 		StatusAttributes: &api.StatusAttributes{
 			Status: t.Status.ToResultStoreStatus(),
 		},
@@ -194,37 +221,46 @@
 
 func (t *ConfiguredTarget) FromResultStoreConfiguredTarget(input *api.ConfiguredTarget) {
 	*t = ConfiguredTarget{
-		Name:         input.GetName(),
-		ConfigID:     input.GetId().ConfigurationId,
-		InvocationID: input.GetId().InvocationId,
-		TargetID:     input.GetId().TargetId,
+		Name: input.GetName(),
+		ID: &ConfiguredTargetID{
+			InvocationID: input.GetId().InvocationId,
+			TargetID:     input.GetId().TargetId,
+			ConfigID:     input.GetId().ConfigurationId,
+		},
 	}
 }
 
 // TestAction maps to a ResultStore Action with a child TestAction.  See full docs at:
 // https://github.com/googleapis/googleapis/blob/master/google/devtools/resultstore/v2/action.proto
 type TestAction struct {
-	Name         string
+	Name       string
+	ID         *TestActionID
+	TestSuite  string
+	TestLogURI string
+	StartTime  time.Time
+	Duration   time.Duration
+	Status     Status
+}
+type TestActionID struct {
 	ID           string
 	InvocationID string
 	TargetID     string
 	ConfigID     string
-	TestSuite    string
-	TestLogURI   string
-	StartTime    time.Time
-	Duration     time.Duration
-	Status       Status
 }
 
 func (a TestAction) ToResultStoreAction() *api.Action {
+	var id *api.Action_Id
+	if a.ID != nil {
+		id = &api.Action_Id{
+			InvocationId:    a.ID.InvocationID,
+			TargetId:        a.ID.TargetID,
+			ConfigurationId: a.ID.ConfigID,
+			ActionId:        a.ID.ID,
+		}
+	}
 	return &api.Action{
 		Name: a.Name,
-		Id: &api.Action_Id{
-			InvocationId:    a.InvocationID,
-			TargetId:        a.TargetID,
-			ConfigurationId: a.ConfigID,
-			ActionId:        "test",
-		},
+		Id:   id,
 		StatusAttributes: &api.StatusAttributes{
 			Status: a.Status.ToResultStoreStatus(),
 		},
@@ -250,11 +286,13 @@
 
 func (a *TestAction) FromResultStoreAction(input *api.Action) {
 	*a = TestAction{
-		Name:         input.GetName(),
-		InvocationID: input.GetId().InvocationId,
-		TargetID:     input.GetId().TargetId,
-		ConfigID:     input.GetId().ConfigurationId,
-		ID:           input.GetId().ActionId,
+		Name: input.GetName(),
+		ID: &TestActionID{
+			InvocationID: input.GetId().InvocationId,
+			TargetID:     input.GetId().TargetId,
+			ConfigID:     input.GetId().ConfigurationId,
+			ID:           input.GetId().ActionId,
+		},
 	}
 }
 
diff --git a/tilo/resultstore/upload_client.go b/tilo/resultstore/upload_client.go
index f67000d..b43c5ca 100644
--- a/tilo/resultstore/upload_client.go
+++ b/tilo/resultstore/upload_client.go
@@ -146,7 +146,7 @@
 		RequestId:          c.uuid(ctx),
 		AuthorizationToken: authToken,
 		Parent:             invocationName,
-		TargetId:           target.ID,
+		TargetId:           target.ID.ID,
 		Target:             target.ToResultStoreTarget(),
 	})
 	if err != nil {
@@ -168,7 +168,7 @@
 		AuthorizationToken: authToken,
 		RequestId:          c.uuid(ctx),
 		Parent:             targetName,
-		ConfigId:           configTarget.ConfigID,
+		ConfigId:           configTarget.ID.ConfigID,
 		ConfiguredTarget:   configTarget.ToResultStoreConfiguredTarget(),
 	})
 	if err != nil {
@@ -190,7 +190,7 @@
 		AuthorizationToken: authToken,
 		RequestId:          c.uuid(ctx),
 		Parent:             configTargetName,
-		ActionId:           action.ID,
+		ActionId:           action.ID.ID,
 		Action:             action.ToResultStoreAction(),
 	})
 	if err != nil {
diff --git a/tilo/resultstore/upload_client_test.go b/tilo/resultstore/upload_client_test.go
index 9a73660..9f8a9c8 100644
--- a/tilo/resultstore/upload_client_test.go
+++ b/tilo/resultstore/upload_client_test.go
@@ -241,7 +241,9 @@
 		defer testBed.Teardown()
 
 		input := &resultstore.Target{
-			ID:         "target_id",
+			ID: &resultstore.TargetID{
+				ID: "target_id",
+			},
 			Properties: map[string]string{"key": "value"},
 			StartTime:  may18_1993,
 			Status:     resultstore.Passed,
@@ -256,9 +258,11 @@
 		}
 
 		expectedOutput := &resultstore.Target{
-			Name:         "resultstore_target_name",
-			ID:           "target_id",
-			InvocationID: "invocation_id",
+			Name: "resultstore_target_name",
+			ID: &resultstore.TargetID{
+				ID:           "target_id",
+				InvocationID: "invocation_id",
+			},
 		}
 
 		testBed.ExpectRPC().
@@ -266,7 +270,7 @@
 				RequestId:          testUUID,
 				AuthorizationToken: "auth_token",
 				Parent:             "invocation_name",
-				TargetId:           input.ID,
+				TargetId:           input.ID.ID,
 				Target:             input.ToResultStoreTarget(),
 			}).
 			Return(apiResponse, nil)
@@ -287,12 +291,14 @@
 		defer testBed.Teardown()
 
 		input := &resultstore.Target{
-			ID:           "target_id",
-			InvocationID: "invocation_id",
-			Properties:   map[string]string{"key": "value"},
-			StartTime:    may18_1993,
-			Duration:     time.Hour,
-			Status:       resultstore.Passed,
+			ID: &resultstore.TargetID{
+				ID:           "target_id",
+				InvocationID: "invocation_id",
+			},
+			Properties: map[string]string{"key": "value"},
+			StartTime:  may18_1993,
+			Duration:   time.Hour,
+			Status:     resultstore.Passed,
 		}
 
 		apiResponse := &api.Target{
@@ -304,9 +310,11 @@
 		}
 
 		expectedOutput := &resultstore.Target{
-			Name:         "resultstore_target_name",
-			ID:           "target_id",
-			InvocationID: "invocation_id",
+			Name: "resultstore_target_name",
+			ID: &resultstore.TargetID{
+				ID:           "target_id",
+				InvocationID: "invocation_id",
+			},
 		}
 
 		testBed.ExpectRPC().
@@ -354,12 +362,14 @@
 		defer testBed.Teardown()
 
 		input := &resultstore.ConfiguredTarget{
-			InvocationID: "invocation_id",
-			TargetID:     "target_id",
-			ConfigID:     "configuration_id",
-			Properties:   map[string]string{"key": "value"},
-			StartTime:    may18_1993,
-			Status:       resultstore.Passed,
+			ID: &resultstore.ConfiguredTargetID{
+				InvocationID: "invocation_id",
+				TargetID:     "target_id",
+				ConfigID:     "configuration_id",
+			},
+			Properties: map[string]string{"key": "value"},
+			StartTime:  may18_1993,
+			Status:     resultstore.Passed,
 		}
 
 		apiResponse := &api.ConfiguredTarget{
@@ -372,10 +382,12 @@
 		}
 
 		expectedOutput := &resultstore.ConfiguredTarget{
-			Name:         "resultstore_configured_target_name",
-			InvocationID: "invocation_id",
-			ConfigID:     "configuration_id",
-			TargetID:     "target_id",
+			Name: "resultstore_configured_target_name",
+			ID: &resultstore.ConfiguredTargetID{
+				InvocationID: "invocation_id",
+				ConfigID:     "configuration_id",
+				TargetID:     "target_id",
+			},
 		}
 
 		testBed.ExpectRPC().
@@ -383,7 +395,7 @@
 				RequestId:          testUUID,
 				AuthorizationToken: "auth_token",
 				Parent:             "target_name",
-				ConfigId:           "configuration_id",
+				ConfigId:           input.ID.ConfigID,
 				ConfiguredTarget:   input.ToResultStoreConfiguredTarget(),
 			}).
 			Return(apiResponse, nil)
@@ -404,7 +416,11 @@
 		defer testBed.Teardown()
 
 		input := &resultstore.ConfiguredTarget{
-			ConfigID:   "configuration_id",
+			ID: &resultstore.ConfiguredTargetID{
+				InvocationID: "invocation_id",
+				TargetID:     "target_id",
+				ConfigID:     "configuration_id",
+			},
 			Properties: map[string]string{"key": "value"},
 			Status:     resultstore.Passed,
 			StartTime:  may18_1993,
@@ -421,10 +437,12 @@
 		}
 
 		expectedOutput := &resultstore.ConfiguredTarget{
-			Name:         "resultstore_configured_target_name",
-			InvocationID: "invocation_id",
-			ConfigID:     "configuration_id",
-			TargetID:     "target_id",
+			Name: "resultstore_configured_target_name",
+			ID: &resultstore.ConfiguredTargetID{
+				InvocationID: "invocation_id",
+				ConfigID:     "configuration_id",
+				TargetID:     "target_id",
+			},
 		}
 
 		testBed.ExpectRPC().
@@ -472,20 +490,21 @@
 		defer testBed.Teardown()
 
 		input := &resultstore.TestAction{
-			ID:           "test",
-			InvocationID: "invocation_id",
-			TargetID:     "target_id",
-			ConfigID:     "configuration_id",
-			TestSuite:    "test_suite",
-			TestLogURI:   "http://test.log",
-			StartTime:    may18_1993,
-			Status:       resultstore.Passed,
+			ID: &resultstore.TestActionID{
+				ID:           "test",
+				InvocationID: "invocation_id",
+				TargetID:     "target_id",
+				ConfigID:     "configuration_id",
+			},
+			TestSuite:  "test_suite",
+			TestLogURI: "http://test.log",
+			StartTime:  may18_1993,
+			Status:     resultstore.Passed,
 		}
 
 		apiResponse := &api.Action{
 			Name: "resultstore_action_name",
 			Id: &api.Action_Id{
-				ActionId:        "test",
 				InvocationId:    "invocation_id",
 				ConfigurationId: "configuration_id",
 				TargetId:        "target_id",
@@ -493,11 +512,12 @@
 		}
 
 		expectedOutput := &resultstore.TestAction{
-			Name:         "resultstore_action_name",
-			ID:           "test",
-			InvocationID: "invocation_id",
-			ConfigID:     "configuration_id",
-			TargetID:     "target_id",
+			Name: "resultstore_action_name",
+			ID: &resultstore.TestActionID{
+				InvocationID: "invocation_id",
+				ConfigID:     "configuration_id",
+				TargetID:     "target_id",
+			},
 		}
 
 		testBed.ExpectRPC().
@@ -526,20 +546,23 @@
 		defer testBed.Teardown()
 
 		input := &resultstore.TestAction{
-			ID:           "test",
-			InvocationID: "invocation_id",
-			TargetID:     "target_id",
-			ConfigID:     "configuration_id",
-			TestSuite:    "test_suite",
-			TestLogURI:   "http://test.log",
-			StartTime:    may18_1993,
-			Duration:     time.Hour,
-			Status:       resultstore.Passed,
+			ID: &resultstore.TestActionID{
+				ID:           "test",
+				InvocationID: "invocation_id",
+				TargetID:     "target_id",
+				ConfigID:     "configuration_id",
+			},
+			TestSuite:  "test_suite",
+			TestLogURI: "http://test.log",
+			StartTime:  may18_1993,
+			Duration:   time.Hour,
+			Status:     resultstore.Passed,
 		}
 
 		apiResponse := &api.Action{
 			Name: "resultstore_action_name",
 			Id: &api.Action_Id{
+				ActionId:        "action_id",
 				InvocationId:    "invocation_id",
 				ConfigurationId: "configuration_id",
 				TargetId:        "target_id",
@@ -547,10 +570,13 @@
 		}
 
 		expectedOutput := &resultstore.TestAction{
-			Name:         "resultstore_action_name",
-			InvocationID: "invocation_id",
-			ConfigID:     "configuration_id",
-			TargetID:     "target_id",
+			Name: "resultstore_action_name",
+			ID: &resultstore.TestActionID{
+				ID:           "action_id",
+				InvocationID: "invocation_id",
+				ConfigID:     "configuration_id",
+				TargetID:     "target_id",
+			},
 		}
 
 		testBed.ExpectRPC().