diff --git a/.optimus.sample.yaml b/.optimus.sample.yaml index 9a46c4c758..22dcc75797 100644 --- a/.optimus.sample.yaml +++ b/.optimus.sample.yaml @@ -27,13 +27,13 @@ log: # for configuring optimus namespace #namespace: # name: sample_namespace -# jobs: -# # folder where job specifications are stored +# job: +# # relative path pointing to folder where job specifications are stored # path: "job" # datastore: # # optimus is capable of supporting multiple datastores # type: bigquery -# # path where resource spec for BQ are stored +# # relative path where resource spec for BQ are stored # path: "bq" # # namespace variables usable in specifications # config: {} diff --git a/Makefile b/Makefile index a0f18d6f3f..887319a427 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ NAME = "github.com/odpf/optimus" LAST_COMMIT := $(shell git rev-parse --short HEAD) LAST_TAG := "$(shell git rev-list --tags --max-count=1)" OPMS_VERSION := "$(shell git describe --tags ${LAST_TAG})-next" -PROTON_COMMIT := "2a30976f7b40884ddd90e1792576c0941426e8bc" +PROTON_COMMIT := "2d837e4d132f88f0cbf65d681281ef035450c4a4" .PHONY: build test test-ci generate-proto unit-test-ci smoke-test integration-test vet coverage clean install lint diff --git a/api/handler/v1beta1/job.go b/api/handler/v1beta1/job.go index c78ea90daf..072006e247 100644 --- a/api/handler/v1beta1/job.go +++ b/api/handler/v1beta1/job.go @@ -2,7 +2,9 @@ package v1beta1 import ( "context" + "errors" "fmt" + "io" "sync" "time" @@ -13,48 +15,114 @@ import ( "google.golang.org/grpc/status" ) -func (sv *RuntimeServiceServer) DeployJobSpecification(req *pb.DeployJobSpecificationRequest, respStream pb.RuntimeService_DeployJobSpecificationServer) error { +func (sv *RuntimeServiceServer) DeployJobSpecification(stream pb.RuntimeService_DeployJobSpecificationServer) error { startTime := time.Now() + errNamespaces := []string{} - namespaceSpec, err := sv.namespaceService.Get(respStream.Context(), req.GetProjectName(), req.GetNamespaceName()) - if err != nil { - return mapToGRPCErr(sv.l, err, "unable to get namespace") + for { + req, err := stream.Recv() + if err != nil { + if err == io.EOF { + break + } + stream.Send(&pb.DeployJobSpecificationResponse{ + Success: false, + Ack: true, + Message: err.Error(), + }) + return err // immediate error returned (grpc error level) + } + namespaceSpec, err := sv.namespaceService.Get(stream.Context(), req.GetProjectName(), req.GetNamespaceName()) + if err != nil { + stream.Send(&pb.DeployJobSpecificationResponse{ + Success: false, + Ack: true, + Message: err.Error(), + }) + errNamespaces = append(errNamespaces, req.NamespaceName) + continue + } + + jobsToKeep, err := sv.getJobsToKeep(stream.Context(), namespaceSpec, req) + if err != nil { + stream.Send(&pb.DeployJobSpecificationResponse{ + Success: false, + Ack: true, + Message: err.Error(), + }) + errNamespaces = append(errNamespaces, req.NamespaceName) + continue + } + + observers := new(progress.ObserverChain) + observers.Join(sv.progressObserver) + observers.Join(&jobSyncObserver{ + stream: stream, + log: sv.l, + mu: new(sync.Mutex), + }) + + // delete specs not sent for deployment from internal repository + if err := sv.jobSvc.KeepOnly(stream.Context(), namespaceSpec, jobsToKeep, observers); err != nil { + stream.Send(&pb.DeployJobSpecificationResponse{ + Success: false, + Ack: true, + Message: fmt.Sprintf("failed to delete jobs: \n%s", err.Error()), + }) + errNamespaces = append(errNamespaces, req.NamespaceName) + continue + } + if err := sv.jobSvc.Sync(stream.Context(), namespaceSpec, observers); err != nil { + stream.Send(&pb.DeployJobSpecificationResponse{ + Success: false, + Ack: true, + Message: fmt.Sprintf("failed to sync jobs: \n%s", err.Error()), + }) + errNamespaces = append(errNamespaces, req.NamespaceName) + continue + } + runtimeDeployJobSpecificationCounter.Add(float64(len(req.Jobs))) + stream.Send(&pb.DeployJobSpecificationResponse{ + Success: true, + Ack: true, + Message: "success", + }) + } + sv.l.Info("finished job deployment", "time", time.Since(startTime)) + if len(errNamespaces) > 0 { + sv.l.Warn("there's error while deploying namespaces: %v", errNamespaces) + return fmt.Errorf("error when deploying: %v", errNamespaces) + } + return nil +} + +func (sv *RuntimeServiceServer) getJobsToKeep(ctx context.Context, namespaceSpec models.NamespaceSpec, req *pb.DeployJobSpecificationRequest) ([]models.JobSpec, error) { + jobs := req.GetJobs() + if len(jobs) == 0 { + return []models.JobSpec{}, nil } var jobsToKeep []models.JobSpec - for _, reqJob := range req.GetJobs() { + for _, reqJob := range jobs { adaptJob, err := sv.adapter.FromJobProto(reqJob) if err != nil { - return status.Errorf(codes.Internal, "%s: cannot adapt job %s", err.Error(), reqJob.GetName()) + sv.l.Error(fmt.Sprintf("%s: cannot adapt job %s", err.Error(), reqJob.GetName())) + continue } - err = sv.jobSvc.Create(respStream.Context(), namespaceSpec, adaptJob) + err = sv.jobSvc.Create(ctx, namespaceSpec, adaptJob) if err != nil { - return status.Errorf(codes.Internal, "%s: failed to save %s", err.Error(), adaptJob.Name) + sv.l.Error(fmt.Sprintf("%s: failed to save %s", err.Error(), adaptJob.Name)) + continue } jobsToKeep = append(jobsToKeep, adaptJob) } - observers := new(progress.ObserverChain) - observers.Join(sv.progressObserver) - observers.Join(&jobSyncObserver{ - stream: respStream, - log: sv.l, - mu: new(sync.Mutex), - }) - - // delete specs not sent for deployment from internal repository - if err := sv.jobSvc.KeepOnly(respStream.Context(), namespaceSpec, jobsToKeep, observers); err != nil { - return status.Errorf(codes.Internal, "failed to delete jobs: \n%s", err.Error()) + if jobsToKeep == nil { + return nil, errors.New("job spec creation is failed") } - if err := sv.jobSvc.Sync(respStream.Context(), namespaceSpec, observers); err != nil { - return status.Errorf(codes.Internal, "failed to sync jobs: \n%s", err.Error()) - } - - runtimeDeployJobSpecificationCounter.Add(float64(len(req.Jobs))) - sv.l.Info("finished job deployment", "time", time.Since(startTime)) - return nil + return jobsToKeep, nil } func (sv *RuntimeServiceServer) ListJobSpecification(ctx context.Context, req *pb.ListJobSpecificationRequest) (*pb.ListJobSpecificationResponse, error) { diff --git a/api/handler/v1beta1/job_test.go b/api/handler/v1beta1/job_test.go index 01f876812c..d640d682db 100644 --- a/api/handler/v1beta1/job_test.go +++ b/api/handler/v1beta1/job_test.go @@ -2,225 +2,266 @@ package v1beta1_test import ( "context" + "errors" + "io" "testing" "time" + "github.com/google/uuid" v1 "github.com/odpf/optimus/api/handler/v1beta1" pb "github.com/odpf/optimus/api/proto/odpf/optimus/core/v1beta1" - - "github.com/google/uuid" "github.com/odpf/optimus/mock" "github.com/odpf/optimus/models" "github.com/odpf/salt/log" "github.com/stretchr/testify/assert" mock2 "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/suite" ) -func TestJobSpecificationOnServer(t *testing.T) { - log := log.NewNoop() - ctx := context.Background() +func TestRuntimeServiceServerJobTestSuite(t *testing.T) { + s := new(RuntimeServiceServerTestSuite) + suite.Run(t, s) +} - t.Run("DeployJobSpecification", func(t *testing.T) { - t.Run("should deploy the job", func(t *testing.T) { - Version := "1.0.1" +func (s *RuntimeServiceServerTestSuite) TestDeployJobSpecification_Success_NoJobSpec() { + stream := new(mock.DeployJobSpecificationServer) + stream.On("Context").Return(s.ctx) + stream.On("Recv").Return(s.jobReq, nil).Once() + stream.On("Recv").Return(nil, io.EOF).Once() - projectName := "a-data-project" - jobName1 := "a-data-job" - taskName := "a-data-task" + s.namespaceService.On("Get", s.ctx, s.jobReq.GetProjectName(), s.jobReq.GetNamespaceName()).Return(s.namespaceSpec, nil).Once() + s.jobService.On("KeepOnly", s.ctx, s.namespaceSpec, mock2.Anything, mock2.Anything).Return(nil).Once() + s.jobService.On("Sync", s.ctx, s.namespaceSpec, mock2.Anything).Return(nil).Once() + stream.On("Send", mock2.Anything).Return(nil).Once() - projectSpec := models.ProjectSpec{ - ID: uuid.Must(uuid.NewRandom()), - Name: projectName, - Config: map[string]string{ - "bucket": "gs://some_folder", - }, - } + runtimeServiceServer := s.newRuntimeServiceServer() + err := runtimeServiceServer.DeployJobSpecification(stream) - namespaceSpec := models.NamespaceSpec{ - ID: uuid.Must(uuid.NewRandom()), - Name: "dev-test-namespace-1", - Config: map[string]string{ - "bucket": "gs://some_folder", - }, - ProjectSpec: projectSpec, - } + s.Assert().NoError(err) + stream.AssertExpectations(s.T()) + s.namespaceService.AssertExpectations(s.T()) + s.jobService.AssertExpectations(s.T()) +} +func (s *RuntimeServiceServerTestSuite) TestDeployJobSpecification_Success_TwoJobSpecs() { + jobSpecs := []*pb.JobSpecification{} + jobSpecs = append(jobSpecs, &pb.JobSpecification{Name: "job-1"}) + jobSpecs = append(jobSpecs, &pb.JobSpecification{Name: "job-2"}) + s.jobReq.Jobs = jobSpecs + adaptedJobs := []models.JobSpec{} + adaptedJobs = append(adaptedJobs, models.JobSpec{Name: "job-1"}) + adaptedJobs = append(adaptedJobs, models.JobSpec{Name: "job-2"}) + + stream := new(mock.DeployJobSpecificationServer) + stream.On("Context").Return(s.ctx) + stream.On("Recv").Return(s.jobReq, nil).Once() + stream.On("Recv").Return(nil, io.EOF).Once() + + s.namespaceService.On("Get", s.ctx, s.jobReq.GetProjectName(), s.jobReq.GetNamespaceName()).Return(s.namespaceSpec, nil).Once() + for i := range jobSpecs { + s.adapter.On("FromJobProto", jobSpecs[i]).Return(adaptedJobs[i], nil).Once() + s.jobService.On("Create", s.ctx, s.namespaceSpec, adaptedJobs[i]).Return(nil).Once() + } + s.jobService.On("KeepOnly", s.ctx, s.namespaceSpec, adaptedJobs, mock2.Anything).Return(nil).Once() + s.jobService.On("Sync", s.ctx, s.namespaceSpec, mock2.Anything).Return(nil).Once() + stream.On("Send", mock2.Anything).Return(nil).Once() + + runtimeServiceServer := s.newRuntimeServiceServer() + err := runtimeServiceServer.DeployJobSpecification(stream) + + s.Assert().NoError(err) + stream.AssertExpectations(s.T()) + s.adapter.AssertExpectations(s.T()) + s.namespaceService.AssertExpectations(s.T()) + s.jobService.AssertExpectations(s.T()) +} - execUnit1 := new(mock.BasePlugin) - execUnit1.On("PluginInfo").Return(&models.PluginInfoResponse{ - Name: taskName, - }, nil) - defer execUnit1.AssertExpectations(t) +func (s *RuntimeServiceServerTestSuite) TestDeployJobSpecification_Fail_StreamRecvError() { + stream := new(mock.DeployJobSpecificationServer) + stream.On("Recv").Return(nil, errors.New("any error")).Once() + stream.On("Send", mock2.Anything).Return(nil).Once() - jobSpecs := []models.JobSpec{ - { - Name: jobName1, - Task: models.JobSpecTask{ - Unit: &models.Plugin{ - Base: execUnit1, - }, - Config: models.JobSpecConfigs{ - { - Name: "do", - Value: "this", - }, - }, - }, - Assets: *models.JobAssets{}.New( - []models.JobSpecAsset{ - { - Name: "query.sql", - Value: "select * from 1", - }, - }), - }, - } + runtimeServiceServer := s.newRuntimeServiceServer() + err := runtimeServiceServer.DeployJobSpecification(stream) - jobSpecRepository := new(mock.JobSpecRepository) - defer jobSpecRepository.AssertExpectations(t) + s.Assert().Error(err) + stream.AssertExpectations(s.T()) +} +func (s *RuntimeServiceServerTestSuite) TestDeployJobSpecification_Fail_NamespaceServiceGetError() { + stream := new(mock.DeployJobSpecificationServer) + stream.On("Context").Return(s.ctx) + stream.On("Recv").Return(s.jobReq, nil).Once() + stream.On("Recv").Return(nil, io.EOF).Once() - jobSpecRepoFactory := new(mock.JobSpecRepoFactory) - defer jobSpecRepoFactory.AssertExpectations(t) + s.namespaceService.On("Get", s.ctx, s.jobReq.GetProjectName(), s.jobReq.GetNamespaceName()).Return(models.NamespaceSpec{}, errors.New("any error")).Once() + stream.On("Send", mock2.Anything).Return(nil).Once() - pluginRepo := new(mock.SupportedPluginRepo) - pluginRepo.On("GetByName", taskName).Return(&models.Plugin{ - Base: execUnit1, - }, nil) - adapter := v1.NewAdapter(pluginRepo, nil) + runtimeServiceServer := s.newRuntimeServiceServer() + err := runtimeServiceServer.DeployJobSpecification(stream) - nsService := new(mock.NamespaceService) - nsService.On("Get", ctx, projectSpec.Name, namespaceSpec.Name).Return(namespaceSpec, nil) - defer nsService.AssertExpectations(t) + s.Assert().Error(err) + stream.AssertExpectations(s.T()) + s.namespaceService.AssertExpectations(s.T()) +} +func (s *RuntimeServiceServerTestSuite) TestDeployJobSpecification_Fail_AdapterFromJobProtoError() { + jobSpecs := []*pb.JobSpecification{} + jobSpecs = append(jobSpecs, &pb.JobSpecification{Name: "job-1"}) + s.jobReq.Jobs = jobSpecs + + stream := new(mock.DeployJobSpecificationServer) + stream.On("Context").Return(s.ctx) + stream.On("Recv").Return(s.jobReq, nil).Once() + stream.On("Recv").Return(nil, io.EOF).Once() + + s.namespaceService.On("Get", s.ctx, s.jobReq.GetProjectName(), s.jobReq.GetNamespaceName()).Return(s.namespaceSpec, nil).Once() + s.adapter.On("FromJobProto", jobSpecs[0]).Return(models.JobSpec{}, errors.New("any error")).Once() + stream.On("Send", mock2.Anything).Return(nil).Once() + + runtimeServiceServer := s.newRuntimeServiceServer() + err := runtimeServiceServer.DeployJobSpecification(stream) + + s.Assert().Error(err) + stream.AssertExpectations(s.T()) + s.adapter.AssertExpectations(s.T()) + s.namespaceService.AssertExpectations(s.T()) +} - projectJobSpecRepository := new(mock.ProjectJobSpecRepository) - defer projectJobSpecRepository.AssertExpectations(t) +func (s *RuntimeServiceServerTestSuite) TestDeployJobSpecification_Fail_JobServiceCreateError() { + jobSpecs := []*pb.JobSpecification{} + jobSpecs = append(jobSpecs, &pb.JobSpecification{Name: "job-1"}) + s.jobReq.Jobs = jobSpecs + adaptedJobs := []models.JobSpec{} + adaptedJobs = append(adaptedJobs, models.JobSpec{Name: "job-1"}) + + stream := new(mock.DeployJobSpecificationServer) + stream.On("Context").Return(s.ctx) + stream.On("Recv").Return(s.jobReq, nil).Once() + stream.On("Recv").Return(nil, io.EOF).Once() + + s.namespaceService.On("Get", s.ctx, s.jobReq.GetProjectName(), s.jobReq.GetNamespaceName()).Return(s.namespaceSpec, nil).Once() + s.adapter.On("FromJobProto", jobSpecs[0]).Return(adaptedJobs[0], nil).Once() + s.jobService.On("Create", s.ctx, s.namespaceSpec, adaptedJobs[0]).Return(errors.New("any error")).Once() + stream.On("Send", mock2.Anything).Return(nil).Once() + + runtimeServiceServer := s.newRuntimeServiceServer() + err := runtimeServiceServer.DeployJobSpecification(stream) + + s.Assert().Error(err) + stream.AssertExpectations(s.T()) + s.adapter.AssertExpectations(s.T()) + s.namespaceService.AssertExpectations(s.T()) + s.jobService.AssertExpectations(s.T()) +} +func (s *RuntimeServiceServerTestSuite) TestDeployJobSpecification_Fail_JobServiceKeepOnlyError() { + stream := new(mock.DeployJobSpecificationServer) + stream.On("Context").Return(s.ctx) + stream.On("Recv").Return(s.jobReq, nil).Once() + stream.On("Recv").Return(nil, io.EOF).Once() + + s.namespaceService.On("Get", s.ctx, s.jobReq.GetProjectName(), s.jobReq.GetNamespaceName()).Return(s.namespaceSpec, nil).Once() + s.jobService.On("KeepOnly", s.ctx, s.namespaceSpec, mock2.Anything, mock2.Anything).Return(errors.New("any error")).Once() + stream.On("Send", mock2.Anything).Return(nil).Once() + + runtimeServiceServer := s.newRuntimeServiceServer() + err := runtimeServiceServer.DeployJobSpecification(stream) + + s.Assert().Error(err) + stream.AssertExpectations(s.T()) + s.namespaceService.AssertExpectations(s.T()) + s.jobService.AssertExpectations(s.T()) +} - projectJobSpecRepoFactory := new(mock.ProjectJobSpecRepoFactory) - defer projectJobSpecRepoFactory.AssertExpectations(t) +func (s *RuntimeServiceServerTestSuite) TestDeployJobSpecification_Fail_JobServiceSyncError() { + stream := new(mock.DeployJobSpecificationServer) + stream.On("Context").Return(s.ctx) + stream.On("Recv").Return(s.jobReq, nil).Once() + stream.On("Recv").Return(nil, io.EOF).Once() - jobService := new(mock.JobService) - jobService.On("Create", ctx, mock2.Anything, namespaceSpec).Return(nil) - jobService.On("KeepOnly", ctx, namespaceSpec, mock2.Anything, mock2.Anything).Return(nil) - jobService.On("Sync", mock2.Anything, namespaceSpec, mock2.Anything).Return(nil) - defer jobService.AssertExpectations(t) + s.namespaceService.On("Get", s.ctx, s.jobReq.GetProjectName(), s.jobReq.GetNamespaceName()).Return(s.namespaceSpec, nil).Once() + s.jobService.On("KeepOnly", s.ctx, s.namespaceSpec, mock2.Anything, mock2.Anything).Return(nil).Once() + s.jobService.On("Sync", s.ctx, s.namespaceSpec, mock2.Anything).Return(errors.New("any error")).Once() + stream.On("Send", mock2.Anything).Return(nil).Once() - grpcRespStream := new(mock.DeployJobSpecificationServer) - grpcRespStream.On("Context").Return(context.Background()) - defer grpcRespStream.AssertExpectations(t) + runtimeServiceServer := s.newRuntimeServiceServer() + err := runtimeServiceServer.DeployJobSpecification(stream) - runtimeServiceServer := v1.NewRuntimeServiceServer( - log, - Version, - jobService, - nil, nil, - nil, - nsService, - nil, - adapter, - nil, - nil, - nil, - ) + s.Assert().Error(err) + stream.AssertExpectations(s.T()) + s.namespaceService.AssertExpectations(s.T()) + s.jobService.AssertExpectations(s.T()) +} - jobSpecsAdapted := []*pb.JobSpecification{} - for _, jobSpec := range jobSpecs { - jobSpecAdapted, _ := adapter.ToJobProto(jobSpec) - jobSpecsAdapted = append(jobSpecsAdapted, jobSpecAdapted) - } - deployRequest := pb.DeployJobSpecificationRequest{ProjectName: projectName, Jobs: jobSpecsAdapted, NamespaceName: namespaceSpec.Name} - err := runtimeServiceServer.DeployJobSpecification(&deployRequest, grpcRespStream) - assert.Nil(t, err) - }) - }) +func (s *RuntimeServiceServerTestSuite) TestGetJobSpecification_Success() { + req := &pb.GetJobSpecificationRequest{} + req.ProjectName = s.projectSpec.Name + req.NamespaceName = s.namespaceSpec.Name + req.JobName = "job-1" + jobSpec := models.JobSpec{Name: req.JobName} + jobSpecProto := &pb.JobSpecification{Name: req.JobName} - t.Run("GetJobSpecification", func(t *testing.T) { - t.Run("should read a job spec", func(t *testing.T) { - Version := "1.0.1" + s.namespaceService.On("Get", s.ctx, req.ProjectName, req.NamespaceName).Return(s.namespaceSpec, nil).Once() + s.jobService.On("GetByName", s.ctx, req.JobName, s.namespaceSpec).Return(jobSpec, nil).Once() + s.adapter.On("ToJobProto", jobSpec).Return(jobSpecProto, nil).Once() - projectName := "a-data-project" - jobName1 := "a-data-job" - taskName := "a-data-task" + runtimeServiceServer := s.newRuntimeServiceServer() + resp, err := runtimeServiceServer.GetJobSpecification(s.ctx, req) - projectSpec := models.ProjectSpec{ - ID: uuid.Must(uuid.NewRandom()), - Name: projectName, - Config: map[string]string{ - "bucket": "gs://some_folder", - }, - } + s.Assert().NoError(err) + s.Assert().NotNil(resp) +} - namespaceSpec := models.NamespaceSpec{ - ID: uuid.Must(uuid.NewRandom()), - Name: "dev-test-namespace-1", - Config: map[string]string{ - "bucket": "gs://some_folder", - }, - ProjectSpec: projectSpec, - } +func (s *RuntimeServiceServerTestSuite) TestGetJobSpecification_Fail_NamespaceServiceGetError() { + req := &pb.GetJobSpecificationRequest{} + req.ProjectName = s.projectSpec.Name + req.NamespaceName = s.namespaceSpec.Name + req.JobName = "job-1" - execUnit1 := new(mock.BasePlugin) - execUnit1.On("PluginInfo").Return(&models.PluginInfoResponse{ - Name: taskName, - }, nil) - defer execUnit1.AssertExpectations(t) + s.namespaceService.On("Get", s.ctx, req.ProjectName, req.NamespaceName).Return(models.NamespaceSpec{}, errors.New("any error")).Once() - jobSpecs := []models.JobSpec{ - { - Name: jobName1, - Task: models.JobSpecTask{ - Unit: &models.Plugin{ - Base: execUnit1, - }, - Config: models.JobSpecConfigs{ - { - Name: "do", - Value: "this", - }, - }, - }, - Assets: *models.JobAssets{}.New( - []models.JobSpecAsset{ - { - Name: "query.sql", - Value: "select * from 1", - }, - }), - }, - } + runtimeServiceServer := s.newRuntimeServiceServer() + resp, err := runtimeServiceServer.GetJobSpecification(s.ctx, req) - allTasksRepo := new(mock.SupportedPluginRepo) - allTasksRepo.On("GetByName", taskName).Return(execUnit1, nil) - adapter := v1.NewAdapter(allTasksRepo, nil) + s.Assert().Error(err) + s.Assert().Nil(resp) +} - namespaceService := new(mock.NamespaceService) - namespaceService.On("Get", ctx, projectSpec.Name, namespaceSpec.Name).Return(namespaceSpec, nil) - defer namespaceService.AssertExpectations(t) +func (s *RuntimeServiceServerTestSuite) TestGetJobSpecification_Fail_JobServiceGetByNameError() { + req := &pb.GetJobSpecificationRequest{} + req.ProjectName = s.projectSpec.Name + req.NamespaceName = s.namespaceSpec.Name + req.JobName = "job-1" - jobService := new(mock.JobService) - jobService.On("GetByName", ctx, jobSpecs[0].Name, namespaceSpec).Return(jobSpecs[0], nil) - defer jobService.AssertExpectations(t) + s.namespaceService.On("Get", s.ctx, req.ProjectName, req.NamespaceName).Return(s.namespaceSpec, nil).Once() + s.jobService.On("GetByName", s.ctx, req.JobName, s.namespaceSpec).Return(models.JobSpec{}, errors.New("any error")).Once() - runtimeServiceServer := v1.NewRuntimeServiceServer( - log, - Version, - jobService, - nil, nil, - nil, - namespaceService, - nil, - adapter, - nil, - nil, - nil, - ) + runtimeServiceServer := s.newRuntimeServiceServer() + resp, err := runtimeServiceServer.GetJobSpecification(s.ctx, req) - jobSpecAdapted, _ := adapter.ToJobProto(jobSpecs[0]) - deployRequest := pb.GetJobSpecificationRequest{ProjectName: projectName, JobName: jobSpecs[0].Name, NamespaceName: namespaceSpec.Name} - jobSpecResp, err := runtimeServiceServer.GetJobSpecification(context.Background(), &deployRequest) - assert.Nil(t, err) - assert.Equal(t, jobSpecAdapted, jobSpecResp.Spec) - }) - }) + s.Assert().Error(err) + s.Assert().Nil(resp) +} + +func (s *RuntimeServiceServerTestSuite) TestGetJobSpecification_Fail_AdapterToJobProtoError() { + req := &pb.GetJobSpecificationRequest{} + req.ProjectName = s.projectSpec.Name + req.NamespaceName = s.namespaceSpec.Name + req.JobName = "job-1" + jobSpec := models.JobSpec{Name: req.JobName} + + s.namespaceService.On("Get", s.ctx, req.ProjectName, req.NamespaceName).Return(s.namespaceSpec, nil).Once() + s.jobService.On("GetByName", s.ctx, req.JobName, s.namespaceSpec).Return(jobSpec, nil).Once() + s.adapter.On("ToJobProto", jobSpec).Return(&pb.JobSpecification{}, errors.New("any error")).Once() + + runtimeServiceServer := s.newRuntimeServiceServer() + resp, err := runtimeServiceServer.GetJobSpecification(s.ctx, req) + s.Assert().Error(err) + s.Assert().Nil(resp) +} + +// TODO: refactor to test suite +func TestJobSpecificationOnServer(t *testing.T) { + log := log.NewNoop() + ctx := context.Background() t.Run("RegisterJobSpecification", func(t *testing.T) { t.Run("should save a job specification", func(t *testing.T) { projectName := "a-data-project" @@ -281,7 +322,7 @@ func TestJobSpecificationOnServer(t *testing.T) { } jobSvc := new(mock.JobService) - jobSvc.On("Create", ctx, jobSpec, namespaceSpec).Return(nil) + jobSvc.On("Create", ctx, namespaceSpec, jobSpec).Return(nil) jobSvc.On("Check", ctx, namespaceSpec, []models.JobSpec{jobSpec}, mock2.Anything).Return(nil) jobSvc.On("Sync", mock2.Anything, namespaceSpec, mock2.Anything).Return(nil) defer jobSvc.AssertExpectations(t) diff --git a/api/handler/v1beta1/resource.go b/api/handler/v1beta1/resource.go index 24f2524e01..659a639404 100644 --- a/api/handler/v1beta1/resource.go +++ b/api/handler/v1beta1/resource.go @@ -2,6 +2,8 @@ package v1beta1 import ( "context" + "fmt" + "io" "sync" "time" @@ -74,36 +76,85 @@ func (sv *RuntimeServiceServer) ReadResource(ctx context.Context, req *pb.ReadRe }, nil } -func (sv *RuntimeServiceServer) DeployResourceSpecification(req *pb.DeployResourceSpecificationRequest, respStream pb.RuntimeService_DeployResourceSpecificationServer) error { +func (sv *RuntimeServiceServer) DeployResourceSpecification(stream pb.RuntimeService_DeployResourceSpecificationServer) error { startTime := time.Now() + errNamespaces := []string{} - namespaceSpec, err := sv.namespaceService.Get(respStream.Context(), req.GetProjectName(), req.GetNamespaceName()) - if err != nil { - return mapToGRPCErr(sv.l, err, "unable to get namespace") - } - var resourceSpecs []models.ResourceSpec - for _, resourceProto := range req.GetResources() { - adapted, err := sv.adapter.FromResourceProto(resourceProto, req.DatastoreName) + for { + request, err := stream.Recv() if err != nil { - return status.Errorf(codes.Internal, "%s: cannot adapt resource %s", err.Error(), resourceProto.GetName()) + if err == io.EOF { + break + } + stream.Send(&pb.DeployResourceSpecificationResponse{ + Success: false, + Ack: true, + Message: err.Error(), + }) + return err // immediate error returned (grpc error level) + } + namespaceSpec, err := sv.namespaceService.Get(stream.Context(), request.GetProjectName(), request.GetNamespaceName()) + if err != nil { + stream.Send(&pb.DeployResourceSpecificationResponse{ + Success: false, + Ack: true, + Message: err.Error(), + }) + errNamespaces = append(errNamespaces, request.NamespaceName) + continue + } + var resourceSpecs []models.ResourceSpec + var errMsgs string + for _, resourceProto := range request.GetResources() { + adapted, err := sv.adapter.FromResourceProto(resourceProto, request.DatastoreName) + if err != nil { + currentMsg := fmt.Sprintf("%s: cannot adapt resource %s", err.Error(), resourceProto.GetName()) + sv.l.Error(currentMsg) + errMsgs += currentMsg + "\n" + continue + } + resourceSpecs = append(resourceSpecs, adapted) } - resourceSpecs = append(resourceSpecs, adapted) - } - observers := new(progress.ObserverChain) - observers.Join(sv.progressObserver) - observers.Join(&resourceObserver{ - stream: respStream, - log: sv.l, - mu: new(sync.Mutex), - }) + if errMsgs != "" { + stream.Send(&pb.DeployResourceSpecificationResponse{ + Success: false, + Ack: true, + Message: errMsgs, + }) + errNamespaces = append(errNamespaces, request.NamespaceName) + continue + } - if err := sv.resourceSvc.UpdateResource(respStream.Context(), namespaceSpec, resourceSpecs, observers); err != nil { - return status.Errorf(codes.Internal, "failed to update resources: \n%s", err.Error()) + observers := new(progress.ObserverChain) + observers.Join(sv.progressObserver) + observers.Join(&resourceObserver{ + stream: stream, + log: sv.l, + mu: new(sync.Mutex), + }) + + if err := sv.resourceSvc.UpdateResource(stream.Context(), namespaceSpec, resourceSpecs, observers); err != nil { + stream.Send(&pb.DeployResourceSpecificationResponse{ + Success: false, + Ack: true, + Message: fmt.Sprintf("failed to update resources: \n%s", err.Error()), + }) + errNamespaces = append(errNamespaces, request.NamespaceName) + continue + } + runtimeDeployResourceSpecificationCounter.Add(float64(len(request.Resources))) + stream.Send(&pb.DeployResourceSpecificationResponse{ + Success: true, + Ack: true, + Message: "success", + }) } - - runtimeDeployResourceSpecificationCounter.Add(float64(len(req.Resources))) sv.l.Info("finished resource deployment in", "time", time.Since(startTime)) + if len(errNamespaces) > 0 { + sv.l.Warn("there's error while deploying namespaces: %v", errNamespaces) + return fmt.Errorf("error when deploying: %v", errNamespaces) + } return nil } diff --git a/api/handler/v1beta1/resource_test.go b/api/handler/v1beta1/resource_test.go index d4889de13a..d6549b8a7c 100644 --- a/api/handler/v1beta1/resource_test.go +++ b/api/handler/v1beta1/resource_test.go @@ -2,19 +2,148 @@ package v1beta1_test import ( "context" + "errors" + "io" "testing" + "github.com/google/uuid" v1 "github.com/odpf/optimus/api/handler/v1beta1" pb "github.com/odpf/optimus/api/proto/odpf/optimus/core/v1beta1" "github.com/odpf/optimus/mock" "github.com/odpf/optimus/models" - - "github.com/google/uuid" "github.com/odpf/salt/log" "github.com/stretchr/testify/assert" mock2 "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/suite" ) +func TestRuntimeServiceServerResourceTestSuite(t *testing.T) { + s := new(RuntimeServiceServerTestSuite) + suite.Run(t, s) +} + +func (s *RuntimeServiceServerTestSuite) TestDeployResourceSpecification_Success_NoResourceSpec() { + stream := new(mock.DeployResourceSpecificationServer) + stream.On("Context").Return(s.ctx) + stream.On("Recv").Return(s.resourceReq, nil).Once() + stream.On("Recv").Return(nil, io.EOF).Once() + + s.namespaceService.On("Get", s.ctx, s.jobReq.GetProjectName(), s.jobReq.GetNamespaceName()).Return(s.namespaceSpec, nil).Once() + s.resourceService.On("UpdateResource", s.ctx, s.namespaceSpec, mock2.Anything, mock2.Anything).Return(nil).Once() + stream.On("Send", mock2.Anything).Return(nil).Once() + + runtimeServiceServer := s.newRuntimeServiceServer() + err := runtimeServiceServer.DeployResourceSpecification(stream) + + s.Assert().NoError(err) + stream.AssertExpectations(s.T()) + s.namespaceService.AssertExpectations(s.T()) + s.resourceService.AssertExpectations(s.T()) +} + +func (s *RuntimeServiceServerTestSuite) TestDeployResourceSpecification_Success_TwoResourceSpec() { + resourceSpecs := []*pb.ResourceSpecification{} + resourceSpecs = append(resourceSpecs, &pb.ResourceSpecification{Name: "resource-1"}) + resourceSpecs = append(resourceSpecs, &pb.ResourceSpecification{Name: "resource-2"}) + s.resourceReq.Resources = resourceSpecs + adaptedResources := []models.ResourceSpec{} + adaptedResources = append(adaptedResources, models.ResourceSpec{Name: "resource-1"}) + adaptedResources = append(adaptedResources, models.ResourceSpec{Name: "resource-2"}) + + stream := new(mock.DeployResourceSpecificationServer) + stream.On("Context").Return(s.ctx) + stream.On("Recv").Return(s.resourceReq, nil).Once() + stream.On("Recv").Return(nil, io.EOF).Once() + + s.namespaceService.On("Get", s.ctx, s.jobReq.GetProjectName(), s.jobReq.GetNamespaceName()).Return(s.namespaceSpec, nil).Once() + for i := range resourceSpecs { + s.adapter.On("FromResourceProto", resourceSpecs[i], s.resourceReq.DatastoreName).Return(adaptedResources[i], nil).Once() + } + s.resourceService.On("UpdateResource", s.ctx, s.namespaceSpec, mock2.Anything, mock2.Anything).Return(nil).Once() + stream.On("Send", mock2.Anything).Return(nil).Once() + + runtimeServiceServer := s.newRuntimeServiceServer() + err := runtimeServiceServer.DeployResourceSpecification(stream) + + s.Assert().NoError(err) + stream.AssertExpectations(s.T()) + s.adapter.AssertExpectations(s.T()) + s.namespaceService.AssertExpectations(s.T()) + s.resourceService.AssertExpectations(s.T()) +} + +func (s *RuntimeServiceServerTestSuite) TestDeployResourceSpecification_Fail_StreamRecvError() { + stream := new(mock.DeployResourceSpecificationServer) + stream.On("Recv").Return(nil, errors.New("any error")).Once() + stream.On("Send", mock2.Anything).Return(nil).Once() + + runtimeServiceServer := s.newRuntimeServiceServer() + err := runtimeServiceServer.DeployResourceSpecification(stream) + + s.Assert().Error(err) + stream.AssertExpectations(s.T()) +} + +func (s *RuntimeServiceServerTestSuite) TestDeployResourceSpecification_Fail_NamespaceServiceGetError() { + stream := new(mock.DeployResourceSpecificationServer) + stream.On("Context").Return(s.ctx) + stream.On("Recv").Return(s.resourceReq, nil).Once() + stream.On("Recv").Return(nil, io.EOF).Once() + + s.namespaceService.On("Get", s.ctx, s.jobReq.GetProjectName(), s.jobReq.GetNamespaceName()).Return(models.NamespaceSpec{}, errors.New("any error")).Once() + stream.On("Send", mock2.Anything).Return(nil).Once() + + runtimeServiceServer := s.newRuntimeServiceServer() + err := runtimeServiceServer.DeployResourceSpecification(stream) + + s.Assert().Error(err) + stream.AssertExpectations(s.T()) + s.namespaceService.AssertExpectations(s.T()) +} + +func (s *RuntimeServiceServerTestSuite) TestDeployResourceSpecification_Fail_AdapterFromResourceProtoError() { + resourceSpecs := []*pb.ResourceSpecification{} + resourceSpecs = append(resourceSpecs, &pb.ResourceSpecification{Name: "resource-1"}) + s.resourceReq.Resources = resourceSpecs + + stream := new(mock.DeployResourceSpecificationServer) + stream.On("Context").Return(s.ctx) + stream.On("Recv").Return(s.resourceReq, nil).Once() + stream.On("Recv").Return(nil, io.EOF).Once() + + s.namespaceService.On("Get", s.ctx, s.jobReq.GetProjectName(), s.jobReq.GetNamespaceName()).Return(s.namespaceSpec, nil).Once() + s.adapter.On("FromResourceProto", resourceSpecs[0], s.resourceReq.DatastoreName).Return(models.ResourceSpec{}, errors.New("any error")).Once() + stream.On("Send", mock2.Anything).Return(nil).Once() + + runtimeServiceServer := s.newRuntimeServiceServer() + err := runtimeServiceServer.DeployResourceSpecification(stream) + + s.Assert().Error(err) + stream.AssertExpectations(s.T()) + s.namespaceService.AssertExpectations(s.T()) + s.adapter.AssertExpectations(s.T()) +} + +func (s *RuntimeServiceServerTestSuite) TestDeployResourceSpecification_Fail_ResourceServiceUpdateResourceError() { + stream := new(mock.DeployResourceSpecificationServer) + stream.On("Context").Return(s.ctx) + stream.On("Recv").Return(s.resourceReq, nil).Once() + stream.On("Recv").Return(nil, io.EOF).Once() + + s.namespaceService.On("Get", s.ctx, s.jobReq.GetProjectName(), s.jobReq.GetNamespaceName()).Return(s.namespaceSpec, nil).Once() + s.resourceService.On("UpdateResource", s.ctx, s.namespaceSpec, mock2.Anything, mock2.Anything).Return(errors.New("any error")).Once() + stream.On("Send", mock2.Anything).Return(nil).Once() + + runtimeServiceServer := s.newRuntimeServiceServer() + err := runtimeServiceServer.DeployResourceSpecification(stream) + + s.Assert().Error(err) + stream.AssertExpectations(s.T()) + s.namespaceService.AssertExpectations(s.T()) + s.resourceService.AssertExpectations(s.T()) +} + +// TODO: refactor to test suite func TestResourcesOnServer(t *testing.T) { log := log.NewNoop() ctx := context.Background() diff --git a/api/handler/v1beta1/runtime_test.go b/api/handler/v1beta1/runtime_test.go index 8575d286f7..3e7b4bca1b 100644 --- a/api/handler/v1beta1/runtime_test.go +++ b/api/handler/v1beta1/runtime_test.go @@ -6,21 +6,88 @@ import ( "testing" "time" + "github.com/google/uuid" v1 "github.com/odpf/optimus/api/handler/v1beta1" pb "github.com/odpf/optimus/api/proto/odpf/optimus/core/v1beta1" + "github.com/odpf/optimus/core/progress" "github.com/odpf/optimus/mock" "github.com/odpf/optimus/models" "github.com/odpf/optimus/run" "github.com/odpf/optimus/utils" - - "google.golang.org/protobuf/types/known/structpb" - "google.golang.org/protobuf/types/known/timestamppb" - - "github.com/google/uuid" "github.com/odpf/salt/log" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" + "google.golang.org/protobuf/types/known/structpb" + "google.golang.org/protobuf/types/known/timestamppb" ) +type RuntimeServiceServerTestSuite struct { + suite.Suite + version string + ctx context.Context //nolint:containedctx + namespaceService *mock.NamespaceService + projectService *mock.ProjectService + secretService *mock.SecretService + runService *mock.RunService // TODO: refactor to service package + jobService *mock.JobService // TODO: refactor to service package + resourceService *mock.DatastoreService // TODO: refactor to service package + jobEventService v1.JobEventService // TODO: refactor to service package + adapter *mock.ProtoAdapter + scheduler models.SchedulerUnit + log log.Logger + progressObserver progress.Observer + + jobReq *pb.DeployJobSpecificationRequest + resourceReq *pb.DeployResourceSpecificationRequest + projectSpec models.ProjectSpec + namespaceSpec models.NamespaceSpec +} + +func (s *RuntimeServiceServerTestSuite) SetupTest() { + s.version = "v1.0.0" + s.ctx = context.Background() + s.namespaceService = new(mock.NamespaceService) + s.adapter = new(mock.ProtoAdapter) + s.jobService = new(mock.JobService) + s.resourceService = new(mock.DatastoreService) + s.log = log.NewNoop() + // ... etdc + + s.projectSpec = models.ProjectSpec{} + s.projectSpec.Name = "project-a" + s.projectSpec.ID = uuid.MustParse("26a0d6a0-13c6-4b30-ae6f-29233df70f31") + + s.namespaceSpec = models.NamespaceSpec{} + s.namespaceSpec.Name = "ns1" + s.namespaceSpec.ID = uuid.MustParse("ceba7919-e07d-48b4-a4ce-141d79a3b59d") + + s.jobReq = &pb.DeployJobSpecificationRequest{} + s.jobReq.ProjectName = s.projectSpec.Name + s.jobReq.NamespaceName = s.namespaceSpec.Name + + s.resourceReq = &pb.DeployResourceSpecificationRequest{} + s.resourceReq.DatastoreName = "datastore-1" + s.resourceReq.ProjectName = s.projectSpec.Name + s.resourceReq.NamespaceName = s.namespaceSpec.Name +} + +func (s *RuntimeServiceServerTestSuite) newRuntimeServiceServer() *v1.RuntimeServiceServer { + return v1.NewRuntimeServiceServer( + s.log, + s.version, + s.jobService, + s.jobEventService, + s.resourceService, + s.projectService, + s.namespaceService, + s.secretService, + s.adapter, + s.progressObserver, + s.runService, + s.scheduler, + ) +} + func TestRuntimeServiceServer(t *testing.T) { log := log.NewNoop() ctx := context.Background() diff --git a/api/proto/odpf/optimus/core/v1beta1/runtime.pb.go b/api/proto/odpf/optimus/core/v1beta1/runtime.pb.go index 226d92193e..3f497ee79d 100644 --- a/api/proto/odpf/optimus/core/v1beta1/runtime.pb.go +++ b/api/proto/odpf/optimus/core/v1beta1/runtime.pb.go @@ -7415,7 +7415,7 @@ var file_odpf_optimus_core_v1beta1_runtime_proto_rawDesc = []byte{ 0x72, 0x61, 0x6d, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x3a, 0x02, 0x38, 0x01, 0x32, 0xc2, 0x33, 0x0a, 0x0e, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, + 0x65, 0x3a, 0x02, 0x38, 0x01, 0x32, 0xc6, 0x33, 0x0a, 0x0e, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x7d, 0x0a, 0x07, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x29, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, @@ -7424,7 +7424,7 @@ var file_odpf_optimus_core_v1beta1_runtime_proto_rawDesc = []byte{ 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x22, 0x10, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x76, 0x65, 0x72, - 0x73, 0x69, 0x6f, 0x6e, 0x3a, 0x01, 0x2a, 0x12, 0x91, 0x01, 0x0a, 0x16, 0x44, 0x65, 0x70, 0x6c, + 0x73, 0x69, 0x6f, 0x6e, 0x3a, 0x01, 0x2a, 0x12, 0x93, 0x01, 0x0a, 0x16, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x38, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x44, @@ -7433,410 +7433,410 @@ var file_odpf_optimus_core_v1beta1_runtime_proto_rawDesc = []byte{ 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0xd8, 0x01, 0x0a, 0x16, - 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x38, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, - 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, - 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, - 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x39, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, - 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x49, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x43, 0x22, 0x3e, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, - 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, - 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2f, 0x7b, - 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, - 0x6a, 0x6f, 0x62, 0x3a, 0x01, 0x2a, 0x12, 0xd7, 0x01, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x4a, 0x6f, - 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x35, - 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, - 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4a, 0x6f, - 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x36, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, - 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, - 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x51, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x4b, 0x12, 0x49, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, - 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, - 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, - 0x7d, 0x2f, 0x6a, 0x6f, 0x62, 0x2f, 0x7b, 0x6a, 0x6f, 0x62, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, - 0x12, 0xe0, 0x01, 0x0a, 0x16, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x53, 0x70, - 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x38, 0x2e, 0x6f, 0x64, - 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, - 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4a, 0x6f, - 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x39, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, - 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, - 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, - 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x51, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x4b, 0x2a, 0x49, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, - 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, - 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, - 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6a, 0x6f, 0x62, 0x2f, 0x7b, 0x6a, 0x6f, 0x62, 0x5f, 0x6e, 0x61, - 0x6d, 0x65, 0x7d, 0x12, 0xcf, 0x01, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x4a, 0x6f, 0x62, 0x53, - 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x36, 0x2e, 0x6f, - 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, - 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4a, 0x6f, 0x62, - 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x37, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, - 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, - 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x46, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x40, 0x12, 0x3e, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x28, 0x01, 0x30, 0x01, 0x12, 0xd8, 0x01, + 0x0a, 0x16, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, + 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x38, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, + 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, + 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x53, 0x70, + 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x39, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, + 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x49, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x43, 0x22, 0x3e, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, - 0x7d, 0x2f, 0x6a, 0x6f, 0x62, 0x12, 0xc1, 0x01, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x4a, 0x6f, 0x62, - 0x54, 0x61, 0x73, 0x6b, 0x12, 0x2c, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, + 0x7d, 0x2f, 0x6a, 0x6f, 0x62, 0x3a, 0x01, 0x2a, 0x12, 0xd7, 0x01, 0x0a, 0x13, 0x47, 0x65, 0x74, + 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x12, 0x35, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, + 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, + 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x36, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, + 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, + 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, + 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x51, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x4b, 0x12, 0x49, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, + 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, + 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x7d, 0x2f, 0x6a, 0x6f, 0x62, 0x2f, 0x7b, 0x6a, 0x6f, 0x62, 0x5f, 0x6e, 0x61, 0x6d, + 0x65, 0x7d, 0x12, 0xe0, 0x01, 0x0a, 0x16, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4a, 0x6f, 0x62, + 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x38, 0x2e, + 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, + 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x39, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, + 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, + 0x74, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, + 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x51, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x4b, 0x2a, 0x49, 0x2f, 0x76, 0x31, 0x62, + 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, + 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6e, 0x61, 0x6d, 0x65, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6a, 0x6f, 0x62, 0x2f, 0x7b, 0x6a, 0x6f, 0x62, 0x5f, + 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x12, 0xcf, 0x01, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x4a, 0x6f, + 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x36, + 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, + 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4a, + 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x37, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, + 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, + 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, + 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x46, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x40, 0x12, 0x3e, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, + 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, + 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x7d, 0x2f, 0x6a, 0x6f, 0x62, 0x12, 0xc1, 0x01, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x4a, + 0x6f, 0x62, 0x54, 0x61, 0x73, 0x6b, 0x12, 0x2c, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, + 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, + 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4a, 0x6f, 0x62, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, - 0x2e, 0x47, 0x65, 0x74, 0x4a, 0x6f, 0x62, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, - 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, - 0x65, 0x74, 0x4a, 0x6f, 0x62, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x56, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x50, 0x12, 0x4e, 0x2f, 0x76, 0x31, 0x62, 0x65, - 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, - 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, - 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6a, 0x6f, 0x62, 0x2f, 0x7b, 0x6a, 0x6f, 0x62, 0x5f, 0x6e, - 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x74, 0x61, 0x73, 0x6b, 0x12, 0xbd, 0x01, 0x0a, 0x15, 0x43, 0x68, - 0x65, 0x63, 0x6b, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x12, 0x37, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, - 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, + 0x2e, 0x47, 0x65, 0x74, 0x4a, 0x6f, 0x62, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x56, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x50, 0x12, 0x4e, 0x2f, 0x76, 0x31, + 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, + 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6e, 0x61, 0x6d, + 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6a, 0x6f, 0x62, 0x2f, 0x7b, 0x6a, 0x6f, 0x62, + 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x74, 0x61, 0x73, 0x6b, 0x12, 0xbd, 0x01, 0x0a, 0x15, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x38, 0x2e, 0x6f, - 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, - 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4a, 0x6f, - 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x31, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2b, 0x22, 0x29, - 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, - 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, - 0x6a, 0x6f, 0x62, 0x2f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x91, 0x01, 0x0a, 0x16, 0x43, 0x68, - 0x65, 0x63, 0x6b, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x38, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, - 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, - 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x39, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x37, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, + 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, + 0x31, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, + 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x38, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x95, 0x01, - 0x0a, 0x0f, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, - 0x74, 0x12, 0x31, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, - 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, - 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x31, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2b, + 0x22, 0x29, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, + 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, + 0x7d, 0x2f, 0x6a, 0x6f, 0x62, 0x2f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x91, 0x01, 0x0a, 0x16, + 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x38, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, + 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, + 0x61, 0x31, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, + 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x39, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, + 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x68, 0x65, + 0x63, 0x6b, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, + 0x95, 0x01, 0x0a, 0x0f, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x6a, + 0x65, 0x63, 0x74, 0x12, 0x31, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, + 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, + 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, + 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, + 0x61, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x6a, 0x65, + 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x15, 0x22, 0x10, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, + 0x6a, 0x65, 0x63, 0x74, 0x3a, 0x01, 0x2a, 0x12, 0xc9, 0x01, 0x0a, 0x18, 0x52, 0x65, 0x67, 0x69, + 0x73, 0x74, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x12, 0x3a, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, - 0x22, 0x10, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, - 0x63, 0x74, 0x3a, 0x01, 0x2a, 0x12, 0xc9, 0x01, 0x0a, 0x18, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, - 0x65, 0x72, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x12, 0x3a, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, - 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, - 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, - 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3b, - 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, - 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, - 0x74, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x34, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x2e, 0x22, 0x29, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, - 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, - 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x3a, 0x01, - 0x2a, 0x12, 0xb6, 0x01, 0x0a, 0x0e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x53, 0x65, - 0x63, 0x72, 0x65, 0x74, 0x12, 0x30, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, - 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, - 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, + 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x3b, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, + 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x67, + 0x69, 0x73, 0x74, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x34, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x2e, 0x22, 0x29, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, + 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, + 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x3a, 0x01, 0x2a, 0x12, 0xb6, 0x01, 0x0a, 0x0e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, + 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x30, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x53, 0x65, 0x63, 0x72, 0x65, - 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3f, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x39, 0x22, 0x34, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, - 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, - 0x65, 0x7d, 0x2f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x2f, 0x7b, 0x73, 0x65, 0x63, 0x72, 0x65, - 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x3a, 0x01, 0x2a, 0x12, 0xb0, 0x01, 0x0a, 0x0c, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x2e, 0x2e, 0x6f, 0x64, - 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, - 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x65, - 0x63, 0x72, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x6f, 0x64, - 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, - 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x65, - 0x63, 0x72, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3f, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x39, 0x1a, 0x34, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, - 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, - 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x2f, 0x7b, 0x73, 0x65, - 0x63, 0x72, 0x65, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x3a, 0x01, 0x2a, 0x12, 0x9c, 0x01, - 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x12, 0x2d, 0x2e, - 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, - 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, - 0x63, 0x72, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x6f, - 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, - 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x63, - 0x72, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2e, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x28, 0x12, 0x26, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, - 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, - 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0xad, 0x01, 0x0a, - 0x0c, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x2e, 0x2e, + 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, + 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, + 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x53, 0x65, 0x63, + 0x72, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3f, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x39, 0x22, 0x34, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, + 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, + 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x2f, 0x7b, 0x73, 0x65, 0x63, + 0x72, 0x65, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x3a, 0x01, 0x2a, 0x12, 0xb0, 0x01, 0x0a, + 0x0c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x2e, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, - 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, - 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, - 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3c, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x36, 0x2a, 0x34, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, + 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3f, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x39, 0x1a, 0x34, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x2f, 0x7b, - 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x12, 0x89, 0x01, 0x0a, - 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x12, 0x2e, 0x2e, - 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, - 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, - 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, + 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x3a, 0x01, 0x2a, 0x12, + 0x9c, 0x01, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x12, + 0x2d, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, + 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, + 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, + 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, + 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, + 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2e, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x28, 0x12, 0x26, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, + 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, + 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0xad, + 0x01, 0x0a, 0x0c, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, + 0x2e, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, + 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x2f, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, + 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x3c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x36, 0x2a, 0x34, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, + 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, + 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, + 0x2f, 0x7b, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x12, 0x89, + 0x01, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x12, + 0x2e, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, + 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, + 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x2f, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, + 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, + 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x12, 0x10, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, + 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0xbd, 0x01, 0x0a, 0x15, 0x4c, + 0x69, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x73, 0x12, 0x37, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, + 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, + 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x38, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, - 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x12, 0x10, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, - 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0xbd, 0x01, 0x0a, 0x15, 0x4c, 0x69, 0x73, - 0x74, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x73, 0x12, 0x37, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, - 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, - 0x69, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x38, 0x2e, 0x6f, 0x64, - 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, - 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x6a, - 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x31, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2b, 0x12, 0x29, 0x2f, + 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x31, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2b, 0x12, + 0x29, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, + 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, + 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0xbf, 0x01, 0x0a, 0x10, 0x52, + 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x12, + 0x32, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, + 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, + 0x73, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x33, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, + 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, + 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x42, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x3c, + 0x22, 0x37, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, + 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, + 0x7d, 0x2f, 0x6a, 0x6f, 0x62, 0x2f, 0x7b, 0x6a, 0x6f, 0x62, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, + 0x2f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0xa5, 0x01, 0x0a, + 0x09, 0x4a, 0x6f, 0x62, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x2b, 0x2e, 0x6f, 0x64, 0x70, + 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, + 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, + 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, + 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x37, 0x12, 0x35, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, - 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6e, - 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0xbf, 0x01, 0x0a, 0x10, 0x52, 0x65, 0x67, - 0x69, 0x73, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x32, 0x2e, + 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6a, + 0x6f, 0x62, 0x2f, 0x7b, 0x6a, 0x6f, 0x62, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x73, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x12, 0xd7, 0x01, 0x0a, 0x10, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, + 0x72, 0x4a, 0x6f, 0x62, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x32, 0x2e, 0x6f, 0x64, 0x70, 0x66, + 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, + 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4a, 0x6f, + 0x62, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x33, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, - 0x65, 0x72, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x33, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, - 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, - 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x42, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x3c, 0x22, 0x37, - 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, - 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, - 0x6a, 0x6f, 0x62, 0x2f, 0x7b, 0x6a, 0x6f, 0x62, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x69, - 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0xa5, 0x01, 0x0a, 0x09, 0x4a, - 0x6f, 0x62, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x2b, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, + 0x65, 0x72, 0x4a, 0x6f, 0x62, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x5a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x54, 0x22, 0x4f, 0x2f, 0x76, 0x31, 0x62, + 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, + 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6e, 0x61, 0x6d, 0x65, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6a, 0x6f, 0x62, 0x2f, 0x7b, 0x6a, 0x6f, 0x62, 0x5f, + 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3a, 0x01, 0x2a, 0x12, 0x7f, + 0x0a, 0x09, 0x47, 0x65, 0x74, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x12, 0x2b, 0x2e, 0x6f, 0x64, + 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, + 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x69, 0x6e, 0x64, 0x6f, + 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, - 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, + 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x12, 0x0f, + 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x12, + 0xa2, 0x01, 0x0a, 0x1b, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x3d, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, + 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x70, 0x6c, + 0x6f, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, + 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3e, + 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, + 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x70, 0x6c, 0x6f, + 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, + 0x28, 0x01, 0x30, 0x01, 0x12, 0xfe, 0x01, 0x0a, 0x19, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x12, 0x3b, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, + 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, + 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x70, 0x65, 0x63, 0x69, + 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x3c, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, + 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, + 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x66, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x60, 0x12, 0x5e, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, + 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, + 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, + 0x7d, 0x2f, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x7b, 0x64, 0x61, 0x74, + 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0xe0, 0x01, 0x0a, 0x0e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x30, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, + 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, + 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x6f, 0x64, 0x70, + 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, + 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x69, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x63, 0x22, 0x5e, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, + 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, + 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, + 0x7d, 0x2f, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x7b, 0x64, 0x61, 0x74, + 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0xe7, 0x01, 0x0a, 0x0c, 0x52, 0x65, 0x61, + 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x2e, 0x2e, 0x6f, 0x64, 0x70, 0x66, + 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, + 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x6f, 0x64, 0x70, 0x66, + 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, + 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x76, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x70, 0x12, 0x6e, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, + 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x7d, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2f, 0x7b, 0x6e, + 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x64, + 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x7b, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, + 0x6f, 0x72, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x2f, 0x7b, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, + 0x65, 0x7d, 0x12, 0xe0, 0x01, 0x0a, 0x0e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x30, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, - 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x3d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x37, 0x12, 0x35, 0x2f, 0x76, 0x31, - 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, - 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6a, 0x6f, 0x62, - 0x2f, 0x7b, 0x6a, 0x6f, 0x62, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x73, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x12, 0xd7, 0x01, 0x0a, 0x10, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4a, - 0x6f, 0x62, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x32, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, + 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, - 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4a, 0x6f, 0x62, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x33, 0x2e, 0x6f, 0x64, - 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, - 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, - 0x4a, 0x6f, 0x62, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x5a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x54, 0x22, 0x4f, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, - 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, - 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, - 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6a, 0x6f, 0x62, 0x2f, 0x7b, 0x6a, 0x6f, 0x62, 0x5f, 0x6e, 0x61, - 0x6d, 0x65, 0x7d, 0x2f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3a, 0x01, 0x2a, 0x12, 0x7f, 0x0a, 0x09, - 0x47, 0x65, 0x74, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x12, 0x2b, 0x2e, 0x6f, 0x64, 0x70, 0x66, - 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, - 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, + 0x74, 0x61, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x69, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x63, 0x1a, 0x5e, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, + 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x7d, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2f, 0x7b, 0x6e, + 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x64, + 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x7b, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, + 0x6f, 0x72, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0xa9, 0x01, 0x0a, 0x0c, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x79, + 0x44, 0x72, 0x79, 0x52, 0x75, 0x6e, 0x12, 0x2e, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, - 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x12, 0x0f, 0x2f, 0x76, - 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x12, 0xa0, 0x01, - 0x0a, 0x1b, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3d, 0x2e, - 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, - 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, - 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3e, 0x2e, 0x6f, + 0x61, 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x44, 0x72, 0x79, 0x52, 0x75, 0x6e, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, + 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, + 0x61, 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x44, 0x72, 0x79, 0x52, 0x75, 0x6e, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x38, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x32, 0x22, + 0x2d, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, + 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, + 0x2f, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x2f, 0x64, 0x72, 0x79, 0x72, 0x75, 0x6e, 0x3a, 0x01, + 0x2a, 0x12, 0x90, 0x01, 0x0a, 0x06, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x12, 0x28, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, - 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x52, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, - 0x12, 0xfe, 0x01, 0x0a, 0x19, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3b, - 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, - 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3c, 0x2e, 0x6f, 0x64, + 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, + 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, + 0x61, 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x31, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2b, 0x22, 0x26, 0x2f, 0x76, 0x31, 0x62, 0x65, + 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, + 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x72, 0x65, 0x70, 0x6c, 0x61, + 0x79, 0x3a, 0x01, 0x2a, 0x12, 0xad, 0x01, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x52, 0x65, 0x70, 0x6c, + 0x61, 0x79, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x31, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, + 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, + 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, - 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x66, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x60, 0x12, 0x5e, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, - 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, - 0x65, 0x7d, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2f, 0x7b, 0x6e, 0x61, - 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x64, 0x61, - 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x7b, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, - 0x72, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x12, 0xe0, 0x01, 0x0a, 0x0e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x12, 0x30, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, + 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x61, + 0x79, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x33, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2d, 0x12, 0x2b, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, + 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, + 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x2f, + 0x7b, 0x69, 0x64, 0x7d, 0x12, 0x9c, 0x01, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x70, + 0x6c, 0x61, 0x79, 0x73, 0x12, 0x2d, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, - 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, - 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, - 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x69, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x63, 0x22, 0x5e, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, - 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, - 0x65, 0x7d, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2f, 0x7b, 0x6e, 0x61, - 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x64, 0x61, - 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x7b, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, - 0x72, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x3a, 0x01, 0x2a, 0x12, 0xe7, 0x01, 0x0a, 0x0c, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x2e, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, + 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, + 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, + 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x28, 0x12, 0x26, 0x2f, 0x76, 0x31, + 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, + 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x72, 0x65, 0x70, + 0x6c, 0x61, 0x79, 0x12, 0xde, 0x01, 0x0a, 0x0c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x44, 0x72, + 0x79, 0x52, 0x75, 0x6e, 0x12, 0x2e, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, + 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, + 0x2e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x44, 0x72, 0x79, 0x52, 0x75, 0x6e, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, + 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, + 0x2e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x44, 0x72, 0x79, 0x52, 0x75, 0x6e, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x6d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x67, 0x22, 0x62, 0x2f, + 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, + 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6e, + 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, + 0x6f, 0x72, 0x65, 0x2f, 0x7b, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x6e, + 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x44, 0x72, 0x79, 0x72, 0x75, + 0x6e, 0x3a, 0x01, 0x2a, 0x12, 0xd8, 0x01, 0x0a, 0x0c, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x42, + 0x61, 0x63, 0x6b, 0x75, 0x70, 0x12, 0x2e, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, - 0x31, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, + 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, - 0x31, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x76, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x70, 0x12, 0x6e, + 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x67, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x61, 0x22, 0x5c, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x7b, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, - 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2f, 0x7b, - 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x12, 0xe0, - 0x01, 0x0a, 0x0e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x12, 0x30, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, - 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, - 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, - 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x69, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x63, 0x1a, 0x5e, - 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, - 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, - 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x64, 0x61, 0x74, 0x61, 0x73, - 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x7b, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, - 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x3a, 0x01, - 0x2a, 0x12, 0xa9, 0x01, 0x0a, 0x0c, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x44, 0x72, 0x79, 0x52, - 0x75, 0x6e, 0x12, 0x2e, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, - 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, - 0x65, 0x70, 0x6c, 0x61, 0x79, 0x44, 0x72, 0x79, 0x52, 0x75, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, - 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, - 0x65, 0x70, 0x6c, 0x61, 0x79, 0x44, 0x72, 0x79, 0x52, 0x75, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x38, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x32, 0x22, 0x2d, 0x2f, 0x76, 0x31, - 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, - 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x72, 0x65, 0x70, - 0x6c, 0x61, 0x79, 0x2f, 0x64, 0x72, 0x79, 0x72, 0x75, 0x6e, 0x3a, 0x01, 0x2a, 0x12, 0x90, 0x01, - 0x0a, 0x06, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x12, 0x28, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, - 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, - 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, - 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, - 0x65, 0x70, 0x6c, 0x61, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x31, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x2b, 0x22, 0x26, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, - 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, - 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x3a, 0x01, 0x2a, - 0x12, 0xad, 0x01, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x53, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x12, 0x31, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, - 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, - 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, - 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, - 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x53, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x33, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x2d, 0x12, 0x2b, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, - 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, - 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x2f, 0x7b, 0x69, 0x64, 0x7d, - 0x12, 0x9c, 0x01, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x73, - 0x12, 0x2d, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, - 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, - 0x74, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x2e, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, + 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x3a, 0x01, 0x2a, 0x12, + 0xd2, 0x01, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x12, + 0x2d, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, - 0x52, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x28, 0x12, 0x26, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, - 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, - 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x12, - 0xde, 0x01, 0x0a, 0x0c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x44, 0x72, 0x79, 0x52, 0x75, 0x6e, - 0x12, 0x2e, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, - 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x42, 0x61, 0x63, - 0x6b, 0x75, 0x70, 0x44, 0x72, 0x79, 0x52, 0x75, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x2f, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, - 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x42, 0x61, 0x63, - 0x6b, 0x75, 0x70, 0x44, 0x72, 0x79, 0x52, 0x75, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x6d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x67, 0x22, 0x62, 0x2f, 0x76, 0x31, 0x62, 0x65, - 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, - 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, - 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, - 0x7b, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, - 0x2f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x44, 0x72, 0x79, 0x72, 0x75, 0x6e, 0x3a, 0x01, 0x2a, - 0x12, 0xd8, 0x01, 0x0a, 0x0c, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x42, 0x61, 0x63, 0x6b, 0x75, - 0x70, 0x12, 0x2e, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, - 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x72, - 0x65, 0x61, 0x74, 0x65, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x2f, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, - 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x72, - 0x65, 0x61, 0x74, 0x65, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x67, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x61, 0x22, 0x5c, 0x2f, 0x76, 0x31, 0x62, - 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, - 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6e, 0x61, 0x6d, 0x65, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, - 0x2f, 0x7b, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, - 0x7d, 0x2f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x3a, 0x01, 0x2a, 0x12, 0xd2, 0x01, 0x0a, 0x0b, - 0x4c, 0x69, 0x73, 0x74, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x12, 0x2d, 0x2e, 0x6f, 0x64, - 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, - 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x42, 0x61, 0x63, 0x6b, - 0x75, 0x70, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x6f, 0x64, 0x70, - 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, - 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x42, 0x61, 0x63, 0x6b, 0x75, - 0x70, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x64, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x5e, 0x12, 0x5c, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, - 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, - 0x6d, 0x65, 0x7d, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2f, 0x7b, 0x6e, - 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x64, - 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x7b, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, - 0x6f, 0x72, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, - 0x12, 0xcc, 0x01, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x12, 0x2b, + 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, - 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x61, - 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x6f, 0x64, - 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, - 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x61, 0x63, 0x6b, 0x75, - 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x64, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x5e, 0x12, 0x5c, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, - 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6e, 0x61, - 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, - 0x72, 0x65, 0x2f, 0x7b, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x6e, 0x61, - 0x6d, 0x65, 0x7d, 0x2f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x12, - 0xa8, 0x01, 0x0a, 0x06, 0x52, 0x75, 0x6e, 0x4a, 0x6f, 0x62, 0x12, 0x28, 0x2e, 0x6f, 0x64, 0x70, - 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, - 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x75, 0x6e, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, - 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, - 0x2e, 0x52, 0x75, 0x6e, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x49, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x43, 0x22, 0x3e, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, - 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, - 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, - 0x6d, 0x65, 0x7d, 0x2f, 0x72, 0x75, 0x6e, 0x3a, 0x01, 0x2a, 0x42, 0x86, 0x01, 0x0a, 0x16, 0x69, - 0x6f, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x6e, 0x2e, 0x6f, 0x70, - 0x74, 0x69, 0x6d, 0x75, 0x73, 0x42, 0x15, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x53, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x50, 0x01, 0x5a, 0x1e, - 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x64, 0x70, 0x66, 0x2f, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x6e, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x92, 0x41, - 0x32, 0x12, 0x05, 0x32, 0x03, 0x30, 0x2e, 0x31, 0x1a, 0x0e, 0x31, 0x32, 0x37, 0x2e, 0x30, 0x2e, - 0x30, 0x2e, 0x31, 0x3a, 0x39, 0x31, 0x30, 0x30, 0x22, 0x04, 0x2f, 0x61, 0x70, 0x69, 0x2a, 0x01, - 0x01, 0x72, 0x10, 0x0a, 0x0e, 0x4f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x20, 0x73, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x42, + 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x64, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x5e, 0x12, 0x5c, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, + 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, + 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, + 0x65, 0x7d, 0x2f, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x7b, 0x64, 0x61, + 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x62, 0x61, + 0x63, 0x6b, 0x75, 0x70, 0x12, 0xcc, 0x01, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x42, 0x61, 0x63, 0x6b, + 0x75, 0x70, 0x12, 0x2b, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, + 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, + 0x65, 0x74, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x2c, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, + 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x42, + 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x64, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x5e, 0x12, 0x5c, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, + 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, + 0x7d, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, + 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x64, 0x61, 0x74, + 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x7b, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, + 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x2f, 0x7b, + 0x69, 0x64, 0x7d, 0x12, 0xa8, 0x01, 0x0a, 0x06, 0x52, 0x75, 0x6e, 0x4a, 0x6f, 0x62, 0x12, 0x28, + 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, + 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x75, 0x6e, 0x4a, 0x6f, + 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, + 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, + 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x75, 0x6e, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x49, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x43, 0x22, 0x3e, 0x2f, 0x76, 0x31, + 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, + 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6e, 0x61, 0x6d, + 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x72, 0x75, 0x6e, 0x3a, 0x01, 0x2a, 0x42, 0x86, + 0x01, 0x0a, 0x16, 0x69, 0x6f, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x6e, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x42, 0x15, 0x52, 0x75, 0x6e, 0x74, 0x69, + 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, + 0x50, 0x01, 0x5a, 0x1e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, + 0x64, 0x70, 0x66, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x6e, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6d, + 0x75, 0x73, 0x92, 0x41, 0x32, 0x12, 0x05, 0x32, 0x03, 0x30, 0x2e, 0x31, 0x1a, 0x0e, 0x31, 0x32, + 0x37, 0x2e, 0x30, 0x2e, 0x30, 0x2e, 0x31, 0x3a, 0x39, 0x31, 0x30, 0x30, 0x22, 0x04, 0x2f, 0x61, + 0x70, 0x69, 0x2a, 0x01, 0x01, 0x72, 0x10, 0x0a, 0x0e, 0x4f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, + 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/api/proto/odpf/optimus/core/v1beta1/runtime_grpc.pb.go b/api/proto/odpf/optimus/core/v1beta1/runtime_grpc.pb.go index 3c43520038..1264d927ff 100644 --- a/api/proto/odpf/optimus/core/v1beta1/runtime_grpc.pb.go +++ b/api/proto/odpf/optimus/core/v1beta1/runtime_grpc.pb.go @@ -25,7 +25,7 @@ type RuntimeServiceClient interface { // of deployments. Message containing ack are status events other are progress // events // State of the world request - DeployJobSpecification(ctx context.Context, in *DeployJobSpecificationRequest, opts ...grpc.CallOption) (RuntimeService_DeployJobSpecificationClient, error) + DeployJobSpecification(ctx context.Context, opts ...grpc.CallOption) (RuntimeService_DeployJobSpecificationClient, error) // CreateJobSpecification registers a new job for a namespace which belongs to a project CreateJobSpecification(ctx context.Context, in *CreateJobSpecificationRequest, opts ...grpc.CallOption) (*CreateJobSpecificationResponse, error) // GetJobSpecification reads a provided job spec of a namespace @@ -68,7 +68,7 @@ type RuntimeServiceClient interface { GetWindow(ctx context.Context, in *GetWindowRequest, opts ...grpc.CallOption) (*GetWindowResponse, error) // DeployResourceSpecification migrate all resource specifications of a datastore in project // State of the world request - DeployResourceSpecification(ctx context.Context, in *DeployResourceSpecificationRequest, opts ...grpc.CallOption) (RuntimeService_DeployResourceSpecificationClient, error) + DeployResourceSpecification(ctx context.Context, opts ...grpc.CallOption) (RuntimeService_DeployResourceSpecificationClient, error) // ListResourceSpecification lists all resource specifications of a datastore in project ListResourceSpecification(ctx context.Context, in *ListResourceSpecificationRequest, opts ...grpc.CallOption) (*ListResourceSpecificationResponse, error) // Database CRUD @@ -108,22 +108,17 @@ func (c *runtimeServiceClient) Version(ctx context.Context, in *VersionRequest, return out, nil } -func (c *runtimeServiceClient) DeployJobSpecification(ctx context.Context, in *DeployJobSpecificationRequest, opts ...grpc.CallOption) (RuntimeService_DeployJobSpecificationClient, error) { +func (c *runtimeServiceClient) DeployJobSpecification(ctx context.Context, opts ...grpc.CallOption) (RuntimeService_DeployJobSpecificationClient, error) { stream, err := c.cc.NewStream(ctx, &RuntimeService_ServiceDesc.Streams[0], "/odpf.optimus.core.v1beta1.RuntimeService/DeployJobSpecification", opts...) if err != nil { return nil, err } x := &runtimeServiceDeployJobSpecificationClient{stream} - if err := x.ClientStream.SendMsg(in); err != nil { - return nil, err - } - if err := x.ClientStream.CloseSend(); err != nil { - return nil, err - } return x, nil } type RuntimeService_DeployJobSpecificationClient interface { + Send(*DeployJobSpecificationRequest) error Recv() (*DeployJobSpecificationResponse, error) grpc.ClientStream } @@ -132,6 +127,10 @@ type runtimeServiceDeployJobSpecificationClient struct { grpc.ClientStream } +func (x *runtimeServiceDeployJobSpecificationClient) Send(m *DeployJobSpecificationRequest) error { + return x.ClientStream.SendMsg(m) +} + func (x *runtimeServiceDeployJobSpecificationClient) Recv() (*DeployJobSpecificationResponse, error) { m := new(DeployJobSpecificationResponse) if err := x.ClientStream.RecvMsg(m); err != nil { @@ -334,22 +333,17 @@ func (c *runtimeServiceClient) GetWindow(ctx context.Context, in *GetWindowReque return out, nil } -func (c *runtimeServiceClient) DeployResourceSpecification(ctx context.Context, in *DeployResourceSpecificationRequest, opts ...grpc.CallOption) (RuntimeService_DeployResourceSpecificationClient, error) { +func (c *runtimeServiceClient) DeployResourceSpecification(ctx context.Context, opts ...grpc.CallOption) (RuntimeService_DeployResourceSpecificationClient, error) { stream, err := c.cc.NewStream(ctx, &RuntimeService_ServiceDesc.Streams[2], "/odpf.optimus.core.v1beta1.RuntimeService/DeployResourceSpecification", opts...) if err != nil { return nil, err } x := &runtimeServiceDeployResourceSpecificationClient{stream} - if err := x.ClientStream.SendMsg(in); err != nil { - return nil, err - } - if err := x.ClientStream.CloseSend(); err != nil { - return nil, err - } return x, nil } type RuntimeService_DeployResourceSpecificationClient interface { + Send(*DeployResourceSpecificationRequest) error Recv() (*DeployResourceSpecificationResponse, error) grpc.ClientStream } @@ -358,6 +352,10 @@ type runtimeServiceDeployResourceSpecificationClient struct { grpc.ClientStream } +func (x *runtimeServiceDeployResourceSpecificationClient) Send(m *DeployResourceSpecificationRequest) error { + return x.ClientStream.SendMsg(m) +} + func (x *runtimeServiceDeployResourceSpecificationClient) Recv() (*DeployResourceSpecificationResponse, error) { m := new(DeployResourceSpecificationResponse) if err := x.ClientStream.RecvMsg(m); err != nil { @@ -494,7 +492,7 @@ type RuntimeServiceServer interface { // of deployments. Message containing ack are status events other are progress // events // State of the world request - DeployJobSpecification(*DeployJobSpecificationRequest, RuntimeService_DeployJobSpecificationServer) error + DeployJobSpecification(RuntimeService_DeployJobSpecificationServer) error // CreateJobSpecification registers a new job for a namespace which belongs to a project CreateJobSpecification(context.Context, *CreateJobSpecificationRequest) (*CreateJobSpecificationResponse, error) // GetJobSpecification reads a provided job spec of a namespace @@ -537,7 +535,7 @@ type RuntimeServiceServer interface { GetWindow(context.Context, *GetWindowRequest) (*GetWindowResponse, error) // DeployResourceSpecification migrate all resource specifications of a datastore in project // State of the world request - DeployResourceSpecification(*DeployResourceSpecificationRequest, RuntimeService_DeployResourceSpecificationServer) error + DeployResourceSpecification(RuntimeService_DeployResourceSpecificationServer) error // ListResourceSpecification lists all resource specifications of a datastore in project ListResourceSpecification(context.Context, *ListResourceSpecificationRequest) (*ListResourceSpecificationResponse, error) // Database CRUD @@ -568,7 +566,7 @@ type UnimplementedRuntimeServiceServer struct { func (UnimplementedRuntimeServiceServer) Version(context.Context, *VersionRequest) (*VersionResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method Version not implemented") } -func (UnimplementedRuntimeServiceServer) DeployJobSpecification(*DeployJobSpecificationRequest, RuntimeService_DeployJobSpecificationServer) error { +func (UnimplementedRuntimeServiceServer) DeployJobSpecification(RuntimeService_DeployJobSpecificationServer) error { return status.Errorf(codes.Unimplemented, "method DeployJobSpecification not implemented") } func (UnimplementedRuntimeServiceServer) CreateJobSpecification(context.Context, *CreateJobSpecificationRequest) (*CreateJobSpecificationResponse, error) { @@ -628,7 +626,7 @@ func (UnimplementedRuntimeServiceServer) RegisterJobEvent(context.Context, *Regi func (UnimplementedRuntimeServiceServer) GetWindow(context.Context, *GetWindowRequest) (*GetWindowResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetWindow not implemented") } -func (UnimplementedRuntimeServiceServer) DeployResourceSpecification(*DeployResourceSpecificationRequest, RuntimeService_DeployResourceSpecificationServer) error { +func (UnimplementedRuntimeServiceServer) DeployResourceSpecification(RuntimeService_DeployResourceSpecificationServer) error { return status.Errorf(codes.Unimplemented, "method DeployResourceSpecification not implemented") } func (UnimplementedRuntimeServiceServer) ListResourceSpecification(context.Context, *ListResourceSpecificationRequest) (*ListResourceSpecificationResponse, error) { @@ -702,15 +700,12 @@ func _RuntimeService_Version_Handler(srv interface{}, ctx context.Context, dec f } func _RuntimeService_DeployJobSpecification_Handler(srv interface{}, stream grpc.ServerStream) error { - m := new(DeployJobSpecificationRequest) - if err := stream.RecvMsg(m); err != nil { - return err - } - return srv.(RuntimeServiceServer).DeployJobSpecification(m, &runtimeServiceDeployJobSpecificationServer{stream}) + return srv.(RuntimeServiceServer).DeployJobSpecification(&runtimeServiceDeployJobSpecificationServer{stream}) } type RuntimeService_DeployJobSpecificationServer interface { Send(*DeployJobSpecificationResponse) error + Recv() (*DeployJobSpecificationRequest, error) grpc.ServerStream } @@ -722,6 +717,14 @@ func (x *runtimeServiceDeployJobSpecificationServer) Send(m *DeployJobSpecificat return x.ServerStream.SendMsg(m) } +func (x *runtimeServiceDeployJobSpecificationServer) Recv() (*DeployJobSpecificationRequest, error) { + m := new(DeployJobSpecificationRequest) + if err := x.ServerStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + func _RuntimeService_CreateJobSpecification_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(CreateJobSpecificationRequest) if err := dec(in); err != nil { @@ -1068,15 +1071,12 @@ func _RuntimeService_GetWindow_Handler(srv interface{}, ctx context.Context, dec } func _RuntimeService_DeployResourceSpecification_Handler(srv interface{}, stream grpc.ServerStream) error { - m := new(DeployResourceSpecificationRequest) - if err := stream.RecvMsg(m); err != nil { - return err - } - return srv.(RuntimeServiceServer).DeployResourceSpecification(m, &runtimeServiceDeployResourceSpecificationServer{stream}) + return srv.(RuntimeServiceServer).DeployResourceSpecification(&runtimeServiceDeployResourceSpecificationServer{stream}) } type RuntimeService_DeployResourceSpecificationServer interface { Send(*DeployResourceSpecificationResponse) error + Recv() (*DeployResourceSpecificationRequest, error) grpc.ServerStream } @@ -1088,6 +1088,14 @@ func (x *runtimeServiceDeployResourceSpecificationServer) Send(m *DeployResource return x.ServerStream.SendMsg(m) } +func (x *runtimeServiceDeployResourceSpecificationServer) Recv() (*DeployResourceSpecificationRequest, error) { + m := new(DeployResourceSpecificationRequest) + if err := x.ServerStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + func _RuntimeService_ListResourceSpecification_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(ListResourceSpecificationRequest) if err := dec(in); err != nil { @@ -1463,6 +1471,7 @@ var RuntimeService_ServiceDesc = grpc.ServiceDesc{ StreamName: "DeployJobSpecification", Handler: _RuntimeService_DeployJobSpecification_Handler, ServerStreams: true, + ClientStreams: true, }, { StreamName: "CheckJobSpecifications", @@ -1473,6 +1482,7 @@ var RuntimeService_ServiceDesc = grpc.ServiceDesc{ StreamName: "DeployResourceSpecification", Handler: _RuntimeService_DeployResourceSpecification_Handler, ServerStreams: true, + ClientStreams: true, }, }, Metadata: "odpf/optimus/core/v1beta1/runtime.proto", diff --git a/cmd/backup.go b/cmd/backup.go index 034c6b7dd2..6437ce6f0f 100644 --- a/cmd/backup.go +++ b/cmd/backup.go @@ -14,7 +14,7 @@ const ( backupTimeout = time.Minute * 15 ) -func backupCommand(l log.Logger, datastoreRepo models.DatastoreRepo, conf config.Optimus) *cli.Command { +func backupCommand(l log.Logger, conf config.Optimus, datastoreRepo models.DatastoreRepo) *cli.Command { cmd := &cli.Command{ Use: "backup", Short: "Backup a resource and its downstream", @@ -26,8 +26,8 @@ func backupCommand(l log.Logger, datastoreRepo models.DatastoreRepo, conf config "group:core": "true", }, } - cmd.AddCommand(backupCreateCommand(l, datastoreRepo, conf)) - cmd.AddCommand(backupListCommand(l, datastoreRepo, conf)) - cmd.AddCommand(backupStatusCommand(l, datastoreRepo, conf)) + cmd.AddCommand(backupCreateCommand(l, conf, datastoreRepo)) + cmd.AddCommand(backupListCommand(l, conf, datastoreRepo)) + cmd.AddCommand(backupStatusCommand(l, conf, datastoreRepo)) return cmd } diff --git a/cmd/backup_create.go b/cmd/backup_create.go index 9c379f1b45..bed8bc9a6f 100644 --- a/cmd/backup_create.go +++ b/cmd/backup_create.go @@ -15,15 +15,13 @@ import ( "google.golang.org/grpc" ) -func backupCreateCommand(l log.Logger, datastoreRepo models.DatastoreRepo, conf config.Optimus) *cli.Command { +func backupCreateCommand(l log.Logger, conf config.Optimus, datastoreRepo models.DatastoreRepo) *cli.Command { var ( backupCmd = &cli.Command{ Use: "create", Short: "Create a backup", Example: "optimus backup create --resource ", } - project = conf.Project.Name - namespace = conf.Namespace.Name dryRun = false ignoreDownstream = false allDownstream = false @@ -32,8 +30,6 @@ func backupCreateCommand(l log.Logger, datastoreRepo models.DatastoreRepo, conf description string storerName string ) - backupCmd.Flags().StringVarP(&project, "project", "p", project, "Project name of optimus managed repository") - backupCmd.Flags().StringVarP(&namespace, "namespace", "n", namespace, "Namespace of the resource within project") backupCmd.Flags().StringVarP(&resourceName, "resource", "r", resourceName, "Resource name created inside the datastore") backupCmd.Flags().StringVarP(&description, "description", "i", description, "Describe intention to help identify the backup") @@ -45,6 +41,7 @@ func backupCreateCommand(l log.Logger, datastoreRepo models.DatastoreRepo, conf backupCmd.Flags().BoolVar(&ignoreDownstream, "ignore-downstream", ignoreDownstream, "Do not take backups for dependent downstream resources") backupCmd.RunE = func(cmd *cli.Command, args []string) error { + namespace := askToSelectNamespace(l, conf) var err error if storerName, err = extractDatastoreName(datastoreRepo, storerName); err != nil { return err @@ -61,13 +58,13 @@ func backupCreateCommand(l log.Logger, datastoreRepo models.DatastoreRepo, conf if allDownstream { allowedDownstreamNamespaces = []string{"*"} } else { - allowedDownstreamNamespaces = []string{namespace} + allowedDownstreamNamespaces = []string{namespace.Name} } } backupDryRunRequest := &pb.BackupDryRunRequest{ - ProjectName: project, - NamespaceName: namespace, + ProjectName: conf.Project.Name, + NamespaceName: namespace.Name, ResourceName: resourceName, DatastoreName: storerName, Description: description, @@ -98,14 +95,14 @@ func backupCreateCommand(l log.Logger, datastoreRepo models.DatastoreRepo, conf } backupRequest := &pb.CreateBackupRequest{ - ProjectName: project, - NamespaceName: namespace, + ProjectName: conf.Project.Name, + NamespaceName: namespace.Name, ResourceName: resourceName, DatastoreName: storerName, Description: description, AllowedDownstreamNamespaces: allowedDownstreamNamespaces, } - for _, ds := range conf.Namespace.Datastore { + for _, ds := range namespace.Datastore { if ds.Type == storerName { backupRequest.Config = ds.Backup } diff --git a/cmd/backup_list.go b/cmd/backup_list.go index e2f556207f..85bdc891bf 100644 --- a/cmd/backup_list.go +++ b/cmd/backup_list.go @@ -16,7 +16,7 @@ import ( cli "github.com/spf13/cobra" ) -func backupListCommand(l log.Logger, datastoreRepo models.DatastoreRepo, conf config.Optimus) *cli.Command { +func backupListCommand(l log.Logger, conf config.Optimus, datastoreRepo models.DatastoreRepo) *cli.Command { var ( backupCmd = &cli.Command{ Use: "list", diff --git a/cmd/backup_status.go b/cmd/backup_status.go index fe0c0e3cf0..eada152ae6 100644 --- a/cmd/backup_status.go +++ b/cmd/backup_status.go @@ -17,7 +17,7 @@ import ( cli "github.com/spf13/cobra" ) -func backupStatusCommand(l log.Logger, datastoreRepo models.DatastoreRepo, conf config.Optimus) *cli.Command { +func backupStatusCommand(l log.Logger, conf config.Optimus, datastoreRepo models.DatastoreRepo) *cli.Command { var ( project string backupCmd = &cli.Command{ diff --git a/cmd/commands.go b/cmd/commands.go index 395c6a2aa7..5792e90e78 100644 --- a/cmd/commands.go +++ b/cmd/commands.go @@ -14,7 +14,6 @@ import ( "github.com/mattn/go-isatty" "github.com/odpf/optimus/config" "github.com/odpf/optimus/models" - "github.com/odpf/optimus/store/local" "github.com/odpf/salt/cmdx" "github.com/odpf/salt/log" "github.com/odpf/salt/term" @@ -112,27 +111,23 @@ func New(plainLog log.Logger, jsonLog log.Logger, conf config.Optimus, pluginRep cmd.PersistentFlags().BoolVar(&disableColoredOut, "no-color", disableColoredOut, "Disable colored output") //init local specs - var jobSpecRepo JobSpecRepository - jobSpecFs := afero.NewBasePathFs(afero.NewOsFs(), conf.Namespace.Job.Path) - if conf.Namespace.Job.Path != "" { - jobSpecRepo = local.NewJobSpecRepository( - jobSpecFs, - local.NewJobSpecAdapter(pluginRepo), - ) - } - datastoreSpecsFs := map[string]afero.Fs{} - for _, dsConfig := range conf.Namespace.Datastore { - datastoreSpecsFs[dsConfig.Type] = afero.NewBasePathFs(afero.NewOsFs(), dsConfig.Path) + datastoreSpecFs := make(map[string]map[string]afero.Fs) + for _, namespace := range conf.Namespaces { + dtSpec := make(map[string]afero.Fs) + for _, dsConfig := range namespace.Datastore { + dtSpec[dsConfig.Type] = afero.NewBasePathFs(afero.NewOsFs(), dsConfig.Path) + } + datastoreSpecFs[namespace.Name] = dtSpec } cmd.AddCommand(versionCommand(plainLog, conf.Host, pluginRepo)) - cmd.AddCommand(configCommand(plainLog, dsRepo)) - cmd.AddCommand(jobCommand(plainLog, jobSpecFs, jobSpecRepo, pluginRepo, conf)) - cmd.AddCommand(deployCommand(plainLog, conf, jobSpecRepo, pluginRepo, dsRepo, datastoreSpecsFs)) - cmd.AddCommand(resourceCommand(plainLog, datastoreSpecsFs, dsRepo)) + cmd.AddCommand(configCommand(plainLog)) + cmd.AddCommand(jobCommand(plainLog, conf, pluginRepo)) + cmd.AddCommand(deployCommand(plainLog, conf, pluginRepo, dsRepo, datastoreSpecFs)) + cmd.AddCommand(resourceCommand(plainLog, conf, dsRepo, datastoreSpecFs)) cmd.AddCommand(serveCommand(jsonLog, conf)) cmd.AddCommand(replayCommand(plainLog, conf)) - cmd.AddCommand(backupCommand(plainLog, dsRepo, conf)) + cmd.AddCommand(backupCommand(plainLog, conf, dsRepo)) cmd.AddCommand(adminCommand(plainLog, conf)) cmd.AddCommand(secretCommand(plainLog, conf)) diff --git a/cmd/config.go b/cmd/config.go index 50c2a58909..efa466958e 100644 --- a/cmd/config.go +++ b/cmd/config.go @@ -6,7 +6,6 @@ import ( "github.com/AlecAivazis/survey/v2" "github.com/odpf/optimus/config" - "github.com/odpf/optimus/models" "github.com/odpf/salt/log" cli "github.com/spf13/cobra" "gopkg.in/yaml.v2" @@ -16,16 +15,16 @@ const ( defaultHost = "localhost" ) -func configCommand(l log.Logger, dsRepo models.DatastoreRepo) *cli.Command { +func configCommand(l log.Logger) *cli.Command { c := &cli.Command{ Use: "config", Short: "Manage optimus configuration required to deploy specifications", } - c.AddCommand(configInitCommand(l, dsRepo)) + c.AddCommand(configInitCommand(l)) return c } -func configInitCommand(l log.Logger, dsRepo models.DatastoreRepo) *cli.Command { +func configInitCommand(l log.Logger) *cli.Command { c := &cli.Command{ Use: "init", Short: "Initialize optimus configuration file", @@ -89,48 +88,6 @@ func configInitCommand(l log.Logger, dsRepo models.DatastoreRepo) *cli.Command { if err := survey.Ask(questions, &answers); err != nil { return err } - - conf.Namespace.Name = answers["NamespaceName"].(string) - // for namespace config - if option, ok := answers["RegisterNamespaceConfig"]; ok && option.(survey.OptionAnswer).Value == AnswerYes { - conf, err = namespaceConfigQuestions(conf) - if err != nil { - return err - } - } - - // for datastore - questions = []*survey.Question{ - { - Name: "JobPath", - Prompt: &survey.Input{ - Message: "Scheduled jobs directory", - Default: "./jobs", - Help: "Relative directory path to jobs specification", - }, - Validate: survey.Required, - }, - { - Name: "RegisterDatastore", - Prompt: &survey.Select{ - Message: "Register datastore configs?", - Options: []string{AnswerYes, AnswerNo}, - Default: AnswerNo, - }, - }, - } - answers = map[string]interface{}{} - if err := survey.Ask(questions, &answers); err != nil { - return err - } - conf.Namespace.Job.Path = answers["JobPath"].(string) - if option, ok := answers["RegisterDatastore"]; ok && option.(survey.OptionAnswer).Value == AnswerYes { - conf, err = datastoreConfigQuestions(conf, dsRepo) - if err != nil { - return err - } - } - confMarshaled, err := yaml.Marshal(conf) if err != nil { return err @@ -181,74 +138,3 @@ func projectConfigQuestions(conf config.Optimus) (config.Optimus, error) { return conf, nil } - -func namespaceConfigQuestions(conf config.Optimus) (config.Optimus, error) { - conf.Namespace.Config = map[string]string{} - registerMore := AnswerYes - for registerMore == AnswerYes { - configAnswers := map[string]interface{}{} - if err := survey.Ask([]*survey.Question{ - { - Name: "Name", - Prompt: &survey.Input{ - Message: "Name of the config", - }, - Validate: survey.MinLength(3), - }, - { - Name: "Value", - Prompt: &survey.Input{ - Message: "Value", - }, - Validate: survey.MinLength(1), - }, - }, &configAnswers); err != nil { - return conf, err - } - - if err := survey.AskOne(&survey.Select{ - Message: "Add one more?", - Options: []string{AnswerYes, AnswerNo}, - Default: AnswerYes, - }, ®isterMore); err != nil { - return conf, err - } - conf.Namespace.Config[configAnswers["Name"].(string)] = configAnswers["Value"].(string) - } - - return conf, nil -} - -func datastoreConfigQuestions(conf config.Optimus, dsRepo models.DatastoreRepo) (config.Optimus, error) { - dsOptions := []string{} - for _, ds := range dsRepo.GetAll() { - dsOptions = append(dsOptions, ds.Name()) - } - conf.Namespace.Datastore = []config.Datastore{} - - configAnswers := map[string]interface{}{} - if err := survey.Ask([]*survey.Question{ - { - Name: "Type", - Prompt: &survey.Select{ - Message: "Type of the datastore", - Options: dsOptions, - }, - }, - { - Name: "Path", - Prompt: &survey.Input{ - Message: "Path for specifications", - }, - Validate: survey.MinLength(1), - }, - }, &configAnswers); err != nil { - return conf, err - } - conf.Namespace.Datastore = append(conf.Namespace.Datastore, config.Datastore{ - Type: configAnswers["Type"].(survey.OptionAnswer).Value, - Path: configAnswers["Path"].(string), - }) - - return conf, nil -} diff --git a/cmd/deploy.go b/cmd/deploy.go index e429538bb9..edc2d1accf 100644 --- a/cmd/deploy.go +++ b/cmd/deploy.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "io" + "strings" "time" "google.golang.org/grpc/codes" @@ -19,7 +20,6 @@ import ( "github.com/odpf/salt/log" "github.com/spf13/afero" cli "github.com/spf13/cobra" - "google.golang.org/grpc" ) const ( @@ -27,11 +27,10 @@ const ( ) // deployCommand pushes current repo to optimus service -func deployCommand(l log.Logger, conf config.Optimus, jobSpecRepo JobSpecRepository, - pluginRepo models.PluginRepository, datastoreRepo models.DatastoreRepo, datastoreSpecFs map[string]afero.Fs) *cli.Command { +func deployCommand(l log.Logger, conf config.Optimus, pluginRepo models.PluginRepository, dsRepo models.DatastoreRepo, + datastoreSpecFs map[string]map[string]afero.Fs) *cli.Command { var ( - projectName string - namespace string + namespaces []string ignoreJobs bool ignoreResources bool verbose bool @@ -46,26 +45,20 @@ func deployCommand(l log.Logger, conf config.Optimus, jobSpecRepo JobSpecReposit }, } ) - cmd.Flags().StringVarP(&projectName, "project", "p", conf.Project.Name, "Optimus project name") - cmd.Flags().StringVarP(&namespace, "namespace", "n", conf.Namespace.Name, "Namespace of optimus project") + cmd.Flags().StringSliceVarP(&namespaces, "namespaces", "N", nil, "Selected namespaces of optimus project") cmd.Flags().BoolVarP(&verbose, "verbose", "v", false, "Print details related to deployment stages") cmd.Flags().BoolVar(&ignoreJobs, "ignore-jobs", false, "Ignore deployment of jobs") cmd.Flags().BoolVar(&ignoreResources, "ignore-resources", false, "Ignore deployment of resources") cmd.RunE = func(c *cli.Command, args []string) error { - if projectName == "" || namespace == "" { - return fmt.Errorf("project and namespace configurations are required") - } - - l.Info(fmt.Sprintf("Deploying project: %s for namespace: %s at %s", projectName, namespace, conf.Host)) + l.Info(fmt.Sprintf("Deploying project: %s to %s", conf.Project.Name, conf.Host)) start := time.Now() - if jobSpecRepo == nil { - // job repo not configured - ignoreJobs = true - } - if err := postDeploymentRequest(l, projectName, namespace, jobSpecRepo, conf, pluginRepo, datastoreRepo, - datastoreSpecFs, ignoreJobs, ignoreResources, verbose); err != nil { + if err := validateNamespaces(datastoreSpecFs, namespaces); err != nil { + return err + } + err := postDeploymentRequest(l, conf, pluginRepo, dsRepo, datastoreSpecFs, namespaces, ignoreJobs, ignoreResources, verbose) + if err != nil { return err } l.Info(coloredSuccess("\nDeployment completed, took %s", time.Since(start).Round(time.Second))) @@ -76,14 +69,14 @@ func deployCommand(l log.Logger, conf config.Optimus, jobSpecRepo JobSpecReposit } // postDeploymentRequest send a deployment request to service -func postDeploymentRequest(l log.Logger, projectName string, namespaceName string, jobSpecRepo JobSpecRepository, - conf config.Optimus, pluginRepo models.PluginRepository, datastoreRepo models.DatastoreRepo, datastoreSpecFs map[string]afero.Fs, - ignoreJobDeployment, ignoreResources bool, verbose bool) (err error) { +func postDeploymentRequest(l log.Logger, conf config.Optimus, pluginRepo models.PluginRepository, + datastoreRepo models.DatastoreRepo, datastoreSpecFs map[string]map[string]afero.Fs, namespaceNames []string, + ignoreJobDeployment, ignoreResources bool, verbose bool) error { dialTimeoutCtx, dialCancel := context.WithTimeout(context.Background(), OptimusDialTimeout) defer dialCancel() - var conn *grpc.ClientConn - if conn, err = createConnection(dialTimeoutCtx, conf.Host); err != nil { + conn, err := createConnection(dialTimeoutCtx, conf.Host) + if err != nil { if errors.Is(err, context.DeadlineExceeded) { l.Error(ErrServerNotReachable(conf.Host).Error()) } @@ -95,40 +88,184 @@ func postDeploymentRequest(l log.Logger, projectName string, namespaceName strin defer deployCancel() runtime := pb.NewRuntimeServiceClient(conn) - adapt := v1handler.NewAdapter(pluginRepo, datastoreRepo) - - projectSpec := &pb.ProjectSpecification{ - Name: projectName, - Config: conf.Project.Config, + if err := registerProject(deployTimeoutCtx, runtime, l, conf); err != nil { + return err } - if err = registerProject(deployTimeoutCtx, l, runtime, projectSpec); err != nil { + if err := registerAllNamespaces(deployTimeoutCtx, runtime, l, conf, namespaceNames); err != nil { return err } - namespaceSpec := &pb.NamespaceSpecification{ - Name: namespaceName, - Config: conf.Namespace.Config, + if !ignoreResources { + if err := deployAllResources(deployTimeoutCtx, + runtime, l, conf, + pluginRepo, datastoreRepo, + datastoreSpecFs, + namespaceNames, + verbose, + ); err != nil { + return err + } + } else { + l.Info("> Skipping resource deployment") + } + if !ignoreJobDeployment { + if err := deployAllJobs(deployTimeoutCtx, + runtime, l, + conf, pluginRepo, + datastoreRepo, namespaceNames, + verbose, + ); err != nil { + return err + } + } else { + l.Info("> Skipping job deployment") + } + return nil +} + +func deployAllJobs(deployTimeoutCtx context.Context, + runtime pb.RuntimeServiceClient, + l log.Logger, conf config.Optimus, pluginRepo models.PluginRepository, + datastoreRepo models.DatastoreRepo, + namespaceNames []string, + verbose bool, +) error { + var selectedNamespaceNames []string + if len(namespaceNames) > 0 { + selectedNamespaceNames = namespaceNames + } else { + for _, namespace := range conf.Namespaces { + selectedNamespaceNames = append(selectedNamespaceNames, namespace.Name) + } + } + + stream, err := runtime.DeployJobSpecification(deployTimeoutCtx) + if err != nil { + if errors.Is(err, context.DeadlineExceeded) { + l.Error(coloredError("Deployment process took too long, timing out")) + } + return errors.New("deployement failed") + } + var specFound bool + for _, namespaceName := range selectedNamespaceNames { + namespace, err := conf.GetNamespaceByName(namespaceName) + if err != nil { + return err + } + jobSpecFs := afero.NewBasePathFs(afero.NewOsFs(), namespace.Job.Path) + jobSpecRepo := local.NewJobSpecRepository( + jobSpecFs, + local.NewJobSpecAdapter(pluginRepo), + ) + l.Info(fmt.Sprintf("\n> [%s] Deploying jobs", namespaceName)) + jobSpecs, err := jobSpecRepo.GetAll() + if err != nil { + return err + } + if len(jobSpecs) == 0 { + l.Warn("[%s] skipping as job spec is empty\n", namespaceName) + continue + } + + var adaptedJobSpecs []*pb.JobSpecification + adapt := v1handler.NewAdapter(pluginRepo, datastoreRepo) + for _, spec := range jobSpecs { + adaptJob, err := adapt.ToJobProto(spec) + if err != nil { + return fmt.Errorf("[%s] failed to serialize: %s - %w", namespaceName, spec.Name, err) + } + adaptedJobSpecs = append(adaptedJobSpecs, adaptJob) + } + specFound = true + if err := stream.Send(&pb.DeployJobSpecificationRequest{ + Jobs: adaptedJobSpecs, + ProjectName: conf.Project.Name, + NamespaceName: namespaceName, + }); err != nil { + return fmt.Errorf("[%s] deployment failed: %w", namespaceName, err) + } } - if err = registerNamespace(deployTimeoutCtx, l, runtime, projectSpec.Name, namespaceSpec); err != nil { + if err := stream.CloseSend(); err != nil { return err } + if !specFound { + return nil + } - if !ignoreResources { - // deploy datastore resources - for storeName, repoFS := range datastoreSpecFs { - l.Info(fmt.Sprintf("\n> Deploying resources for %s", storeName)) + var counter int + var streamErrs []error + spinner := NewProgressBar() + if !verbose { + spinner.StartProgress(len(selectedNamespaceNames), "please wait") + } + for { + resp, err := stream.Recv() + if err != nil { + if err == io.EOF { + break + } + return err + } + if resp.GetAck() { + if !resp.GetSuccess() { + streamErrs = append(streamErrs, errors.New(resp.GetMessage())) + } + counter++ + spinner.SetProgress(counter) + } + } + spinner.Stop() + if len(streamErrs) > 0 { + for _, e := range streamErrs { + l.Error(e.Error()) + } + return errors.New("one or more errors are encountered during deploy") + } + return nil +} + +func deployAllResources(deployTimeoutCtx context.Context, + runtime pb.RuntimeServiceClient, + l log.Logger, conf config.Optimus, pluginRepo models.PluginRepository, + datastoreRepo models.DatastoreRepo, + datastoreSpecFs map[string]map[string]afero.Fs, + namespaceNames []string, + verbose bool, +) error { + var selectedNamespaceNames []string + if len(namespaceNames) > 0 { + selectedNamespaceNames = namespaceNames + } else { + for _, namespace := range conf.Namespaces { + selectedNamespaceNames = append(selectedNamespaceNames, namespace.Name) + } + } + + // send call + stream, err := runtime.DeployResourceSpecification(deployTimeoutCtx) + if err != nil { + if errors.Is(err, context.DeadlineExceeded) { + l.Error(coloredError("Deployment process took too long, timing out")) + } + return fmt.Errorf("deployement failed: %w", err) + } + var specFound bool + for _, namespaceName := range selectedNamespaceNames { + adapt := v1handler.NewAdapter(pluginRepo, datastoreRepo) + for storeName, repoFS := range datastoreSpecFs[namespaceName] { + l.Info(fmt.Sprintf("\n> [%s] Deploying resources for %s", namespaceName, storeName)) ds, err := datastoreRepo.GetByName(storeName) if err != nil { - return fmt.Errorf("unsupported datastore: %s", storeName) + return fmt.Errorf("[%s] unsupported datastore: %s", namespaceName, storeName) } resourceSpecRepo := local.NewResourceSpecRepository(repoFS, ds) resourceSpecs, err := resourceSpecRepo.GetAll(context.Background()) if err == models.ErrNoResources { - l.Info(coloredNotice("no resource specifications found")) + l.Info(coloredNotice("[%s] no resource specifications found", namespaceName)) continue } if err != nil { - return fmt.Errorf("resourceSpecRepo.GetAll(): %w", err) + return fmt.Errorf("[%s] resourceSpecRepo.GetAll(): %w", namespaceName, err) } // prepare specs @@ -136,151 +273,127 @@ func postDeploymentRequest(l log.Logger, projectName string, namespaceName strin for _, spec := range resourceSpecs { adapted, err := adapt.ToResourceProto(spec) if err != nil { - return fmt.Errorf("failed to serialize: %s: %w", spec.Name, err) + return fmt.Errorf("[%s] failed to serialize: %s - %w", namespaceName, spec.Name, err) } adaptedSpecs = append(adaptedSpecs, adapted) } - - // send call - respStream, err := runtime.DeployResourceSpecification(deployTimeoutCtx, &pb.DeployResourceSpecificationRequest{ + specFound = true + if err := stream.Send(&pb.DeployResourceSpecificationRequest{ Resources: adaptedSpecs, - ProjectName: projectSpec.Name, + ProjectName: conf.Project.Name, DatastoreName: storeName, - NamespaceName: namespaceSpec.Name, - }) - if err != nil { - if errors.Is(err, context.DeadlineExceeded) { - l.Error(coloredError("Deployment process took too long, timing out")) - } - return fmt.Errorf("deployement failed: %w", err) - } - - // track progress - deployCounter := 0 - totalSpecs := len(adaptedSpecs) - spinner := NewProgressBar() - if !verbose { - spinner.StartProgress(totalSpecs, "please wait") - } - for { - resp, err := respStream.Recv() - if err != nil { - if err == io.EOF { - break - } - return fmt.Errorf("failed to receive deployment ack: %w", err) - } - if resp.Ack { - // ack for the resource spec - if !resp.GetSuccess() { - return fmt.Errorf("unable to deploy resource: %s %s", resp.GetResourceName(), resp.GetMessage()) - } - deployCounter++ - spinner.SetProgress(deployCounter) - if verbose { - l.Info(fmt.Sprintf("%d/%d. %s successfully deployed", deployCounter, totalSpecs, resp.GetResourceName())) - } - } else { - if verbose { - // ordinary progress event - l.Info(fmt.Sprintf("info '%s': %s", resp.GetResourceName(), resp.GetMessage())) - } - } + NamespaceName: namespaceName, + }); err != nil { + return fmt.Errorf("[%s] deployment failed: %w", namespaceName, err) } - spinner.Stop() - l.Info(coloredSuccess("Successfully deployed %d/%d resources.", deployCounter, totalSpecs)) } - } else { - l.Info("> Skipping resource deployment") + } + if err := stream.CloseSend(); err != nil { + return err + } + if !specFound { + return nil } - if !ignoreJobDeployment { - // deploy job specifications - l.Info("\n> Deploying jobs") - jobSpecs, err := jobSpecRepo.GetAll() + var counter int + var streamErrs []error + spinner := NewProgressBar() + if !verbose { + spinner.StartProgress(len(selectedNamespaceNames), "please wait") + } + for { + resp, err := stream.Recv() if err != nil { + if err == io.EOF { + break + } return err } - - var adaptedJobSpecs []*pb.JobSpecification - for _, spec := range jobSpecs { - adaptJob, err := adapt.ToJobProto(spec) - if err != nil { - return fmt.Errorf("failed to serialize: %s: %w", spec.Name, err) + if resp.GetAck() { + if !resp.GetSuccess() { + streamErrs = append(streamErrs, errors.New(resp.GetMessage())) } - adaptedJobSpecs = append(adaptedJobSpecs, adaptJob) + counter++ + spinner.SetProgress(counter) } - respStream, err := runtime.DeployJobSpecification(deployTimeoutCtx, &pb.DeployJobSpecificationRequest{ - Jobs: adaptedJobSpecs, - ProjectName: projectSpec.Name, - NamespaceName: namespaceSpec.Name, - }) - if err != nil { - if errors.Is(err, context.DeadlineExceeded) { - l.Error(coloredError("Deployment process took too long, timing out")) - } - return fmt.Errorf("deployement failed: %w", err) + } + spinner.Stop() + if len(streamErrs) > 0 { + for _, e := range streamErrs { + l.Error(e.Error()) } + return errors.New("one or more errors are encountered during deploy") + } + return nil +} - ackCounter := 0 - totalJobs := len(jobSpecs) - spinner := NewProgressBar() - if !verbose { - spinner.StartProgress(totalJobs, "please wait") - spinner.SetProgress(0) - } - var streamError error - for { - resp, err := respStream.Recv() - if err != nil { - if err == io.EOF { - break - } - streamError = err - break - } - if resp.Ack { - // ack for the job spec - if !resp.GetSuccess() { - return fmt.Errorf("unable to deploy: %s %s", resp.GetJobName(), resp.GetMessage()) - } - ackCounter++ - spinner.SetProgress(ackCounter) - if verbose { - l.Info(fmt.Sprintf("%d/%d. %s successfully deployed", ackCounter, totalJobs, resp.GetJobName())) - } - } else { - if verbose { - // ordinary progress event - if resp.GetJobName() != "" { - l.Info(fmt.Sprintf("info '%s': %s", resp.GetJobName(), resp.GetMessage())) - } else { - l.Info(fmt.Sprintf("info: %s", resp.GetMessage())) - } - } - } +func registerAllNamespaces( + deployTimeoutCtx context.Context, runtime pb.RuntimeServiceClient, + l log.Logger, conf config.Optimus, namespaceNames []string, +) error { + var selectedNamespaceNames []string + if len(namespaceNames) > 0 { + selectedNamespaceNames = namespaceNames + } else { + for _, namespace := range conf.Namespaces { + selectedNamespaceNames = append(selectedNamespaceNames, namespace.Name) } - spinner.Stop() + } - if streamError != nil { - if ackCounter == totalJobs { - // if we have uploaded all jobs successfully, further steps in pipeline - // should not cause errors to fail and should end with warnings if any. - l.Warn(coloredNotice("jobs deployed with warning"), "err", streamError) - } else { - return fmt.Errorf("failed to receive success deployment ack: %w", streamError) - } + ch := make(chan error, len(selectedNamespaceNames)) + defer close(ch) + for _, namespaceName := range selectedNamespaceNames { + go func(name string) { + ch <- registerNamespace(deployTimeoutCtx, runtime, l, conf, name) + }(namespaceName) + } + var errMsg string + for i := 0; i < len(selectedNamespaceNames); i++ { + if err := <-ch; err != nil { + errMsg += err.Error() + "\n" } - l.Info(coloredSuccess("Successfully deployed %d/%d jobs.", ackCounter, totalJobs)) - } else { - l.Info("> Skipping job deployment") } + if len(errMsg) > 0 { + return errors.New(errMsg) + } + return nil +} +func registerNamespace(deployTimeoutCtx context.Context, runtime pb.RuntimeServiceClient, + l log.Logger, conf config.Optimus, namespaceName string, +) error { + namespace, err := conf.GetNamespaceByName(namespaceName) + if err != nil { + return err + } + registerResponse, err := runtime.RegisterProjectNamespace(deployTimeoutCtx, &pb.RegisterProjectNamespaceRequest{ + ProjectName: conf.Project.Name, + Namespace: &pb.NamespaceSpecification{ + Name: namespaceName, + Config: namespace.Config, + }, + }) + if err != nil { + if status.Code(err) == codes.FailedPrecondition { + l.Warn(coloredNotice("[%s] Ignoring namespace config changes: %s", namespaceName, err.Error())) + return nil + } + return fmt.Errorf("failed to update namespace configurations: %w", err) + } else if !registerResponse.Success { + return fmt.Errorf("failed to update namespace configurations, %s", registerResponse.Message) + } + l.Info("\n> Updated namespace configuration") return nil } -func registerProject(deployTimeoutCtx context.Context, l log.Logger, runtime pb.RuntimeServiceClient, - projectSpec *pb.ProjectSpecification) (err error) { +func registerProject( + deployTimeoutCtx context.Context, runtime pb.RuntimeServiceClient, + l log.Logger, conf config.Optimus, +) (err error) { + projectSpec := &pb.ProjectSpecification{ + Name: conf.Project.Name, + Config: conf.Project.Config, + } registerResponse, err := runtime.RegisterProject(deployTimeoutCtx, &pb.RegisterProjectRequest{ Project: projectSpec, }) @@ -297,21 +410,15 @@ func registerProject(deployTimeoutCtx context.Context, l log.Logger, runtime pb. return nil } -func registerNamespace(deployTimeoutCtx context.Context, l log.Logger, runtime pb.RuntimeServiceClient, - projectName string, namespaceSpec *pb.NamespaceSpecification) (err error) { - registerResponse, err := runtime.RegisterProjectNamespace(deployTimeoutCtx, &pb.RegisterProjectNamespaceRequest{ - ProjectName: projectName, - Namespace: namespaceSpec, - }) - if err != nil { - if status.Code(err) == codes.FailedPrecondition { - l.Warn(coloredNotice("> Ignoring namespace config changes: %s", err.Error())) - return nil +func validateNamespaces(datastoreSpecFs map[string]map[string]afero.Fs, selectedNamespaceNames []string) error { + var unknownNamespaceNames []string + for _, namespaceName := range selectedNamespaceNames { + if datastoreSpecFs[namespaceName] == nil { + unknownNamespaceNames = append(unknownNamespaceNames, namespaceName) } - return fmt.Errorf("failed to update namespace configurations: %w", err) - } else if !registerResponse.Success { - return fmt.Errorf("failed to update namespace configurations, %s", registerResponse.Message) } - l.Info("\n> Updated namespace configuration") + if len(unknownNamespaceNames) > 0 { + return fmt.Errorf("namespaces [%s] are not found", strings.Join(unknownNamespaceNames, ", ")) + } return nil } diff --git a/cmd/job.go b/cmd/job.go index 248e712f82..5aedf46f89 100644 --- a/cmd/job.go +++ b/cmd/job.go @@ -5,12 +5,10 @@ import ( "github.com/odpf/optimus/models" "github.com/odpf/salt/log" - "github.com/spf13/afero" cli "github.com/spf13/cobra" ) -func jobCommand(l log.Logger, jobSpecFs afero.Fs, jobSpecRepo JobSpecRepository, - pluginRepo models.PluginRepository, conf config.Optimus) *cli.Command { +func jobCommand(l log.Logger, conf config.Optimus, pluginRepo models.PluginRepository) *cli.Command { cmd := &cli.Command{ Use: "job", Short: "Interact with schedulable Job", @@ -18,13 +16,12 @@ func jobCommand(l log.Logger, jobSpecFs afero.Fs, jobSpecRepo JobSpecRepository, "group:core": "true", }, } - if jobSpecRepo != nil { - cmd.AddCommand(jobCreateCommand(l, jobSpecFs, jobSpecRepo, pluginRepo)) - cmd.AddCommand(jobAddHookCommand(l, jobSpecRepo, pluginRepo)) - cmd.AddCommand(jobRenderTemplateCommand(l, jobSpecRepo)) - cmd.AddCommand(jobValidateCommand(l, pluginRepo, jobSpecRepo, conf)) - cmd.AddCommand(jobRunCommand(l, jobSpecRepo, pluginRepo, conf)) - } - cmd.AddCommand(jobStatusCommand(l, conf)) + + cmd.AddCommand(jobCreateCommand(l, conf, pluginRepo)) + cmd.AddCommand(jobAddHookCommand(l, conf, pluginRepo)) + cmd.AddCommand(jobRenderTemplateCommand(l, conf, pluginRepo)) + cmd.AddCommand(jobValidateCommand(l, conf, pluginRepo, conf.Project.Name, conf.Host)) + cmd.AddCommand(jobRunCommand(l, conf, pluginRepo, conf.Project.Name, conf.Host)) + cmd.AddCommand(jobStatusCommand(l, conf.Project.Name, conf.Host)) return cmd } diff --git a/cmd/job_create.go b/cmd/job_create.go index d4c0b2e1ed..991340911d 100644 --- a/cmd/job_create.go +++ b/cmd/job_create.go @@ -12,6 +12,7 @@ import ( "github.com/AlecAivazis/survey/v2" petname "github.com/dustinkirkland/golang-petname" + "github.com/odpf/optimus/config" "github.com/odpf/optimus/models" "github.com/odpf/optimus/store/local" "github.com/odpf/optimus/utils" @@ -31,13 +32,18 @@ var ( specFileNames = []string{local.ResourceSpecFileName, local.JobSpecFileName} ) -func jobCreateCommand(l log.Logger, jobSpecFs afero.Fs, jobSpecRepo JobSpecRepository, - pluginRepo models.PluginRepository) *cli.Command { - return &cli.Command{ +func jobCreateCommand(l log.Logger, conf config.Optimus, pluginRepo models.PluginRepository) *cli.Command { + cmd := &cli.Command{ Use: "create", Short: "Create a new Job", Example: "optimus job create", RunE: func(cmd *cli.Command, args []string) error { + namespace := askToSelectNamespace(l, conf) + jobSpecFs := afero.NewBasePathFs(afero.NewOsFs(), namespace.Job.Path) + jobSpecRepo := local.NewJobSpecRepository( + jobSpecFs, + local.NewJobSpecAdapter(pluginRepo), + ) jwd, err := getWorkingDirectory(jobSpecFs, "") if err != nil { return err @@ -65,6 +71,7 @@ func jobCreateCommand(l log.Logger, jobSpecFs afero.Fs, jobSpecRepo JobSpecRepos return nil }, } + return cmd } // getWorkingDirectory returns the directory where the new spec folder should be created diff --git a/cmd/job_hook.go b/cmd/job_hook.go index 5cc8f5d85a..f272f78a80 100644 --- a/cmd/job_hook.go +++ b/cmd/job_hook.go @@ -6,13 +6,16 @@ import ( "fmt" "github.com/AlecAivazis/survey/v2" + "github.com/odpf/optimus/config" "github.com/odpf/optimus/models" + "github.com/odpf/optimus/store/local" "github.com/odpf/optimus/utils" "github.com/odpf/salt/log" + "github.com/spf13/afero" cli "github.com/spf13/cobra" ) -func jobAddHookCommand(l log.Logger, jobSpecRepo JobSpecRepository, pluginRepo models.PluginRepository) *cli.Command { +func jobAddHookCommand(l log.Logger, conf config.Optimus, pluginRepo models.PluginRepository) *cli.Command { cmd := &cli.Command{ Use: "addhook", Aliases: []string{"add_hook", "add-hook", "addHook", "attach_hook", "attach-hook", "attachHook"}, @@ -20,6 +23,12 @@ func jobAddHookCommand(l log.Logger, jobSpecRepo JobSpecRepository, pluginRepo m Long: "Add a runnable instance that will be triggered before or after the base transformation.", Example: "optimus addhook", RunE: func(cmd *cli.Command, args []string) error { + namespace := askToSelectNamespace(l, conf) + jobSpecFs := afero.NewBasePathFs(afero.NewOsFs(), namespace.Job.Path) + jobSpecRepo := local.NewJobSpecRepository( + jobSpecFs, + local.NewJobSpecAdapter(pluginRepo), + ) selectJobName, err := selectJobSurvey(jobSpecRepo) if err != nil { return err diff --git a/cmd/job_render.go b/cmd/job_render.go index 03e0eb4877..266b57d099 100644 --- a/cmd/job_render.go +++ b/cmd/job_render.go @@ -6,62 +6,70 @@ import ( "path/filepath" "time" + "github.com/odpf/optimus/config" "github.com/odpf/optimus/models" "github.com/odpf/optimus/run" + "github.com/odpf/optimus/store/local" "github.com/odpf/optimus/utils" "github.com/odpf/salt/log" + "github.com/spf13/afero" cli "github.com/spf13/cobra" ) -func jobRenderTemplateCommand(l log.Logger, jobSpecRepo JobSpecRepository) *cli.Command { +func jobRenderTemplateCommand(l log.Logger, conf config.Optimus, pluginRepo models.PluginRepository) *cli.Command { cmd := &cli.Command{ Use: "render", Short: "Apply template values in job specification to current 'render' directory", Long: "Process optimus job specification based on macros/functions used.", Example: "optimus job render []", - } - cmd.RunE = func(c *cli.Command, args []string) error { - var err error - var jobName string - if len(args) == 0 { - // doing it locally for now, ideally using optimus service will give - // more accurate results - jobName, err = selectJobSurvey(jobSpecRepo) + RunE: func(c *cli.Command, args []string) error { + namespace := askToSelectNamespace(l, conf) + jobSpecFs := afero.NewBasePathFs(afero.NewOsFs(), namespace.Job.Path) + jobSpecRepo := local.NewJobSpecRepository( + jobSpecFs, + local.NewJobSpecAdapter(pluginRepo), + ) + var jobName string + var err error + if len(args) == 0 { + // doing it locally for now, ideally using optimus service will give + // more accurate results + jobName, err = selectJobSurvey(jobSpecRepo) + if err != nil { + return err + } + } else { + jobName = args[0] + } + jobSpec, err := jobSpecRepo.GetByName(jobName) if err != nil { return err } - } else { - jobName = args[0] - } - jobSpec, err := jobSpecRepo.GetByName(jobName) - if err != nil { - return err - } - // create temporary directory - renderedPath := filepath.Join(".", "render", jobSpec.Name) - _ = os.MkdirAll(renderedPath, 0770) - l.Info(fmt.Sprintf("Rendering assets in %s", renderedPath)) + // create temporary directory + renderedPath := filepath.Join(".", "render", jobSpec.Name) + _ = os.MkdirAll(renderedPath, 0770) + l.Info(fmt.Sprintf("Rendering assets in %s", renderedPath)) - now := time.Now() - l.Info(fmt.Sprintf("Assuming execution time as current time of %s\n", now.Format(models.InstanceScheduledAtTimeLayout))) + now := time.Now() + l.Info(fmt.Sprintf("Assuming execution time as current time of %s\n", now.Format(models.InstanceScheduledAtTimeLayout))) - templateEngine := run.NewGoEngine() - templates, err := run.DumpAssets(jobSpec, now, templateEngine, true) - if err != nil { - return err - } - - writeToFileFn := utils.WriteStringToFileIndexed() - for name, content := range templates { - if err := writeToFileFn(filepath.Join(renderedPath, name), content, l.Writer()); err != nil { + templateEngine := run.NewGoEngine() + templates, err := run.DumpAssets(jobSpec, now, templateEngine, true) + if err != nil { return err } - } - l.Info(coloredSuccess("\nRender complete.")) - return nil - } + writeToFileFn := utils.WriteStringToFileIndexed() + for name, content := range templates { + if err := writeToFileFn(filepath.Join(renderedPath, name), content, l.Writer()); err != nil { + return err + } + } + l.Info(coloredSuccess("\nRender complete.")) + return nil + }, + } return cmd } diff --git a/cmd/job_run.go b/cmd/job_run.go index f3109cec32..ce33aae4f2 100644 --- a/cmd/job_run.go +++ b/cmd/job_run.go @@ -10,7 +10,9 @@ import ( pb "github.com/odpf/optimus/api/proto/odpf/optimus/core/v1beta1" "github.com/odpf/optimus/config" "github.com/odpf/optimus/models" + "github.com/odpf/optimus/store/local" "github.com/odpf/salt/log" + "github.com/spf13/afero" cli "github.com/spf13/cobra" "google.golang.org/grpc" ) @@ -19,29 +21,26 @@ const ( runJobTimeout = time.Minute * 1 ) -func jobRunCommand(l log.Logger, jobSpecRepo JobSpecRepository, pluginRepo models.PluginRepository, - conf config.Optimus) *cli.Command { - var ( - projectName = conf.Project.Name - namespace = conf.Namespace.Name - ) +func jobRunCommand(l log.Logger, conf config.Optimus, pluginRepo models.PluginRepository, projectName, host string) *cli.Command { cmd := &cli.Command{ Use: "run", Short: "[EXPERIMENTAL] run the provided job on optimus cluster", Args: cli.MinimumNArgs(1), - Example: "optimus job run [--project g-optimus]", + Example: "optimus job run ", Hidden: true, - } - cmd.Flags().StringVarP(&projectName, "project", "p", projectName, "Project name of optimus managed repository") - cmd.Flags().StringVarP(&namespace, "namespace", "n", namespace, "Namespace of job that needs to run") - - cmd.RunE = func(c *cli.Command, args []string) error { - jobSpec, err := jobSpecRepo.GetByName(args[0]) - if err != nil { - return err - } - - return runJobSpecificationRequest(l, projectName, namespace, conf.Host, jobSpec, pluginRepo) + RunE: func(c *cli.Command, args []string) error { + namespace := askToSelectNamespace(l, conf) + jobSpecFs := afero.NewBasePathFs(afero.NewOsFs(), namespace.Job.Path) + jobSpecRepo := local.NewJobSpecRepository( + jobSpecFs, + local.NewJobSpecAdapter(pluginRepo), + ) + jobSpec, err := jobSpecRepo.GetByName(args[0]) + if err != nil { + return err + } + return runJobSpecificationRequest(l, projectName, namespace.Name, host, jobSpec, pluginRepo) + }, } return cmd } diff --git a/cmd/job_status.go b/cmd/job_status.go index f32904b37d..c5a8a860be 100644 --- a/cmd/job_status.go +++ b/cmd/job_status.go @@ -7,8 +7,6 @@ import ( "sort" "time" - "github.com/odpf/optimus/config" - pb "github.com/odpf/optimus/api/proto/odpf/optimus/core/v1beta1" "github.com/odpf/salt/log" cli "github.com/spf13/cobra" @@ -19,26 +17,24 @@ const ( jobStatusTimeout = time.Second * 30 ) -func jobStatusCommand(l log.Logger, conf config.Optimus) *cli.Command { - var ( - optimusHost = conf.Host - projectName = conf.Project.Name - ) +func jobStatusCommand(l log.Logger, defaultProjectName, defaultHost string) *cli.Command { cmd := &cli.Command{ Use: "status", Short: "Get current job status", Example: `optimus job status [--project \"project-id\"]`, Args: cli.MinimumNArgs(1), } + projectName := defaultProjectName + host := defaultHost cmd.Flags().StringVarP(&projectName, "project", "p", projectName, "Project name of optimus managed repository") - cmd.Flags().StringVar(&optimusHost, "host", optimusHost, "Optimus service endpoint url") + cmd.Flags().StringVar(&host, "host", defaultHost, "Optimus service endpoint url") cmd.RunE = func(c *cli.Command, args []string) error { jobName := args[0] l.Info(fmt.Sprintf("Requesting status for project %s, job %s from %s", - projectName, jobName, optimusHost)) + projectName, jobName, host)) - return getJobStatusRequest(l, jobName, optimusHost, projectName) + return getJobStatusRequest(l, jobName, host, projectName) } return cmd } diff --git a/cmd/job_validate.go b/cmd/job_validate.go index 361b53b7e8..8d78b31796 100644 --- a/cmd/job_validate.go +++ b/cmd/job_validate.go @@ -7,12 +7,13 @@ import ( "io" "time" - "github.com/odpf/optimus/config" - v1handler "github.com/odpf/optimus/api/handler/v1beta1" pb "github.com/odpf/optimus/api/proto/odpf/optimus/core/v1beta1" + "github.com/odpf/optimus/config" "github.com/odpf/optimus/models" + "github.com/odpf/optimus/store/local" "github.com/odpf/salt/log" + "github.com/spf13/afero" cli "github.com/spf13/cobra" "google.golang.org/grpc" ) @@ -21,40 +22,35 @@ const ( validateTimeout = time.Minute * 5 ) -func jobValidateCommand(l log.Logger, pluginRepo models.PluginRepository, jobSpecRepo JobSpecRepository, - conf config.Optimus) *cli.Command { - var ( - projectName string - namespace string - verbose bool - cmd = &cli.Command{ - Use: "validate", - Short: "Run basic checks on all jobs", - Long: "Check if specifications are valid for deployment", - Example: "optimus job validate", - } - ) - - cmd.Flags().StringVarP(&projectName, "project", "p", conf.Project.Name, "Optimus project name") - cmd.Flags().StringVarP(&namespace, "namespace", "n", conf.Namespace.Name, "Namespace of optimus project") - cmd.Flags().BoolVarP(&verbose, "verbose", "v", false, "Print details related to operation") - cmd.RunE = func(c *cli.Command, args []string) error { - if projectName == "" || namespace == "" { - return fmt.Errorf("project and namespace configurations are required") - } - l.Info(fmt.Sprintf("Validating job specifications for project: %s, namespace: %s", projectName, namespace)) - start := time.Now() - jobSpecs, err := jobSpecRepo.GetAll() - if err != nil { - return fmt.Errorf("directory '%s': %v", conf.Namespace.Job.Path, err) - } +func jobValidateCommand(l log.Logger, conf config.Optimus, pluginRepo models.PluginRepository, projectName, host string) *cli.Command { + var verbose bool + cmd := &cli.Command{ + Use: "validate", + Short: "Run basic checks on all jobs", + Long: "Check if specifications are valid for deployment", + Example: "optimus job validate", + RunE: func(c *cli.Command, args []string) error { + namespace := askToSelectNamespace(l, conf) + jobSpecFs := afero.NewBasePathFs(afero.NewOsFs(), namespace.Job.Path) + jobSpecRepo := local.NewJobSpecRepository( + jobSpecFs, + local.NewJobSpecAdapter(pluginRepo), + ) + l.Info(fmt.Sprintf("Validating job specifications for project: %s, namespace: %s", projectName, namespace.Name)) + start := time.Now() + jobSpecs, err := jobSpecRepo.GetAll() + if err != nil { + return fmt.Errorf("directory '%s': %v", namespace.Job.Path, err) + } - if err := validateJobSpecificationRequest(l, projectName, namespace, pluginRepo, jobSpecs, conf.Host, verbose); err != nil { - return err - } - l.Info(coloredSuccess("Jobs validated successfully, took %s", time.Since(start).Round(time.Second))) - return nil + if err := validateJobSpecificationRequest(l, projectName, namespace.Name, pluginRepo, jobSpecs, host, verbose); err != nil { + return err + } + l.Info(coloredSuccess("Jobs validated successfully, took %s", time.Since(start).Round(time.Second))) + return nil + }, } + cmd.Flags().BoolVarP(&verbose, "verbose", "v", false, "Print details related to operation") return cmd } diff --git a/cmd/namespace.go b/cmd/namespace.go new file mode 100644 index 0000000000..be6828461b --- /dev/null +++ b/cmd/namespace.go @@ -0,0 +1,36 @@ +package cmd + +import ( + "github.com/odpf/optimus/config" + + "github.com/AlecAivazis/survey/v2" + "github.com/odpf/salt/log" +) + +func askToSelectNamespace(l log.Logger, conf config.Optimus) *config.Namespace { + options := make([]string, len(conf.Namespaces)) + for i, namespace := range conf.Namespaces { + options[i] = namespace.Name + } + prompt := &survey.Select{ + Message: "Please choose the namespace:", + Options: options, + } + for { + var response string + if err := survey.AskOne(prompt, &response); err != nil { + l.Error(err.Error()) + continue + } + if response == "" { + l.Error("Namespace name cannot be empty") + continue + } + namespace, err := conf.GetNamespaceByName(response) + if err != nil { + l.Error(err.Error()) + continue + } + return namespace + } +} diff --git a/cmd/replay.go b/cmd/replay.go index 7980ce1c3d..d2c4ed8c83 100644 --- a/cmd/replay.go +++ b/cmd/replay.go @@ -60,7 +60,7 @@ func replayCommand(l log.Logger, conf config.Optimus) *cli.Command { "group:core": "true", }, } - cmd.AddCommand(replayRunCommand(l, conf)) + cmd.AddCommand(replayCreateCommand(l, conf)) cmd.AddCommand(replayStatusCommand(l, conf)) cmd.AddCommand(replayListCommand(l, conf)) return cmd diff --git a/cmd/replay_create.go b/cmd/replay_create.go index 1e5f2c6421..043b0dd498 100644 --- a/cmd/replay_create.go +++ b/cmd/replay_create.go @@ -19,7 +19,7 @@ import ( "google.golang.org/grpc" ) -func replayRunCommand(l log.Logger, conf config.Optimus) *cli.Command { +func replayCreateCommand(l log.Logger, conf config.Optimus) *cli.Command { var ( dryRun = false forceRun = false @@ -27,7 +27,7 @@ func replayRunCommand(l log.Logger, conf config.Optimus) *cli.Command { allDownstream = false skipConfirm = false projectName = conf.Project.Name - namespaceName = conf.Namespace.Name + namespaceName string ) reCmd := &cli.Command{ @@ -52,6 +52,8 @@ Date ranges are inclusive. } reCmd.Flags().StringVarP(&projectName, "project", "p", projectName, "Project name of optimus managed repository") reCmd.Flags().StringVarP(&namespaceName, "namespace", "n", namespaceName, "Namespace of job that needs to be replayed") + reCmd.MarkFlagRequired("namespace") + reCmd.Flags().BoolVarP(&dryRun, "dry-run", "d", dryRun, "Only do a trial run with no permanent changes") reCmd.Flags().BoolVarP(&forceRun, "force", "f", forceRun, "Run replay even if a previous run is in progress") reCmd.Flags().BoolVar(&skipConfirm, "confirm", skipConfirm, "Skip asking for confirmation") diff --git a/cmd/resource.go b/cmd/resource.go index ff7ad5527a..cd8a6c883b 100644 --- a/cmd/resource.go +++ b/cmd/resource.go @@ -8,6 +8,7 @@ import ( "strings" "github.com/AlecAivazis/survey/v2" + "github.com/odpf/optimus/config" "github.com/odpf/optimus/models" "github.com/odpf/optimus/store" "github.com/odpf/optimus/store/local" @@ -22,7 +23,7 @@ var ( `invalid name (can only contain characters A-Z (in either case), 0-9, "-", "_" or "." and must start with an alphanumeric character)`) ) -func resourceCommand(l log.Logger, datastoreSpecsFs map[string]afero.Fs, datastoreRepo models.DatastoreRepo) *cli.Command { +func resourceCommand(l log.Logger, conf config.Optimus, datastoreRepo models.DatastoreRepo, datastoreSpecsFs map[string]map[string]afero.Fs) *cli.Command { cmd := &cli.Command{ Use: "resource", Short: "Interact with data resource", @@ -30,16 +31,17 @@ func resourceCommand(l log.Logger, datastoreSpecsFs map[string]afero.Fs, datasto "group:core": "true", }, } - cmd.AddCommand(createResourceSubCommand(l, datastoreSpecsFs, datastoreRepo)) + cmd.AddCommand(createResourceSubCommand(l, conf, datastoreSpecsFs, datastoreRepo)) return cmd } -func createResourceSubCommand(l log.Logger, datastoreSpecFs map[string]afero.Fs, datastoreRepo models.DatastoreRepo) *cli.Command { - return &cli.Command{ +func createResourceSubCommand(l log.Logger, conf config.Optimus, datastoreSpecFs map[string]map[string]afero.Fs, datastoreRepo models.DatastoreRepo) *cli.Command { + cmd := &cli.Command{ Use: "create", Short: "Create a new resource", Example: "optimus resource create", RunE: func(cmd *cli.Command, args []string) error { + namespace := askToSelectNamespace(l, conf) availableStorer := []string{} for _, s := range datastoreRepo.GetAll() { availableStorer = append(availableStorer, s.Name()) @@ -51,7 +53,7 @@ func createResourceSubCommand(l log.Logger, datastoreSpecFs map[string]afero.Fs, }, &storerName); err != nil { return err } - repoFS, ok := datastoreSpecFs[storerName] + repoFS, ok := datastoreSpecFs[namespace.Name][storerName] if !ok { return fmt.Errorf("unregistered datastore, please use configuration file to set datastore path") } @@ -119,6 +121,7 @@ func createResourceSubCommand(l log.Logger, datastoreSpecFs map[string]afero.Fs, return nil }, } + return cmd } // IsResourceNameUnique return a validator that checks if the resource already exists with the same name diff --git a/cmd/secret.go b/cmd/secret.go index 7337a23ad4..67118e7600 100644 --- a/cmd/secret.go +++ b/cmd/secret.go @@ -58,7 +58,7 @@ Use base64 flag if the value has been encoded. `, } secretCmd.Flags().StringVarP(&projectName, "project", "p", conf.Project.Name, "Project name of optimus managed repository") - secretCmd.Flags().StringVarP(&namespaceName, "namespace", "n", conf.Namespace.Name, "Namespace of deployee") + secretCmd.Flags().StringVarP(&namespaceName, "namespace", "n", namespaceName, "Namespace of deployee") secretCmd.Flags().BoolVar(&encoded, "base64", false, "Create secret with value that has been encoded") secretCmd.Flags().BoolVar(&updateOnly, "update-only", false, "Only update existing secret, do not create new") secretCmd.Flags().StringVarP(&filePath, "file", "f", filePath, "Provide file path to create secret from file instead") @@ -111,13 +111,11 @@ Use base64 flag if the value has been encoded. NamespaceName: namespaceName, } return updateSecret(l, conf.Host, updateSecretRequest) - } else { - l.Info(coloredNotice("Aborting...")) - return nil } - } else { - return fmt.Errorf("%s: request failed for creating secret %s", err, secretName) + l.Info(coloredNotice("Aborting...")) + return nil } + return fmt.Errorf("%s: request failed for creating secret %s", err, secretName) } return nil } @@ -145,7 +143,7 @@ func secretListSubCommand(l log.Logger, conf config.Optimus) *cli.Command { } func secretDeleteSubCommand(l log.Logger, conf config.Optimus) *cli.Command { - var projectName string + var projectName, namespaceName string cmd := &cli.Command{ Use: "delete", @@ -154,6 +152,7 @@ func secretDeleteSubCommand(l log.Logger, conf config.Optimus) *cli.Command { Long: `This operation deletes a secret registered with optimus.`, } cmd.Flags().StringVarP(&projectName, "project", "p", conf.Project.Name, "Project name of optimus managed repository") + cmd.Flags().StringVarP(&namespaceName, "namespace", "n", namespaceName, "Namespace name of optimus managed repository") cmd.RunE = func(cmd *cli.Command, args []string) error { secretName, err := getSecretName(args) @@ -164,7 +163,7 @@ func secretDeleteSubCommand(l log.Logger, conf config.Optimus) *cli.Command { deleteSecretRequest := &pb.DeleteSecretRequest{ ProjectName: projectName, SecretName: secretName, - NamespaceName: conf.Namespace.Name, + NamespaceName: namespaceName, } return deleteSecret(l, conf.Host, deleteSecretRequest) } diff --git a/config/config.go b/config/config.go index ffe753e9c6..b29a0cb794 100644 --- a/config/config.go +++ b/config/config.go @@ -1,6 +1,7 @@ package config import ( + "fmt" "strconv" "time" ) @@ -15,13 +16,28 @@ type Optimus struct { // optimus server host Host string `mapstructure:"host"` - Project Project `mapstructure:"project"` - Namespace Namespace `mapstructure:"namespace"` + Project Project `mapstructure:"project"` + Namespaces []*Namespace `mapstructure:"namespaces"` Server ServerConfig `mapstructure:"serve"` Log LogConfig `mapstructure:"log"` Scheduler SchedulerConfig `mapstructure:"scheduler"` Telemetry TelemetryConfig `mapstructure:"telemetry"` + + namespaceNameToNamespace map[string]*Namespace +} + +func (o *Optimus) GetNamespaceByName(name string) (*Namespace, error) { + if o.namespaceNameToNamespace == nil { + o.namespaceNameToNamespace = make(map[string]*Namespace) + for _, namespace := range o.Namespaces { + o.namespaceNameToNamespace[namespace.Name] = namespace + } + } + if o.namespaceNameToNamespace[name] == nil { + return nil, fmt.Errorf("namespace [%s] is not found", name) + } + return o.namespaceNameToNamespace[name], nil } type Datastore struct { @@ -36,7 +52,7 @@ type Datastore struct { } type Job struct { - // directory to find specifications + // directory to find specifications relative to where the config is located Path string `mapstructure:"path"` } diff --git a/config/loader.go b/config/loader.go index 3dc8907096..83f836337b 100644 --- a/config/loader.go +++ b/config/loader.go @@ -1,11 +1,14 @@ package config import ( + "errors" "fmt" "os" - "path/filepath" + "strings" "github.com/odpf/salt/config" + "github.com/spf13/afero" + "github.com/spf13/viper" ) const ( @@ -14,39 +17,71 @@ const ( FileExtension = "yaml" ) -// InitOptimus Load configuration file from following paths +type LoadConfigFunc func(interface{}, afero.Fs, ...string) error + +// LoadOptimusConfig Load configuration file from following paths // ./ // / // ~/.optimus/ -func InitOptimus() (*Optimus, error) { - var o Optimus +// Namespaces will be loaded only from current project ./ +func LoadOptimusConfig(dirPaths ...string) (*Optimus, error) { + fs := afero.NewReadOnlyFs(afero.NewOsFs()) - currPath, err := os.Getwd() - if err != nil { - return nil, fmt.Errorf("error getting current work directory path: %w", err) + var targetPath string + if len(dirPaths) > 0 { + targetPath = dirPaths[0] + } else { + currPath, err := os.Getwd() + if err != nil { + return nil, fmt.Errorf("error getting current work directory path: %w", err) + } + targetPath = currPath } - execPath, err := os.Executable() - if err != nil { - return nil, fmt.Errorf("error getting the executable path: %w", err) + + optimus := Optimus{} + if err := loadConfig(&optimus, fs, targetPath); err != nil { + return nil, errors.New("error loading config") } - currentHomeDir, err := os.UserHomeDir() - if err != nil { - return nil, fmt.Errorf("error getting the home directory: %w", err) + if err := validateNamespaceDuplication(&optimus); err != nil { + return nil, err } - optimusDir := filepath.Join(currentHomeDir, ".optimus") + return &optimus, nil +} - l := config.NewLoader( +func validateNamespaceDuplication(optimus *Optimus) error { + nameToAppearance := make(map[string]int) + for _, namespace := range optimus.Namespaces { + nameToAppearance[namespace.Name]++ + } + var duplicateNames []string + for name, appearance := range nameToAppearance { + if appearance > 1 { + duplicateNames = append(duplicateNames, name) + } + } + if len(duplicateNames) > 0 { + return fmt.Errorf("namespaces [%s] are duplicate", strings.Join(duplicateNames, ", ")) + } + return nil +} + +func loadConfig(cfg interface{}, fs afero.Fs, dirPath string) error { + // getViperWithDefault + SetFs + v := viper.New() + v.SetConfigName("config") + v.SetConfigType("yaml") + v.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) + v.SetFs(fs) + + opts := []config.LoaderOption{ + config.WithViper(v), config.WithName(FileName), config.WithType(FileExtension), - config.WithPath(currPath), - config.WithPath(filepath.Dir(execPath)), - config.WithPath(optimusDir), config.WithEnvPrefix("OPTIMUS"), config.WithEnvKeyReplacer(".", "_"), - ) - - if err := l.Load(&o); err != nil { - return nil, fmt.Errorf("error loading config: %w", err) + config.WithPath(dirPath), } - return &o, nil + + l := config.NewLoader(opts...) + return l.Load(cfg) } diff --git a/config/loader_test.go b/config/loader_test.go new file mode 100644 index 0000000000..f9bf4434c0 --- /dev/null +++ b/config/loader_test.go @@ -0,0 +1,152 @@ +package config_test + +import ( + "os" + "path" + "testing" + "time" + + "github.com/odpf/optimus/config" + + "github.com/stretchr/testify/assert" +) + +const ( + configFileName = ".optimus.yaml" + optimusConfigDirName = "./optimus" +) + +const optimusConfigContent = ` +version: 1 +log: + level: info +host: "localhost:9100" +project: + name: sample_project + config: + environment: integration + scheduler_host: http://example.io/ + storage_path: file://absolute_path_to_a_directory +serve: + port: 9100 + host: localhost + ingress_host: optimus.example.io:80 + app_key: Yjo4a0jn1NvYdq79SADC/KaVv9Wu0Ffc + replay_num_workers: 1 + replay_worker_timeout: "100s" + replay_run_timeout: "10s" + db: + dsn: postgres://user:password@localhost:5432/database?sslmode=disable + max_idle_connection: 5 + max_open_connection: 10 +scheduler: + name: airflow2 + skip_init: true +telemetry: + profile_addr: ":9110" + jaeger_addr: "http://localhost:14268/api/traces" +namespaces: +- name: namespace-a + job: + path: ./jobs-a +- name: namespace-b + job: + path: ./jobs-b +` + +func setup(content string) { + teardown() + if err := os.Mkdir(optimusConfigDirName, 0o750); err != nil { + panic(err) + } + confPath := path.Join(optimusConfigDirName, configFileName) + if err := os.WriteFile(confPath, []byte(content), 0o660); err != nil { + panic(err) + } +} + +func teardown() { + if err := os.RemoveAll(optimusConfigDirName); err != nil { + panic(err) + } +} + +func TestLoadOptimusConfig(t *testing.T) { + t.Run("should return config and nil if no error is found", func(t *testing.T) { + setup(optimusConfigContent + ` +- name: namespace-b + job: + path: ./jobs-b +`) + defer teardown() + + expectedErrMsg := "namespaces [namespace-b] are duplicate" + + actualConf, actualErr := config.LoadOptimusConfig(optimusConfigDirName) + + assert.Nil(t, actualConf) + assert.EqualError(t, actualErr, expectedErrMsg) + }) + + t.Run("should return config and nil if no error is found", func(t *testing.T) { + setup(optimusConfigContent) + defer teardown() + + expectedConf := &config.Optimus{ + Version: 1, + Log: config.LogConfig{ + Level: "info", + }, + Host: "localhost:9100", + Project: config.Project{ + Name: "sample_project", + Config: map[string]string{ + "environment": "integration", + "scheduler_host": "http://example.io/", + "storage_path": "file://absolute_path_to_a_directory", + }, + }, + Server: config.ServerConfig{ + Port: 9100, + Host: "localhost", + IngressHost: "optimus.example.io:80", + AppKey: "Yjo4a0jn1NvYdq79SADC/KaVv9Wu0Ffc", + ReplayNumWorkers: 1, + ReplayWorkerTimeout: 100 * time.Second, + ReplayRunTimeout: 10 * time.Second, + DB: config.DBConfig{ + DSN: "postgres://user:password@localhost:5432/database?sslmode=disable", + MaxIdleConnection: 5, + MaxOpenConnection: 10, + }, + }, + Scheduler: config.SchedulerConfig{ + Name: "airflow2", + SkipInit: true, + }, + Telemetry: config.TelemetryConfig{ + ProfileAddr: ":9110", + JaegerAddr: "http://localhost:14268/api/traces", + }, + Namespaces: []*config.Namespace{ + { + Name: "namespace-a", + Job: config.Job{ + Path: "./jobs-a", + }, + }, + { + Name: "namespace-b", + Job: config.Job{ + Path: "./jobs-b", + }, + }, + }, + } + + actualConf, actualErr := config.LoadOptimusConfig(optimusConfigDirName) + + assert.EqualValues(t, expectedConf, actualConf) + assert.NoError(t, actualErr) + }) +} diff --git a/go.mod b/go.mod index cfdff13243..f1ff638170 100644 --- a/go.mod +++ b/go.mod @@ -11,6 +11,8 @@ require ( github.com/dustinkirkland/golang-petname v0.0.0-20191129215211-8e5a1ed0cff0 github.com/emirpasic/gods v1.12.0 github.com/golang-migrate/migrate/v4 v4.14.1 + github.com/golang/glog v1.0.0 // indirect + github.com/golang/protobuf v1.5.2 // indirect github.com/google/go-github v17.0.0+incompatible github.com/google/uuid v1.3.0 github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8 @@ -37,6 +39,7 @@ require ( github.com/slack-go/slack v0.9.1 github.com/spf13/afero v1.6.0 github.com/spf13/cobra v1.2.1 + github.com/spf13/viper v1.8.1 github.com/stretchr/testify v1.7.0 github.com/xlab/treeprint v1.1.0 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.25.0 @@ -68,20 +71,14 @@ require ( github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 // indirect github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect github.com/boltdb/bolt v1.3.1 // indirect - github.com/bufbuild/buf v0.54.1 // indirect github.com/cespare/xxhash/v2 v2.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/fatih/color v1.7.0 // indirect github.com/felixge/httpsnoop v1.0.2 // indirect github.com/fsnotify/fsnotify v1.5.1 // indirect - github.com/ghodss/yaml v1.0.0 // indirect - github.com/gofrs/flock v0.8.1 // indirect github.com/gofrs/uuid v4.0.0+incompatible // indirect - github.com/golang/glog v1.0.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/golang/protobuf v1.5.2 // indirect github.com/google/btree v1.0.0 // indirect github.com/google/go-cmp v0.5.6 // indirect github.com/google/go-querystring v1.1.0 // indirect @@ -114,8 +111,6 @@ require ( github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.2 // indirect github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect - github.com/klauspost/compress v1.13.5 // indirect - github.com/klauspost/pgzip v1.2.5 // indirect github.com/lib/pq v1.10.2 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/magiconair/properties v1.8.5 // indirect @@ -135,7 +130,6 @@ require ( github.com/oklog/run v1.0.0 // indirect github.com/pelletier/go-toml v1.9.3 // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/pkg/profile v1.6.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/common v0.26.0 // indirect @@ -146,10 +140,8 @@ require ( github.com/spf13/cast v1.3.1 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/spf13/viper v1.8.1 // indirect github.com/stretchr/objx v0.2.0 // indirect github.com/subosito/gotenv v1.2.0 // indirect - github.com/twitchtv/twirp v8.1.0+incompatible // indirect go.etcd.io/bbolt v1.3.5 // indirect go.opencensus.io v0.23.0 // indirect go.opentelemetry.io/otel/internal/metric v0.24.0 // indirect @@ -164,6 +156,5 @@ require ( golang.org/x/text v0.3.7 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0 // indirect gopkg.in/ini.v1 v1.62.0 // indirect ) diff --git a/go.sum b/go.sum index f42cbe6282..33fca38ef5 100644 --- a/go.sum +++ b/go.sum @@ -177,8 +177,6 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas= -github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k= github.com/bkaradzic/go-lz4 v1.0.0/go.mod h1:0YdlkowM3VswSROI7qDxhRvJ3sLhlFrRRwjwegp5jy4= @@ -188,8 +186,6 @@ github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4= github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= github.com/briandowns/spinner v1.18.0 h1:SJs0maNOs4FqhBwiJ3Gr7Z1D39/rukIVGQvpNZVHVcM= github.com/briandowns/spinner v1.18.0/go.mod h1:QOuQk7x+EaDASo80FEXwlwiA+j/PPIcX3FScO+3/ZPQ= -github.com/bufbuild/buf v0.54.1 h1:k5zYgSOlNg17mZCBgKo6TWktCiSvBdOx+v591t3JPjY= -github.com/bufbuild/buf v0.54.1/go.mod h1:BRVv/lQDPFt0AGt79VgXMQK5HHXRW5VFhAu+mNnOmeM= github.com/cenkalti/backoff/v4 v4.0.2/go.mod h1:eEew/i+1Q6OrCDZh3WiXYv3+nJwBASZ8Bog/87DQnVg= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -270,7 +266,6 @@ github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4 github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI= github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= github.com/fsouza/fake-gcs-server v1.17.0/go.mod h1:D1rTE4YCyHFNa99oyJJ5HyclvN/0uQR+pM/VdlL83bw= -github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= @@ -301,8 +296,6 @@ github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6Wezm github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= github.com/gocql/gocql v0.0.0-20190301043612-f6df8288f9b4/go.mod h1:4Fw1eo5iaEhDUs8XyuhSVCVy52Jq3L+/3GJgYkwc+/0= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= -github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= @@ -604,11 +597,7 @@ github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQL github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= -github.com/klauspost/compress v1.13.5 h1:9O69jUPDcsT9fEm74W92rZL9FQY7rCdaXVneq+yyzl4= github.com/klauspost/compress v1.13.5/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE= -github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -743,8 +732,6 @@ github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/profile v1.6.0 h1:hUDfIISABYI59DyeB3OTay/HxSRwTQ8rB/H83k6r5dM= -github.com/pkg/profile v1.6.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdLa18= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -841,8 +828,6 @@ github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/tidwall/pretty v0.0.0-20180105212114-65a9db5fad51/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= -github.com/twitchtv/twirp v8.1.0+incompatible h1:KGXanpa9LXdVE/V5P/tA27rkKFmXRGCtSNT7zdeeVOY= -github.com/twitchtv/twirp v8.1.0+incompatible/go.mod h1:RRJoFSAmTEh2weEqWtpPE3vFK5YBhA6bqp2l1kfCC5A= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/xanzy/go-gitlab v0.15.0/go.mod h1:8zdQa/ri1dfn8eS3Ir1SyfvOKlw7WBJ8DVThkpGiXrs= @@ -1146,7 +1131,6 @@ golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1379,10 +1363,8 @@ google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQ google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.40.0-dev.0.20210708170655-30dfb4b933a5/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.40.0 h1:AGJ0Ih4mHjSeibYkFGh1dD9KJ/eOtZ93I6hoHhukQ5Q= google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0 h1:M1YKkFIboKNieVO5DLUEVzQfGwJD30Nv2jfUgzb5UcE= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= diff --git a/main.go b/main.go index 415b18fab6..e84915e9ce 100644 --- a/main.go +++ b/main.go @@ -42,19 +42,19 @@ func (p *PlainFormatter) Format(entry *logrus.Entry) ([]byte, error) { func main() { rand.Seed(time.Now().UTC().UnixNano()) - configuration, err := config.InitOptimus() + optimusConfig, err := config.LoadOptimusConfig() if err != nil { - fmt.Printf("ERROR: %s", err.Error()) + fmt.Printf("ERROR: %s\n", err.Error()) os.Exit(1) } var jsonLogger log.Logger var plainLogger log.Logger pluginLogLevel := hclog.Info - if configuration.Log.Level != "" { - jsonLogger = log.NewLogrus(log.LogrusWithLevel(configuration.Log.Level), log.LogrusWithWriter(os.Stderr)) - plainLogger = log.NewLogrus(log.LogrusWithLevel(configuration.Log.Level), log.LogrusWithFormatter(new(PlainFormatter))) - if strings.ToLower(configuration.Log.Level) == "debug" { + if optimusConfig.Log.Level != "" { + jsonLogger = log.NewLogrus(log.LogrusWithLevel(optimusConfig.Log.Level), log.LogrusWithWriter(os.Stderr)) + plainLogger = log.NewLogrus(log.LogrusWithLevel(optimusConfig.Log.Level), log.LogrusWithFormatter(new(PlainFormatter))) + if strings.ToLower(optimusConfig.Log.Level) == "debug" { pluginLogLevel = hclog.Debug } } else { @@ -63,7 +63,7 @@ func main() { } // init telemetry - teleShutdown, err := config.InitTelemetry(jsonLogger, configuration.Telemetry) + teleShutdown, err := config.InitTelemetry(jsonLogger, optimusConfig.Telemetry) if err != nil { fmt.Printf("ERROR: %s\n", err.Error()) os.Exit(1) @@ -86,13 +86,14 @@ func main() { command := cmd.New( plainLogger, jsonLogger, - *configuration, + *optimusConfig, models.PluginRegistry, models.DatastoreRegistry, ) if err := command.Execute(); err != nil { hPlugin.CleanupClients() // no need to print err here, `command` does that already + fmt.Println(err) fmt.Println(errRequestFail) os.Exit(1) } diff --git a/mock/job.go b/mock/job.go index 88b7514aae..04f8bac4d5 100644 --- a/mock/job.go +++ b/mock/job.go @@ -127,8 +127,8 @@ type JobService struct { mock.Mock } -func (srv *JobService) Create(ctx context.Context, spec2 models.NamespaceSpec, spec models.JobSpec) error { - args := srv.Called(ctx, spec, spec2) +func (srv *JobService) Create(ctx context.Context, namespaceSpec models.NamespaceSpec, jobSpec models.JobSpec) error { + args := srv.Called(ctx, namespaceSpec, jobSpec) return args.Error(0) } diff --git a/mock/proto_adapter.go b/mock/proto_adapter.go new file mode 100644 index 0000000000..21495d3f0b --- /dev/null +++ b/mock/proto_adapter.go @@ -0,0 +1,231 @@ +package mock + +import ( + optimus "github.com/odpf/optimus/api/proto/odpf/optimus/core/v1beta1" + "github.com/odpf/optimus/core/tree" + "github.com/odpf/optimus/models" + + "github.com/stretchr/testify/mock" +) + +// ProtoAdapter is an autogenerated mock type for the ProtoAdapter type +type ProtoAdapter struct { + mock.Mock +} + +// FromJobProto provides a mock function with given fields: _a0 +func (_m *ProtoAdapter) FromJobProto(_a0 *optimus.JobSpecification) (models.JobSpec, error) { + ret := _m.Called(_a0) + + var r0 models.JobSpec + if rf, ok := ret.Get(0).(func(*optimus.JobSpecification) models.JobSpec); ok { + r0 = rf(_a0) + } else { + r0 = ret.Get(0).(models.JobSpec) + } + + var r1 error + if rf, ok := ret.Get(1).(func(*optimus.JobSpecification) error); ok { + r1 = rf(_a0) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// FromNamespaceProto provides a mock function with given fields: specification +func (_m *ProtoAdapter) FromNamespaceProto(specification *optimus.NamespaceSpecification) models.NamespaceSpec { + ret := _m.Called(specification) + + var r0 models.NamespaceSpec + if rf, ok := ret.Get(0).(func(*optimus.NamespaceSpecification) models.NamespaceSpec); ok { + r0 = rf(specification) + } else { + r0 = ret.Get(0).(models.NamespaceSpec) + } + + return r0 +} + +// FromProjectProto provides a mock function with given fields: _a0 +func (_m *ProtoAdapter) FromProjectProto(_a0 *optimus.ProjectSpecification) models.ProjectSpec { + ret := _m.Called(_a0) + + var r0 models.ProjectSpec + if rf, ok := ret.Get(0).(func(*optimus.ProjectSpecification) models.ProjectSpec); ok { + r0 = rf(_a0) + } else { + r0 = ret.Get(0).(models.ProjectSpec) + } + + return r0 +} + +// FromResourceProto provides a mock function with given fields: res, storeName +func (_m *ProtoAdapter) FromResourceProto(res *optimus.ResourceSpecification, storeName string) (models.ResourceSpec, error) { + ret := _m.Called(res, storeName) + + var r0 models.ResourceSpec + if rf, ok := ret.Get(0).(func(*optimus.ResourceSpecification, string) models.ResourceSpec); ok { + r0 = rf(res, storeName) + } else { + r0 = ret.Get(0).(models.ResourceSpec) + } + + var r1 error + if rf, ok := ret.Get(1).(func(*optimus.ResourceSpecification, string) error); ok { + r1 = rf(res, storeName) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ToInstanceProto provides a mock function with given fields: _a0 +func (_m *ProtoAdapter) ToInstanceProto(_a0 models.InstanceSpec) (*optimus.InstanceSpec, error) { + ret := _m.Called(_a0) + + var r0 *optimus.InstanceSpec + if rf, ok := ret.Get(0).(func(models.InstanceSpec) *optimus.InstanceSpec); ok { + r0 = rf(_a0) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*optimus.InstanceSpec) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(models.InstanceSpec) error); ok { + r1 = rf(_a0) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ToJobProto provides a mock function with given fields: _a0 +func (_m *ProtoAdapter) ToJobProto(_a0 models.JobSpec) (*optimus.JobSpecification, error) { + ret := _m.Called(_a0) + + var r0 *optimus.JobSpecification + if rf, ok := ret.Get(0).(func(models.JobSpec) *optimus.JobSpecification); ok { + r0 = rf(_a0) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*optimus.JobSpecification) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(models.JobSpec) error); ok { + r1 = rf(_a0) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ToNamespaceProto provides a mock function with given fields: spec +func (_m *ProtoAdapter) ToNamespaceProto(spec models.NamespaceSpec) *optimus.NamespaceSpecification { + ret := _m.Called(spec) + + var r0 *optimus.NamespaceSpecification + if rf, ok := ret.Get(0).(func(models.NamespaceSpec) *optimus.NamespaceSpecification); ok { + r0 = rf(spec) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*optimus.NamespaceSpecification) + } + } + + return r0 +} + +// ToProjectProto provides a mock function with given fields: _a0 +func (_m *ProtoAdapter) ToProjectProto(_a0 models.ProjectSpec) *optimus.ProjectSpecification { + ret := _m.Called(_a0) + + var r0 *optimus.ProjectSpecification + if rf, ok := ret.Get(0).(func(models.ProjectSpec) *optimus.ProjectSpecification); ok { + r0 = rf(_a0) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*optimus.ProjectSpecification) + } + } + + return r0 +} + +// ToReplayExecutionTreeNode provides a mock function with given fields: res +func (_m *ProtoAdapter) ToReplayExecutionTreeNode(res *tree.TreeNode) (*optimus.ReplayExecutionTreeNode, error) { + ret := _m.Called(res) + + var r0 *optimus.ReplayExecutionTreeNode + if rf, ok := ret.Get(0).(func(*tree.TreeNode) *optimus.ReplayExecutionTreeNode); ok { + r0 = rf(res) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*optimus.ReplayExecutionTreeNode) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(*tree.TreeNode) error); ok { + r1 = rf(res) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ToReplayStatusTreeNode provides a mock function with given fields: res +func (_m *ProtoAdapter) ToReplayStatusTreeNode(res *tree.TreeNode) (*optimus.ReplayStatusTreeNode, error) { + ret := _m.Called(res) + + var r0 *optimus.ReplayStatusTreeNode + if rf, ok := ret.Get(0).(func(*tree.TreeNode) *optimus.ReplayStatusTreeNode); ok { + r0 = rf(res) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*optimus.ReplayStatusTreeNode) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(*tree.TreeNode) error); ok { + r1 = rf(res) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ToResourceProto provides a mock function with given fields: res +func (_m *ProtoAdapter) ToResourceProto(res models.ResourceSpec) (*optimus.ResourceSpecification, error) { + ret := _m.Called(res) + + var r0 *optimus.ResourceSpecification + if rf, ok := ret.Get(0).(func(models.ResourceSpec) *optimus.ResourceSpecification); ok { + r0 = rf(res) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*optimus.ResourceSpecification) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(models.ResourceSpec) error); ok { + r1 = rf(res) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} diff --git a/mock/runtime.go b/mock/runtime.go index 8ad50981cb..f3ba635d8f 100644 --- a/mock/runtime.go +++ b/mock/runtime.go @@ -3,41 +3,246 @@ package mock import ( "context" - pb "github.com/odpf/optimus/api/proto/odpf/optimus/core/v1beta1" + optimus "github.com/odpf/optimus/api/proto/odpf/optimus/core/v1beta1" + "github.com/stretchr/testify/mock" "google.golang.org/grpc/metadata" ) +// DeployJobSpecificationServer is an autogenerated mock type for the DeployJobSpecificationServer type type DeployJobSpecificationServer struct { mock.Mock } -func (r *DeployJobSpecificationServer) Send(response *pb.DeployJobSpecificationResponse) error { - args := r.Called(response) - return args.Error(0) +// Context provides a mock function with given fields: +func (_m *DeployJobSpecificationServer) Context() context.Context { + ret := _m.Called() + + var r0 context.Context + if rf, ok := ret.Get(0).(func() context.Context); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(context.Context) + } + } + + return r0 +} + +// Recv provides a mock function with given fields: +func (_m *DeployJobSpecificationServer) Recv() (*optimus.DeployJobSpecificationRequest, error) { + ret := _m.Called() + + var r0 *optimus.DeployJobSpecificationRequest + if rf, ok := ret.Get(0).(func() *optimus.DeployJobSpecificationRequest); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*optimus.DeployJobSpecificationRequest) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// RecvMsg provides a mock function with given fields: m +func (_m *DeployJobSpecificationServer) RecvMsg(m interface{}) error { + ret := _m.Called(m) + + var r0 error + if rf, ok := ret.Get(0).(func(interface{}) error); ok { + r0 = rf(m) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Send provides a mock function with given fields: _a0 +func (_m *DeployJobSpecificationServer) Send(_a0 *optimus.DeployJobSpecificationResponse) error { + ret := _m.Called(_a0) + + var r0 error + if rf, ok := ret.Get(0).(func(*optimus.DeployJobSpecificationResponse) error); ok { + r0 = rf(_a0) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// SendHeader provides a mock function with given fields: _a0 +func (_m *DeployJobSpecificationServer) SendHeader(_a0 metadata.MD) error { + ret := _m.Called(_a0) + + var r0 error + if rf, ok := ret.Get(0).(func(metadata.MD) error); ok { + r0 = rf(_a0) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// SendMsg provides a mock function with given fields: m +func (_m *DeployJobSpecificationServer) SendMsg(m interface{}) error { + ret := _m.Called(m) + + var r0 error + if rf, ok := ret.Get(0).(func(interface{}) error); ok { + r0 = rf(m) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// SetHeader provides a mock function with given fields: _a0 +func (_m *DeployJobSpecificationServer) SetHeader(_a0 metadata.MD) error { + ret := _m.Called(_a0) + + var r0 error + if rf, ok := ret.Get(0).(func(metadata.MD) error); ok { + r0 = rf(_a0) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// SetTrailer provides a mock function with given fields: _a0 +func (_m *DeployJobSpecificationServer) SetTrailer(_a0 metadata.MD) { + _m.Called(_a0) +} + +// DeployResourceSpecificationServer is an autogenerated mock type for the DeployResourceSpecificationServer type +type DeployResourceSpecificationServer struct { + mock.Mock +} + +// Context provides a mock function with given fields: +func (_m *DeployResourceSpecificationServer) Context() context.Context { + ret := _m.Called() + + var r0 context.Context + if rf, ok := ret.Get(0).(func() context.Context); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(context.Context) + } + } + + return r0 +} + +// Recv provides a mock function with given fields: +func (_m *DeployResourceSpecificationServer) Recv() (*optimus.DeployResourceSpecificationRequest, error) { + ret := _m.Called() + + var r0 *optimus.DeployResourceSpecificationRequest + if rf, ok := ret.Get(0).(func() *optimus.DeployResourceSpecificationRequest); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*optimus.DeployResourceSpecificationRequest) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 } -func (r *DeployJobSpecificationServer) SetHeader(md metadata.MD) error { - panic("implement me") +// RecvMsg provides a mock function with given fields: m +func (_m *DeployResourceSpecificationServer) RecvMsg(m interface{}) error { + ret := _m.Called(m) + + var r0 error + if rf, ok := ret.Get(0).(func(interface{}) error); ok { + r0 = rf(m) + } else { + r0 = ret.Error(0) + } + + return r0 } -func (r *DeployJobSpecificationServer) SendHeader(md metadata.MD) error { - panic("implement me") +// Send provides a mock function with given fields: _a0 +func (_m *DeployResourceSpecificationServer) Send(_a0 *optimus.DeployResourceSpecificationResponse) error { + ret := _m.Called(_a0) + + var r0 error + if rf, ok := ret.Get(0).(func(*optimus.DeployResourceSpecificationResponse) error); ok { + r0 = rf(_a0) + } else { + r0 = ret.Error(0) + } + + return r0 } -func (r *DeployJobSpecificationServer) SetTrailer(md metadata.MD) { - panic("implement me") +// SendHeader provides a mock function with given fields: _a0 +func (_m *DeployResourceSpecificationServer) SendHeader(_a0 metadata.MD) error { + ret := _m.Called(_a0) + + var r0 error + if rf, ok := ret.Get(0).(func(metadata.MD) error); ok { + r0 = rf(_a0) + } else { + r0 = ret.Error(0) + } + + return r0 } -func (r *DeployJobSpecificationServer) Context() context.Context { - args := r.Called() - return args.Get(0).(context.Context) +// SendMsg provides a mock function with given fields: m +func (_m *DeployResourceSpecificationServer) SendMsg(m interface{}) error { + ret := _m.Called(m) + + var r0 error + if rf, ok := ret.Get(0).(func(interface{}) error); ok { + r0 = rf(m) + } else { + r0 = ret.Error(0) + } + + return r0 } -func (r *DeployJobSpecificationServer) SendMsg(m interface{}) error { - panic("implement me") +// SetHeader provides a mock function with given fields: _a0 +func (_m *DeployResourceSpecificationServer) SetHeader(_a0 metadata.MD) error { + ret := _m.Called(_a0) + + var r0 error + if rf, ok := ret.Get(0).(func(metadata.MD) error); ok { + r0 = rf(_a0) + } else { + r0 = ret.Error(0) + } + + return r0 } -func (r *DeployJobSpecificationServer) RecvMsg(m interface{}) error { - panic("implement me") +// SetTrailer provides a mock function with given fields: _a0 +func (_m *DeployResourceSpecificationServer) SetTrailer(_a0 metadata.MD) { + _m.Called(_a0) }