github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/cmd/juju/commands/synctools.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package commands
     5  
     6  import (
     7  	"bytes"
     8  	"io"
     9  
    10  	"github.com/juju/cmd"
    11  	"github.com/juju/gnuflag"
    12  	"github.com/juju/loggo"
    13  	"github.com/juju/version"
    14  
    15  	"github.com/juju/juju/apiserver/params"
    16  	"github.com/juju/juju/cmd/juju/block"
    17  	"github.com/juju/juju/cmd/modelcmd"
    18  	"github.com/juju/juju/environs/filestorage"
    19  	"github.com/juju/juju/environs/sync"
    20  	envtools "github.com/juju/juju/environs/tools"
    21  	coretools "github.com/juju/juju/tools"
    22  )
    23  
    24  var syncTools = sync.SyncTools
    25  
    26  func newSyncToolsCommand() cmd.Command {
    27  	return modelcmd.Wrap(&syncToolsCommand{})
    28  }
    29  
    30  // syncToolsCommand copies all the tools from the us-east-1 bucket to the local
    31  // bucket.
    32  type syncToolsCommand struct {
    33  	modelcmd.ModelCommandBase
    34  	allVersions  bool
    35  	versionStr   string
    36  	majorVersion int
    37  	minorVersion int
    38  	dryRun       bool
    39  	dev          bool
    40  	public       bool
    41  	source       string
    42  	stream       string
    43  	localDir     string
    44  	destination  string
    45  }
    46  
    47  var _ cmd.Command = (*syncToolsCommand)(nil)
    48  
    49  const synctoolsDoc = `
    50  This copies the Juju agent software from the official tools store (located
    51  at https://streams.canonical.com/juju) into a model. It is generally done
    52  when the model is without Internet access.
    53  
    54  Instead of the above site, a local directory can be specified as source.
    55  The online store will, of course, need to be contacted at some point to get
    56  the software.
    57  
    58  Examples:
    59      # Download the software (version auto-selected) to the model:
    60      juju sync-tools --debug
    61  
    62      # Download a specific version of the software locally:
    63      juju sync-tools --debug --version 2.0 --local-dir=/home/ubuntu/sync-tools
    64  
    65      # Get locally available software to the model:
    66      juju sync-tools --debug --source=/home/ubuntu/sync-tools
    67  
    68  See also:
    69      upgrade-juju
    70  
    71  `
    72  
    73  func (c *syncToolsCommand) Info() *cmd.Info {
    74  	return &cmd.Info{
    75  		Name:    "sync-tools",
    76  		Purpose: "Copy tools from the official tool store into a local model.",
    77  		Doc:     synctoolsDoc,
    78  	}
    79  }
    80  
    81  func (c *syncToolsCommand) SetFlags(f *gnuflag.FlagSet) {
    82  	c.ModelCommandBase.SetFlags(f)
    83  	f.BoolVar(&c.allVersions, "all", false, "Copy all versions, not just the latest")
    84  	f.StringVar(&c.versionStr, "version", "", "Copy a specific major[.minor] version")
    85  	f.BoolVar(&c.dryRun, "dry-run", false, "Don't copy, just print what would be copied")
    86  	f.BoolVar(&c.dev, "dev", false, "Consider development versions as well as released ones\n    DEPRECATED: use --stream instead")
    87  	f.BoolVar(&c.public, "public", false, "Tools are for a public cloud, so generate mirrors information")
    88  	f.StringVar(&c.source, "source", "", "Local source directory")
    89  	f.StringVar(&c.stream, "stream", "", "Simplestreams stream for which to sync metadata")
    90  	f.StringVar(&c.localDir, "local-dir", "", "Local destination directory")
    91  	f.StringVar(&c.destination, "destination", "", "Local destination directory\n    DEPRECATED: use --local-dir instead")
    92  }
    93  
    94  func (c *syncToolsCommand) Init(args []string) error {
    95  	if c.destination != "" {
    96  		// Override localDir with destination as localDir now replaces destination
    97  		c.localDir = c.destination
    98  		logger.Infof("Use of the --destination flag is deprecated in 1.18. Please use --local-dir instead.")
    99  	}
   100  	if c.versionStr != "" {
   101  		var err error
   102  		if c.majorVersion, c.minorVersion, err = version.ParseMajorMinor(c.versionStr); err != nil {
   103  			return err
   104  		}
   105  	}
   106  	if c.dev {
   107  		c.stream = envtools.TestingStream
   108  	}
   109  	return cmd.CheckEmpty(args)
   110  }
   111  
   112  // syncToolsAPI provides an interface with a subset of the
   113  // api.Client API. This exists to enable mocking.
   114  type syncToolsAPI interface {
   115  	FindTools(majorVersion, minorVersion int, series, arch string) (params.FindToolsResult, error)
   116  	UploadTools(r io.ReadSeeker, v version.Binary, series ...string) (coretools.List, error)
   117  	Close() error
   118  }
   119  
   120  var getSyncToolsAPI = func(c *syncToolsCommand) (syncToolsAPI, error) {
   121  	return c.NewAPIClient()
   122  }
   123  
   124  func (c *syncToolsCommand) Run(ctx *cmd.Context) (resultErr error) {
   125  	// Register writer for output on screen.
   126  	writer := loggo.NewMinimumLevelWriter(
   127  		cmd.NewCommandLogWriter("juju.environs.sync", ctx.Stdout, ctx.Stderr),
   128  		loggo.INFO)
   129  	loggo.RegisterWriter("synctools", writer)
   130  	defer loggo.RemoveWriter("synctools")
   131  
   132  	sctx := &sync.SyncContext{
   133  		AllVersions:  c.allVersions,
   134  		MajorVersion: c.majorVersion,
   135  		MinorVersion: c.minorVersion,
   136  		DryRun:       c.dryRun,
   137  		Stream:       c.stream,
   138  		Source:       c.source,
   139  	}
   140  
   141  	if c.localDir != "" {
   142  		stor, err := filestorage.NewFileStorageWriter(c.localDir)
   143  		if err != nil {
   144  			return err
   145  		}
   146  		writeMirrors := envtools.DoNotWriteMirrors
   147  		if c.public {
   148  			writeMirrors = envtools.WriteMirrors
   149  		}
   150  		sctx.TargetToolsFinder = sync.StorageToolsFinder{Storage: stor}
   151  		sctx.TargetToolsUploader = sync.StorageToolsUploader{
   152  			Storage:       stor,
   153  			WriteMetadata: true,
   154  			WriteMirrors:  writeMirrors,
   155  		}
   156  	} else {
   157  		if c.public {
   158  			logger.Infof("--public is ignored unless --local-dir is specified")
   159  		}
   160  		api, err := getSyncToolsAPI(c)
   161  		if err != nil {
   162  			return err
   163  		}
   164  		defer api.Close()
   165  		adapter := syncToolsAPIAdapter{api}
   166  		sctx.TargetToolsFinder = adapter
   167  		sctx.TargetToolsUploader = adapter
   168  	}
   169  	return block.ProcessBlockedError(syncTools(sctx), block.BlockChange)
   170  }
   171  
   172  // syncToolsAPIAdapter implements sync.ToolsFinder and
   173  // sync.ToolsUploader, adapting a syncToolsAPI. This
   174  // enables the use of sync.SyncTools with the client
   175  // API.
   176  type syncToolsAPIAdapter struct {
   177  	syncToolsAPI
   178  }
   179  
   180  func (s syncToolsAPIAdapter) FindTools(majorVersion int, stream string) (coretools.List, error) {
   181  	result, err := s.syncToolsAPI.FindTools(majorVersion, -1, "", "")
   182  	if err != nil {
   183  		return nil, err
   184  	}
   185  	if result.Error != nil {
   186  		if params.IsCodeNotFound(result.Error) {
   187  			return nil, coretools.ErrNoMatches
   188  		}
   189  		return nil, result.Error
   190  	}
   191  	return result.List, nil
   192  }
   193  
   194  func (s syncToolsAPIAdapter) UploadTools(toolsDir, stream string, tools *coretools.Tools, data []byte) error {
   195  	_, err := s.syncToolsAPI.UploadTools(bytes.NewReader(data), tools.Version)
   196  	return err
   197  }