add gateway (part)
[arvados.git] / services / boot / download.go
1 package main
2
3 import (
4         "archive/zip"
5         "context"
6         "fmt"
7         "io"
8         "io/ioutil"
9         "log"
10         "net/http"
11         "os"
12         "path"
13         "strings"
14 )
15
16 type download struct {
17         URL  string
18         Dest string
19         Size int64
20         Mode os.FileMode
21         Hash string
22 }
23
24 func (d *download) Boot(ctx context.Context) error {
25         fi, err := os.Stat(d.Dest)
26         if os.IsNotExist(err) {
27                 // fall through to fix
28         } else if err != nil {
29                 return err
30         } else if d.Size > 0 && fi.Size() != d.Size {
31                 err = fmt.Errorf("Size mismatch: %q is %d bytes, expected %d", d.Dest, fi.Size(), d.Size)
32         } else if d.Mode > 0 && fi.Mode() != d.Mode {
33                 err = fmt.Errorf("Mode mismatch: %q is %s, expected %s", d.Dest, fi.Mode(), d.Mode)
34         } else {
35                 return nil
36         }
37
38         defer feedbackf(ctx, "downloading %s", d.URL)()
39
40         out, err := ioutil.TempFile(path.Dir(d.Dest), path.Base(d.Dest))
41         if err != nil {
42                 return err
43         }
44         defer func() {
45                 if out != nil {
46                         os.Remove(out.Name())
47                         out.Close()
48                 }
49         }()
50
51         resp, err := http.Get(d.URL)
52         if err != nil {
53                 return err
54         }
55         n, err := io.Copy(out, resp.Body)
56         resp.Body.Close()
57         if err != nil {
58                 return err
59         }
60
61         if strings.HasSuffix(d.URL, ".zip") && !strings.HasSuffix(d.Dest, ".zip") {
62                 r, err := zip.NewReader(out, n)
63                 if err != nil {
64                         return err
65                 }
66                 defer os.Remove(out.Name())
67                 out = nil
68
69                 found := false
70                 for _, f := range r.File {
71                         if !strings.HasSuffix(d.Dest, "/"+f.Name) {
72                                 continue
73                         }
74                         rc, err := f.Open()
75                         if err != nil {
76                                 return err
77                         }
78                         defer rc.Close()
79
80                         out, err = ioutil.TempFile(path.Dir(d.Dest), path.Base(d.Dest))
81                         if err != nil {
82                                 return err
83                         }
84
85                         n, err = io.Copy(out, rc)
86                         if err != nil {
87                                 return err
88                         }
89                         found = true
90                         break
91                 }
92                 if !found {
93                         return fmt.Errorf("File not found in archive")
94                 }
95         }
96
97         if d.Size > 0 && d.Size != n {
98                 return fmt.Errorf("Size mismatch: got %d bytes, expected %d", n, d.Size)
99         } else if d.Size == 0 {
100                 log.Printf("%s: size was %d", d, n)
101         }
102         if err = out.Close(); err != nil {
103                 return err
104         }
105         if err = os.Chmod(out.Name(), d.Mode); err != nil {
106                 return err
107         }
108         err = os.Rename(out.Name(), d.Dest)
109         if err == nil {
110                 // skip deferred os.Remove(out.Name())
111                 out = nil
112         }
113         return err
114 }