1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
|
/*
* Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
// Package requests provides structures and functions to handle requests.
package requests
import (
"time"
. "git.tizen.org/tools/boruta"
)
// ReqsCollection contains information (also historical) about handled requests.
// It implements Requests interface.
type ReqsCollection struct {
requests map[ReqID]*ReqInfo
queue *prioQueue
}
// NewRequestQueue provides initialized priority queue for requests.
func NewRequestQueue() *ReqsCollection {
return &ReqsCollection{
requests: make(map[ReqID]*ReqInfo),
queue: newPrioQueue(),
}
}
// NewRequest is part of implementation of Requests interface. It validates
// provided arguments and creates request or returns an error. Caller must make
// sure that provided time values are in UTC.
func (reqs *ReqsCollection) NewRequest(caps Capabilities,
priority Priority, owner UserInfo, validAfter time.Time,
deadline time.Time) (ReqID, error) {
req := &ReqInfo{
ID: ReqID(len(reqs.requests) + 1),
Priority: priority,
Owner: owner,
Deadline: deadline,
ValidAfter: validAfter,
State: WAIT,
Caps: caps,
}
if !req.Deadline.IsZero() && req.Deadline.Before(time.Now().UTC()) {
return 0, ErrDeadlineInThePast
}
if req.ValidAfter.After(req.Deadline) && !req.Deadline.IsZero() {
return 0, ErrInvalidTimeRange
}
if req.ValidAfter.IsZero() {
req.ValidAfter = time.Now().UTC()
}
if req.Deadline.IsZero() {
// TODO(mwereski): make defaultValue configurable when config is
// introduced
req.Deadline = time.Now().AddDate(0, 1, 0).UTC()
}
// TODO(mwereski): Check if user has rights to set given priority.
if req.Priority < HiPrio || req.Priority > LoPrio {
return 0, ErrPriority
}
// TODO(mwereski): Check if capabilities can be satisfied.
reqs.queue.pushRequest(req)
reqs.requests[req.ID] = req
return req.ID, nil
}
// CloseRequest is part of implementation of Requests interface. It checks that
// request is in WAIT state and changes it to CANCEL or in INPROGRESS state and
// changes it to DONE. NotFoundError may be returned if request with given reqID
// doesn't exist in the queue or ErrModificationForbidden if request is in state
// which can't be closed.
func (reqs *ReqsCollection) CloseRequest(reqID ReqID) error {
req, ok := reqs.requests[reqID]
if !ok {
return NotFoundError("Request")
}
switch req.State {
case WAIT:
req.State = CANCEL
reqs.queue.removeRequest(req)
case INPROGRESS:
req.State = DONE
// TODO(mwereski): release worker
default:
return ErrModificationForbidden
}
return nil
}
// modificationPossible is simple helper function that checks if it is possible
// to modify request it given state.
func modificationPossible(state ReqState) bool {
return state == WAIT
}
// SetRequestPriority is part of implementation of Requests interface. It will
// change priority for given request ID only if modification of request is
// possible. NotFoundError, ErrModificationForbidden or ErrPriority (if given
// priority is out of bounds) may be returned.
func (reqs *ReqsCollection) SetRequestPriority(reqID ReqID, priority Priority) error {
req, ok := reqs.requests[reqID]
if !ok {
return NotFoundError("Request")
}
// TODO(mwereski): Check if user has rights to set given priority.
if priority < HiPrio || priority > LoPrio {
return ErrPriority
}
if !modificationPossible(req.State) {
return ErrModificationForbidden
}
if priority == req.Priority {
return nil
}
reqs.queue.setRequestPriority(req, priority)
req.Priority = priority
return nil
}
// SetRequestValidAfter is part of implementation of Requests interface.
// It changes date after which request will be sent to worker. Provided time is
// converted to UTC. Request must exist, must be in WAIT state and given date
// must be before deadline of request. Otherwise NotFoundError,
// ErrModificationForbidden or ErrInvalidTimeRange will be returned.
func (reqs *ReqsCollection) SetRequestValidAfter(reqID ReqID, validAfter time.Time) error {
req, ok := reqs.requests[reqID]
if !ok {
return NotFoundError("Request")
}
if !modificationPossible(req.State) {
return ErrModificationForbidden
}
if validAfter.After(req.Deadline) && !req.Deadline.IsZero() {
return ErrInvalidTimeRange
}
req.ValidAfter = validAfter
// TODO(mwereski): check if request is ready to go.
return nil
}
// SetRequestDeadline is part of implementation of Requests interface. It changes
// date before which request must be sent to worker. Provided time is converted
// to UTC. Request must exist, must be in WAIT state. Given date must be in the
// future and must be after ValidAfer. In case of not meeting these constrains
// following errors are returned: NotFoundError, ErrModificationForbidden,
// ErrDeadlineInThePast and ErrInvalidTimeRange.
func (reqs *ReqsCollection) SetRequestDeadline(reqID ReqID, deadline time.Time) error {
req, ok := reqs.requests[reqID]
if !ok {
return NotFoundError("Request")
}
if !modificationPossible(req.State) {
return ErrModificationForbidden
}
if !deadline.IsZero() && deadline.Before(time.Now().UTC()) {
return ErrDeadlineInThePast
}
if !deadline.IsZero() && deadline.Before(req.ValidAfter) {
return ErrInvalidTimeRange
}
req.Deadline = deadline
// TODO(mwereski): check if request is ready to go.
return nil
}
// GetRequestInfo is part of implementation of Requests interface. It returns
// ReqInfo for given request ID or NotFoundError if request with given ID doesn't
// exits in the collection.
func (reqs *ReqsCollection) GetRequestInfo(reqID ReqID) (ReqInfo, error) {
req, ok := reqs.requests[reqID]
if !ok {
return ReqInfo{}, NotFoundError("Request")
}
return *req, nil
}
// ListRequests is part of implementation of Requests interface. It returns slice
// of ReqInfo that matches ListFilter.
func (reqs *ReqsCollection) ListRequests(filter ListFilter) ([]ReqInfo, error) {
res := make([]ReqInfo, 0, len(reqs.requests))
for _, req := range reqs.requests {
if filter == nil || filter.Match(req) {
res = append(res, *req)
}
}
return res, nil
}
// AcquireWorker is part of implementation of Requests interface. When worker is
// assigned to the requests then owner of such requests may call AcquireWorker
// to get all information required to use assigned worker.
func (reqs *ReqsCollection) AcquireWorker(reqID ReqID) (AccessInfo, error) {
req, ok := reqs.requests[reqID]
if !ok {
return AccessInfo{}, NotFoundError("Request")
}
if req.State != INPROGRESS || req.Job == nil {
return AccessInfo{}, ErrWorkerNotAssigned
}
// TODO(mwereski): create job and get access info
return AccessInfo{}, nil
}
// ProlongAccess is part of implementation of Requests interface. When owner of
// the request has acquired worker that to extend time for which the worker is
// assigned to the request.
func (reqs *ReqsCollection) ProlongAccess(reqID ReqID) error {
req, ok := reqs.requests[reqID]
if !ok {
return NotFoundError("Request")
}
if req.State != INPROGRESS || req.Job == nil {
return ErrWorkerNotAssigned
}
// TODO(mwereski): prolong access
return nil
}
|