[stable_version] Add support for milestone info and firmware image locations.

Automated repair needs to be able to download arbitrary firmware images that
are not necessarily bundled with the OS. In order to do this, we store a
fragment of the path to the Google Storage location, in the same format as
the FAFT stable version.

BUG=b/179386397
TEST=regenerate configs.

Change-Id: I75f61c26a9a8bed566a7b83e9b27fc660003cd3c
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/infra/proto/+/2676168
Commit-Queue: Gregory Nisbet <gregorynisbet@google.com>
Reviewed-by: Allen Li <ayatane@chromium.org>
Reviewed-by: Otabek Kasimov <otabek@google.com>
diff --git a/go/lab_platform/stable_firmware_version.pb.go b/go/lab_platform/stable_firmware_version.pb.go
index 63be0f0..cd17485 100644
--- a/go/lab_platform/stable_firmware_version.pb.go
+++ b/go/lab_platform/stable_firmware_version.pb.go
@@ -26,7 +26,7 @@
 
 // StableFirmwareVersion is an association between a given ChromeOS version
 // and a string uniquely identifying the firmware version.
-// next tag: 3
+// next tag: 4
 type StableFirmwareVersion struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
@@ -34,6 +34,16 @@
 
 	Key     *StableVersionKey `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
 	Version string            `protobuf:"bytes,2,opt,name=version,proto3" json:"version,omitempty"`
+	// A fragment of the Google Storage location for a firmware image.
+	// The motivating use case is the devserver API as of 2021 Q1.
+	//
+	// e.g. "SOMEBOARD-firmware/R80-14444.333.0", which maps to
+	// gs://some-bucket/[[image]]/name-of-firmware-bundle.tgz .
+	//
+	// This fragment can be consumed by the devserver API and permits
+	// users to download an firmware bundle without downloading
+	// the entire OS image that it is part of.
+	Image string `protobuf:"bytes,3,opt,name=image,proto3" json:"image,omitempty"`
 }
 
 func (x *StableFirmwareVersion) Reset() {
@@ -82,6 +92,13 @@
 	return ""
 }
 
+func (x *StableFirmwareVersion) GetImage() string {
+	if x != nil {
+		return x.Image
+	}
+	return ""
+}
+
 var File_lab_platform_stable_firmware_version_proto protoreflect.FileDescriptor
 
 var file_lab_platform_stable_firmware_version_proto_rawDesc = []byte{
@@ -91,17 +108,18 @@
 	0x62, 0x5f, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x1a, 0x25, 0x6c, 0x61, 0x62, 0x5f,
 	0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x2f, 0x73, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f,
 	0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x6b, 0x65, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74,
-	0x6f, 0x22, 0x63, 0x0a, 0x15, 0x53, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x69, 0x72, 0x6d, 0x77,
+	0x6f, 0x22, 0x79, 0x0a, 0x15, 0x53, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x69, 0x72, 0x6d, 0x77,
 	0x61, 0x72, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x30, 0x0a, 0x03, 0x6b, 0x65,
 	0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x6c, 0x61, 0x62, 0x5f, 0x70, 0x6c,
 	0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x2e, 0x53, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x65, 0x72,
 	0x73, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x18, 0x0a, 0x07,
 	0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76,
-	0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x42, 0x38, 0x5a, 0x36, 0x67, 0x6f, 0x2e, 0x63, 0x68, 0x72,
-	0x6f, 0x6d, 0x69, 0x75, 0x6d, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x69,
-	0x75, 0x6d, 0x6f, 0x73, 0x2f, 0x69, 0x6e, 0x66, 0x72, 0x61, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
-	0x2f, 0x67, 0x6f, 0x2f, 0x6c, 0x61, 0x62, 0x5f, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d,
-	0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+	0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x18,
+	0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x42, 0x38, 0x5a, 0x36,
+	0x67, 0x6f, 0x2e, 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x69, 0x75, 0x6d, 0x2e, 0x6f, 0x72, 0x67, 0x2f,
+	0x63, 0x68, 0x72, 0x6f, 0x6d, 0x69, 0x75, 0x6d, 0x6f, 0x73, 0x2f, 0x69, 0x6e, 0x66, 0x72, 0x61,
+	0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x2f, 0x6c, 0x61, 0x62, 0x5f, 0x70, 0x6c,
+	0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 }
 
 var (
diff --git a/src/lab_platform/stable_firmware_version.proto b/src/lab_platform/stable_firmware_version.proto
index b434f22..c320cb1 100644
--- a/src/lab_platform/stable_firmware_version.proto
+++ b/src/lab_platform/stable_firmware_version.proto
@@ -12,8 +12,19 @@
 
 // StableFirmwareVersion is an association between a given ChromeOS version
 // and a string uniquely identifying the firmware version.
-// next tag: 3
+// next tag: 4
 message StableFirmwareVersion {
     StableVersionKey key = 1;
     string version = 2;
+
+    // A fragment of the Google Storage location for a firmware image.
+    // The motivating use case is the devserver API as of 2021 Q1.
+    //
+    // e.g. "SOMEBOARD-firmware/R80-14444.333.0", which maps to
+    // gs://some-bucket/[[image]]/name-of-firmware-bundle.tgz .
+    //
+    // This fragment can be consumed by the devserver API and permits
+    // users to download an firmware bundle without downloading
+    // the entire OS image that it is part of.
+    string image = 3;
 }