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