// DownloadSnap downloads the snap with the given name and optionally revision using the provided store and options. It returns the final full path of the snap inside the opts.TargetDir and a snap.Info for the snap. func DownloadSnap(sto Store, name string, revision snap.Revision, opts *DownloadOptions) (targetFn string, info *snap.Info, err error) { if opts == nil { opts = &DownloadOptions{} } targetDir := opts.TargetDir if targetDir == "" { pwd, err := os.Getwd() if err != nil { return "", nil, err } targetDir = pwd } snap, err := sto.Snap(name, opts.Channel, opts.DevMode, revision, opts.User) if err != nil { return "", nil, fmt.Errorf("cannot find snap %q: %v", name, err) } baseName := filepath.Base(snap.MountFile()) targetFn = filepath.Join(targetDir, baseName) pb := progress.NewTextProgress() if err = sto.Download(context.TODO(), name, targetFn, &snap.DownloadInfo, pb, opts.User); err != nil { return "", nil, err } return targetFn, snap, nil }
func wait(cli *client.Client, id string) (*client.Change, error) { pb := progress.NewTextProgress() defer func() { pb.Finished() }() tMax := time.Time{} var lastID string lastLog := map[string]string{} for { chg, err := cli.Change(id) if err != nil { // a client.Error means we were able to communicate with // the server (got an answer) if e, ok := err.(*client.Error); ok { return nil, e } // an non-client error here means the server most // likely went away // XXX: it actually can be a bunch of other things; fix client to expose it better now := time.Now() if tMax.IsZero() { tMax = now.Add(maxGoneTime) } if now.After(tMax) { return nil, err } pb.Spin(i18n.G("Waiting for server to restart")) time.Sleep(pollTime) continue } if !tMax.IsZero() { pb.Finished() tMax = time.Time{} } for _, t := range chg.Tasks { switch { case t.Status != "Doing": continue case t.Progress.Total == 1: pb.Spin(t.Summary) nowLog := lastLogStr(t.Log) if lastLog[t.ID] != nowLog { pb.Notify(nowLog) lastLog[t.ID] = nowLog } case t.ID == lastID: pb.Set(float64(t.Progress.Done)) default: pb.Start(t.Progress.Label, float64(t.Progress.Total)) lastID = t.ID } break } if chg.Ready { if chg.Status == "Done" { return chg, nil } if chg.Err != "" { return chg, errors.New(chg.Err) } return nil, fmt.Errorf(i18n.G("change finished in status %q with no error message"), chg.Status) } // note this very purposely is not a ticker; we want // to sleep 100ms between calls, not call once every // 100ms. time.Sleep(pollTime) } }