Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(22)

Side by Side Diff: scheduler/appengine/apiservers/scheduler.go

Issue 2986033003: [scheduler]: ACLs phase 1 - per Job ACL specification and enforcement. (Closed)
Patch Set: Review. Created 3 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « scheduler/appengine/acl/doc.go ('k') | scheduler/appengine/apiservers/scheduler_test.go » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2016 The LUCI Authors. 1 // Copyright 2016 The LUCI Authors.
2 // 2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); 3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License. 4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at 5 // You may obtain a copy of the License at
6 // 6 //
7 // http://www.apache.org/licenses/LICENSE-2.0 7 // http://www.apache.org/licenses/LICENSE-2.0
8 // 8 //
9 // Unless required by applicable law or agreed to in writing, software 9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, 10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and 12 // See the License for the specific language governing permissions and
13 // limitations under the License. 13 // limitations under the License.
14 14
15 package apiservers 15 package apiservers
16 16
17 import ( 17 import (
18 "github.com/luci/luci-go/scheduler/api/scheduler/v1" 18 "github.com/luci/luci-go/scheduler/api/scheduler/v1"
19 "github.com/luci/luci-go/scheduler/appengine/acl"
20 "github.com/luci/luci-go/scheduler/appengine/catalog" 19 "github.com/luci/luci-go/scheduler/appengine/catalog"
21 "github.com/luci/luci-go/scheduler/appengine/engine" 20 "github.com/luci/luci-go/scheduler/appengine/engine"
22 "github.com/luci/luci-go/scheduler/appengine/presentation" 21 "github.com/luci/luci-go/scheduler/appengine/presentation"
23 "github.com/luci/luci-go/server/auth"
24 "golang.org/x/net/context" 22 "golang.org/x/net/context"
25 "google.golang.org/grpc" 23 "google.golang.org/grpc"
26 "google.golang.org/grpc/codes" 24 "google.golang.org/grpc/codes"
27 25
28 "github.com/golang/protobuf/ptypes/empty" 26 "github.com/golang/protobuf/ptypes/empty"
29 ) 27 )
30 28
31 // SchedulerServer implements scheduler.Scheduler API. 29 // SchedulerServer implements scheduler.Scheduler API.
32 type SchedulerServer struct { 30 type SchedulerServer struct {
33 Engine engine.Engine 31 Engine engine.Engine
34 Catalog catalog.Catalog 32 Catalog catalog.Catalog
35 } 33 }
36 34
37 var _ scheduler.SchedulerServer = (*SchedulerServer)(nil) 35 var _ scheduler.SchedulerServer = (*SchedulerServer)(nil)
38 36
39 // GetJobs fetches all jobs satisfying JobsRequest and visibility ACLs. 37 // GetJobs fetches all jobs satisfying JobsRequest and visibility ACLs.
40 func (s SchedulerServer) GetJobs(ctx context.Context, in *scheduler.JobsRequest) (*scheduler.JobsReply, error) { 38 func (s SchedulerServer) GetJobs(ctx context.Context, in *scheduler.JobsRequest) (*scheduler.JobsReply, error) {
41 if in.GetCursor() != "" { 39 if in.GetCursor() != "" {
42 // Paging in GetJobs isn't implemented until we have enough jobs to care. 40 // Paging in GetJobs isn't implemented until we have enough jobs to care.
43 // Until then, not empty cursor implies no more jobs to return. 41 // Until then, not empty cursor implies no more jobs to return.
44 return &scheduler.JobsReply{Jobs: []*scheduler.Job{}, NextCursor : ""}, nil 42 return &scheduler.JobsReply{Jobs: []*scheduler.Job{}, NextCursor : ""}, nil
45 } 43 }
46 var ejobs []*engine.Job 44 var ejobs []*engine.Job
47 var err error 45 var err error
48 if in.GetProject() == "" { 46 if in.GetProject() == "" {
49 » » ejobs, err = s.Engine.GetAllJobs(ctx) 47 » » ejobs, err = s.Engine.GetVisibleJobs(ctx)
50 } else { 48 } else {
51 » » ejobs, err = s.Engine.GetProjectJobs(ctx, in.GetProject()) 49 » » ejobs, err = s.Engine.GetVisibleProjectJobs(ctx, in.GetProject() )
52 } 50 }
53 if err != nil { 51 if err != nil {
54 » » return nil, grpc.Errorf(codes.Internal, "datastore error: %s", e rr) 52 » » return nil, grpc.Errorf(codes.Internal, "internal error: %s", er r)
55 } 53 }
56 54
57 jobs := make([]*scheduler.Job, len(ejobs)) 55 jobs := make([]*scheduler.Job, len(ejobs))
58 for i, ej := range ejobs { 56 for i, ej := range ejobs {
59 traits, err := presentation.GetJobTraits(ctx, s.Catalog, ej) 57 traits, err := presentation.GetJobTraits(ctx, s.Catalog, ej)
60 if err != nil { 58 if err != nil {
61 return nil, grpc.Errorf(codes.Internal, "failed to get t raits: %s", err) 59 return nil, grpc.Errorf(codes.Internal, "failed to get t raits: %s", err)
62 } 60 }
63 jobs[i] = &scheduler.Job{ 61 jobs[i] = &scheduler.Job{
64 JobRef: &scheduler.JobRef{ 62 JobRef: &scheduler.JobRef{
65 Project: ej.ProjectID, 63 Project: ej.ProjectID,
66 Job: ej.GetJobName(), 64 Job: ej.GetJobName(),
67 }, 65 },
68 Schedule: ej.Schedule, 66 Schedule: ej.Schedule,
69 State: &scheduler.JobState{ 67 State: &scheduler.JobState{
70 UiStatus: string(presentation.GetPublicStateKind (ej, traits)), 68 UiStatus: string(presentation.GetPublicStateKind (ej, traits)),
71 }, 69 },
72 Paused: ej.Paused, 70 Paused: ej.Paused,
73 } 71 }
74 } 72 }
75 return &scheduler.JobsReply{Jobs: jobs, NextCursor: ""}, nil 73 return &scheduler.JobsReply{Jobs: jobs, NextCursor: ""}, nil
76 } 74 }
77 75
78 func (s SchedulerServer) GetInvocations(ctx context.Context, in *scheduler.Invoc ationsRequest) (*scheduler.InvocationsReply, error) { 76 func (s SchedulerServer) GetInvocations(ctx context.Context, in *scheduler.Invoc ationsRequest) (*scheduler.InvocationsReply, error) {
79 » ejob, err := s.Engine.GetJob(ctx, getJobId(in.GetJobRef())) 77 » ejob, err := s.Engine.GetVisibleJob(ctx, getJobId(in.GetJobRef()))
80 if err != nil { 78 if err != nil {
81 » » return nil, grpc.Errorf(codes.Internal, "datastore error: %s", e rr) 79 » » return nil, grpc.Errorf(codes.Internal, "internal error: %s", er r)
82 } 80 }
83 if ejob == nil { 81 if ejob == nil {
84 return nil, grpc.Errorf(codes.NotFound, "Job does not exist or y ou have no access") 82 return nil, grpc.Errorf(codes.NotFound, "Job does not exist or y ou have no access")
85 } 83 }
86 84
87 pageSize := 50 85 pageSize := 50
88 if in.PageSize > 0 && int(in.PageSize) < pageSize { 86 if in.PageSize > 0 && int(in.PageSize) < pageSize {
89 pageSize = int(in.PageSize) 87 pageSize = int(in.PageSize)
90 } 88 }
91 89
92 » einvs, cursor, err := s.Engine.ListInvocations(ctx, ejob.JobID, pageSize , in.GetCursor()) 90 » einvs, cursor, err := s.Engine.ListVisibleInvocations(ctx, ejob.JobID, p ageSize, in.GetCursor())
93 if err != nil { 91 if err != nil {
94 » » return nil, grpc.Errorf(codes.Internal, "datastore error: %s", e rr) 92 » » return nil, grpc.Errorf(codes.Internal, "internal error: %s", er r)
95 } 93 }
96 invs := make([]*scheduler.Invocation, len(einvs)) 94 invs := make([]*scheduler.Invocation, len(einvs))
97 for i, einv := range einvs { 95 for i, einv := range einvs {
98 invs[i] = &scheduler.Invocation{ 96 invs[i] = &scheduler.Invocation{
99 InvocationRef: &scheduler.InvocationRef{ 97 InvocationRef: &scheduler.InvocationRef{
100 JobRef: &scheduler.JobRef{ 98 JobRef: &scheduler.JobRef{
101 Project: ejob.ProjectID, 99 Project: ejob.ProjectID,
102 Job: ejob.GetJobName(), 100 Job: ejob.GetJobName(),
103 }, 101 },
104 InvocationId: einv.ID, 102 InvocationId: einv.ID,
105 }, 103 },
106 StartedTs: einv.Started.UnixNano() / 1000, 104 StartedTs: einv.Started.UnixNano() / 1000,
107 TriggeredBy: string(einv.TriggeredBy), 105 TriggeredBy: string(einv.TriggeredBy),
108 Status: string(einv.Status), 106 Status: string(einv.Status),
109 Final: einv.Status.Final(), 107 Final: einv.Status.Final(),
110 ConfigRevision: einv.Revision, 108 ConfigRevision: einv.Revision,
111 ViewUrl: einv.ViewURL, 109 ViewUrl: einv.ViewURL,
112 } 110 }
113 if einv.Status.Final() { 111 if einv.Status.Final() {
114 invs[i].FinishedTs = einv.Finished.UnixNano() / 1000 112 invs[i].FinishedTs = einv.Finished.UnixNano() / 1000
115 } 113 }
116 } 114 }
117 return &scheduler.InvocationsReply{Invocations: invs, NextCursor: cursor }, nil 115 return &scheduler.InvocationsReply{Invocations: invs, NextCursor: cursor }, nil
118 } 116 }
119 117
120 //// Actions. 118 //// Actions.
121 119
122 func (s SchedulerServer) PauseJob(ctx context.Context, in *scheduler.JobRef) (*e mpty.Empty, error) { 120 func (s SchedulerServer) PauseJob(ctx context.Context, in *scheduler.JobRef) (*e mpty.Empty, error) {
123 return runAction(ctx, in, func() error { 121 return runAction(ctx, in, func() error {
124 » » return s.Engine.PauseJob(ctx, getJobId(in), auth.CurrentIdentity (ctx)) 122 » » return s.Engine.PauseJob(ctx, getJobId(in))
125 }) 123 })
126 } 124 }
127 125
128 func (s SchedulerServer) ResumeJob(ctx context.Context, in *scheduler.JobRef) (* empty.Empty, error) { 126 func (s SchedulerServer) ResumeJob(ctx context.Context, in *scheduler.JobRef) (* empty.Empty, error) {
129 return runAction(ctx, in, func() error { 127 return runAction(ctx, in, func() error {
130 » » return s.Engine.ResumeJob(ctx, getJobId(in), auth.CurrentIdentit y(ctx)) 128 » » return s.Engine.ResumeJob(ctx, getJobId(in))
131 }) 129 })
132 } 130 }
133 131
134 func (s SchedulerServer) AbortJob(ctx context.Context, in *scheduler.JobRef) (*e mpty.Empty, error) { 132 func (s SchedulerServer) AbortJob(ctx context.Context, in *scheduler.JobRef) (*e mpty.Empty, error) {
135 return runAction(ctx, in, func() error { 133 return runAction(ctx, in, func() error {
136 » » return s.Engine.AbortJob(ctx, getJobId(in), auth.CurrentIdentity (ctx)) 134 » » return s.Engine.AbortJob(ctx, getJobId(in))
137 }) 135 })
138 } 136 }
139 137
140 func (s SchedulerServer) AbortInvocation(ctx context.Context, in *scheduler.Invo cationRef) ( 138 func (s SchedulerServer) AbortInvocation(ctx context.Context, in *scheduler.Invo cationRef) (
141 *empty.Empty, error) { 139 *empty.Empty, error) {
142 return runAction(ctx, in.GetJobRef(), func() error { 140 return runAction(ctx, in.GetJobRef(), func() error {
143 » » return s.Engine.AbortInvocation(ctx, getJobId(in.GetJobRef()), i n.GetInvocationId(), auth.CurrentIdentity(ctx)) 141 » » return s.Engine.AbortInvocation(ctx, getJobId(in.GetJobRef()), i n.GetInvocationId())
144 }) 142 })
145 } 143 }
146 144
147 //// Private helpers. 145 //// Private helpers.
148 146
149 func runAction(ctx context.Context, jobRef *scheduler.JobRef, action func() erro r) (*empty.Empty, error) { 147 func runAction(ctx context.Context, jobRef *scheduler.JobRef, action func() erro r) (*empty.Empty, error) {
150 if !acl.IsJobOwner(ctx, jobRef.GetProject(), jobRef.GetJob()) {
151 return nil, grpc.Errorf(codes.PermissionDenied, "No permission t o execute action")
152 }
153 switch err := action(); { 148 switch err := action(); {
154 case err == nil: 149 case err == nil:
155 return &empty.Empty{}, nil 150 return &empty.Empty{}, nil
156 case err == engine.ErrNoSuchJob: 151 case err == engine.ErrNoSuchJob:
157 » » return nil, grpc.Errorf(codes.NotFound, "no such job") 152 » » return nil, grpc.Errorf(codes.NotFound, "no such job or no READ permission")
153 » case err == engine.ErrNoOwnerPermission:
154 » » return nil, grpc.Errorf(codes.PermissionDenied, "no OWNER permis sion")
158 case err == engine.ErrNoSuchInvocation: 155 case err == engine.ErrNoSuchInvocation:
159 return nil, grpc.Errorf(codes.NotFound, "no such invocation") 156 return nil, grpc.Errorf(codes.NotFound, "no such invocation")
160 default: 157 default:
161 return nil, grpc.Errorf(codes.Internal, "internal error: %s", er r) 158 return nil, grpc.Errorf(codes.Internal, "internal error: %s", er r)
162 } 159 }
163 } 160 }
164 161
165 func getJobId(jobRef *scheduler.JobRef) string { 162 func getJobId(jobRef *scheduler.JobRef) string {
166 return jobRef.GetProject() + "/" + jobRef.GetJob() 163 return jobRef.GetProject() + "/" + jobRef.GetJob()
167 } 164 }
OLDNEW
« no previous file with comments | « scheduler/appengine/acl/doc.go ('k') | scheduler/appengine/apiservers/scheduler_test.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698