~mvo/snappy/snappy-sync-bootloader-udf

« back to all changes in this revision

Viewing changes to snappy/systemimage_native.go

  • Committer: Snappy Tarmac
  • Author(s): Michael Vogt
  • Date: 2015-03-03 14:51:40 UTC
  • mfrom: (189.2.18 snappy-native-si-info)
  • Revision ID: snappy_tarmac-20150303145140-h8shvkgll5x7dsbk
Remove dbus dependency for system image updates by mvo approved by sergiusens

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
package snappy
 
2
 
 
3
import (
 
4
        "bufio"
 
5
        "encoding/json"
 
6
        "fmt"
 
7
        "io"
 
8
        "io/ioutil"
 
9
        "net/http"
 
10
        "os"
 
11
        "os/exec"
 
12
        "path"
 
13
        "strings"
 
14
        "time"
 
15
 
 
16
        "github.com/mvo5/goconfigparser"
 
17
 
 
18
        "launchpad.net/snappy/helpers"
 
19
)
 
20
 
 
21
var systemImageServer = "https://system-image.ubuntu.com/"
 
22
 
 
23
type updateStatus struct {
 
24
        targetVersion        string
 
25
        targetVersionDetails string
 
26
        updateSize           int64
 
27
        lastUpdate           time.Time
 
28
}
 
29
 
 
30
type channelImage struct {
 
31
        Descripton     string              `json:"description,omitempty"`
 
32
        Type           string              `json:"type, omitempty"`
 
33
        Version        int                 `json:"version, omitempty"`
 
34
        VersionDetails string              `json:"version_detail, omitempty"`
 
35
        Files          []channelImageFiles `json:"files"`
 
36
}
 
37
 
 
38
type channelImageFiles struct {
 
39
        Size int64 `json:"size"`
 
40
}
 
41
 
 
42
type channelImageGlobal struct {
 
43
        GeneratedAt string `json:"generated_at"`
 
44
}
 
45
 
 
46
type channelJSON struct {
 
47
        Global channelImageGlobal `json:"global"`
 
48
        Images []channelImage     `json:"images"`
 
49
}
 
50
 
 
51
func systemImageClientCheckForUpdates(configFile string) (us updateStatus, err error) {
 
52
        cfg := goconfigparser.New()
 
53
        if err := cfg.ReadFile(configFile); err != nil {
 
54
                return us, err
 
55
        }
 
56
        channel, _ := cfg.Get("service", "channel")
 
57
        device, _ := cfg.Get("service", "device")
 
58
 
 
59
        indexURL := systemImageServer + "/" + path.Join(channel, device, "index.json")
 
60
 
 
61
        resp, err := http.Get(indexURL)
 
62
        if err != nil {
 
63
                return us, err
 
64
        }
 
65
        defer resp.Body.Close()
 
66
 
 
67
        if resp.StatusCode != 200 {
 
68
                return us, fmt.Errorf("systemImageDbusProxy: unexpected http statusCode %v for %s", resp.StatusCode, indexURL)
 
69
        }
 
70
 
 
71
        // and decode json
 
72
        var channelData channelJSON
 
73
        dec := json.NewDecoder(resp.Body)
 
74
        if err := dec.Decode(&channelData); err != nil {
 
75
                return us, err
 
76
        }
 
77
 
 
78
        // global property
 
79
        us.lastUpdate, _ = time.Parse("Mon Jan 2 15:04:05 MST 2006", channelData.Global.GeneratedAt)
 
80
 
 
81
        // FIXME: find latest image of type "full" here
 
82
        latestImage := channelData.Images[len(channelData.Images)-1]
 
83
        us.targetVersion = fmt.Sprintf("%d", latestImage.Version)
 
84
        us.targetVersionDetails = latestImage.VersionDetails
 
85
 
 
86
        // FIXME: this is not accurate right now as it does not take
 
87
        //        the deltas into account
 
88
        for _, f := range latestImage.Files {
 
89
                us.updateSize += f.Size
 
90
        }
 
91
 
 
92
        return us, nil
 
93
}
 
94
 
 
95
type genericJSON struct {
 
96
        Type    string  `json:"type, omitempty"`
 
97
        Message string  `json:"msg, omitempty"`
 
98
        Now     float64 `json:"now, omitempty"`
 
99
        Total   float64 `json:"total, omitempty"`
 
100
}
 
101
 
 
102
func systemImageDownloadUpdate(configFile string, pb ProgressMeter) (err error) {
 
103
        cmd := exec.Command(systemImageCli, "--machine-readable", "-C", configFile)
 
104
 
 
105
        // collect progress over stdout
 
106
        var stdout io.Reader
 
107
        if pb != nil {
 
108
                stdout, err = cmd.StdoutPipe()
 
109
                if err != nil {
 
110
                        return err
 
111
                }
 
112
        }
 
113
 
 
114
        // collect error message (traceback etc)
 
115
        stderr, err := cmd.StderrPipe()
 
116
        if err != nil {
 
117
                return err
 
118
        }
 
119
        stderrCh := make(chan []byte)
 
120
        go func() {
 
121
                stderrContent, _ := ioutil.ReadAll(stderr)
 
122
                stderrCh <- stderrContent
 
123
        }()
 
124
 
 
125
        // run it
 
126
        if err := cmd.Start(); err != nil {
 
127
                return err
 
128
        }
 
129
 
 
130
        // and parse progress
 
131
        if pb != nil {
 
132
                scanner := bufio.NewScanner(stdout)
 
133
                // s-i is funny, total changes during the runs
 
134
                total := 0.0
 
135
                pb.Start(100)
 
136
 
 
137
                for scanner.Scan() {
 
138
                        if os.Getenv("SNAPPY_DEBUG") != "" {
 
139
                                fmt.Println(scanner.Text())
 
140
                        }
 
141
 
 
142
                        jsonStream := strings.NewReader(scanner.Text())
 
143
                        dec := json.NewDecoder(jsonStream)
 
144
                        var genericData genericJSON
 
145
                        if err := dec.Decode(&genericData); err != nil {
 
146
                                continue
 
147
                        }
 
148
 
 
149
                        switch {
 
150
                        case genericData.Type == "spinner":
 
151
                                pb.Spin(genericData.Message)
 
152
                        case genericData.Type == "error":
 
153
                                return fmt.Errorf("error from %s: %s", systemImageCli, genericData.Message)
 
154
                        case genericData.Type == "progress":
 
155
                                if total != genericData.Total {
 
156
                                        total = genericData.Total
 
157
                                        pb.SetTotal(total)
 
158
                                }
 
159
                                pb.Set(genericData.Now)
 
160
                        }
 
161
                }
 
162
 
 
163
                if err := scanner.Err(); err != nil {
 
164
                        return err
 
165
                }
 
166
        }
 
167
 
 
168
        if err := cmd.Wait(); err != nil {
 
169
                stderrContent := <-stderrCh
 
170
                retCode, _ := helpers.ExitCode(err)
 
171
                return fmt.Errorf("%s failed with return code %v: %s", systemImageCli, retCode, string(stderrContent))
 
172
        }
 
173
 
 
174
        return err
 
175
}