github.com/mwhudson/juju@v0.0.0-20160512215208-90ff01f3497f/resource/context/internal/resourcedir.go (about)

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package internal
     5  
     6  // TODO(ericsnow) Move this file elsewhere?
     7  //  (e.g. top-level resource pkg, charm/resource)
     8  
     9  import (
    10  	"io"
    11  
    12  	"github.com/juju/errors"
    13  )
    14  
    15  // DirectorySpec identifies information for a resource directory.
    16  type DirectorySpec struct {
    17  	// Name is the resource name.
    18  	Name string
    19  
    20  	// Dirname is the path to the resource directory.
    21  	Dirname string
    22  
    23  	// Deps is the external dependencies of DirectorySpec.
    24  	Deps DirectorySpecDeps
    25  }
    26  
    27  // NewDirectorySpec returns a new directory spec for the given info.
    28  func NewDirectorySpec(dataDir, name string, deps DirectorySpecDeps) *DirectorySpec {
    29  	dirname := deps.Join(dataDir, name)
    30  
    31  	spec := &DirectorySpec{
    32  		Name:    name,
    33  		Dirname: dirname,
    34  
    35  		Deps: deps,
    36  	}
    37  	return spec
    38  }
    39  
    40  // Resolve returns the fully resolved file path, relative to the directory.
    41  func (spec DirectorySpec) Resolve(path ...string) string {
    42  	return spec.Deps.Join(append([]string{spec.Dirname}, path...)...)
    43  }
    44  
    45  // TODO(ericsnow) Make IsUpToDate a stand-alone function?
    46  
    47  // IsUpToDate determines whether or not the content matches the resource directory.
    48  func (spec DirectorySpec) IsUpToDate(content Content) (bool, error) {
    49  	// TODO(katco): Check to see if we have latest version
    50  	return false, nil
    51  }
    52  
    53  // Initialize preps the spec'ed directory and returns it.
    54  func (spec DirectorySpec) Initialize() (*Directory, error) {
    55  	if err := spec.Deps.MkdirAll(spec.Dirname); err != nil {
    56  		return nil, errors.Annotate(err, "could not create resource dir")
    57  	}
    58  
    59  	return NewDirectory(&spec, spec.Deps), nil
    60  }
    61  
    62  // DirectorySpecDeps exposes the external depenedencies of DirectorySpec.
    63  type DirectorySpecDeps interface {
    64  	DirectoryDeps
    65  
    66  	// Join exposes the functionality of filepath.Join().
    67  	Join(...string) string
    68  
    69  	// MkdirAll exposes the functionality of os.MkdirAll().
    70  	MkdirAll(string) error
    71  }
    72  
    73  // TempDirectorySpec represents a resource directory placed under a temporary data dir.
    74  type TempDirectorySpec struct {
    75  	*DirectorySpec
    76  
    77  	// CleanUp cleans up the temp directory in which the resource
    78  	// directory is placed.
    79  	CleanUp func() error
    80  }
    81  
    82  // NewTempDirectorySpec creates a new temp directory spec
    83  // for the given resource.
    84  func NewTempDirectorySpec(name string, deps TempDirDeps) (*TempDirectorySpec, error) {
    85  	tempDir, err := deps.NewTempDir()
    86  	if err != nil {
    87  		return nil, errors.Trace(err)
    88  	}
    89  
    90  	spec := &TempDirectorySpec{
    91  		DirectorySpec: NewDirectorySpec(tempDir, name, deps),
    92  		CleanUp: func() error {
    93  			return deps.RemoveDir(tempDir)
    94  		},
    95  	}
    96  	return spec, nil
    97  }
    98  
    99  // TempDirDeps exposes the external functionality needed by
   100  // NewTempDirectorySpec().
   101  type TempDirDeps interface {
   102  	DirectorySpecDeps
   103  
   104  	// NewTempDir returns the path to a new temporary directory.
   105  	NewTempDir() (string, error)
   106  
   107  	// RemoveDir deletes the specified directory.
   108  	RemoveDir(string) error
   109  }
   110  
   111  // Close implements io.Closer.
   112  func (spec TempDirectorySpec) Close() error {
   113  	if err := spec.CleanUp(); err != nil {
   114  		return errors.Annotate(err, "could not clean up temp dir")
   115  	}
   116  	return nil
   117  }
   118  
   119  // Directory represents a resource directory.
   120  type Directory struct {
   121  	*DirectorySpec
   122  
   123  	// Deps holds the external dependencies of the directory.
   124  	Deps DirectoryDeps
   125  }
   126  
   127  // NewDirectory returns a new directory for the provided spec.
   128  func NewDirectory(spec *DirectorySpec, deps DirectoryDeps) *Directory {
   129  	dir := &Directory{
   130  		DirectorySpec: spec,
   131  		Deps:          deps,
   132  	}
   133  	return dir
   134  }
   135  
   136  // Write writes all relevant files from the given source
   137  // to the directory.
   138  func (dir *Directory) Write(opened ContentSource) error {
   139  	// TODO(ericsnow) Also write the info file...
   140  
   141  	relPath := opened.Info().Path
   142  	if err := dir.WriteContent(relPath, opened.Content()); err != nil {
   143  		return errors.Trace(err)
   144  	}
   145  
   146  	return nil
   147  }
   148  
   149  // WriteContent writes the resource file to the given path
   150  // within the directory.
   151  func (dir *Directory) WriteContent(relPath string, content Content) error {
   152  	if len(relPath) == 0 {
   153  		// TODO(ericsnow) Use rd.readInfo().Path, like openResource() does?
   154  		return errors.NotImplementedf("")
   155  	}
   156  	filename := dir.Resolve(relPath)
   157  
   158  	target, err := dir.Deps.CreateWriter(filename)
   159  	if err != nil {
   160  		return errors.Annotate(err, "could not create new file for resource")
   161  	}
   162  	defer dir.Deps.CloseAndLog(target, filename)
   163  
   164  	if err := dir.Deps.WriteContent(target, content); err != nil {
   165  		return errors.Trace(err)
   166  	}
   167  
   168  	return nil
   169  }
   170  
   171  // DirectoryDeps exposes the external functionality needed by Directory.
   172  type DirectoryDeps interface {
   173  	// CreateWriter creates a new writer to which the resource file
   174  	// will be written.
   175  	CreateWriter(string) (io.WriteCloser, error)
   176  
   177  	// CloseAndLog closes the closer and logs any error.
   178  	CloseAndLog(io.Closer, string)
   179  
   180  	// WriteContent writes the content to the directory.
   181  	WriteContent(io.Writer, Content) error
   182  }