add "download consul binary" task
[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) String() string {
24         return fmt.Sprintf("Download %q from %q", d.Dest, d.URL)
25 }
26
27 func (d *download) Check() error {
28         fi, err := os.Stat(d.Dest)
29         if err != nil {
30                 return err
31         }
32         if d.Size > 0 && fi.Size() != d.Size {
33                 return fmt.Errorf("Size mismatch: %q is %d bytes, expected %d", d.Dest, fi.Size(), d.Size)
34         }
35         if d.Mode > 0 && fi.Mode() != d.Mode {
36                 return fmt.Errorf("Mode mismatch: %q is %s, expected %s", d.Dest, fi.Mode(), d.Mode)
37         }
38         return nil
39 }
40
41 func (d *download) CanFix() bool {
42         return true
43 }
44
45 func (d *download) Fix() error {
46         out, err := ioutil.TempFile(path.Dir(d.Dest), path.Base(d.Dest))
47         if err != nil {
48                 return err
49         }
50         defer func() {
51                 if out != nil {
52                         os.Remove(out.Name())
53                         out.Close()
54                 }
55         }()
56
57         resp, err := http.Get(d.URL)
58         if err != nil {
59                 return err
60         }
61         n, err := io.Copy(out, resp.Body)
62         resp.Body.Close()
63         if err != nil {
64                 return err
65         }
66
67         if strings.HasSuffix(d.URL, ".zip") && !strings.HasSuffix(d.Dest, ".zip") {
68                 r, err := zip.NewReader(out, n)
69                 if err != nil {
70                         return err
71                 }
72                 defer os.Remove(out.Name())
73                 out = nil
74
75                 found := false
76                 for _, f := range r.File {
77                         if !strings.HasSuffix(d.Dest, "/"+f.Name) {
78                                 continue
79                         }
80                         rc, err := f.Open()
81                         if err != nil {
82                                 return err
83                         }
84                         defer rc.Close()
85
86                         out, err = ioutil.TempFile(path.Dir(d.Dest), path.Base(d.Dest))
87                         if err != nil {
88                                 return err
89                         }
90
91                         n, err = io.Copy(out, rc)
92                         if err != nil {
93                                 return err
94                         }
95                         found = true
96                         break
97                 }
98                 if !found {
99                         return fmt.Errorf("File not found in archive")
100                 }
101         }
102
103         if d.Size > 0 && d.Size != n {
104                 return fmt.Errorf("Size mismatch: got %d bytes, expected %d", n, d.Size)
105         } else if d.Size == 0 {
106                 log.Printf("%s: size was %d", d, n)
107         }
108         if err = out.Close(); err != nil {
109                 return err
110         }
111         if err = os.Chmod(out.Name(), d.Mode); err != nil {
112                 return err
113         }
114         err = os.Rename(out.Name(), d.Dest)
115         if err == nil {
116                 // skip deferred os.Remove(out.Name())
117                 out = nil
118         }
119         return err
120 }