<commit_before>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package commands

import (
	"bytes"
	"io"

	"github.com/juju/cmd"
	"github.com/juju/loggo"
	"github.com/juju/version"
	"launchpad.net/gnuflag"

	"github.com/juju/juju/apiserver/params"
	"github.com/juju/juju/cmd/juju/block"
	"github.com/juju/juju/cmd/modelcmd"
	"github.com/juju/juju/environs/filestorage"
	"github.com/juju/juju/environs/sync"
	envtools "github.com/juju/juju/environs/tools"
	coretools "github.com/juju/juju/tools"
)

var syncTools = sync.SyncTools

func newSyncToolsCommand() cmd.Command {
	return modelcmd.Wrap(&syncToolsCommand{})
}

// syncToolsCommand copies all the tools from the us-east-1 bucket to the local
// bucket.
type syncToolsCommand struct {
	modelcmd.ModelCommandBase
	allVersions  bool
	versionStr   string
	majorVersion int
	minorVersion int
	dryRun       bool
	dev          bool
	public       bool
	source       string
	stream       string
	localDir     string
	destination  string
}

var _ cmd.Command = (*syncToolsCommand)(nil)

func (c *syncToolsCommand) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "sync-tools",
		Purpose: "copy tools from the official tool store into a local model",
		Doc: `
This copies the Juju tools tarball from the official tools store (located
at https://streams.canonical.com/juju) into your model.
This is generally done when you want Juju to be able to run without having to
access the Internet. Alternatively you can specify a local directory as source.

Sometimes this is because the model does not have public access,
and sometimes you just want to avoid having to access data outside of
the local cloud.
`,
	}
}

func (c *syncToolsCommand) SetFlags(f *gnuflag.FlagSet) {
	f.BoolVar(&c.allVersions, "all", false, "copy all versions, not just the latest")
	f.StringVar(&c.versionStr, "version", "", "copy a specific major[.minor] version")
	f.BoolVar(&c.dryRun, "dry-run", false, "don't copy, just print what would be copied")
	f.BoolVar(&c.dev, "dev", false, "consider development versions as well as released ones\n    DEPRECATED: use --stream instead")
	f.BoolVar(&c.public, "public", false, "tools are for a public cloud, so generate mirrors information")
	f.StringVar(&c.source, "source", "", "local source directory")
	f.StringVar(&c.stream, "stream", "", "simplestreams stream for which to sync metadata")
	f.StringVar(&c.localDir, "local-dir", "", "local destination directory")
	f.StringVar(&c.destination, "destination", "", "local destination directory")
}

func (c *syncToolsCommand) Init(args []string) error {
	if c.destination != "" {
		// Override localDir with destination as localDir now replaces destination
		c.localDir = c.destination
		logger.Infof("Use of the --destination flag is deprecated in 1.18. Please use --local-dir instead.")
	}
	if c.versionStr != "" {
		var err error
		if c.majorVersion, c.minorVersion, err = version.ParseMajorMinor(c.versionStr); err != nil {
			return err
		}
	}
	if c.dev {
		c.stream = envtools.TestingStream
	}
	return cmd.CheckEmpty(args)
}

// syncToolsAPI provides an interface with a subset of the
// api.Client API. This exists to enable mocking.
type syncToolsAPI interface {
	FindTools(majorVersion, minorVersion int, series, arch string) (params.FindToolsResult, error)
	UploadTools(r io.ReadSeeker, v version.Binary, series ...string) (coretools.List, error)<commit_msg>Improve sync-tools command documentation<commit_after>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package commands

import (
	"bytes"
	"io"

	"github.com/juju/cmd"
	"github.com/juju/loggo"
	"github.com/juju/version"
	"launchpad.net/gnuflag"

	"github.com/juju/juju/apiserver/params"
	"github.com/juju/juju/cmd/juju/block"
	"github.com/juju/juju/cmd/modelcmd"
	"github.com/juju/juju/environs/filestorage"
	"github.com/juju/juju/environs/sync"
	envtools "github.com/juju/juju/environs/tools"
	coretools "github.com/juju/juju/tools"
)

var syncTools = sync.SyncTools

func newSyncToolsCommand() cmd.Command {
	return modelcmd.Wrap(&syncToolsCommand{})
}

// syncToolsCommand copies all the tools from the us-east-1 bucket to the local
// bucket.
type syncToolsCommand struct {
	modelcmd.ModelCommandBase
	allVersions  bool
	versionStr   string
	majorVersion int
	minorVersion int
	dryRun       bool
	dev          bool
	public       bool
	source       string
	stream       string
	localDir     string
	destination  string
}

var _ cmd.Command = (*syncToolsCommand)(nil)

const synctoolsDoc = `
This copies the Juju agent software from the official tools store (located
at https://streams.canonical.com/juju) into a model. It is generally done
when the model is without Internet access.

Instead of the above site, a local directory can be specified as source.
The online store will, of course, need to be contacted at some point to get
the software.

Examples:

Download the software (version auto-selected) to the model:

    juju sync-tools --debug

Download a specific version of the software locally:

    juju sync-tools --debug --version 2.0 --local-dir=/home/ubuntu/sync-tools

Get locally available software to the model:

    juju sync-tools --debug --source=/home/ubuntu/sync-tools

See also: upgrade-juju

`

func (c *syncToolsCommand) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "sync-tools",
		Purpose: "Copy tools from the official tool store into a local model.",
		Doc:     synctoolsDoc,
	}
}

func (c *syncToolsCommand) SetFlags(f *gnuflag.FlagSet) {
	f.BoolVar(&c.allVersions, "all", false, "copy all versions, not just the latest")
	f.StringVar(&c.versionStr, "version", "", "copy a specific major[.minor] version")
	f.BoolVar(&c.dryRun, "dry-run", false, "don't copy, just print what would be copied")
	f.BoolVar(&c.dev, "dev", false, "consider development versions as well as released ones\n    DEPRECATED: use --stream instead")
	f.BoolVar(&c.public, "public", false, "tools are for a public cloud, so generate mirrors information")
	f.StringVar(&c.source, "source", "", "local source directory")
	f.StringVar(&c.stream, "stream", "", "simplestreams stream for which to sync metadata")
	f.StringVar(&c.localDir, "local-dir", "", "local destination directory")
	f.StringVar(&c.destination, "destination", "", "local destination directory\n    DEPRECATED: use --local-dir instead")
}

func (c *syncToolsCommand) Init(args []string) error {
	if c.destination != "" {
		// Override localDir with destination as localDir now replaces destination
		c.localDir = c.destination
		logger.Infof("Use of the --destination flag is deprecated in 1.18. Please use --local-dir instead.")
	}
	if c.versionStr != "" {
		var err error
		if c.majorVersion, c.minorVersion, err = version.ParseMajorMinor(c.versionStr); err != nil {
			return err
		}
	}
	if c.dev {
		c.stream = envtools.TestingStream
	}
	return cmd.CheckEmpty(args)
}

// syncToolsAPI provides an interface with a subset of the
// api.Client API. This exists to enable mocking.
type syncToolsAPI interface {
	FindTools(majorVersion, minorVersion int, series, arch string) (params.FindToolsResult, error)
	UploadTools(r io.ReadSeeker, v version.Binary, series ...string) (coretools.List, error)
{'fraction_non_alphanumeric': 0.11332227750138198, 'fraction_numerical': 0.002763957987838585, 'mean_word_length': 4.096478873239437, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4307469', 'n_tokens_mistral': 2423, 'n_tokens_neox': 2282, 'n_words': 731}