UPSTREAM: <carry>: add last_run_creation

Signed-off-by: Humair Khan <HumairAK@users.noreply.github.com>
This commit is contained in:
Humair Khan 2024-05-31 14:21:56 -04:00
parent 0115b8ee11
commit bf77909897
No known key found for this signature in database
GPG Key ID: 8F728B8B7C835606
12 changed files with 467 additions and 270 deletions

View File

@ -99,6 +99,9 @@ message Experiment {
// Output. Specifies whether this experiment is in archived or available state. // Output. Specifies whether this experiment is in archived or available state.
StorageState storage_state = 6; StorageState storage_state = 6;
// Output. The creation time of the last run in this experiment.
google.protobuf.Timestamp last_run_created_at = 7;
} }
message CreateExperimentRequest { message CreateExperimentRequest {

View File

@ -111,6 +111,8 @@ type Experiment struct {
Namespace string `protobuf:"bytes,5,opt,name=namespace,proto3" json:"namespace,omitempty"` Namespace string `protobuf:"bytes,5,opt,name=namespace,proto3" json:"namespace,omitempty"`
// Output. Specifies whether this experiment is in archived or available state. // Output. Specifies whether this experiment is in archived or available state.
StorageState Experiment_StorageState `protobuf:"varint,6,opt,name=storage_state,json=storageState,proto3,enum=kubeflow.pipelines.backend.api.v2beta1.Experiment_StorageState" json:"storage_state,omitempty"` StorageState Experiment_StorageState `protobuf:"varint,6,opt,name=storage_state,json=storageState,proto3,enum=kubeflow.pipelines.backend.api.v2beta1.Experiment_StorageState" json:"storage_state,omitempty"`
// Output. The time the created time of the last run in this experiment.
LastRunCreatedAt *timestamppb.Timestamp `protobuf:"bytes,7,opt,name=last_run_created_at,json=lastRunCreatedAt,proto3" json:"last_run_created_at,omitempty"`
} }
func (x *Experiment) Reset() { func (x *Experiment) Reset() {
@ -187,6 +189,13 @@ func (x *Experiment) GetStorageState() Experiment_StorageState {
return Experiment_STORAGE_STATE_UNSPECIFIED return Experiment_STORAGE_STATE_UNSPECIFIED
} }
func (x *Experiment) GetLastRunCreatedAt() *timestamppb.Timestamp {
if x != nil {
return x.LastRunCreatedAt
}
return nil
}
type CreateExperimentRequest struct { type CreateExperimentRequest struct {
state protoimpl.MessageState state protoimpl.MessageState
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
@ -596,7 +605,7 @@ var file_backend_api_v2beta1_experiment_proto_rawDesc = []byte{
0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d,
0x70, 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x70, 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73,
0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x81, 0x03, 0x0a, 0x0a, 0x45, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xcc, 0x03, 0x0a, 0x0a, 0x45,
0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x78, 0x70, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x78, 0x70,
0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
0x52, 0x0c, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x21, 0x52, 0x0c, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x21,
@ -616,128 +625,133 @@ var file_backend_api_v2beta1_experiment_proto_rawDesc = []byte{
0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x45, 0x78, 0x70, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x45, 0x78, 0x70,
0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x53, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x53,
0x74, 0x61, 0x74, 0x65, 0x52, 0x0c, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x53, 0x74, 0x61, 0x74, 0x61, 0x74, 0x65, 0x52, 0x0c, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x53, 0x74, 0x61,
0x74, 0x65, 0x22, 0x4a, 0x0a, 0x0c, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x49, 0x0a, 0x13, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x63,
0x74, 0x65, 0x12, 0x1d, 0x0a, 0x19, 0x53, 0x54, 0x4f, 0x52, 0x41, 0x47, 0x45, 0x5f, 0x53, 0x54, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32,
0x41, 0x54, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
0x00, 0x12, 0x0d, 0x0a, 0x09, 0x41, 0x56, 0x41, 0x49, 0x4c, 0x41, 0x42, 0x4c, 0x45, 0x10, 0x01, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x10, 0x6c, 0x61, 0x73,
0x12, 0x0c, 0x0a, 0x08, 0x41, 0x52, 0x43, 0x48, 0x49, 0x56, 0x45, 0x44, 0x10, 0x02, 0x22, 0x6d, 0x74, 0x52, 0x75, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x22, 0x4a, 0x0a,
0x0a, 0x17, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x45, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x0c, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1d, 0x0a,
0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x52, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x19, 0x53, 0x54, 0x4f, 0x52, 0x41, 0x47, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x55,
0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09,
0x6b, 0x75, 0x62, 0x65, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x70, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x41, 0x56, 0x41, 0x49, 0x4c, 0x41, 0x42, 0x4c, 0x45, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x41,
0x65, 0x73, 0x2e, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x52, 0x43, 0x48, 0x49, 0x56, 0x45, 0x44, 0x10, 0x02, 0x22, 0x6d, 0x0a, 0x17, 0x43, 0x72, 0x65,
0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x45, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x61, 0x74, 0x65, 0x45, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71,
0x74, 0x52, 0x0a, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x22, 0x3b, 0x0a, 0x75, 0x65, 0x73, 0x74, 0x12, 0x52, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65,
0x14, 0x47, 0x65, 0x74, 0x45, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x6b, 0x75, 0x62, 0x65, 0x66,
0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d,
0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x78,
0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x22, 0xa3, 0x01, 0x0a, 0x16, 0x4c,
0x69, 0x73, 0x74, 0x45, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x6f,
0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x61, 0x67, 0x65, 0x54,
0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x73, 0x69, 0x7a,
0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, 0x61, 0x67, 0x65, 0x53, 0x69, 0x7a,
0x65, 0x12, 0x17, 0x0a, 0x07, 0x73, 0x6f, 0x72, 0x74, 0x5f, 0x62, 0x79, 0x18, 0x03, 0x20, 0x01,
0x28, 0x09, 0x52, 0x06, 0x73, 0x6f, 0x72, 0x74, 0x42, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x69,
0x6c, 0x74, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x66, 0x69, 0x6c, 0x74,
0x65, 0x72, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18,
0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65,
0x22, 0xb6, 0x01, 0x0a, 0x17, 0x4c, 0x69, 0x73, 0x74, 0x45, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d,
0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x54, 0x0a, 0x0b,
0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28,
0x0b, 0x32, 0x32, 0x2e, 0x6b, 0x75, 0x62, 0x65, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x70, 0x69, 0x70,
0x65, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x2e, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2e, 0x61,
0x70, 0x69, 0x2e, 0x76, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x45, 0x78, 0x70, 0x65, 0x72,
0x69, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x0b, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e,
0x74, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x73, 0x69, 0x7a, 0x65,
0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x53, 0x69, 0x7a,
0x65, 0x12, 0x26, 0x0a, 0x0f, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x74,
0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x65, 0x78, 0x74,
0x50, 0x61, 0x67, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x3e, 0x0a, 0x17, 0x44, 0x65, 0x6c,
0x65, 0x74, 0x65, 0x45, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65,
0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x78, 0x70,
0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x22, 0x3f, 0x0a, 0x18, 0x41, 0x72, 0x63,
0x68, 0x69, 0x76, 0x65, 0x45, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d,
0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x78,
0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x22, 0x41, 0x0a, 0x1a, 0x55, 0x6e,
0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x45, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e,
0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x78, 0x70, 0x65,
0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
0x0c, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x32, 0xb8, 0x08,
0x0a, 0x11, 0x45, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x72, 0x76,
0x69, 0x63, 0x65, 0x12, 0xb6, 0x01, 0x0a, 0x10, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x45, 0x78,
0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x3f, 0x2e, 0x6b, 0x75, 0x62, 0x65, 0x66,
0x6c, 0x6f, 0x77, 0x2e, 0x70, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x2e, 0x62, 0x61, 0x6c, 0x6f, 0x77, 0x2e, 0x70, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x2e, 0x62, 0x61,
0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x62, 0x65, 0x74, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x62, 0x65, 0x74, 0x61,
0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x45, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x31, 0x2e, 0x45, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x0a, 0x65, 0x78,
0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x6b, 0x75, 0x62, 0x65, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x22, 0x3b, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x45,
0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x70, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x2e, 0x62, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x62, 0x65, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69,
0x61, 0x31, 0x2e, 0x45, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x22, 0x2d, 0x82, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d,
0xd3, 0xe4, 0x93, 0x02, 0x27, 0x22, 0x19, 0x2f, 0x61, 0x70, 0x69, 0x73, 0x2f, 0x76, 0x32, 0x62, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x22, 0xa3, 0x01, 0x0a, 0x16, 0x4c, 0x69, 0x73, 0x74, 0x45, 0x78,
0x65, 0x74, 0x61, 0x31, 0x2f, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x3a, 0x0a, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0xb4, 0x01, 0x0a, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01,
0x0d, 0x47, 0x65, 0x74, 0x45, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x3c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x61, 0x67, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12,
0x2e, 0x6b, 0x75, 0x62, 0x65, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x70, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x1b, 0x0a, 0x09, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01,
0x6e, 0x65, 0x73, 0x2e, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x28, 0x05, 0x52, 0x08, 0x70, 0x61, 0x67, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x17, 0x0a, 0x07,
0x76, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x72, 0x74, 0x5f, 0x62, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73,
0x69, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x6b, 0x6f, 0x72, 0x74, 0x42, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18,
0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x1c, 0x0a,
0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09,
0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, 0xb6, 0x01, 0x0a, 0x17,
0x4c, 0x69, 0x73, 0x74, 0x45, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x54, 0x0a, 0x0b, 0x65, 0x78, 0x70, 0x65, 0x72,
0x69, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x6b,
0x75, 0x62, 0x65, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x70, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x75, 0x62, 0x65, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x70, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65,
0x73, 0x2e, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x73, 0x2e, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32,
0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x45, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x45, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74,
0x22, 0x31, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2b, 0x12, 0x29, 0x2f, 0x61, 0x70, 0x69, 0x73, 0x2f, 0x52, 0x0b, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1d, 0x0a,
0x76, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28,
0x6e, 0x74, 0x73, 0x2f, 0x7b, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x05, 0x52, 0x09, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x26, 0x0a, 0x0f,
0x69, 0x64, 0x7d, 0x12, 0xb5, 0x01, 0x0a, 0x0f, 0x4c, 0x69, 0x73, 0x74, 0x45, 0x78, 0x70, 0x65, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18,
0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x3e, 0x2e, 0x6b, 0x75, 0x62, 0x65, 0x66, 0x6c, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x65, 0x78, 0x74, 0x50, 0x61, 0x67, 0x65, 0x54,
0x6f, 0x77, 0x2e, 0x70, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x2e, 0x62, 0x61, 0x63, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x3e, 0x0a, 0x17, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x45, 0x78,
0x6b, 0x65, 0x6e, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12,
0x2e, 0x4c, 0x69, 0x73, 0x74, 0x45, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x23, 0x0a, 0x0d, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3f, 0x2e, 0x6b, 0x75, 0x62, 0x65, 0x66, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65,
0x6f, 0x77, 0x2e, 0x70, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x2e, 0x62, 0x61, 0x63, 0x6e, 0x74, 0x49, 0x64, 0x22, 0x3f, 0x0a, 0x18, 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x45,
0x6b, 0x65, 0x6e, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x2e, 0x4c, 0x69, 0x73, 0x74, 0x45, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x21, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d,
0x12, 0x19, 0x2f, 0x61, 0x70, 0x69, 0x73, 0x2f, 0x76, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x22, 0x41, 0x0a, 0x1a, 0x55, 0x6e, 0x61, 0x72, 0x63, 0x68, 0x69,
0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0xa8, 0x01, 0x0a, 0x11,
0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x45, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e,
0x74, 0x12, 0x40, 0x2e, 0x6b, 0x75, 0x62, 0x65, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x70, 0x69, 0x70,
0x65, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x2e, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2e, 0x61,
0x70, 0x69, 0x2e, 0x76, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x41, 0x72, 0x63, 0x68, 0x69,
0x76, 0x65, 0x45, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x76, 0x65, 0x45, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75,
0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x65, 0x73, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e,
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x39, 0x82, 0xd3, 0xe4, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x78, 0x70, 0x65,
0x93, 0x02, 0x33, 0x22, 0x31, 0x2f, 0x61, 0x70, 0x69, 0x73, 0x2f, 0x76, 0x32, 0x62, 0x65, 0x74, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x32, 0xb8, 0x08, 0x0a, 0x11, 0x45, 0x78, 0x70,
0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0xb6,
0x01, 0x0a, 0x10, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x45, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d,
0x65, 0x6e, 0x74, 0x12, 0x3f, 0x2e, 0x6b, 0x75, 0x62, 0x65, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x70,
0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x2e, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64,
0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65,
0x61, 0x74, 0x65, 0x45, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x6b, 0x75, 0x62, 0x65, 0x66, 0x6c, 0x6f, 0x77, 0x2e,
0x70, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x2e, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e,
0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x45, 0x78,
0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x22, 0x2d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x27,
0x22, 0x19, 0x2f, 0x61, 0x70, 0x69, 0x73, 0x2f, 0x76, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f,
0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x3a, 0x0a, 0x65, 0x78, 0x70,
0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0xb4, 0x01, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x45,
0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x3c, 0x2e, 0x6b, 0x75, 0x62, 0x65,
0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x70, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x2e, 0x62,
0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x62, 0x65, 0x74,
0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x6b, 0x75, 0x62, 0x65, 0x66, 0x6c,
0x6f, 0x77, 0x2e, 0x70, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x2e, 0x62, 0x61, 0x63,
0x6b, 0x65, 0x6e, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31,
0x2e, 0x45, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x22, 0x31, 0x82, 0xd3, 0xe4,
0x93, 0x02, 0x2b, 0x12, 0x29, 0x2f, 0x61, 0x70, 0x69, 0x73, 0x2f, 0x76, 0x32, 0x62, 0x65, 0x74,
0x61, 0x31, 0x2f, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x7b, 0x61, 0x31, 0x2f, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x7b,
0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x7d, 0x3a, 0x61, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x7d, 0x12, 0xb5,
0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x12, 0xae, 0x01, 0x0a, 0x13, 0x55, 0x6e, 0x61, 0x72, 0x63, 0x01, 0x0a, 0x0f, 0x4c, 0x69, 0x73, 0x74, 0x45, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e,
0x68, 0x69, 0x76, 0x65, 0x45, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x42, 0x74, 0x73, 0x12, 0x3e, 0x2e, 0x6b, 0x75, 0x62, 0x65, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x70, 0x69,
0x2e, 0x6b, 0x75, 0x62, 0x65, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x70, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x2e, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2e,
0x6e, 0x65, 0x73, 0x2e, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74,
0x76, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, 0x6e, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x45, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65,
0x65, 0x45, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3f, 0x2e, 0x6b, 0x75, 0x62, 0x65, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x70, 0x69,
0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x2e, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2e,
0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x3b, 0x82, 0xd3, 0xe4, 0x93, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74,
0x02, 0x35, 0x22, 0x33, 0x2f, 0x61, 0x70, 0x69, 0x73, 0x2f, 0x76, 0x32, 0x62, 0x65, 0x74, 0x61, 0x45, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x31, 0x2f, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x7b, 0x65, 0x6e, 0x73, 0x65, 0x22, 0x21, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x12, 0x19, 0x2f, 0x61, 0x70,
0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x7d, 0x3a, 0x75, 0x6e, 0x69, 0x73, 0x2f, 0x76, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x65, 0x78, 0x70, 0x65, 0x72,
0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x12, 0x9e, 0x01, 0x0a, 0x10, 0x44, 0x65, 0x6c, 0x65, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0xa8, 0x01, 0x0a, 0x11, 0x41, 0x72, 0x63, 0x68, 0x69,
0x74, 0x65, 0x45, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x3f, 0x2e, 0x6b, 0x76, 0x65, 0x45, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x40, 0x2e, 0x6b,
0x75, 0x62, 0x65, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x70, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x75, 0x62, 0x65, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x70, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65,
0x73, 0x2e, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x73, 0x2e, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32,
0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x45, 0x78, 0x70, 0x65, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x45, 0x78, 0x70,
0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16,
0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x39, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x33, 0x22, 0x31,
0x2f, 0x61, 0x70, 0x69, 0x73, 0x2f, 0x76, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x65, 0x78,
0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x7b, 0x65, 0x78, 0x70, 0x65, 0x72,
0x69, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x7d, 0x3a, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76,
0x65, 0x12, 0xae, 0x01, 0x0a, 0x13, 0x55, 0x6e, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x45,
0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x42, 0x2e, 0x6b, 0x75, 0x62, 0x65,
0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x70, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x2e, 0x62,
0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x62, 0x65, 0x74,
0x61, 0x31, 0x2e, 0x55, 0x6e, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x45, 0x78, 0x70, 0x65,
0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e,
0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x31, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2b, 0x2a, 0x29, 0x2f, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x3b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x35, 0x22, 0x33, 0x2f,
0x61, 0x70, 0x69, 0x73, 0x2f, 0x76, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x65, 0x78, 0x70, 0x61, 0x70, 0x69, 0x73, 0x2f, 0x76, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x65, 0x78, 0x70,
0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x7b, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x7b, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69,
0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x7d, 0x42, 0x3d, 0x5a, 0x3b, 0x67, 0x69, 0x74, 0x68, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x7d, 0x3a, 0x75, 0x6e, 0x61, 0x72, 0x63, 0x68, 0x69,
0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6b, 0x75, 0x62, 0x65, 0x66, 0x6c, 0x6f, 0x77, 0x2f, 0x76, 0x65, 0x12, 0x9e, 0x01, 0x0a, 0x10, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x45, 0x78, 0x70,
0x70, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x2f, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x3f, 0x2e, 0x6b, 0x75, 0x62, 0x65, 0x66, 0x6c,
0x64, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x67, 0x6f, 0x6f, 0x77, 0x2e, 0x70, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x2e, 0x62, 0x61, 0x63,
0x5f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 0x6b, 0x65, 0x6e, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31,
0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x45, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e,
0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79,
0x22, 0x31, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2b, 0x2a, 0x29, 0x2f, 0x61, 0x70, 0x69, 0x73, 0x2f,
0x76, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65,
0x6e, 0x74, 0x73, 0x2f, 0x7b, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x5f,
0x69, 0x64, 0x7d, 0x42, 0x3d, 0x5a, 0x3b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f,
0x6d, 0x2f, 0x6b, 0x75, 0x62, 0x65, 0x66, 0x6c, 0x6f, 0x77, 0x2f, 0x70, 0x69, 0x70, 0x65, 0x6c,
0x69, 0x6e, 0x65, 0x73, 0x2f, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2f, 0x61, 0x70, 0x69,
0x2f, 0x76, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x67, 0x6f, 0x5f, 0x63, 0x6c, 0x69, 0x65,
0x6e, 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
} }
var ( var (
@ -770,25 +784,26 @@ var file_backend_api_v2beta1_experiment_proto_goTypes = []interface{}{
var file_backend_api_v2beta1_experiment_proto_depIdxs = []int32{ var file_backend_api_v2beta1_experiment_proto_depIdxs = []int32{
9, // 0: kubeflow.pipelines.backend.api.v2beta1.Experiment.created_at:type_name -> google.protobuf.Timestamp 9, // 0: kubeflow.pipelines.backend.api.v2beta1.Experiment.created_at:type_name -> google.protobuf.Timestamp
0, // 1: kubeflow.pipelines.backend.api.v2beta1.Experiment.storage_state:type_name -> kubeflow.pipelines.backend.api.v2beta1.Experiment.StorageState 0, // 1: kubeflow.pipelines.backend.api.v2beta1.Experiment.storage_state:type_name -> kubeflow.pipelines.backend.api.v2beta1.Experiment.StorageState
1, // 2: kubeflow.pipelines.backend.api.v2beta1.CreateExperimentRequest.experiment:type_name -> kubeflow.pipelines.backend.api.v2beta1.Experiment 9, // 2: kubeflow.pipelines.backend.api.v2beta1.Experiment.last_run_created_at:type_name -> google.protobuf.Timestamp
1, // 3: kubeflow.pipelines.backend.api.v2beta1.ListExperimentsResponse.experiments:type_name -> kubeflow.pipelines.backend.api.v2beta1.Experiment 1, // 3: kubeflow.pipelines.backend.api.v2beta1.CreateExperimentRequest.experiment:type_name -> kubeflow.pipelines.backend.api.v2beta1.Experiment
2, // 4: kubeflow.pipelines.backend.api.v2beta1.ExperimentService.CreateExperiment:input_type -> kubeflow.pipelines.backend.api.v2beta1.CreateExperimentRequest 1, // 4: kubeflow.pipelines.backend.api.v2beta1.ListExperimentsResponse.experiments:type_name -> kubeflow.pipelines.backend.api.v2beta1.Experiment
3, // 5: kubeflow.pipelines.backend.api.v2beta1.ExperimentService.GetExperiment:input_type -> kubeflow.pipelines.backend.api.v2beta1.GetExperimentRequest 2, // 5: kubeflow.pipelines.backend.api.v2beta1.ExperimentService.CreateExperiment:input_type -> kubeflow.pipelines.backend.api.v2beta1.CreateExperimentRequest
4, // 6: kubeflow.pipelines.backend.api.v2beta1.ExperimentService.ListExperiments:input_type -> kubeflow.pipelines.backend.api.v2beta1.ListExperimentsRequest 3, // 6: kubeflow.pipelines.backend.api.v2beta1.ExperimentService.GetExperiment:input_type -> kubeflow.pipelines.backend.api.v2beta1.GetExperimentRequest
7, // 7: kubeflow.pipelines.backend.api.v2beta1.ExperimentService.ArchiveExperiment:input_type -> kubeflow.pipelines.backend.api.v2beta1.ArchiveExperimentRequest 4, // 7: kubeflow.pipelines.backend.api.v2beta1.ExperimentService.ListExperiments:input_type -> kubeflow.pipelines.backend.api.v2beta1.ListExperimentsRequest
8, // 8: kubeflow.pipelines.backend.api.v2beta1.ExperimentService.UnarchiveExperiment:input_type -> kubeflow.pipelines.backend.api.v2beta1.UnarchiveExperimentRequest 7, // 8: kubeflow.pipelines.backend.api.v2beta1.ExperimentService.ArchiveExperiment:input_type -> kubeflow.pipelines.backend.api.v2beta1.ArchiveExperimentRequest
6, // 9: kubeflow.pipelines.backend.api.v2beta1.ExperimentService.DeleteExperiment:input_type -> kubeflow.pipelines.backend.api.v2beta1.DeleteExperimentRequest 8, // 9: kubeflow.pipelines.backend.api.v2beta1.ExperimentService.UnarchiveExperiment:input_type -> kubeflow.pipelines.backend.api.v2beta1.UnarchiveExperimentRequest
1, // 10: kubeflow.pipelines.backend.api.v2beta1.ExperimentService.CreateExperiment:output_type -> kubeflow.pipelines.backend.api.v2beta1.Experiment 6, // 10: kubeflow.pipelines.backend.api.v2beta1.ExperimentService.DeleteExperiment:input_type -> kubeflow.pipelines.backend.api.v2beta1.DeleteExperimentRequest
1, // 11: kubeflow.pipelines.backend.api.v2beta1.ExperimentService.GetExperiment:output_type -> kubeflow.pipelines.backend.api.v2beta1.Experiment 1, // 11: kubeflow.pipelines.backend.api.v2beta1.ExperimentService.CreateExperiment:output_type -> kubeflow.pipelines.backend.api.v2beta1.Experiment
5, // 12: kubeflow.pipelines.backend.api.v2beta1.ExperimentService.ListExperiments:output_type -> kubeflow.pipelines.backend.api.v2beta1.ListExperimentsResponse 1, // 12: kubeflow.pipelines.backend.api.v2beta1.ExperimentService.GetExperiment:output_type -> kubeflow.pipelines.backend.api.v2beta1.Experiment
10, // 13: kubeflow.pipelines.backend.api.v2beta1.ExperimentService.ArchiveExperiment:output_type -> google.protobuf.Empty 5, // 13: kubeflow.pipelines.backend.api.v2beta1.ExperimentService.ListExperiments:output_type -> kubeflow.pipelines.backend.api.v2beta1.ListExperimentsResponse
10, // 14: kubeflow.pipelines.backend.api.v2beta1.ExperimentService.UnarchiveExperiment:output_type -> google.protobuf.Empty 10, // 14: kubeflow.pipelines.backend.api.v2beta1.ExperimentService.ArchiveExperiment:output_type -> google.protobuf.Empty
10, // 15: kubeflow.pipelines.backend.api.v2beta1.ExperimentService.DeleteExperiment:output_type -> google.protobuf.Empty 10, // 15: kubeflow.pipelines.backend.api.v2beta1.ExperimentService.UnarchiveExperiment:output_type -> google.protobuf.Empty
10, // [10:16] is the sub-list for method output_type 10, // 16: kubeflow.pipelines.backend.api.v2beta1.ExperimentService.DeleteExperiment:output_type -> google.protobuf.Empty
4, // [4:10] is the sub-list for method input_type 11, // [11:17] is the sub-list for method output_type
4, // [4:4] is the sub-list for extension type_name 5, // [5:11] is the sub-list for method input_type
4, // [4:4] is the sub-list for extension extendee 5, // [5:5] is the sub-list for extension type_name
0, // [0:4] is the sub-list for field type_name 5, // [5:5] is the sub-list for extension extendee
0, // [0:5] is the sub-list for field type_name
} }
func init() { file_backend_api_v2beta1_experiment_proto_init() } func init() { file_backend_api_v2beta1_experiment_proto_init() }

View File

@ -30,6 +30,10 @@ type V2beta1Experiment struct {
// Output. Unique experiment ID. Generated by API server. // Output. Unique experiment ID. Generated by API server.
ExperimentID string `json:"experiment_id,omitempty"` ExperimentID string `json:"experiment_id,omitempty"`
// Output. The time the created time of the last run in this experiment.
// Format: date-time
LastRunCreatedAt strfmt.DateTime `json:"last_run_created_at,omitempty"`
// Optional input field. Specify the namespace this experiment belongs to. // Optional input field. Specify the namespace this experiment belongs to.
Namespace string `json:"namespace,omitempty"` Namespace string `json:"namespace,omitempty"`
@ -45,6 +49,10 @@ func (m *V2beta1Experiment) Validate(formats strfmt.Registry) error {
res = append(res, err) res = append(res, err)
} }
if err := m.validateLastRunCreatedAt(formats); err != nil {
res = append(res, err)
}
if err := m.validateStorageState(formats); err != nil { if err := m.validateStorageState(formats); err != nil {
res = append(res, err) res = append(res, err)
} }
@ -68,6 +76,19 @@ func (m *V2beta1Experiment) validateCreatedAt(formats strfmt.Registry) error {
return nil return nil
} }
func (m *V2beta1Experiment) validateLastRunCreatedAt(formats strfmt.Registry) error {
if swag.IsZero(m.LastRunCreatedAt) { // not required
return nil
}
if err := validate.FormatOf("last_run_created_at", "body", "date-time", m.LastRunCreatedAt.String(), formats); err != nil {
return err
}
return nil
}
func (m *V2beta1Experiment) validateStorageState(formats strfmt.Registry) error { func (m *V2beta1Experiment) validateStorageState(formats strfmt.Registry) error {
if swag.IsZero(m.StorageState) { // not required if swag.IsZero(m.StorageState) { // not required

View File

@ -227,6 +227,11 @@
"storage_state": { "storage_state": {
"$ref": "#/definitions/v2beta1ExperimentStorageState", "$ref": "#/definitions/v2beta1ExperimentStorageState",
"description": "Output. Specifies whether this experiment is in archived or available state." "description": "Output. Specifies whether this experiment is in archived or available state."
},
"last_run_created_at": {
"type": "string",
"format": "date-time",
"description": "Output. The time the created time of the last run in this experiment."
} }
} }
}, },

View File

@ -1523,6 +1523,11 @@
"storage_state": { "storage_state": {
"$ref": "#/definitions/v2beta1ExperimentStorageState", "$ref": "#/definitions/v2beta1ExperimentStorageState",
"description": "Output. Specifies whether this experiment is in archived or available state." "description": "Output. Specifies whether this experiment is in archived or available state."
},
"last_run_created_at": {
"type": "string",
"format": "date-time",
"description": "Output. The time the created time of the last run in this experiment."
} }
} }
}, },

View File

@ -15,12 +15,13 @@
package model package model
type Experiment struct { type Experiment struct {
UUID string `gorm:"column:UUID; not null; primary_key;"` UUID string `gorm:"column:UUID; not null; primary_key;"`
Name string `gorm:"column:Name; not null; unique_index:idx_name_namespace;"` Name string `gorm:"column:Name; not null; unique_index:idx_name_namespace;"`
Description string `gorm:"column:Description; not null;"` Description string `gorm:"column:Description; not null;"`
CreatedAtInSec int64 `gorm:"column:CreatedAtInSec; not null;"` CreatedAtInSec int64 `gorm:"column:CreatedAtInSec; not null;"`
Namespace string `gorm:"column:Namespace; not null; unique_index:idx_name_namespace;"` LastRunCreatedAtInSec int64 `gorm:"column:LastRunCreatedAtInSec; not null;"`
StorageState StorageState `gorm:"column:StorageState; not null;"` Namespace string `gorm:"column:Namespace; not null; unique_index:idx_name_namespace;"`
StorageState StorageState `gorm:"column:StorageState; not null;"`
} }
// Note: Experiment.StorageState can have values: "STORAGE_STATE_UNSPECIFIED", "AVAILABLE" or "ARCHIVED" // Note: Experiment.StorageState can have values: "STORAGE_STATE_UNSPECIFIED", "AVAILABLE" or "ARCHIVED"
@ -44,14 +45,15 @@ func (e *Experiment) DefaultSortField() string {
} }
var experimentAPIToModelFieldMap = map[string]string{ var experimentAPIToModelFieldMap = map[string]string{
"id": "UUID", // v1beta1 API "id": "UUID", // v1beta1 API
"experiment_id": "UUID", // v2beta1 API "experiment_id": "UUID", // v2beta1 API
"name": "Name", // v1beta1 API "name": "Name", // v1beta1 API
"display_name": "Name", // v2beta1 API "display_name": "Name", // v2beta1 API
"created_at": "CreatedAtInSec", "created_at": "CreatedAtInSec",
"description": "Description", "last_run_created_at": "LastRunCreatedAtInSec", // v2beta1 API
"namespace": "Namespace", // v2beta1 API "description": "Description",
"storage_state": "StorageState", "namespace": "Namespace", // v2beta1 API
"storage_state": "StorageState",
} }
// APIToModelFieldMap returns a map from API names to field names for model // APIToModelFieldMap returns a map from API names to field names for model
@ -80,6 +82,8 @@ func (e *Experiment) GetFieldValue(name string) interface{} {
return e.Name return e.Name
case "CreatedAtInSec": case "CreatedAtInSec":
return e.CreatedAtInSec return e.CreatedAtInSec
case "LastRunCreatedAtInSec":
return e.LastRunCreatedAtInSec
case "Description": case "Description":
return e.Description return e.Description
case "Namespace": case "Namespace":

View File

@ -550,6 +550,13 @@ func (r *ResourceManager) CreateRun(ctx context.Context, run *model.Run) (*model
if err != nil { if err != nil {
return nil, util.Wrap(err, "Failed to create a run") return nil, util.Wrap(err, "Failed to create a run")
} }
// Upon run creation, update owning experiment
err = r.experimentStore.UpdateLastRun(newRun)
if err != nil {
return nil, util.Wrap(err, fmt.Sprintf("Failed to update last_run_created_at in experiment %s for run %s", newRun.ExperimentId, newRun.UUID))
}
return newRun, nil return newRun, nil
} }
@ -1256,6 +1263,10 @@ func (r *ResourceManager) ReportWorkflowResource(ctx context.Context, execSpec u
} else { } else {
runId = run.UUID runId = run.UUID
} }
// Upon run creation, update owning experiment
if updateError = r.experimentStore.UpdateLastRun(run); updateError != nil {
return nil, util.Wrapf(updateError, "Failed to report a workflow for existing run %s during updating the owning experiment.", runId)
}
} }
if execStatus.IsInFinalState() { if execStatus.IsInFinalState() {
err := addWorkflowLabel(ctx, r.getWorkflowClient(execSpec.ExecutionNamespace()), execSpec.ExecutionName(), util.LabelKeyWorkflowPersistedFinalState, "true") err := addWorkflowLabel(ctx, r.getWorkflowClient(execSpec.ExecutionNamespace()), execSpec.ExecutionName(), util.LabelKeyWorkflowPersistedFinalState, "true")

View File

@ -113,12 +113,13 @@ func toApiExperiment(experiment *model.Experiment) *apiv2beta1.Experiment {
storageState = apiv2beta1.Experiment_StorageState(apiv2beta1.Experiment_StorageState_value["STORAGE_STATE_UNSPECIFIED"]) storageState = apiv2beta1.Experiment_StorageState(apiv2beta1.Experiment_StorageState_value["STORAGE_STATE_UNSPECIFIED"])
} }
return &apiv2beta1.Experiment{ return &apiv2beta1.Experiment{
ExperimentId: experiment.UUID, ExperimentId: experiment.UUID,
DisplayName: experiment.Name, DisplayName: experiment.Name,
Description: experiment.Description, Description: experiment.Description,
CreatedAt: &timestamp.Timestamp{Seconds: experiment.CreatedAtInSec}, CreatedAt: &timestamp.Timestamp{Seconds: experiment.CreatedAtInSec},
Namespace: experiment.Namespace, LastRunCreatedAt: &timestamp.Timestamp{Seconds: experiment.LastRunCreatedAtInSec},
StorageState: storageState, Namespace: experiment.Namespace,
StorageState: storageState,
} }
} }

