github.com/mwhudson/juju@v0.0.0-20160512215208-90ff01f3497f/downloader/downloader.go (about)

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package downloader
     5  
     6  import (
     7  	"io"
     8  	"net/url"
     9  	"os"
    10  
    11  	"github.com/juju/errors"
    12  	"github.com/juju/loggo"
    13  	"github.com/juju/utils"
    14  )
    15  
    16  var logger = loggo.GetLogger("juju.downloader")
    17  
    18  // Downloader provides the functionality for downloading files.
    19  type Downloader struct {
    20  	// OpenBlob is the func used to gain access to the blob, whether
    21  	// through an HTTP request or some other means.
    22  	OpenBlob func(*url.URL) (io.ReadCloser, error)
    23  }
    24  
    25  // NewArgs holds the arguments to New().
    26  type NewArgs struct {
    27  	// HostnameVerification is that which should be used for the client.
    28  	// If it is disableSSLHostnameVerification then a non-validating
    29  	// client will be used.
    30  	HostnameVerification utils.SSLHostnameVerification
    31  }
    32  
    33  // New returns a new Downloader for the given args.
    34  func New(args NewArgs) *Downloader {
    35  	return &Downloader{
    36  		OpenBlob: NewHTTPBlobOpener(args.HostnameVerification),
    37  	}
    38  }
    39  
    40  // Start starts a new download and returns it.
    41  func (dlr Downloader) Start(req Request) *Download {
    42  	dl := StartDownload(req, dlr.OpenBlob)
    43  	return dl
    44  }
    45  
    46  // Download starts a new download, waits for it to complete, and
    47  // returns the local name of the file.
    48  func (dlr Downloader) Download(req Request, abort <-chan struct{}) (filename string, err error) {
    49  	if err := os.MkdirAll(req.TargetDir, 0755); err != nil {
    50  		return "", errors.Trace(err)
    51  	}
    52  	dl := dlr.Start(req)
    53  	file, err := dl.Wait(abort)
    54  	if file != nil {
    55  		defer file.Close()
    56  	}
    57  	if err != nil {
    58  		return "", errors.Trace(err)
    59  	}
    60  	return file.Name(), nil
    61  }
    62  
    63  // DownloadWithAlternates tries each of the provided requests until
    64  // one succeeds. If none succeed then the error from the most recent
    65  // attempt is returned. At least one request must be provided.
    66  func (dlr Downloader) DownloadWithAlternates(requests []Request, abort <-chan struct{}) (filename string, err error) {
    67  	if len(requests) == 0 {
    68  		return "", errors.New("no requests to try")
    69  	}
    70  
    71  	for _, req := range requests {
    72  		filename, err = dlr.Download(req, abort)
    73  		if errors.IsNotValid(err) {
    74  			break
    75  		}
    76  		if err == nil {
    77  			break
    78  		}
    79  		logger.Errorf("download request to %s failed: %v", req.URL, err)
    80  		// Try the next one.
    81  	}
    82  	if err != nil {
    83  		return "", errors.Trace(err)
    84  	}
    85  	return filename, nil
    86  }