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

package backups

import (
	"fmt"
	"io"
	"os"
	"time"

	"github.com/juju/cmd"
	"github.com/juju/errors"
	"launchpad.net/gnuflag"
)

const (
	notset           = "juju-backup-<date>-<time>.tar.gz"
	filenameTemplate = "juju-backup-%04d%02d%02d-%02d%02d%02d.tar.gz"
	downloadWarning  = "WARNING: downloading backup archives is recommended; " +
		"backups stored remotely are not guaranteed to be available"
)

const createDoc = `
"create" requests that juju create a backup of its state and print the
backup's unique ID.  You may provide a note to associate with the backup.

The backup archive and associated metadata are stored remotely by juju.

The --download option may be used without the --filename option.  In
that case, the backup archive will be stored in the current working
directory with a name matching juju-backup-<date>-<time>.tar.gz.

WARNING: Remotely stored backups will be lost when the environment is
destroyed.  Furthermore, the remotely backup is not guaranteed to be
available.

Therefore, you should use the --download or --filename options, or use
"juju backups download", to get a local copy of the backup archive.
This local copy can then be used to restore an environment even if that
environment was already destroyed or is otherwise unavailable.
`

// CreateCommand is the sub-command for creating a new backup.
type CreateCommand struct {
	CommandBase
	// Quiet indicates that the full metadata should not be dumped.
	Quiet bool
	// NoDownload means the backups archive should not be downloaded.
	NoDownload bool
	// Filename is where the backup should be downloaded.
	Filename string
	// Notes is the custom message to associated with the new backup.
	Notes string
}

// Info implements Command.Info.
func (c *CreateCommand) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "create",
		Args:    "[<notes>]",
		Purpose: "create a backup",
		Doc:     createDoc,
	}
}

// SetFlags implements Command.SetFlags.
func (c *CreateCommand) SetFlags(f *gnuflag.FlagSet) {
	f.BoolVar(&c.Quiet, "quiet", false, "do not print the metadata")
	f.BoolVar(&c.NoDownload, "no-download", false, "do not download the archive")
	f.StringVar(&c.Filename, "filename", notset, "download to this file")
}

// Init implements Command.Init.
func (c *CreateCommand) Init(args []string) error {
	notes, err := cmd.ZeroOrOneArgs(args)
	if err != nil {
		return err
	}
	c.Notes = notes

	if c.Filename != notset && c.NoDownload {
		return errors.Errorf("cannot mix --no-download and --filename")
	}

	return nil
}

// Run implements Command.Run.
func (c *CreateCommand) Run(ctx *cmd.Context) error {
	client, err := c.NewAPIClient()
	if err != nil {
		return errors.Trace(err)
	}
	defer client.Close()

	result, err := client.Create(c.Notes)
	if err != nil {
		return errors.Trace(err)
	}

	if !c.Quiet {
		c.dumpMetadata(ctx, result)
	}

	fmt.Fprintln(ctx.Stdout, result.ID)

	// Handle download.
	filename := c.decideFilename(ctx, c.Filename, result.Started)
	if filename != "" {
		if err := c.download(ctx, client, result.ID, filename); err != nil {
			return errors.Trace(err)
		}
	}

	return nil
}

func (c *CreateCommand) decideFilename(ctx *cmd.Context, filename string, timestamp time.Time) string {
	if filename == "" {
		fmt.Fprintln(ctx.Stderr, "missing filename")
	} else if filename == notset {
		if c.NoDownload {
			if !c.Quiet {
				fmt.Fprintln(ctx.Stderr, downloadWarning)
			}
			filename = ""
		} else {
			y, m, d := timestamp.Date()
			H, M, S := timestamp.Clock()
			filename = fmt.Sprintf(filenameTemplate, y, m, d, H, M, S)
		}
	}
	return filename
}

