Chromium Code Reviews| Index: scheduler/appengine/apiservers/scheduler.go |
| diff --git a/scheduler/appengine/apiservers/scheduler.go b/scheduler/appengine/apiservers/scheduler.go |
| index 3de1d4e5aed5995af4b3f0770ecc83cce455d774..6e55d1ac5f6911cf53d2188cbaf7bae48e3fdd3c 100644 |
| --- a/scheduler/appengine/apiservers/scheduler.go |
| +++ b/scheduler/appengine/apiservers/scheduler.go |
| @@ -5,13 +5,16 @@ |
| package apiservers |
| import ( |
| - "github.com/luci/luci-go/scheduler/api/scheduler/v1" |
| + scheduler "github.com/luci/luci-go/scheduler/api/scheduler/v1" |
|
Vadim Sh.
2017/07/05 18:38:48
nit: remove this alias, it is already "scheduler".
tandrii(chromium)
2017/07/05 19:21:51
Done.
I keep adding it if I open my vim configured
|
| "github.com/luci/luci-go/scheduler/appengine/catalog" |
| "github.com/luci/luci-go/scheduler/appengine/engine" |
| "github.com/luci/luci-go/scheduler/appengine/presentation" |
| + "github.com/luci/luci-go/server/auth" |
| "golang.org/x/net/context" |
| "google.golang.org/grpc" |
| "google.golang.org/grpc/codes" |
| + |
| + "github.com/golang/protobuf/ptypes/empty" |
| ) |
| // SchedulerServer implements scheduler.Scheduler API. |
| @@ -24,6 +27,11 @@ var _ scheduler.SchedulerServer = (*SchedulerServer)(nil) |
| // GetJobs fetches all jobs satisfying JobsRequest and visibility ACLs. |
| func (s SchedulerServer) GetJobs(ctx context.Context, in *scheduler.JobsRequest) (*scheduler.JobsReply, error) { |
| + if in.GetCursor() != "" { |
| + // Paging in GetJobs isn't implemented until we have enough jobs to care. |
| + // Until then, not empty cursor implies no more jobs to return. |
| + return &scheduler.JobsReply{Jobs: []*scheduler.Job{}, NextCursor: ""}, nil |
| + } |
| var ejobs []*engine.Job |
| var err error |
| if in.GetProject() == "" { |
| @@ -42,19 +50,21 @@ func (s SchedulerServer) GetJobs(ctx context.Context, in *scheduler.JobsRequest) |
| return nil, grpc.Errorf(codes.Internal, "failed to get traits: %s", err) |
| } |
| jobs[i] = &scheduler.Job{ |
| - Name: ej.GetJobName(), |
| - Project: ej.ProjectID, |
| + JobRef: &scheduler.JobRef{ |
| + Project: ej.ProjectID, |
| + Job: ej.GetJobName(), |
| + }, |
| Schedule: ej.Schedule, |
| State: &scheduler.JobState{ |
| UiStatus: string(presentation.GetPublicStateKind(ej, traits)), |
| }, |
| } |
| } |
| - return &scheduler.JobsReply{Jobs: jobs}, nil |
| + return &scheduler.JobsReply{Jobs: jobs, NextCursor: ""}, nil |
| } |
| func (s SchedulerServer) GetInvocations(ctx context.Context, in *scheduler.InvocationsRequest) (*scheduler.InvocationsReply, error) { |
| - ejob, err := s.Engine.GetJob(ctx, in.GetProject()+"/"+in.GetJob()) |
| + ejob, err := s.Engine.GetJob(ctx, getJobId(in.GetJobRef())) |
| if err != nil { |
| return nil, grpc.Errorf(codes.Internal, "datastore error: %s", err) |
| } |
| @@ -74,9 +84,13 @@ func (s SchedulerServer) GetInvocations(ctx context.Context, in *scheduler.Invoc |
| invs := make([]*scheduler.Invocation, len(einvs)) |
| for i, einv := range einvs { |
| invs[i] = &scheduler.Invocation{ |
| - Id: einv.ID, |
| - Job: ejob.GetJobName(), |
| - Project: ejob.ProjectID, |
| + InvocationRef: &scheduler.InvocationRef{ |
| + JobRef: &scheduler.JobRef{ |
| + Project: ejob.ProjectID, |
| + Job: ejob.GetJobName(), |
| + }, |
| + InvocationId: einv.ID, |
| + }, |
| StartedTs: einv.Started.UnixNano() / 1000, |
| TriggeredBy: string(einv.TriggeredBy), |
| Status: string(einv.Status), |
| @@ -90,3 +104,52 @@ func (s SchedulerServer) GetInvocations(ctx context.Context, in *scheduler.Invoc |
| } |
| return &scheduler.InvocationsReply{Invocations: invs, NextCursor: cursor}, nil |
| } |
| + |
| +//// Actions. |
| + |
| +func (s SchedulerServer) PauseJob(ctx context.Context, in *scheduler.JobRef) (*empty.Empty, error) { |
| + return runAction(ctx, in, func() error { |
| + return s.Engine.PauseJob(ctx, getJobId(in), auth.CurrentIdentity(ctx)) |
| + }) |
| +} |
| + |
| +func (s SchedulerServer) ResumeJob(ctx context.Context, in *scheduler.JobRef) (*empty.Empty, error) { |
| + return runAction(ctx, in, func() error { |
| + return s.Engine.ResumeJob(ctx, getJobId(in), auth.CurrentIdentity(ctx)) |
| + }) |
| +} |
| + |
| +func (s SchedulerServer) AbortJob(ctx context.Context, in *scheduler.JobRef) (*empty.Empty, error) { |
| + return runAction(ctx, in, func() error { |
| + return s.Engine.AbortJob(ctx, getJobId(in), auth.CurrentIdentity(ctx)) |
| + }) |
| +} |
| + |
| +func (s SchedulerServer) AbortInvocation(ctx context.Context, in *scheduler.InvocationRef) ( |
| + *empty.Empty, error) { |
| + return runAction(ctx, in.GetJobRef(), func() error { |
| + return s.Engine.AbortInvocation(ctx, getJobId(in.GetJobRef()), in.GetInvocationId(), auth.CurrentIdentity(ctx)) |
| + }) |
| +} |
| + |
| +//// Private helpers. |
| + |
| +func runAction(ctx context.Context, jobRef *scheduler.JobRef, action func() error) (*empty.Empty, error) { |
| + if !presentation.IsJobOwner(ctx, jobRef.GetProject(), jobRef.GetJob()) { |
|
Vadim Sh.
2017/07/05 18:38:48
strictly speaking, this function should be in 'acl
tandrii(chromium)
2017/07/05 19:21:51
Agree, added TODO in acl.go. Will execute on it on
|
| + return nil, grpc.Errorf(codes.PermissionDenied, "No permission to execute action") |
| + } |
| + switch err := action(); { |
| + case err == nil: |
| + return nil, nil |
|
Vadim Sh.
2017/07/05 18:38:47
return &empty.Empty{}, nil
(i think...)
tandrii(chromium)
2017/07/05 19:21:51
that's good point. I've not tested it with rpcexpl
|
| + case err == engine.ErrNoSuchJob: |
| + return nil, grpc.Errorf(codes.NotFound, "no such job") |
| + case err == engine.ErrNoSuchInvocation: |
| + return nil, grpc.Errorf(codes.NotFound, "no such invocation") |
| + default: |
| + return nil, grpc.Errorf(codes.Internal, "internal error: %s", err) |
| + } |
| +} |
| + |
| +func getJobId(jobRef *scheduler.JobRef) string { |
| + return jobRef.GetProject() + "/" + jobRef.GetJob() |
| +} |