1
1
package main
2
2
3
3
import (
4
+ "bytes"
4
5
"errors"
5
6
"flag"
6
7
"fmt"
@@ -11,17 +12,18 @@ import (
11
12
"os"
12
13
"path/filepath"
13
14
"strings"
15
+ "time"
14
16
17
+ "github.com/barelyhuman/go/env"
15
18
"github.com/barelyhuman/goblin/build"
16
19
"github.com/barelyhuman/goblin/resolver"
20
+ "github.com/barelyhuman/goblin/storage"
17
21
"github.com/joho/godotenv"
18
22
)
19
23
20
24
var shTemplates * template.Template
21
25
var serverURL string
22
-
23
- // FIXME: Disabled storage and caching for initial version
24
- // var storageClient *storage.Storage
26
+ var storageClient storage.Storage
25
27
26
28
func HandleRequest (rw http.ResponseWriter , req * http.Request ) {
27
29
path := req .URL .Path
@@ -40,6 +42,7 @@ func HandleRequest(rw http.ResponseWriter, req *http.Request) {
40
42
}
41
43
42
44
if strings .HasPrefix (path , "/binary" ) {
45
+ log .Print ("handle binary" )
43
46
fetchBinary (rw , req )
44
47
return
45
48
}
@@ -63,20 +66,12 @@ func StartServer(port string) {
63
66
}
64
67
}
65
68
66
- func envDefault (key string , def string ) string {
67
- if s := os .Getenv (key ); len (strings .TrimSpace (s )) == 0 {
68
- return def
69
- } else {
70
- return s
71
- }
72
- }
73
-
74
69
// TODO: cleanup code
75
70
// TODO: move everything into their own interface/structs
76
71
func main () {
77
72
78
73
envFile := flag .String ("env" , ".env" , "path to read the env config from" )
79
- portFlag := envDefault ("PORT" , "3000" )
74
+ portFlag := env . Get ("PORT" , "3000" )
80
75
81
76
flag .Parse ()
82
77
@@ -88,19 +83,64 @@ func main() {
88
83
}
89
84
90
85
shTemplates = template .Must (template .ParseGlob ("templates/*" ))
91
- serverURL = envDefault ("ORIGIN_URL" , "http://localhost:3000" )
86
+ serverURL = env . Get ("ORIGIN_URL" , "http://localhost:" + portFlag )
92
87
93
- // FIXME: Disabled storage and caching for initial version
94
- // storageClient = & storage.Storage{}
95
- // storageClient.BucketName = os.Getenv("BUCKET_NAME" )
96
- // err := storageClient.Connect()
97
- // if err != nil {
98
- // log.Fatal(err)
99
- // }
88
+ if isStorageEnabled () {
89
+ storageClient = storage .NewAWSStorage ( env . Get ( "STORAGE_BUCKET" , "goblin-cache" ))
90
+ err := storageClient . Connect ( )
91
+ if err != nil {
92
+ log . Fatal ( err )
93
+ }
94
+ }
100
95
96
+ clearStorageBackgroundJob ()
101
97
StartServer (portFlag )
102
98
}
103
99
100
+ func clearStorageBackgroundJob () {
101
+ cacheHoldEnv := env .Get ("CLEAR_CACHE_TIME" , "" )
102
+ if len (cacheHoldEnv ) == 0 {
103
+ return
104
+ }
105
+
106
+ cacheHoldDuration , _ := time .ParseDuration (cacheHoldEnv )
107
+
108
+ cleaner := func (storageClient storage.Storage ) {
109
+ log .Println ("Cleaning Cached Storage Object" )
110
+ objects := storageClient .ListObjects ()
111
+ for _ , obj := range objects {
112
+ objExpiry := obj .LastModified .Add (cacheHoldDuration )
113
+ if time .Now ().Equal (objExpiry ) || time .Now ().After (objExpiry ) {
114
+ storageClient .RemoveObject (obj .Key )
115
+ }
116
+ }
117
+ }
118
+
119
+ ticker := time .NewTicker (cacheHoldDuration )
120
+ quit := make (chan struct {})
121
+
122
+ go func () {
123
+ for {
124
+ select {
125
+ case <- ticker .C :
126
+ cleaner (storageClient )
127
+ case <- quit :
128
+ ticker .Stop ()
129
+ return
130
+ }
131
+ }
132
+ }()
133
+ }
134
+
135
+ func isStorageEnabled () bool {
136
+ useStorageEnv := env .Get ("STORAGE_ENABLED" , "false" )
137
+ useStorage := false
138
+ if useStorageEnv == "true" {
139
+ useStorage = true
140
+ }
141
+ return useStorage
142
+ }
143
+
104
144
func normalizePackage (pkg string ) string {
105
145
// strip leading protocol
106
146
pkg = strings .Replace (pkg , "https://" , "" , 1 )
@@ -228,38 +268,49 @@ func fetchBinary(rw http.ResponseWriter, req *http.Request) {
228
268
Module : mod ,
229
269
}
230
270
231
- // TODO: check the storage for existing binary for the module
232
- // and return from the storage instead
233
-
234
271
immutable (rw )
235
272
236
- // FIXME: Disabled storage and caching for initial version
237
- // var buf bytes.Buffer
238
- // err := bin.WriteBuild(io.MultiWriter(rw, &buf))
273
+ artifactName := constructArtifactName (bin )
239
274
240
- err := bin .WriteBuild (io .MultiWriter (rw ))
275
+ if isStorageEnabled () && storageClient .HasObject (artifactName ) {
276
+ url , _ := storageClient .GetSignedURL (artifactName )
277
+ log .Println ("From cache" )
278
+ http .Redirect (rw , req , url , http .StatusSeeOther )
279
+ return
280
+ }
281
+
282
+ var buf bytes.Buffer
283
+ err := bin .WriteBuild (io .MultiWriter (rw , & buf ))
241
284
242
285
if err != nil {
243
286
rw .WriteHeader (http .StatusInternalServerError )
244
287
fmt .Fprint (rw , err .Error ())
245
288
return
246
289
}
247
290
291
+ if isStorageEnabled () {
292
+ err = storageClient .Upload (
293
+ artifactName ,
294
+ buf ,
295
+ )
296
+
297
+ if err != nil {
298
+ log .Println ("Failed to upload" , err )
299
+ }
300
+ }
301
+
248
302
err = bin .Cleanup ()
249
303
if err != nil {
250
304
log .Println ("cleaning binary build" , err )
251
305
}
306
+ }
252
307
253
- // FIXME: Disabled storage and caching for initial version
254
- // err = storageClient.Upload(bin.Module, bin.Dest)
255
- // if err != nil {
256
- // fmt.Fprint(rw, err.Error())
257
- // return
258
- // }
259
-
260
- // url, err := storageClient.GetSignedURL(bin.Module, bin.Name)
261
- // if err != nil {
262
- // fmt.Fprint(rw, err.Error())
263
- // return
264
- // }
308
+ func constructArtifactName (bin * build.Binary ) string {
309
+ var artifactName strings.Builder
310
+ artifactName .Write ([]byte (bin .Name ))
311
+ artifactName .Write ([]byte ("-" ))
312
+ artifactName .Write ([]byte (bin .OS ))
313
+ artifactName .Write ([]byte ("-" ))
314
+ artifactName .Write ([]byte (bin .Arch ))
315
+ return artifactName .String ()
265
316
}
0 commit comments