func (c *CreateCommand) download(ctx *cmd.Context, client APIClient, id string, filename string) error {
	fmt.Fprintln(ctx.Stdout, "downloading to "+filename)

	archive, err := client.Download(id)
	if err != nil {
		return errors.Trace(err)
	}<commit_msg>Refactor some nested if statements.<commit_after>// Copyright 2014 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package backups

import (
	"fmt"
	"io"
	"os"
	"time"

	"github.com/juju/cmd"
	"github.com/juju/errors"
	"launchpad.net/gnuflag"
)

const (
	notset           = "juju-backup-<date>-<time>.tar.gz"
	filenameTemplate = "juju-backup-%04d%02d%02d-%02d%02d%02d.tar.gz"
	downloadWarning  = "WARNING: downloading backup archives is recommended; " +
		"backups stored remotely are not guaranteed to be available"
)

const createDoc = `
"create" requests that juju create a backup of its state and print the
backup's unique ID.  You may provide a note to associate with the backup.

The backup archive and associated metadata are stored remotely by juju.

The --download option may be used without the --filename option.  In
that case, the backup archive will be stored in the current working
directory with a name matching juju-backup-<date>-<time>.tar.gz.

WARNING: Remotely stored backups will be lost when the environment is
destroyed.  Furthermore, the remotely backup is not guaranteed to be
available.

Therefore, you should use the --download or --filename options, or use
"juju backups download", to get a local copy of the backup archive.
This local copy can then be used to restore an environment even if that
environment was already destroyed or is otherwise unavailable.
`

// CreateCommand is the sub-command for creating a new backup.
type CreateCommand struct {
	CommandBase
	// Quiet indicates that the full metadata should not be dumped.
	Quiet bool
	// NoDownload means the backups archive should not be downloaded.
	NoDownload bool
	// Filename is where the backup should be downloaded.
	Filename string
	// Notes is the custom message to associated with the new backup.
	Notes string
}

// Info implements Command.Info.
func (c *CreateCommand) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "create",
		Args:    "[<notes>]",
		Purpose: "create a backup",
		Doc:     createDoc,
	}
}

// SetFlags implements Command.SetFlags.
func (c *CreateCommand) SetFlags(f *gnuflag.FlagSet) {
	f.BoolVar(&c.Quiet, "quiet", false, "do not print the metadata")
	f.BoolVar(&c.NoDownload, "no-download", false, "do not download the archive")
	f.StringVar(&c.Filename, "filename", notset, "download to this file")
}

// Init implements Command.Init.
func (c *CreateCommand) Init(args []string) error {
	notes, err := cmd.ZeroOrOneArgs(args)
	if err != nil {
		return err
	}
	c.Notes = notes

	if c.Filename != notset && c.NoDownload {
		return errors.Errorf("cannot mix --no-download and --filename")
	}

	return nil
}

// Run implements Command.Run.
func (c *CreateCommand) Run(ctx *cmd.Context) error {
	client, err := c.NewAPIClient()
	if err != nil {
		return errors.Trace(err)
	}
	defer client.Close()

	result, err := client.Create(c.Notes)
	if err != nil {
		return errors.Trace(err)
	}

	if !c.Quiet {
		c.dumpMetadata(ctx, result)
	}

	fmt.Fprintln(ctx.Stdout, result.ID)

	// Handle download.
	filename := c.decideFilename(ctx, c.Filename, result.Started)
	if filename != "" {
		if err := c.download(ctx, client, result.ID, filename); err != nil {
			return errors.Trace(err)
		}
	}

	return nil
}

func (c *CreateCommand) decideFilename(ctx *cmd.Context, filename string, timestamp time.Time) string {
	if filename != notset {
		if filename == "" {
			fmt.Fprintln(ctx.Stderr, "missing filename")
		}
		return filename
	}

	if c.NoDownload {
		if !c.Quiet {
			fmt.Fprintln(ctx.Stderr, downloadWarning)
		}
		return ""
	}

	// Downloading but no filename given, so generate one.
	y, m, d := timestamp.Date()
	H, M, S := timestamp.Clock()
	return fmt.Sprintf(filenameTemplate, y, m, d, H, M, S)
}

func (c *CreateCommand) download(ctx *cmd.Context, client APIClient, id string, filename string) error {
	fmt.Fprintln(ctx.Stdout, "downloading to "+filename)

	archive, err := client.Download(id)
	if err != nil {
		return errors.Trace(err)
	}
{'fraction_non_alphanumeric': 0.11371955067524928, 'fraction_numerical': 0.00429130379906601, 'mean_word_length': 3.785024154589372, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 45, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7217566', 'n_tokens_mistral': 2645, 'n_tokens_neox': 2500, 'n_words': 896}