func downloadFile(url string, pbar progress.Meter) (fn string, err error) { name := "classic" w, err := ioutil.TempFile("", name) if err != nil { return "", err } defer func() { if err != nil { os.Remove(w.Name()) } }() defer w.Close() req, err := http.NewRequest("GET", url, nil) if err != nil { return "", err } client := &http.Client{} resp, err := client.Do(req) if err != nil { return "", err } defer resp.Body.Close() if resp.StatusCode != 200 { return "", fmt.Errorf("failed to download %s: %v", url, resp.StatusCode) } if pbar != nil { pbar.Start(name, float64(resp.ContentLength)) mw := io.MultiWriter(w, pbar) _, err = io.Copy(mw, resp.Body) pbar.Finished() } else { _, err = io.Copy(w, resp.Body) } return w.Name(), err }
// download writes an http.Request showing a progress.Meter func download(name string, w io.Writer, req *http.Request, pbar progress.Meter) error { client := &http.Client{} resp, err := client.Do(req) if err != nil { return err } defer resp.Body.Close() if resp.StatusCode != 200 { return &ErrDownload{Code: resp.StatusCode, URL: req.URL} } if pbar != nil { pbar.Start(name, float64(resp.ContentLength)) mw := io.MultiWriter(w, pbar) _, err = io.Copy(mw, resp.Body) pbar.Finished() } else { _, err = io.Copy(w, resp.Body) } return err }
// Install installs the snap func (s *SystemImagePart) Install(pb progress.Meter, flags InstallFlags) (name string, err error) { if provisioning.IsSideLoaded(bootloaderDir()) { return "", ErrSideLoaded } if pb != nil { // ensure the progress finishes when we are done defer func() { pb.Finished() }() } // Ensure there is always a kernel + initrd to boot with, even // if the update does not provide new versions. if s.needsBootAssetSync() { if pb != nil { pb.Notify("Syncing boot files") } err = s.partition.SyncBootloaderFiles(bootAssetFilePaths()) if err != nil { return "", err } } // find out what config file to use, the other partition may be // empty so we need to fallback to the current one if it is configFile := systemImageClientConfig err = s.partition.RunWithOther(partition.RO, func(otherRoot string) (err error) { // XXX: Note that systemImageDownloadUpdate() requires // the s-i _client_ config file whereas otherIsEmpty() // checks the s-i _channel_ config file. otherConfigFile := filepath.Join(dirs.GlobalRootDir, otherRoot, systemImageClientConfig) if !otherIsEmpty(otherRoot) && helpers.FileExists(otherConfigFile) { configFile = otherConfigFile } // NOTE: we need to pass the config dir here configDir := filepath.Dir(configFile) return systemImageDownloadUpdate(configDir, pb) }) if err != nil { return "", err } // Check that the final system state is as expected. if err = s.verifyUpgradeWasApplied(); err != nil { return "", err } // XXX: ToggleNextBoot() calls handleAssets() (but not SyncBootloader // files :/) - handleAssets() may copy kernel/initramfs to the // sync mounted /boot/uboot, so its very slow, tell the user // at least that something is going on if pb != nil { pb.Notify("Updating boot files") } if err = s.partition.ToggleNextBoot(); err != nil { return "", err } return SystemImagePartName, nil }