@@ -22,6 +22,7 @@ import (
2222 "os"
2323 "os/exec"
2424 "os/signal"
25+ "path/filepath"
2526 "sort"
2627 "strconv"
2728 "strings"
@@ -35,6 +36,9 @@ func Curl(url string) (string, error) {
3536 return string (res ), err
3637}
3738
39+ const bucketDir = "/mnt/bucket"
40+ const bucketServePath = "/bucket"
41+
3842var GlobalDebug = (os .Getenv ("DEBUG" ) != "" )
3943var envs = []string {}
4044var msg = ""
@@ -56,6 +60,7 @@ func Debug(doit bool, format string, args ...interface{}) {
5660
5761// Just print an cool essage to the Writer that's passed in
5862func PrintMessage (w io.Writer , showAll bool ) {
63+ // http://patorjk.com/software/taag/#p=display&f=Graceful&t=Code%0AEngine
5964 fmt .Fprintf (w , "%s:\n " , msg )
6065 fmt .Fprintln (w , `. ___ __ ____ ____` )
6166 fmt .Fprintln (w , `./ __)/ \( \( __)` )
@@ -84,6 +89,16 @@ func PrintMessage(w io.Writer, showAll bool) {
8489 }
8590 fmt .Fprintf (w , "%s\n " , env )
8691 }
92+
93+ if IsBucketMounted () {
94+ fmt .Fprintf (w , "--------------\n " )
95+ fmt .Fprintf (w , "\n " )
96+ fmt .Fprintf (w , "Deteceted a mounted COS bucket under '%s'.\n " , bucketDir )
97+ fmt .Fprintf (w , "Feel free to explore!\n " )
98+ if ! IsJob () {
99+ fmt .Fprintf (w , "https://%s%s\n " , AppURL (), bucketServePath )
100+ }
101+ }
87102}
88103
89104// This func will handle all incoming HTTP requests
@@ -145,13 +160,82 @@ func HandleHTTP(w http.ResponseWriter, r *http.Request) {
145160 // But if there is a body, echo it back to the client.
146161 if len (body ) == 0 {
147162 w .Header ().Add ("Content-Type" , "text/plain" )
148- // http://patorjk.com/software/taag/#p=display&f=Graceful&t=Code%0AEngine
149163 PrintMessage (w , showAll )
150164 } else {
151165 fmt .Fprintf (w , string (body )+ "\n " )
152166 }
153167}
154168
169+ func IsJob () bool {
170+ // If we're being run as a Batch Jobthen the JOB_INDEX env var will be set.
171+ return os .Getenv ("JOB_INDEX" ) != ""
172+ }
173+
174+ func AppURL () string {
175+ return fmt .Sprintf ("%s.%s.%s" , os .Getenv ("CE_APP" ), os .Getenv ("CE_SUBDOMAIN" ), os .Getenv ("CE_DOMAIN" ))
176+ }
177+
178+ func IsBucketMounted () bool {
179+ info , err := os .Stat (bucketDir )
180+ return err == nil && info .IsDir ()
181+ }
182+
183+ func NewBucketRouter () http.Handler {
184+ mux := http .NewServeMux ()
185+
186+ mux .HandleFunc ("/" , BucketLandingPageHandler )
187+ mux .HandleFunc ("/upload" , FileUploadHandler )
188+
189+ fileServer := http .FileServer (http .Dir (bucketDir ))
190+ mux .Handle ("/files/" , http .StripPrefix ("/files/" , fileServer ))
191+
192+ return http .StripPrefix (bucketServePath , mux )
193+ }
194+
195+ func BucketLandingPageHandler (w http.ResponseWriter , r * http.Request ) {
196+ w .Header ().Set ("Content-Type" , "text/html; charset=utf-8" )
197+ fmt .Fprintf (w , `
198+ <h2>Bucket Explorer</h2>
199+ <p>Use this form to upload files to the bucket mounted at '%s'.</p>
200+ <form action="%s/upload" method="post" enctype="multipart/form-data">
201+ <input type="file" name="file" id="file">
202+ <input type="submit" value="Upload File">
203+ </form>
204+ <br>
205+ <h3><a href="%s/files/">Browse Uploaded Files</a></h3>
206+ ` , bucketDir , bucketServePath , bucketServePath )
207+ }
208+
209+ func FileUploadHandler (w http.ResponseWriter , r * http.Request ) {
210+ if r .Method != http .MethodPost {
211+ http .Error (w , "Method not allowed" , http .StatusMethodNotAllowed )
212+ return
213+ }
214+
215+ file , handler , err := r .FormFile ("file" )
216+ if err != nil {
217+ http .Error (w , "Error retrieving the file" , http .StatusBadRequest )
218+ return
219+ }
220+ defer file .Close ()
221+
222+ dstPath := filepath .Join (bucketDir , handler .Filename )
223+ dst , err := os .Create (dstPath )
224+ if err != nil {
225+ http .Error (w , "Unable to create the file for writing" , http .StatusInternalServerError )
226+ return
227+ }
228+ defer dst .Close ()
229+
230+ if _ , err := io .Copy (dst , file ); err != nil {
231+ http .Error (w , "Unable to copy file content" , http .StatusInternalServerError )
232+ return
233+ }
234+
235+ Debug (false , "Successfully uploaded file: %s" , handler .Filename )
236+ http .Redirect (w , r , bucketServePath , http .StatusSeeOther )
237+ }
238+
155239func main () {
156240 ctx := context .Background ()
157241 signals := make (chan os.Signal , 1 )
@@ -208,12 +292,10 @@ func main() {
208292 Debug (false , "Envs:\n %s" , strings .Join (envs , "\n " ))
209293
210294 // Real work.
211- // If we're being run as a Batch Jobthen the JOB_INDEX env var
212- // will be set. In which case, just print the message to stdout.
295+ // If we're being run as a Batch Job just print the message to stdout.
213296 // Otherwise we're an App and we need to start the HTTP server
214297 // to processing incoming requests
215- if jobIndex := os .Getenv ("JOB_INDEX" ); jobIndex != "" {
216-
298+ if IsJob () {
217299 // Jobs can be either started in 'task' mode and run to completion or in 'daemon' mode which
218300 jobMode := os .Getenv ("JOB_MODE" )
219301
@@ -234,7 +316,7 @@ func main() {
234316 time .Sleep (time .Duration (sleepDuration ) * time .Second )
235317 }
236318
237- fmt .Printf ("Hello from helloworld! I'm a %s job! Index: %s of %s\n \n " , jobMode , jobIndex , os .Getenv ("JOB_ARRAY_SIZE" ))
319+ fmt .Printf ("Hello from helloworld! I'm a %s job! Index: %s of %s\n \n " , jobMode , os . Getenv ( "JOB_INDEX" ) , os .Getenv ("JOB_ARRAY_SIZE" ))
238320 PrintMessage (os .Stdout , os .Getenv ("SHOW" ) == "" )
239321
240322 // If the 'CRASH' or 'FAIL' env vars are set then crash!
@@ -255,6 +337,10 @@ func main() {
255337 // Debug the http handler for all requests
256338 http .HandleFunc ("/" , HandleHTTP )
257339
340+ if IsBucketMounted () {
341+ http .Handle (bucketServePath + "/" , NewBucketRouter ())
342+ }
343+
258344 // HTTP_DELAY will pause for 'delay' seconds before starting the
259345 // HTTP server. This is useful for simulating a long readiness probe
260346 if delay := os .Getenv ("HTTP_DELAY" ); delay != "" {
0 commit comments