github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/resource/context/internal/context.go (about)

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package internal
     5  
     6  import (
     7  	"io"
     8  
     9  	"github.com/juju/errors"
    10  )
    11  
    12  // ContextDownload downloads the named resource and returns the path
    13  // to which it was downloaded. If the resource does not exist or has
    14  // not been uploaded yet then errors.NotFound is returned.
    15  //
    16  // Note that the downloaded file is checked for correctness.
    17  func ContextDownload(deps ContextDownloadDeps) (path string, err error) {
    18  	// TODO(katco): Potential race-condition: two commands running at
    19  	// once. Solve via collision using os.Mkdir() with a uniform
    20  	// temp dir name (e.g. "<datadir>/.<res name>.download")?
    21  
    22  	resDirSpec := deps.NewContextDirectorySpec()
    23  
    24  	remote, err := deps.OpenResource()
    25  	if err != nil {
    26  		return "", errors.Trace(err)
    27  	}
    28  	defer deps.CloseAndLog(remote, "remote resource")
    29  	path = resDirSpec.Resolve(remote.Info().Path)
    30  
    31  	isUpToDate, err := resDirSpec.IsUpToDate(remote.Content())
    32  	if err != nil {
    33  		return "", errors.Trace(err)
    34  	}
    35  	if isUpToDate {
    36  		// We're up to date already!
    37  		return path, nil
    38  	}
    39  
    40  	if err := deps.Download(resDirSpec, remote); err != nil {
    41  		return "", errors.Trace(err)
    42  	}
    43  
    44  	return path, nil
    45  }
    46  
    47  // ContextDownloadDeps provides the externally defined functions
    48  // on which ContextDownload depends. The functionality all relates
    49  // to a single resource.
    50  type ContextDownloadDeps interface {
    51  	// NewContextDirectorySpec returns the dir spec for the resource
    52  	// in the hook context.
    53  	NewContextDirectorySpec() ContextDirectorySpec
    54  
    55  	// OpenResource reads the resource info and opens the resource
    56  	// content for reading.
    57  	OpenResource() (ContextOpenedResource, error)
    58  
    59  	// CloseAndLog closes the closer and logs any error.
    60  	CloseAndLog(io.Closer, string)
    61  
    62  	// Download writes the remote to the target directory.
    63  	Download(DownloadTarget, ContextOpenedResource) error
    64  }
    65  
    66  // ContextDirectorySpec exposes the functionality of a resource dir spec
    67  // in a hook context.
    68  type ContextDirectorySpec interface {
    69  	Resolver
    70  
    71  	// Initializeprepares the target directory and returns it.
    72  	Initialize() (DownloadDirectory, error)
    73  
    74  	// IsUpToDate indicates whether or not the resource dir is in sync
    75  	// with the content.
    76  	IsUpToDate(Content) (bool, error)
    77  }
    78  
    79  // NewContextDirectorySpec returns a new directory spec for the context.
    80  func NewContextDirectorySpec(dataDir, name string, deps DirectorySpecDeps) ContextDirectorySpec {
    81  	return &contextDirectorySpec{
    82  		DirectorySpec: NewDirectorySpec(dataDir, name, deps),
    83  	}
    84  }
    85  
    86  type contextDirectorySpec struct {
    87  	*DirectorySpec
    88  }
    89  
    90  // Initializeimplements ContextDirectorySpec.
    91  func (spec contextDirectorySpec) Initialize() (DownloadDirectory, error) {
    92  	return spec.DirectorySpec.Initialize()
    93  }
    94  
    95  // ContextDownloadDirectory is an adapter for TempDirectorySpec.
    96  type ContextDownloadDirectory struct {
    97  	*TempDirectorySpec
    98  }
    99  
   100  // Initialize implements DownloadTarget.
   101  func (dir ContextDownloadDirectory) Initialize() (DownloadDirectory, error) {
   102  	return dir.TempDirectorySpec.Initialize()
   103  }
   104  
   105  // ContextOpenedResource exposes the functionality of an "opened"
   106  // resource.
   107  type ContextOpenedResource interface {
   108  	ContentSource
   109  	io.Closer
   110  }
   111  
   112  // NewContextContentChecker returns a content checker for the hook context.
   113  func NewContextContentChecker(content Content, deps NewContextContentCheckerDeps) ContentChecker {
   114  	if content.Fingerprint.IsZero() {
   115  		return &NopChecker{}
   116  	}
   117  
   118  	sizer := deps.NewSizeTracker()
   119  	checksumWriter := deps.NewChecksumWriter()
   120  	//checker.checksumWriter = charmresource.NewFingerprintHash()
   121  	return NewContentChecker(content, sizer, checksumWriter)
   122  }
   123  
   124  // NewContextContentCheckerDeps exposes the functionality needed
   125  // by NewContextContentChecker().
   126  type NewContextContentCheckerDeps interface {
   127  	// NewSizeTracker returns a new size tracker.
   128  	NewSizeTracker() SizeTracker
   129  
   130  	// NewChecksumWriter returns a new checksum writer.
   131  	NewChecksumWriter() ChecksumWriter
   132  }