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.
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 {

View File

@ -111,6 +111,8 @@ type Experiment struct {
Namespace string `protobuf:"bytes,5,opt,name=namespace,proto3" json:"namespace,omitempty"`
// 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"`
// 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() {
@ -187,6 +189,13 @@ func (x *Experiment) GetStorageState() Experiment_StorageState {
return Experiment_STORAGE_STATE_UNSPECIFIED
}
func (x *Experiment) GetLastRunCreatedAt() *timestamppb.Timestamp {
if x != nil {
return x.LastRunCreatedAt
}
return nil
}
type CreateExperimentRequest struct {
state protoimpl.MessageState
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,
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,
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,
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,
@ -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,
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, 0x65, 0x22, 0x4a, 0x0a, 0x0c, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x53, 0x74, 0x61,
0x74, 0x65, 0x12, 0x1d, 0x0a, 0x19, 0x53, 0x54, 0x4f, 0x52, 0x41, 0x47, 0x45, 0x5f, 0x53, 0x54,
0x41, 0x54, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10,
0x00, 0x12, 0x0d, 0x0a, 0x09, 0x41, 0x56, 0x41, 0x49, 0x4c, 0x41, 0x42, 0x4c, 0x45, 0x10, 0x01,
0x12, 0x0c, 0x0a, 0x08, 0x41, 0x52, 0x43, 0x48, 0x49, 0x56, 0x45, 0x44, 0x10, 0x02, 0x22, 0x6d,
0x0a, 0x17, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x45, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65,
0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x52, 0x0a, 0x0a, 0x65, 0x78, 0x70,
0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 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, 0x0a, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x22, 0x3b, 0x0a,
0x14, 0x47, 0x65, 0x74, 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, 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,
0x74, 0x65, 0x12, 0x49, 0x0a, 0x13, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x63,
0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32,
0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x10, 0x6c, 0x61, 0x73,
0x74, 0x52, 0x75, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x22, 0x4a, 0x0a,
0x0c, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1d, 0x0a,
0x19, 0x53, 0x54, 0x4f, 0x52, 0x41, 0x47, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x55,
0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09,
0x41, 0x56, 0x41, 0x49, 0x4c, 0x41, 0x42, 0x4c, 0x45, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x41,
0x52, 0x43, 0x48, 0x49, 0x56, 0x45, 0x44, 0x10, 0x02, 0x22, 0x6d, 0x0a, 0x17, 0x43, 0x72, 0x65,
0x61, 0x74, 0x65, 0x45, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x12, 0x52, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65,
0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 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, 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,
0x31, 0x2e, 0x45, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x0a, 0x65, 0x78,
0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x22, 0x3b, 0x0a, 0x14, 0x47, 0x65, 0x74, 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, 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,
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, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x5f,
0x69, 0x64, 0x7d, 0x12, 0xb5, 0x01, 0x0a, 0x0f, 0x4c, 0x69, 0x73, 0x74, 0x45, 0x78, 0x70, 0x65,
0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x3e, 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, 0x4c, 0x69, 0x73, 0x74, 0x45, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x73,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 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, 0x4c, 0x69, 0x73, 0x74, 0x45, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x73,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x21, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1b,
0x12, 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, 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,
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, 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,
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, 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,
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, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x62, 0x75, 0x66, 0x2e, 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, 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, 0x75, 0x6e,
0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x12, 0x9e, 0x01, 0x0a, 0x10, 0x44, 0x65, 0x6c, 0x65,
0x74, 0x65, 0x45, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x3f, 0x2e, 0x6b,
0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x7d, 0x12, 0xb5,
0x01, 0x0a, 0x0f, 0x4c, 0x69, 0x73, 0x74, 0x45, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e,
0x74, 0x73, 0x12, 0x3e, 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, 0x4c, 0x69, 0x73, 0x74,
0x45, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x1a, 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, 0x4c, 0x69, 0x73, 0x74,
0x45, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x22, 0x21, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x12, 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, 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, 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,
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,
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,
0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x7d, 0x3a, 0x75, 0x6e, 0x61, 0x72, 0x63, 0x68, 0x69,
0x76, 0x65, 0x12, 0x9e, 0x01, 0x0a, 0x10, 0x44, 0x65, 0x6c, 0x65, 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, 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 (
@ -770,25 +784,26 @@ var file_backend_api_v2beta1_experiment_proto_goTypes = []interface{}{
var file_backend_api_v2beta1_experiment_proto_depIdxs = []int32{
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
1, // 2: kubeflow.pipelines.backend.api.v2beta1.CreateExperimentRequest.experiment:type_name -> kubeflow.pipelines.backend.api.v2beta1.Experiment
1, // 3: kubeflow.pipelines.backend.api.v2beta1.ListExperimentsResponse.experiments: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
3, // 5: kubeflow.pipelines.backend.api.v2beta1.ExperimentService.GetExperiment:input_type -> kubeflow.pipelines.backend.api.v2beta1.GetExperimentRequest
4, // 6: kubeflow.pipelines.backend.api.v2beta1.ExperimentService.ListExperiments:input_type -> kubeflow.pipelines.backend.api.v2beta1.ListExperimentsRequest
7, // 7: kubeflow.pipelines.backend.api.v2beta1.ExperimentService.ArchiveExperiment:input_type -> kubeflow.pipelines.backend.api.v2beta1.ArchiveExperimentRequest
8, // 8: kubeflow.pipelines.backend.api.v2beta1.ExperimentService.UnarchiveExperiment:input_type -> kubeflow.pipelines.backend.api.v2beta1.UnarchiveExperimentRequest
6, // 9: kubeflow.pipelines.backend.api.v2beta1.ExperimentService.DeleteExperiment:input_type -> kubeflow.pipelines.backend.api.v2beta1.DeleteExperimentRequest
1, // 10: kubeflow.pipelines.backend.api.v2beta1.ExperimentService.CreateExperiment:output_type -> kubeflow.pipelines.backend.api.v2beta1.Experiment
1, // 11: kubeflow.pipelines.backend.api.v2beta1.ExperimentService.GetExperiment: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
10, // 13: kubeflow.pipelines.backend.api.v2beta1.ExperimentService.ArchiveExperiment:output_type -> google.protobuf.Empty
10, // 14: kubeflow.pipelines.backend.api.v2beta1.ExperimentService.UnarchiveExperiment:output_type -> google.protobuf.Empty
10, // 15: kubeflow.pipelines.backend.api.v2beta1.ExperimentService.DeleteExperiment:output_type -> google.protobuf.Empty
10, // [10:16] is the sub-list for method output_type
4, // [4:10] is the sub-list for method input_type
4, // [4:4] is the sub-list for extension type_name
4, // [4:4] is the sub-list for extension extendee
0, // [0:4] is the sub-list for field type_name
9, // 2: kubeflow.pipelines.backend.api.v2beta1.Experiment.last_run_created_at:type_name -> google.protobuf.Timestamp
1, // 3: kubeflow.pipelines.backend.api.v2beta1.CreateExperimentRequest.experiment:type_name -> kubeflow.pipelines.backend.api.v2beta1.Experiment
1, // 4: kubeflow.pipelines.backend.api.v2beta1.ListExperimentsResponse.experiments:type_name -> kubeflow.pipelines.backend.api.v2beta1.Experiment
2, // 5: kubeflow.pipelines.backend.api.v2beta1.ExperimentService.CreateExperiment:input_type -> kubeflow.pipelines.backend.api.v2beta1.CreateExperimentRequest
3, // 6: kubeflow.pipelines.backend.api.v2beta1.ExperimentService.GetExperiment:input_type -> kubeflow.pipelines.backend.api.v2beta1.GetExperimentRequest
4, // 7: kubeflow.pipelines.backend.api.v2beta1.ExperimentService.ListExperiments:input_type -> kubeflow.pipelines.backend.api.v2beta1.ListExperimentsRequest
7, // 8: kubeflow.pipelines.backend.api.v2beta1.ExperimentService.ArchiveExperiment:input_type -> kubeflow.pipelines.backend.api.v2beta1.ArchiveExperimentRequest
8, // 9: kubeflow.pipelines.backend.api.v2beta1.ExperimentService.UnarchiveExperiment:input_type -> kubeflow.pipelines.backend.api.v2beta1.UnarchiveExperimentRequest
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.CreateExperiment:output_type -> kubeflow.pipelines.backend.api.v2beta1.Experiment
1, // 12: kubeflow.pipelines.backend.api.v2beta1.ExperimentService.GetExperiment:output_type -> kubeflow.pipelines.backend.api.v2beta1.Experiment
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.ArchiveExperiment:output_type -> google.protobuf.Empty
10, // 15: kubeflow.pipelines.backend.api.v2beta1.ExperimentService.UnarchiveExperiment:output_type -> google.protobuf.Empty
10, // 16: kubeflow.pipelines.backend.api.v2beta1.ExperimentService.DeleteExperiment:output_type -> google.protobuf.Empty
11, // [11:17] is the sub-list for method output_type
5, // [5:11] is the sub-list for method input_type
5, // [5:5] is the sub-list for extension 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() }

View File

@ -30,6 +30,10 @@ type V2beta1Experiment struct {
// Output. Unique experiment ID. Generated by API server.
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.
Namespace string `json:"namespace,omitempty"`
@ -45,6 +49,10 @@ func (m *V2beta1Experiment) Validate(formats strfmt.Registry) error {
res = append(res, err)
}
if err := m.validateLastRunCreatedAt(formats); err != nil {
res = append(res, err)
}
if err := m.validateStorageState(formats); err != nil {
res = append(res, err)
}
@ -68,6 +76,19 @@ func (m *V2beta1Experiment) validateCreatedAt(formats strfmt.Registry) error {
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 {
if swag.IsZero(m.StorageState) { // not required

View File

@ -227,6 +227,11 @@
"storage_state": {
"$ref": "#/definitions/v2beta1ExperimentStorageState",
"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": {
"$ref": "#/definitions/v2beta1ExperimentStorageState",
"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
type Experiment struct {
UUID string `gorm:"column:UUID; not null; primary_key;"`
Name string `gorm:"column:Name; not null; unique_index:idx_name_namespace;"`
Description string `gorm:"column:Description; not null;"`
CreatedAtInSec int64 `gorm:"column:CreatedAtInSec; not null;"`
Namespace string `gorm:"column:Namespace; not null; unique_index:idx_name_namespace;"`
StorageState StorageState `gorm:"column:StorageState; not null;"`
UUID string `gorm:"column:UUID; not null; primary_key;"`
Name string `gorm:"column:Name; not null; unique_index:idx_name_namespace;"`
Description string `gorm:"column:Description; not null;"`
CreatedAtInSec int64 `gorm:"column:CreatedAtInSec; not null;"`
LastRunCreatedAtInSec int64 `gorm:"column:LastRunCreatedAtInSec; 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"
@ -44,14 +45,15 @@ func (e *Experiment) DefaultSortField() string {
}
var experimentAPIToModelFieldMap = map[string]string{
"id": "UUID", // v1beta1 API
"experiment_id": "UUID", // v2beta1 API
"name": "Name", // v1beta1 API
"display_name": "Name", // v2beta1 API
"created_at": "CreatedAtInSec",
"description": "Description",
"namespace": "Namespace", // v2beta1 API
"storage_state": "StorageState",
"id": "UUID", // v1beta1 API
"experiment_id": "UUID", // v2beta1 API
"name": "Name", // v1beta1 API
"display_name": "Name", // v2beta1 API
"created_at": "CreatedAtInSec",
"last_run_created_at": "LastRunCreatedAtInSec", // v2beta1 API
"description": "Description",
"namespace": "Namespace", // v2beta1 API
"storage_state": "StorageState",
}
// 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
case "CreatedAtInSec":
return e.CreatedAtInSec
case "LastRunCreatedAtInSec":
return e.LastRunCreatedAtInSec
case "Description":
return e.Description
case "Namespace":

View File

@ -550,6 +550,13 @@ func (r *ResourceManager) CreateRun(ctx context.Context, run *model.Run) (*model
if err != nil {
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
}
@ -1256,6 +1263,10 @@ func (r *ResourceManager) ReportWorkflowResource(ctx context.Context, execSpec u
} else {
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() {
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"])
}
return &apiv2beta1.Experiment{
ExperimentId: experiment.UUID,
DisplayName: experiment.Name,
Description: experiment.Description,
CreatedAt: &timestamp.Timestamp{Seconds: experiment.CreatedAtInSec},
Namespace: experiment.Namespace,
StorageState: storageState,
ExperimentId: experiment.UUID,
DisplayName: experiment.Name,
Description: experiment.Description,
CreatedAt: &timestamp.Timestamp{Seconds: experiment.CreatedAtInSec},
LastRunCreatedAt: &timestamp.Timestamp{Seconds: experiment.LastRunCreatedAtInSec},
Namespace: experiment.Namespace,
StorageState: storageState,
}
}

View File

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

View File

@ -16,6 +16,8 @@ package server
import (
"context"
"google.golang.org/protobuf/types/known/structpb"
"sigs.k8s.io/yaml"
"strings"
"testing"
@ -66,12 +68,13 @@ func TestCreateExperiment(t *testing.T) {
result, err := server.CreateExperiment(nil, &apiV2beta1.CreateExperimentRequest{Experiment: experiment})
assert.Nil(t, err)
expectedExperiment := &apiV2beta1.Experiment{
ExperimentId: DefaultFakeUUID,
DisplayName: "ex1",
Description: "first experiment",
CreatedAt: &timestamp.Timestamp{Seconds: 1},
StorageState: apiV2beta1.Experiment_AVAILABLE,
Namespace: "",
ExperimentId: DefaultFakeUUID,
DisplayName: "ex1",
Description: "first experiment",
CreatedAt: &timestamp.Timestamp{Seconds: 1},
LastRunCreatedAt: &timestamp.Timestamp{Seconds: 0},
StorageState: apiV2beta1.Experiment_AVAILABLE,
Namespace: "",
}
assert.Equal(t, expectedExperiment, result)
}
@ -395,16 +398,18 @@ func TestCreateExperiment_Multiuser(t *testing.T) {
{
"Valid",
&apiV2beta1.Experiment{
DisplayName: "exp1",
Description: "first experiment",
Namespace: "ns1",
DisplayName: "exp1",
Description: "first experiment",
LastRunCreatedAt: &timestamp.Timestamp{Seconds: 0},
Namespace: "ns1",
},
&apiV2beta1.Experiment{
ExperimentId: DefaultFakeUUID,
DisplayName: "exp1",
Description: "first experiment",
Namespace: "ns1",
StorageState: apiV2beta1.Experiment_AVAILABLE,
ExperimentId: DefaultFakeUUID,
DisplayName: "exp1",
Description: "first experiment",
LastRunCreatedAt: &timestamp.Timestamp{Seconds: 0},
Namespace: "ns1",
StorageState: apiV2beta1.Experiment_AVAILABLE,
},
false,
"",
@ -481,12 +486,13 @@ func TestGetExperiment(t *testing.T) {
result, err := server.GetExperiment(nil, &apiV2beta1.GetExperimentRequest{ExperimentId: createResult.ExperimentId})
assert.Nil(t, err)
expectedExperiment := &apiV2beta1.Experiment{
ExperimentId: createResult.ExperimentId,
DisplayName: "ex1",
Description: "first experiment",
CreatedAt: &timestamp.Timestamp{Seconds: 1},
StorageState: apiV2beta1.Experiment_AVAILABLE,
Namespace: "",
ExperimentId: createResult.ExperimentId,
DisplayName: "ex1",
Description: "first experiment",
CreatedAt: &timestamp.Timestamp{Seconds: 1},
LastRunCreatedAt: &timestamp.Timestamp{Seconds: 0},
StorageState: apiV2beta1.Experiment_AVAILABLE,
Namespace: "",
}
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})
assert.Nil(t, err)
expectedExperiment := &apiV2beta1.Experiment{
ExperimentId: createResult.ExperimentId,
DisplayName: "exp1",
Description: "first experiment",
CreatedAt: &timestamp.Timestamp{Seconds: 1},
Namespace: "ns1",
StorageState: apiV2beta1.Experiment_AVAILABLE,
ExperimentId: createResult.ExperimentId,
DisplayName: "exp1",
Description: "first experiment",
CreatedAt: &timestamp.Timestamp{Seconds: 1},
LastRunCreatedAt: &timestamp.Timestamp{Seconds: 0},
Namespace: "ns1",
StorageState: apiV2beta1.Experiment_AVAILABLE,
}
assert.Equal(t, expectedExperiment, result)
}
@ -667,17 +674,97 @@ func TestListExperiments(t *testing.T) {
assert.Nil(t, err)
result, err := server.ListExperiments(nil, &apiV2beta1.ListExperimentsRequest{})
expectedExperiment := []*apiV2beta1.Experiment{{
ExperimentId: createResult.ExperimentId,
DisplayName: "ex1",
Description: "first experiment",
CreatedAt: &timestamp.Timestamp{Seconds: 1},
StorageState: apiV2beta1.Experiment_AVAILABLE,
Namespace: "",
ExperimentId: createResult.ExperimentId,
DisplayName: "ex1",
Description: "first experiment",
CreatedAt: &timestamp.Timestamp{Seconds: 1},
LastRunCreatedAt: &timestamp.Timestamp{Seconds: 0},
StorageState: apiV2beta1.Experiment_AVAILABLE,
Namespace: "",
}}
assert.Nil(t, err)
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) {
clientManager := resource.NewFakeClientManagerOrFatal(util.NewFakeTimeForEpoch())
resourceManager := resource.NewResourceManager(clientManager, &resource.ResourceManagerOptions{CollectMetrics: false})
@ -913,12 +1000,13 @@ func TestListExperiments_Multiuser_NoDefault(t *testing.T) {
false,
"",
[]*apiV2beta1.Experiment{{
ExperimentId: createResult.ExperimentId,
DisplayName: "exp1",
Description: "first experiment",
CreatedAt: &timestamp.Timestamp{Seconds: 1},
Namespace: "ns1",
StorageState: apiV2beta1.Experiment_AVAILABLE,
ExperimentId: createResult.ExperimentId,
DisplayName: "exp1",
Description: "first experiment",
CreatedAt: &timestamp.Timestamp{Seconds: 1},
LastRunCreatedAt: &timestamp.Timestamp{Seconds: 0},
Namespace: "ns1",
StorageState: apiV2beta1.Experiment_AVAILABLE,
}},
},
{

View File

@ -33,6 +33,7 @@ type ExperimentStoreInterface interface {
ArchiveExperiment(expId string) error
UnarchiveExperiment(expId string) error
DeleteExperiment(uuid string) error
UpdateLastRun(run *model.Run) error
}
type ExperimentStore struct {
@ -48,6 +49,7 @@ var experimentColumns = []string{
"Name",
"Description",
"CreatedAtInSec",
"LastRunCreatedAtInSec",
"Namespace",
"StorageState",
}
@ -195,17 +197,19 @@ func (s *ExperimentStore) scanRows(rows *sql.Rows) ([]*model.Experiment, error)
for rows.Next() {
var uuid, name, description, namespace, storageState string
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 {
return experiments, err
}
experiment := &model.Experiment{
UUID: uuid,
Name: name,
Description: description,
CreatedAtInSec: createdAtInSec.Int64,
Namespace: namespace,
StorageState: model.StorageState(storageState).ToV2(),
UUID: uuid,
Name: name,
Description: description,
CreatedAtInSec: createdAtInSec.Int64,
LastRunCreatedAtInSec: lastRunCreatedAtInSec.Int64,
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.
if experiment.StorageState == "" || experiment.StorageState == model.StorageStateUnspecified {
@ -220,6 +224,9 @@ func (s *ExperimentStore) CreateExperiment(experiment *model.Experiment) (*model
newExperiment := *experiment
now := s.time.Now().Unix()
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()
if err != nil {
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.
Insert("experiments").
SetMap(sq.Eq{
"UUID": newExperiment.UUID,
"CreatedAtInSec": newExperiment.CreatedAtInSec,
"Name": newExperiment.Name,
"Description": newExperiment.Description,
"Namespace": newExperiment.Namespace,
"StorageState": newExperiment.StorageState.ToV2().ToString(),
"UUID": newExperiment.UUID,
"CreatedAtInSec": newExperiment.CreatedAtInSec,
"LastRunCreatedAtInSec": newExperiment.LastRunCreatedAtInSec,
"Name": newExperiment.Name,
"Description": newExperiment.Description,
"Namespace": newExperiment.Namespace,
"StorageState": newExperiment.StorageState.ToV2().ToString(),
}).
ToSql()
if err != nil {
@ -411,6 +419,28 @@ func (s *ExperimentStore) UnarchiveExperiment(expId string) error {
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.
func NewExperimentStore(db *DB, time util.TimeInterface, uuid util.UUIDGeneratorInterface) *ExperimentStore {
return &ExperimentStore{

View File

@ -60,18 +60,20 @@ func TestListExperiments_Pagination(t *testing.T) {
experimentStore.uuid = util.NewFakeUUIDGeneratorOrFatal(fakeIDFour, nil)
experimentStore.CreateExperiment(createExperiment("experiment2"))
expectedExperiment1 := &model.Experiment{
UUID: fakeID,
CreatedAtInSec: 1,
Name: "experiment1",
Description: "My name is experiment1",
StorageState: "AVAILABLE",
UUID: fakeID,
CreatedAtInSec: 1,
LastRunCreatedAtInSec: 0,
Name: "experiment1",
Description: "My name is experiment1",
StorageState: "AVAILABLE",
}
expectedExperiment4 := &model.Experiment{
UUID: fakeIDFour,
CreatedAtInSec: 4,
Name: "experiment2",
Description: "My name is experiment2",
StorageState: "AVAILABLE",
UUID: fakeIDFour,
CreatedAtInSec: 4,
LastRunCreatedAtInSec: 0,
Name: "experiment2",
Description: "My name is experiment2",
StorageState: "AVAILABLE",
}
experimentsExpected := []*model.Experiment{expectedExperiment1, expectedExperiment4}
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)
expectedExperiment2 := &model.Experiment{
UUID: fakeIDTwo,
CreatedAtInSec: 2,
Name: "experiment3",
Description: "My name is experiment3",
StorageState: "AVAILABLE",
UUID: fakeIDTwo,
CreatedAtInSec: 2,
LastRunCreatedAtInSec: 0,
Name: "experiment3",
Description: "My name is experiment3",
StorageState: "AVAILABLE",
}
expectedExperiment3 := &model.Experiment{
UUID: fakeIDThree,
CreatedAtInSec: 3,
Name: "experiment4",
Description: "My name is experiment4",
StorageState: "AVAILABLE",
UUID: fakeIDThree,
CreatedAtInSec: 3,
LastRunCreatedAtInSec: 0,
Name: "experiment4",
Description: "My name is experiment4",
StorageState: "AVAILABLE",
}
experimentsExpected2 := []*model.Experiment{expectedExperiment2, expectedExperiment3}