View File

@ -1974,77 +1974,87 @@ func TestToApiExperimentsV1(t *testing.T) {
func TestToApiExperiments(t *testing.T) { func TestToApiExperiments(t *testing.T) {
exp1 := &model.Experiment{ exp1 := &model.Experiment{
UUID: "exp1", UUID: "exp1",
CreatedAtInSec: 1, CreatedAtInSec: 1,
Name: "experiment1", LastRunCreatedAtInSec: 1,
Description: "My name is experiment1", Name: "experiment1",
StorageState: "AVAILABLE", Description: "My name is experiment1",
StorageState: "AVAILABLE",
} }
exp2 := &model.Experiment{ exp2 := &model.Experiment{
UUID: "exp2", UUID: "exp2",
CreatedAtInSec: 2, CreatedAtInSec: 2,
Name: "experiment2", LastRunCreatedAtInSec: 2,
Description: "My name is experiment2", Name: "experiment2",
StorageState: "ARCHIVED", Description: "My name is experiment2",
StorageState: "ARCHIVED",
} }
exp3 := &model.Experiment{ exp3 := &model.Experiment{
UUID: "exp3", UUID: "exp3",
CreatedAtInSec: 1, CreatedAtInSec: 1,
Name: "experiment3", LastRunCreatedAtInSec: 1,
Description: "experiment3 was created using V1 APIV1BETA1", Name: "experiment3",
StorageState: "STORAGESTATE_AVAILABLE", Description: "experiment3 was created using V1 APIV1BETA1",
StorageState: "STORAGESTATE_AVAILABLE",
} }
exp4 := &model.Experiment{ exp4 := &model.Experiment{
UUID: "exp4", UUID: "exp4",
CreatedAtInSec: 2, CreatedAtInSec: 2,
Name: "experiment4", LastRunCreatedAtInSec: 2,
Description: "experiment4 was created using V1 APIV1BETA1", Name: "experiment4",
StorageState: "STORAGESTATE_ARCHIVED", Description: "experiment4 was created using V1 APIV1BETA1",
StorageState: "STORAGESTATE_ARCHIVED",
} }
exp5 := &model.Experiment{ exp5 := &model.Experiment{
UUID: "exp5", UUID: "exp5",
CreatedAtInSec: 1, CreatedAtInSec: 1,
Name: "experiment5", LastRunCreatedAtInSec: 1,
Description: "My name is experiment5", Name: "experiment5",
StorageState: "this is invalid storage state", Description: "My name is experiment5",
StorageState: "this is invalid storage state",
} }
apiExps := toApiExperiments([]*model.Experiment{exp1, exp2, exp3, exp4, nil, exp5}) apiExps := toApiExperiments([]*model.Experiment{exp1, exp2, exp3, exp4, nil, exp5})
expectedApiExps := []*apiv2beta1.Experiment{ expectedApiExps := []*apiv2beta1.Experiment{
{ {
ExperimentId: "exp1", ExperimentId: "exp1",
DisplayName: "experiment1", DisplayName: "experiment1",
Description: "My name is experiment1", Description: "My name is experiment1",
CreatedAt: &timestamp.Timestamp{Seconds: 1}, CreatedAt: &timestamp.Timestamp{Seconds: 1},
StorageState: apiv2beta1.Experiment_StorageState(apiv2beta1.Experiment_StorageState_value["AVAILABLE"]), LastRunCreatedAt: &timestamp.Timestamp{Seconds: 1},
StorageState: apiv2beta1.Experiment_StorageState(apiv2beta1.Experiment_StorageState_value["AVAILABLE"]),
}, },
{ {
ExperimentId: "exp2", ExperimentId: "exp2",
DisplayName: "experiment2", DisplayName: "experiment2",
Description: "My name is experiment2", Description: "My name is experiment2",
CreatedAt: &timestamp.Timestamp{Seconds: 2}, CreatedAt: &timestamp.Timestamp{Seconds: 2},
StorageState: apiv2beta1.Experiment_StorageState(apiv2beta1.Experiment_StorageState_value["ARCHIVED"]), LastRunCreatedAt: &timestamp.Timestamp{Seconds: 2},
StorageState: apiv2beta1.Experiment_StorageState(apiv2beta1.Experiment_StorageState_value["ARCHIVED"]),
}, },
{ {
ExperimentId: "exp3", ExperimentId: "exp3",
DisplayName: "experiment3", DisplayName: "experiment3",
Description: "experiment3 was created using V1 APIV1BETA1", Description: "experiment3 was created using V1 APIV1BETA1",
CreatedAt: &timestamp.Timestamp{Seconds: 1}, CreatedAt: &timestamp.Timestamp{Seconds: 1},
StorageState: apiv2beta1.Experiment_StorageState(apiv2beta1.Experiment_StorageState_value["AVAILABLE"]), LastRunCreatedAt: &timestamp.Timestamp{Seconds: 1},
StorageState: apiv2beta1.Experiment_StorageState(apiv2beta1.Experiment_StorageState_value["AVAILABLE"]),
}, },
{ {
ExperimentId: "exp4", ExperimentId: "exp4",
DisplayName: "experiment4", DisplayName: "experiment4",
Description: "experiment4 was created using V1 APIV1BETA1", Description: "experiment4 was created using V1 APIV1BETA1",
CreatedAt: &timestamp.Timestamp{Seconds: 2}, CreatedAt: &timestamp.Timestamp{Seconds: 2},
StorageState: apiv2beta1.Experiment_StorageState(apiv2beta1.Experiment_StorageState_value["ARCHIVED"]), LastRunCreatedAt: &timestamp.Timestamp{Seconds: 2},
StorageState: apiv2beta1.Experiment_StorageState(apiv2beta1.Experiment_StorageState_value["ARCHIVED"]),
}, },
{}, {},
{ {
ExperimentId: "exp5", ExperimentId: "exp5",
DisplayName: "experiment5", DisplayName: "experiment5",
Description: "My name is experiment5", Description: "My name is experiment5",
CreatedAt: &timestamp.Timestamp{Seconds: 1}, CreatedAt: &timestamp.Timestamp{Seconds: 1},
StorageState: apiv2beta1.Experiment_StorageState(apiv2beta1.Experiment_StorageState_value["STORAGE_STATE_UNSPECIFIED"]), LastRunCreatedAt: &timestamp.Timestamp{Seconds: 1},
StorageState: apiv2beta1.Experiment_StorageState(apiv2beta1.Experiment_StorageState_value["STORAGE_STATE_UNSPECIFIED"]),
}, },
} }
assert.Equal(t, expectedApiExps, apiExps) assert.Equal(t, expectedApiExps, apiExps)

View File

@ -16,6 +16,8 @@ package server
import ( import (
"context" "context"
"google.golang.org/protobuf/types/known/structpb"
"sigs.k8s.io/yaml"
"strings" "strings"
"testing" "testing"
@ -66,12 +68,13 @@ func TestCreateExperiment(t *testing.T) {
result, err := server.CreateExperiment(nil, &apiV2beta1.CreateExperimentRequest{Experiment: experiment}) result, err := server.CreateExperiment(nil, &apiV2beta1.CreateExperimentRequest{Experiment: experiment})
assert.Nil(t, err) assert.Nil(t, err)
expectedExperiment := &apiV2beta1.Experiment{ expectedExperiment := &apiV2beta1.Experiment{
ExperimentId: DefaultFakeUUID, ExperimentId: DefaultFakeUUID,
DisplayName: "ex1", DisplayName: "ex1",
Description: "first experiment", Description: "first experiment",
CreatedAt: &timestamp.Timestamp{Seconds: 1}, CreatedAt: &timestamp.Timestamp{Seconds: 1},
StorageState: apiV2beta1.Experiment_AVAILABLE, LastRunCreatedAt: &timestamp.Timestamp{Seconds: 0},
Namespace: "", StorageState: apiV2beta1.Experiment_AVAILABLE,
Namespace: "",
} }
assert.Equal(t, expectedExperiment, result) assert.Equal(t, expectedExperiment, result)
} }
@ -395,16 +398,18 @@ func TestCreateExperiment_Multiuser(t *testing.T) {
{ {
"Valid", "Valid",
&apiV2beta1.Experiment{ &apiV2beta1.Experiment{
DisplayName: "exp1", DisplayName: "exp1",
Description: "first experiment", Description: "first experiment",
Namespace: "ns1", LastRunCreatedAt: &timestamp.Timestamp{Seconds: 0},
Namespace: "ns1",
}, },
&apiV2beta1.Experiment{ &apiV2beta1.Experiment{
ExperimentId: DefaultFakeUUID, ExperimentId: DefaultFakeUUID,
DisplayName: "exp1", DisplayName: "exp1",
Description: "first experiment", Description: "first experiment",
Namespace: "ns1", LastRunCreatedAt: &timestamp.Timestamp{Seconds: 0},
StorageState: apiV2beta1.Experiment_AVAILABLE, Namespace: "ns1",
StorageState: apiV2beta1.Experiment_AVAILABLE,
}, },
false, false,
"", "",
@ -481,12 +486,13 @@ func TestGetExperiment(t *testing.T) {
result, err := server.GetExperiment(nil, &apiV2beta1.GetExperimentRequest{ExperimentId: createResult.ExperimentId}) result, err := server.GetExperiment(nil, &apiV2beta1.GetExperimentRequest{ExperimentId: createResult.ExperimentId})
assert.Nil(t, err) assert.Nil(t, err)
expectedExperiment := &apiV2beta1.Experiment{ expectedExperiment := &apiV2beta1.Experiment{
ExperimentId: createResult.ExperimentId, ExperimentId: createResult.ExperimentId,
DisplayName: "ex1", DisplayName: "ex1",
Description: "first experiment", Description: "first experiment",
CreatedAt: &timestamp.Timestamp{Seconds: 1}, CreatedAt: &timestamp.Timestamp{Seconds: 1},
StorageState: apiV2beta1.Experiment_AVAILABLE, LastRunCreatedAt: &timestamp.Timestamp{Seconds: 0},
Namespace: "", StorageState: apiV2beta1.Experiment_AVAILABLE,
Namespace: "",
} }
assert.Equal(t, expectedExperiment, result) assert.Equal(t, expectedExperiment, result)
} }
@ -619,12 +625,13 @@ func TestGetExperiment_Multiuser(t *testing.T) {
result, err := server.GetExperiment(ctx, &apiV2beta1.GetExperimentRequest{ExperimentId: createResult.ExperimentId}) result, err := server.GetExperiment(ctx, &apiV2beta1.GetExperimentRequest{ExperimentId: createResult.ExperimentId})
assert.Nil(t, err) assert.Nil(t, err)
expectedExperiment := &apiV2beta1.Experiment{ expectedExperiment := &apiV2beta1.Experiment{
ExperimentId: createResult.ExperimentId, ExperimentId: createResult.ExperimentId,
DisplayName: "exp1", DisplayName: "exp1",
Description: "first experiment", Description: "first experiment",
CreatedAt: &timestamp.Timestamp{Seconds: 1}, CreatedAt: &timestamp.Timestamp{Seconds: 1},
Namespace: "ns1", LastRunCreatedAt: &timestamp.Timestamp{Seconds: 0},
StorageState: apiV2beta1.Experiment_AVAILABLE, Namespace: "ns1",
StorageState: apiV2beta1.Experiment_AVAILABLE,
} }
assert.Equal(t, expectedExperiment, result) assert.Equal(t, expectedExperiment, result)
} }
@ -667,17 +674,97 @@ func TestListExperiments(t *testing.T) {
assert.Nil(t, err) assert.Nil(t, err)
result, err := server.ListExperiments(nil, &apiV2beta1.ListExperimentsRequest{}) result, err := server.ListExperiments(nil, &apiV2beta1.ListExperimentsRequest{})
expectedExperiment := []*apiV2beta1.Experiment{{ expectedExperiment := []*apiV2beta1.Experiment{{
ExperimentId: createResult.ExperimentId, ExperimentId: createResult.ExperimentId,
DisplayName: "ex1", DisplayName: "ex1",
Description: "first experiment", Description: "first experiment",
CreatedAt: &timestamp.Timestamp{Seconds: 1}, CreatedAt: &timestamp.Timestamp{Seconds: 1},
StorageState: apiV2beta1.Experiment_AVAILABLE, LastRunCreatedAt: &timestamp.Timestamp{Seconds: 0},
Namespace: "", StorageState: apiV2beta1.Experiment_AVAILABLE,
Namespace: "",
}} }}
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, expectedExperiment, result.Experiments) assert.Equal(t, expectedExperiment, result.Experiments)
} }
func TestListExperimentsByLastRunCreation(t *testing.T) {
// Create experiment and runs/jobs under it.
clients, manager, experiment1, _ := initWithExperimentAndPipelineVersion(t)
defer clients.Close()
// Create another experiment
clients.UpdateUUID(util.NewFakeUUIDGeneratorOrFatal(DefaultFakeIdTwo, nil))
manager = resource.NewResourceManager(clients, &resource.ResourceManagerOptions{CollectMetrics: false})
server := ExperimentServer{resourceManager: manager, options: &ExperimentServerOptions{CollectMetrics: false}}
experiment := &apiV2beta1.Experiment{DisplayName: "exp2"}
experiment2, err := server.CreateExperiment(nil, &apiV2beta1.CreateExperimentRequest{Experiment: experiment})
assert.Nil(t, err)
// Create a generic run object
pipelineSpecStruct := &structpb.Struct{}
yaml.Unmarshal([]byte(v2SpecHelloWorld), pipelineSpecStruct)
genericRun := &apiV2beta1.Run{
PipelineSource: &apiV2beta1.Run_PipelineSpec{
PipelineSpec: pipelineSpecStruct,
},
RuntimeConfig: &apiV2beta1.RuntimeConfig{
Parameters: map[string]*structpb.Value{
"param1": structpb.NewStringValue("world"),
},
},
}
// Create a run in experiment 1
clients.UpdateUUID(util.NewFakeUUIDGeneratorOrFatal(DefaultFakeIdThree, nil))
manager = resource.NewResourceManager(clients, &resource.ResourceManagerOptions{CollectMetrics: false})
runServer := NewRunServer(manager, &RunServerOptions{CollectMetrics: false})
genericRun.DisplayName = "run1"
genericRun.ExperimentId = experiment1.UUID
_, err = runServer.CreateRun(nil, &apiV2beta1.CreateRunRequest{Run: genericRun})
assert.Nil(t, err)
// Create a run in experiment 2
clients.UpdateUUID(util.NewFakeUUIDGeneratorOrFatal(DefaultFakeIdFour, nil))
manager = resource.NewResourceManager(clients, &resource.ResourceManagerOptions{CollectMetrics: false})
runServer = NewRunServer(manager, &RunServerOptions{CollectMetrics: false})
genericRun.DisplayName = "run2"
genericRun.ExperimentId = experiment2.ExperimentId
_, err = runServer.CreateRun(nil, &apiV2beta1.CreateRunRequest{Run: genericRun})
assert.Nil(t, err)
// Expected runs, note that because run 2 in experiment 2
// was created last, experiment 2 has the latest run execution
experimentServer := ExperimentServer{resourceManager: manager, options: &ExperimentServerOptions{CollectMetrics: false}}
expected1 := &apiV2beta1.Experiment{
ExperimentId: experiment1.UUID,
DisplayName: "exp1",
Description: "",
CreatedAt: &timestamp.Timestamp{Seconds: 1},
LastRunCreatedAt: &timestamp.Timestamp{Seconds: 6},
StorageState: apiV2beta1.Experiment_AVAILABLE,
Namespace: "",
}
expected2 := &apiV2beta1.Experiment{
ExperimentId: experiment2.ExperimentId,
DisplayName: "exp2",
Description: "",
CreatedAt: &timestamp.Timestamp{Seconds: 5},
LastRunCreatedAt: &timestamp.Timestamp{Seconds: 8},
StorageState: apiV2beta1.Experiment_AVAILABLE,
Namespace: "",
}
// First list runs sorted by last_run_created_at ascending
listExperimentsRequest := &apiV2beta1.ListExperimentsRequest{SortBy: "last_run_created_at asc"}
result, err := experimentServer.ListExperiments(nil, listExperimentsRequest)
assert.Nil(t, err)
assert.Equal(t, []*apiV2beta1.Experiment{expected1, expected2}, result.Experiments)
// Then list runs sorted by last_run_created_at descending, note the order is switched
listExperimentsRequest = &apiV2beta1.ListExperimentsRequest{SortBy: "last_run_created_at desc"}
result, err = experimentServer.ListExperiments(nil, listExperimentsRequest)
assert.Equal(t, []*apiV2beta1.Experiment{expected2, expected1}, result.Experiments)
}
func TestListExperimentsV1_Failed(t *testing.T) { func TestListExperimentsV1_Failed(t *testing.T) {
clientManager := resource.NewFakeClientManagerOrFatal(util.NewFakeTimeForEpoch()) clientManager := resource.NewFakeClientManagerOrFatal(util.NewFakeTimeForEpoch())
resourceManager := resource.NewResourceManager(clientManager, &resource.ResourceManagerOptions{CollectMetrics: false}) resourceManager := resource.NewResourceManager(clientManager, &resource.ResourceManagerOptions{CollectMetrics: false})
@ -913,12 +1000,13 @@ func TestListExperiments_Multiuser_NoDefault(t *testing.T) {
false, false,
"", "",
[]*apiV2beta1.Experiment{{ []*apiV2beta1.Experiment{{
ExperimentId: createResult.ExperimentId, ExperimentId: createResult.ExperimentId,
DisplayName: "exp1", DisplayName: "exp1",
Description: "first experiment", Description: "first experiment",
CreatedAt: &timestamp.Timestamp{Seconds: 1}, CreatedAt: &timestamp.Timestamp{Seconds: 1},
Namespace: "ns1", LastRunCreatedAt: &timestamp.Timestamp{Seconds: 0},
StorageState: apiV2beta1.Experiment_AVAILABLE, Namespace: "ns1",
StorageState: apiV2beta1.Experiment_AVAILABLE,
}}, }},
}, },
{ {

View File

@ -33,6 +33,7 @@ type ExperimentStoreInterface interface {
ArchiveExperiment(expId string) error ArchiveExperiment(expId string) error
UnarchiveExperiment(expId string) error UnarchiveExperiment(expId string) error
DeleteExperiment(uuid string) error DeleteExperiment(uuid string) error
UpdateLastRun(run *model.Run) error
} }
type ExperimentStore struct { type ExperimentStore struct {
@ -48,6 +49,7 @@ var experimentColumns = []string{
"Name", "Name",
"Description", "Description",
"CreatedAtInSec", "CreatedAtInSec",
"LastRunCreatedAtInSec",
"Namespace", "Namespace",
"StorageState", "StorageState",
} }
@ -195,17 +197,19 @@ func (s *ExperimentStore) scanRows(rows *sql.Rows) ([]*model.Experiment, error)
for rows.Next() { for rows.Next() {
var uuid, name, description, namespace, storageState string var uuid, name, description, namespace, storageState string
var createdAtInSec sql.NullInt64 var createdAtInSec sql.NullInt64
err := rows.Scan(&uuid, &name, &description, &createdAtInSec, &namespace, &storageState) var lastRunCreatedAtInSec sql.NullInt64
err := rows.Scan(&uuid, &name, &description, &createdAtInSec, &lastRunCreatedAtInSec, &namespace, &storageState)
if err != nil { if err != nil {
return experiments, err return experiments, err
} }
experiment := &model.Experiment{ experiment := &model.Experiment{
UUID: uuid, UUID: uuid,
Name: name, Name: name,
Description: description, Description: description,
CreatedAtInSec: createdAtInSec.Int64, CreatedAtInSec: createdAtInSec.Int64,
Namespace: namespace, LastRunCreatedAtInSec: lastRunCreatedAtInSec.Int64,
StorageState: model.StorageState(storageState).ToV2(), Namespace: namespace,
StorageState: model.StorageState(storageState).ToV2(),
} }
// Since storage state is a field added after initial KFP release, it is possible that existing experiments don't have this field and we use AVAILABLE in that case. // Since storage state is a field added after initial KFP release, it is possible that existing experiments don't have this field and we use AVAILABLE in that case.
if experiment.StorageState == "" || experiment.StorageState == model.StorageStateUnspecified { if experiment.StorageState == "" || experiment.StorageState == model.StorageStateUnspecified {
@ -220,6 +224,9 @@ func (s *ExperimentStore) CreateExperiment(experiment *model.Experiment) (*model
newExperiment := *experiment newExperiment := *experiment
now := s.time.Now().Unix() now := s.time.Now().Unix()
newExperiment.CreatedAtInSec = now newExperiment.CreatedAtInSec = now
// When an experiment has no runs
// we default to "1970-01-01T00:00:00Z"
newExperiment.LastRunCreatedAtInSec = 0
id, err := s.uuid.NewRandom() id, err := s.uuid.NewRandom()
if err != nil { if err != nil {
return nil, util.NewInternalServerError(err, "Failed to create an experiment id") return nil, util.NewInternalServerError(err, "Failed to create an experiment id")
@ -236,12 +243,13 @@ func (s *ExperimentStore) CreateExperiment(experiment *model.Experiment) (*model
sql, args, err := sq. sql, args, err := sq.
Insert("experiments"). Insert("experiments").
SetMap(sq.Eq{ SetMap(sq.Eq{
"UUID": newExperiment.UUID, "UUID": newExperiment.UUID,
"CreatedAtInSec": newExperiment.CreatedAtInSec, "CreatedAtInSec": newExperiment.CreatedAtInSec,
"Name": newExperiment.Name, "LastRunCreatedAtInSec": newExperiment.LastRunCreatedAtInSec,
"Description": newExperiment.Description, "Name": newExperiment.Name,
"Namespace": newExperiment.Namespace, "Description": newExperiment.Description,
"StorageState": newExperiment.StorageState.ToV2().ToString(), "Namespace": newExperiment.Namespace,
"StorageState": newExperiment.StorageState.ToV2().ToString(),
}). }).
ToSql() ToSql()
if err != nil { if err != nil {
@ -411,6 +419,28 @@ func (s *ExperimentStore) UnarchiveExperiment(expId string) error {
return nil return nil
} }
func (s *ExperimentStore) UpdateLastRun(run *model.Run) error {
expId := run.ExperimentId
// UpdateLastRun results in the experiment getting last_run_created_at updated
query, args, err := sq.
Update("experiments").
SetMap(sq.Eq{
"LastRunCreatedAtInSec": run.CreatedAtInSec,
}).
Where(sq.Eq{"UUID": expId}).
ToSql()
if err != nil {
return util.NewInternalServerError(err,
"Failed to create query to set experiment LastRunCreatedAtInSec %s. error: '%v'", expId, err.Error())
}
_, err = s.db.Exec(query, args...)
if err != nil {
return util.NewInternalServerError(err,
"Failed to set experiment LastRunCreatedAtInSec %s. error: '%v'", expId, err.Error())
}
return nil
}
// factory function for experiment store. // factory function for experiment store.
func NewExperimentStore(db *DB, time util.TimeInterface, uuid util.UUIDGeneratorInterface) *ExperimentStore { func NewExperimentStore(db *DB, time util.TimeInterface, uuid util.UUIDGeneratorInterface) *ExperimentStore {
return &ExperimentStore{ return &ExperimentStore{

View File

@ -60,18 +60,20 @@ func TestListExperiments_Pagination(t *testing.T) {
experimentStore.uuid = util.NewFakeUUIDGeneratorOrFatal(fakeIDFour, nil) experimentStore.uuid = util.NewFakeUUIDGeneratorOrFatal(fakeIDFour, nil)
experimentStore.CreateExperiment(createExperiment("experiment2")) experimentStore.CreateExperiment(createExperiment("experiment2"))
expectedExperiment1 := &model.Experiment{ expectedExperiment1 := &model.Experiment{
UUID: fakeID, UUID: fakeID,
CreatedAtInSec: 1, CreatedAtInSec: 1,
Name: "experiment1", LastRunCreatedAtInSec: 0,
Description: "My name is experiment1", Name: "experiment1",
StorageState: "AVAILABLE", Description: "My name is experiment1",
StorageState: "AVAILABLE",
} }
expectedExperiment4 := &model.Experiment{ expectedExperiment4 := &model.Experiment{
UUID: fakeIDFour, UUID: fakeIDFour,
CreatedAtInSec: 4, CreatedAtInSec: 4,
Name: "experiment2", LastRunCreatedAtInSec: 0,
Description: "My name is experiment2", Name: "experiment2",
StorageState: "AVAILABLE", Description: "My name is experiment2",
StorageState: "AVAILABLE",
} }
experimentsExpected := []*model.Experiment{expectedExperiment1, expectedExperiment4} experimentsExpected := []*model.Experiment{expectedExperiment1, expectedExperiment4}
opts, err := list.NewOptions(&model.Experiment{}, 2, "name", nil) opts, err := list.NewOptions(&model.Experiment{}, 2, "name", nil)
@ -85,18 +87,20 @@ func TestListExperiments_Pagination(t *testing.T) {
assert.Equal(t, 4, total_size) assert.Equal(t, 4, total_size)
expectedExperiment2 := &model.Experiment{ expectedExperiment2 := &model.Experiment{
UUID: fakeIDTwo, UUID: fakeIDTwo,
CreatedAtInSec: 2, CreatedAtInSec: 2,
Name: "experiment3", LastRunCreatedAtInSec: 0,
Description: "My name is experiment3", Name: "experiment3",
StorageState: "AVAILABLE", Description: "My name is experiment3",
StorageState: "AVAILABLE",
} }
expectedExperiment3 := &model.Experiment{ expectedExperiment3 := &model.Experiment{
UUID: fakeIDThree, UUID: fakeIDThree,
CreatedAtInSec: 3, CreatedAtInSec: 3,
Name: "experiment4", LastRunCreatedAtInSec: 0,
Description: "My name is experiment4", Name: "experiment4",
StorageState: "AVAILABLE", Description: "My name is experiment4",
StorageState: "AVAILABLE",
} }
experimentsExpected2 := []*model.Experiment{expectedExperiment2, expectedExperiment3} experimentsExpected2 := []*model.Experiment{expectedExperiment2, expectedExperiment3}