@@ -19,16 +19,17 @@ import (
19
19
"context"
20
20
"encoding/json"
21
21
"fmt"
22
+ "io"
22
23
"net/http"
23
24
"runtime"
24
25
"sync"
25
26
"time"
26
27
27
28
eiffelevents "github.com/eiffel-community/eiffelevents-sdk-go"
28
29
config "github.com/eiffel-community/etos-api/internal/configs/iut"
30
+ "github.com/eiffel-community/etos-api/internal/database"
29
31
"github.com/eiffel-community/etos-api/pkg/application"
30
32
packageurl "github.com/package-url/packageurl-go"
31
- clientv3 "go.etcd.io/etcd/client/v3"
32
33
33
34
"github.com/google/uuid"
34
35
"github.com/julienschmidt/httprouter"
@@ -38,14 +39,14 @@ import (
38
39
type V1Alpha1Application struct {
39
40
logger * logrus.Entry
40
41
cfg config.Config
41
- database * clientv3. Client
42
+ database database. Opener
42
43
wg * sync.WaitGroup
43
44
}
44
45
45
46
type V1Alpha1Handler struct {
46
47
logger * logrus.Entry
47
48
cfg config.Config
48
- database * clientv3. Client
49
+ database database. Opener
49
50
wg * sync.WaitGroup
50
51
}
51
52
@@ -71,11 +72,11 @@ func (a *V1Alpha1Application) Close() {
71
72
}
72
73
73
74
// New returns a new V1Alpha1Application object/struct
74
- func New (cfg config.Config , log * logrus.Entry , ctx context.Context , cli * clientv3. Client ) application.Application {
75
+ func New (cfg config.Config , log * logrus.Entry , ctx context.Context , db database. Opener ) application.Application {
75
76
return & V1Alpha1Application {
76
77
logger : log ,
77
78
cfg : cfg ,
78
- database : cli ,
79
+ database : db ,
79
80
wg : & sync.WaitGroup {},
80
81
}
81
82
}
@@ -120,25 +121,30 @@ type StatusRequest struct {
120
121
Id uuid.UUID `json:"id"`
121
122
}
122
123
123
- type StopRequest struct {
124
- Id uuid.UUID `json:"id"`
125
- }
126
-
127
124
// Start creates a number of IUTs and stores them in the ETCD database returning a checkout ID.
128
125
func (h V1Alpha1Handler ) Start (w http.ResponseWriter , r * http.Request , ps httprouter.Params ) {
126
+ identifier , err := uuid .Parse (r .Header .Get ("X-Etos-Id" ))
127
+ logger := h .logger .WithField ("identifier" , identifier ).WithContext (r .Context ())
128
+ if err != nil {
129
+ RespondWithError (w , http .StatusInternalServerError , err .Error ())
130
+ return
131
+ }
129
132
checkOutID := uuid .New ()
130
133
131
134
w .Header ().Set ("X-Content-Type-Options" , "nosniff" )
132
135
w .Header ().Set ("Content-Type" , "application/json" )
133
136
134
137
var startReq StartRequest
135
138
if err := json .NewDecoder (r .Body ).Decode (& startReq ); err != nil {
139
+ logger .Errorf ("Failed to decode request body: %s" , r .Body )
136
140
RespondWithError (w , http .StatusBadRequest , err .Error ())
137
141
return
138
142
}
139
143
defer r .Body .Close ()
140
144
purl , err := packageurl .FromString (startReq .ArtifactIdentity )
145
+
141
146
if err != nil {
147
+ logger .Errorf ("Failed to create a purl struct from artifact identity: %s" , startReq .ArtifactIdentity )
142
148
RespondWithError (w , http .StatusBadRequest , err .Error ())
143
149
return
144
150
}
@@ -149,74 +155,86 @@ func (h V1Alpha1Handler) Start(w http.ResponseWriter, r *http.Request, ps httpro
149
155
}
150
156
iuts , err := json .Marshal (purls )
151
157
if err != nil {
158
+ logger .Errorf ("Failed to marshal purls: %s" , purls )
152
159
RespondWithError (w , http .StatusInternalServerError , err .Error ())
153
160
return
154
161
}
155
- _ , err = h .database .Put (r .Context (), fmt .Sprintf ("/iut/%s" , checkOutID .String ()), string (iuts ))
162
+ client := h .database .Open (r .Context (), identifier )
163
+ _ , err = client .Write ([]byte (string (iuts )))
156
164
if err != nil {
165
+ logger .Errorf ("Failed to write to database: %s" , string (iuts ))
157
166
RespondWithError (w , http .StatusInternalServerError , err .Error ())
158
167
return
159
168
}
160
169
startResp := StartResponse {Id : checkOutID }
170
+ logger .Debugf ("Start response: %s" , startResp )
161
171
w .WriteHeader (http .StatusOK )
162
172
response , _ := json .Marshal (startResp )
163
173
_ , _ = w .Write (response )
164
174
}
165
175
166
176
// Status creates a simple DONE Status response with IUTs.
167
177
func (h V1Alpha1Handler ) Status (w http.ResponseWriter , r * http.Request , ps httprouter.Params ) {
168
- identifier := r .Header .Get ("X-Etos-Id" )
178
+ identifier , err := uuid .Parse (r .Header .Get ("X-Etos-Id" ))
179
+ if err != nil {
180
+ RespondWithError (w , http .StatusInternalServerError , err .Error ())
181
+ }
169
182
logger := h .logger .WithField ("identifier" , identifier ).WithContext (r .Context ())
170
183
171
184
id , err := uuid .Parse (r .URL .Query ().Get ("id" ))
185
+ client := h .database .Open (r .Context (), identifier )
186
+
187
+ data , err := io .ReadAll (client )
172
188
173
- key := fmt .Sprintf ("/iut/%s" , id )
174
- dbResp , err := h .database .Get (r .Context (), key )
175
189
if err != nil {
176
- logger .Errorf ("Failed to look up status request id: %s" , id )
177
- RespondWithError (w , http .StatusInternalServerError , err .Error ())
178
- return
179
- }
180
- if len (dbResp .Kvs ) == 0 {
181
- err = fmt .Errorf ("No key found: %s" , key )
190
+ logger .Errorf ("Failed to look up status request id: %s, %s" , identifier , err .Error ())
182
191
RespondWithError (w , http .StatusInternalServerError , err .Error ())
183
192
return
184
193
}
185
194
statusResp := StatusResponse {
186
195
Id : id ,
187
196
Status : "DONE" ,
188
197
}
189
- if err = json .Unmarshal (dbResp .Kvs [0 ].Value , & statusResp .Iuts ); err != nil {
198
+ if err = json .Unmarshal (data , & statusResp .Iuts ); err != nil {
199
+ logger .Errorf ("Failed to unmarshal data: %s" , data )
190
200
RespondWithError (w , http .StatusInternalServerError , err .Error ())
191
201
return
192
202
}
193
203
response , err := json .Marshal (statusResp )
194
204
if err != nil {
205
+ logger .Errorf ("Failed to marshal status response: %s" , statusResp )
195
206
RespondWithError (w , http .StatusInternalServerError , err .Error ())
196
207
return
197
208
}
209
+ logger .Debugf ("Status response: %s" , statusResp )
198
210
w .WriteHeader (http .StatusOK )
199
211
_ , _ = w .Write (response )
212
+
200
213
}
201
214
202
215
// Stop deletes the given IUTs from the database and returns an empty response.
203
216
func (h V1Alpha1Handler ) Stop (w http.ResponseWriter , r * http.Request , ps httprouter.Params ) {
204
- identifier := r .Header .Get ("X-Etos-Id" )
217
+ identifier , err := uuid .Parse (r .Header .Get ("X-Etos-Id" ))
218
+ if err != nil {
219
+ RespondWithError (w , http .StatusInternalServerError , err .Error ())
220
+ }
205
221
logger := h .logger .WithField ("identifier" , identifier ).WithContext (r .Context ())
206
222
207
- var stopReq StopRequest
208
- defer r .Body .Close ()
209
- if err := json .NewDecoder (r .Body ).Decode (& stopReq ); err != nil {
210
- logger .Errorf ("Bad delete request: %s" , err .Error ())
211
- RespondWithError (w , http .StatusBadRequest , err .Error ())
212
- return
223
+ client := h .database .Open (r .Context (), identifier )
224
+ deleter , canDelete := client .(database.Deleter )
225
+ if ! canDelete {
226
+ logger .Warning ("The database does not support delete. Writing nil." )
227
+ _ , err = client .Write (nil )
228
+ } else {
229
+ err = deleter .Delete ()
213
230
}
214
- _ , err := h . database . Delete ( r . Context (), fmt . Sprintf ( "/iut/%s" , stopReq . Id ))
231
+
215
232
if err != nil {
216
- logger .Errorf ("Etcd delete failed: %s" , err .Error ())
233
+ logger .Errorf ("Database delete failed: %s" , err .Error ())
217
234
RespondWithError (w , http .StatusInternalServerError , err .Error ())
218
235
return
219
236
}
237
+ logger .Debugf ("Stop request succeeded" )
220
238
w .WriteHeader (http .StatusNoContent )
221
239
}
222
240
0 commit comments