github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/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/loggo"
    12  	"github.com/juju/version"
    13  	"launchpad.net/gnuflag"
    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  func (c *syncToolsCommand) Info() *cmd.Info {
    50  	return &cmd.Info{
    51  		Name:    "sync-tools",
    52  		Purpose: "copy tools from the official tool store into a local model",
    53  		Doc: `
    54  This copies the Juju tools tarball from the official tools store (located
    55  at https://streams.canonical.com/juju) into your model.
    56  This is generally done when you want Juju to be able to run without having to
    57  access the Internet. Alternatively you can specify a local directory as source.
    58  
    59  Sometimes this is because the model does not have public access,
    60  and sometimes you just want to avoid having to access data outside of
    61  the local cloud.
    62  `,
    63  	}
    64  }
    65  
    66  func (c *syncToolsCommand) SetFlags(f *gnuflag.FlagSet) {
    67  	f.BoolVar(&c.allVersions, "all", false, "copy all versions, not just the latest")
    68  	f.StringVar(&c.versionStr, "version", "", "copy a specific major[.minor] version")
    69  	f.BoolVar(&c.dryRun, "dry-run", false, "don't copy, just print what would be copied")
    70  	f.BoolVar(&c.dev, "dev", false, "consider development versions as well as released ones\n    DEPRECATED: use --stream instead")
    71  	f.BoolVar(&c.public, "public", false, "tools are for a public cloud, so generate mirrors information")
    72  	f.StringVar(&c.source, "source", "", "local source directory")
    73  	f.StringVar(&c.stream, "stream", "", "simplestreams stream for which to sync metadata")
    74  	f.StringVar(&c.localDir, "local-dir", "", "local destination directory")
    75  	f.StringVar(&c.destination, "destination", "", "local destination directory")
    76  }
    77  
    78  func (c *syncToolsCommand) Init(args []string) error {
    79  	if c.destination != "" {
    80  		// Override localDir with destination as localDir now replaces destination
    81  		c.localDir = c.destination
    82  		logger.Warningf("Use of the --destination flag is deprecated in 1.18. Please use --local-dir instead.")
    83  	}
    84  	if c.versionStr != "" {
    85  		var err error
    86  		if c.majorVersion, c.minorVersion, err = version.ParseMajorMinor(c.versionStr); err != nil {
    87  			return err
    88  		}
    89  	}
    90  	if c.dev {
    91  		c.stream = envtools.TestingStream
    92  	}
    93  	return cmd.CheckEmpty(args)
    94  }
    95  
    96  // syncToolsAPI provides an interface with a subset of the
    97  // api.Client API. This exists to enable mocking.
    98  type syncToolsAPI interface {
    99  	FindTools(majorVersion, minorVersion int, series, arch string) (params.FindToolsResult, error)
   100  	UploadTools(r io.ReadSeeker, v version.Binary, series ...string) (coretools.List, error)
   101  	Close() error
   102  }
   103  
   104  var getSyncToolsAPI = func(c *syncToolsCommand) (syncToolsAPI, error) {
   105  	return c.NewAPIClient()
   106  }
   107  
   108  func (c *syncToolsCommand) Run(ctx *cmd.Context) (resultErr error) {
   109  	// Register writer for output on screen.
   110  	loggo.RegisterWriter("synctools", cmd.NewCommandLogWriter("juju.environs.sync", ctx.Stdout, ctx.Stderr), loggo.INFO)
   111  	defer loggo.RemoveWriter("synctools")
   112  
   113  	sctx := &sync.SyncContext{
   114  		AllVersions:  c.allVersions,
   115  		MajorVersion: c.majorVersion,
   116  		MinorVersion: c.minorVersion,
   117  		DryRun:       c.dryRun,
   118  		Stream:       c.stream,
   119  		Source:       c.source,
   120  	}
   121  
   122  	if c.localDir != "" {
   123  		stor, err := filestorage.NewFileStorageWriter(c.localDir)
   124  		if err != nil {
   125  			return err
   126  		}
   127  		writeMirrors := envtools.DoNotWriteMirrors
   128  		if c.public {
   129  			writeMirrors = envtools.WriteMirrors
   130  		}
   131  		sctx.TargetToolsFinder = sync.StorageToolsFinder{Storage: stor}
   132  		sctx.TargetToolsUploader = sync.StorageToolsUploader{
   133  			Storage:       stor,
   134  			WriteMetadata: true,
   135  			WriteMirrors:  writeMirrors,
   136  		}
   137  	} else {
   138  		if c.public {
   139  			logger.Warningf("--public is ignored unless --local-dir is specified")
   140  		}
   141  		api, err := getSyncToolsAPI(c)
   142  		if err != nil {
   143  			return err
   144  		}
   145  		defer api.Close()
   146  		adapter := syncToolsAPIAdapter{api}
   147  		sctx.TargetToolsFinder = adapter
   148  		sctx.TargetToolsUploader = adapter
   149  	}
   150  	return block.ProcessBlockedError(syncTools(sctx), block.BlockChange)
   151  }
   152  
   153  // syncToolsAPIAdapter implements sync.ToolsFinder and
   154  // sync.ToolsUploader, adapting a syncToolsAPI. This
   155  // enables the use of sync.SyncTools with the client
   156  // API.
   157  type syncToolsAPIAdapter struct {
   158  	syncToolsAPI
   159  }
   160  
   161  func (s syncToolsAPIAdapter) FindTools(majorVersion int, stream string) (coretools.List, error) {
   162  	result, err := s.syncToolsAPI.FindTools(majorVersion, -1, "", "")
   163  	if err != nil {
   164  		return nil, err
   165  	}
   166  	if result.Error != nil {
   167  		if params.IsCodeNotFound(result.Error) {
   168  			return nil, coretools.ErrNoMatches
   169  		}
   170  		return nil, result.Error
   171  	}
   172  	return result.List, nil
   173  }
   174  
   175  func (s syncToolsAPIAdapter) UploadTools(toolsDir, stream string, tools *coretools.Tools, data []byte) error {
   176  	_, err := s.syncToolsAPI.UploadTools(bytes.NewReader(data), tools.Version)
   177  	return err
   178  }