<commit_before>package cli

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"time"
)

// App is the main structure of a cli application. It is recomended that
// an app be created with the cli.NewApp() function
type App struct {
	// The name of the program. Defaults to os.Args[0]
	Name string
	// Description of the program.
	Usage string
	// Version of the program
	Version string
	// List of commands to execute
	Commands []Command
	// List of help topics (help not associated with a command)
	HelpTopics []HelpTopic
	// List of flags to parse
	Flags []Flag
	// Boolean to enable bash completion commands
	EnableBashCompletion bool
	// Boolean to hide built-in help command
	HideHelp bool
	// Boolean to hide built-in version flag
	HideVersion bool
	// An action to execute when the bash-completion flag is set
	BashComplete func(context *Context)
	// An action to execute before any subcommands are run, but after the context is ready
	// If a non-nil error is returned, no subcommands are run
	Before func(context *Context) error
	// An action to execute after any subcommands are run, but after the subcommand has finished
	// It is run even if Action() panics
	After func(context *Context) error
	// The action to execute when no subcommands are specified
	Action func(context *Context)
	// Execute this function if the proper command cannot be found
	CommandNotFound func(context *Context, command string)
	// Compilation date
	Compiled time.Time
	// List of all authors who contributed
	Authors []Author
	// Copyright of the binary if any
	Copyright string
	// Name of Author (Note: Use App.Authors, this is deprecated)
	Author string
	// Email of Author (Note: Use App.Authors, this is deprecated)
	Email string
	// Writer writer to write output to
	Writer io.Writer
}

// Tries to find out when this binary was compiled.
// Returns the current time if it fails to find it.
func compileTime() time.Time {
	info, err := os.Stat(os.Args[0])
	if err != nil {
		return time.Now()
	}
	return info.ModTime()
}

// Creates a new cli Application with some reasonable defaults for Name, Usage, Version and Action.
func NewApp() *App {
	return &App{
		Name:         os.Args[0],
		Usage:        "A new cli application",
		Version:      "0.0.0",
		BashComplete: DefaultAppComplete,
		Action:       helpCommand.Action,
		Compiled:     compileTime(),
		Writer:       os.Stdout,
	}
}

// Entry point to the cli app. Parses the arguments slice and routes to the proper flag/args combination
func (a *App) Run(arguments []string) (err error) {
	if a.Author != "" || a.Email != "" {
		a.Authors = append(a.Authors, Author{Name: a.Author, Email: a.Email})
	}

	// append help to commands
	if a.Command(helpCommand.Name) == nil && !a.HideHelp {
		a.Commands = append(a.Commands, helpCommand)
		if (HelpFlag != BoolFlag{}) {
			a.appendFlag(HelpFlag)
		}
	}

	//append version/help flags
	if a.EnableBashCompletion {
		a.appendFlag(BashCompletionFlag)
	}

	if !a.HideVersion {
		a.appendFlag(VersionFlag)
	}

	// parse flags
	set := flagSet(a.Name, a.Flags)
	set.SetOutput(ioutil.Discard)
	err = set.Parse(arguments[1:])
	nerr := normalizeFlags(a.Flags, set)
	if nerr != nil {
		fmt.Fprintln(a.Writer, nerr)
		context := NewContext(a, set, nil)
		ShowAppHelp(context)
		return nerr
	}
	context := NewContext(a, set, nil)

	if err != nil {
		fmt.Fprintln(a.Writer, "Incorrect Usage.")
		fmt.Fprintln(a.Writer)
		ShowAppHelp(context)
		return err
	}

	if checkCompletions(context) {
		return nil
	}

	if checkHelp(context) {
		return nil
	}

	if checkVersion(context) {
		return nil
	}

	if a.After != nil {
		defer func() {
			afterErr := a.After(context)
			if afterErr != nil {
				if err != nil {
					err = NewMultiError(err, afterErr)
				} else {
					err = afterErr
				}
			}
		}()
	}

	if a.Before != nil {
		err := a.Before(context)
		if err != nil {
			return err
		}
	}

	args := context.Args()
	if args.Present() {
		name := args.First()
		c := a.Command(name)
		if c != nil {
			return c.Run(context)
		}
	}

	// Run default Action
	a.Action(context)
	return nil
}

// Another entry point to the cli app, takes care of passing arguments and error handling
func (a *App) RunAndExitOnError() {
	if err := a.Run(os.Args); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}

// Invokes the subcommand given the context, parses ctx.Args() to generate command-specific flags
func (a *App) RunAsSubcommand(ctx *Context) (err error) {
	// append help to commands
	if len(a.Commands) > 0 {
		if a.Command(helpCommand.Name) == nil && !a.HideHelp {
			a.Commands = append(a.Commands, helpCommand)
			if (HelpFlag != BoolFlag{}) {
				a.appendFlag(HelpFlag)
			}
		}
	}

	// append flags
	if a.EnableBashCompletion {
		a.appendFlag(BashCompletionFlag)
	}

	// parse flags
	set := flagSet(a.Name, a.Flags)
	set.SetOutput(ioutil.Discard)
	err = set.Parse(ctx.Args().Tail())
	nerr := normalizeFlags(a.Flags, set)
	context := NewContext(a, set, ctx)

	if nerr != nil {
		fmt.Fprintln(a.Writer, nerr)
		fmt.Fprintln(a.Writer)
		if len(a.Commands) > 0 {
			ShowSubcommandHelp(context)
		} else {
			ShowCommandHelp(ctx, context.Args().First())
		}
		return nerr
	}

	if err != nil {
		fmt.Fprintln(a.Writer, "Incorrect Usage.")
		fmt.Fprintln(a.Writer)
		ShowSubcommandHelp(context)
		return err
	}

	if checkCompletions(context) {
		return nil
	}

	if len(a.Commands) > 0 {
		if checkSubcommandHelp(context) {
			return nil
		}
	} else {
		if checkCommandHelp(ctx, context.Args().First()) {
			return nil
		}
	}

	if a.After != nil {
		defer func() {
			afterErr := a.After(context)
			if afterErr != nil {
				if err != nil {
					err = NewMultiError(err, afterErr)
				} else {
					err = afterErr
				}
			}
		}()
	}

	if a.Before != nil {
		err := a.Before(context)
		if err != nil {
			return err
		}
	}

	args := context.Args()
	if args.Present() {
		name := args.First()
		c := a.Command(name)
		if c != nil {
			return c.Run(context)
		}
	}

	// Run default Action
	a.Action(context)

	return nil
}

// Returns the named command on App. Returns nil if the command does not exist
func (a *App) Command(name string) *Command {
	for _, c := range a.Commands {
		if c.HasName(name) {
			return &c
		}
	}

	return nil
}

func (a *App) hasFlag(flag Flag) bool {
	for _, f := range a.Flags {
		if flag == f {
			return true
		}
	}

	return false
}

func (a *App) appendFlag(flag Flag) {
	if !a.hasFlag(flag) {
		a.Flags = append(a.Flags, flag)
	}
}

// Author represents someone who has contributed to a cli project.
type Author struct {
	Name  string // The Authors name
	Email string // The Authors email
}

// String makes Author comply to the Stringer interface, to allow an easy print in the templating process
func (a Author) String() string {
	e := ""
	if a.Email != "" {
		e = "<" + a.Email + "> "
	}

	return fmt.Sprintf("%v %v", a.Name, e)
}
<commit_msg>app: don't use compileTime()<commit_after>package cli

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"time"
)

// App is the main structure of a cli application. It is recomended that
// an app be created with the cli.NewApp() function
type App struct {
	// The name of the program. Defaults to os.Args[0]
	Name string
	// Description of the program.
	Usage string
	// Version of the program
	Version string
	// List of commands to execute
	Commands []Command
	// List of help topics (help not associated with a command)
	HelpTopics []HelpTopic
	// List of flags to parse
	Flags []Flag
	// Boolean to enable bash completion commands
	EnableBashCompletion bool
	// Boolean to hide built-in help command
	HideHelp bool
	// Boolean to hide built-in version flag
	HideVersion bool
	// An action to execute when the bash-completion flag is set
	BashComplete func(context *Context)
	// An action to execute before any subcommands are run, but after the context is ready
	// If a non-nil error is returned, no subcommands are run
	Before func(context *Context) error
	// An action to execute after any subcommands are run, but after the subcommand has finished
	// It is run even if Action() panics
	After func(context *Context) error
	// The action to execute when no subcommands are specified
	Action func(context *Context)
	// Execute this function if the proper command cannot be found
	CommandNotFound func(context *Context, command string)
	// Compilation date
	Compiled time.Time
	// List of all authors who contributed
	Authors []Author
	// Copyright of the binary if any
	Copyright string
	// Name of Author (Note: Use App.Authors, this is deprecated)
	Author string
	// Email of Author (Note: Use App.Authors, this is deprecated)
	Email string
	// Writer writer to write output to
	Writer io.Writer
}

// Tries to find out when this binary was compiled.
// Returns the current time if it fails to find it.
func compileTime() time.Time {
	info, err := os.Stat(os.Args[0])
	if err != nil {
		return time.Now()
	}
	return info.ModTime()
}

// Creates a new cli Application with some reasonable defaults for Name, Usage, Version and Action.
func NewApp() *App {
	return &App{
		Name:         os.Args[0],
		Usage:        "A new cli application",
		Version:      "0.0.0",
		BashComplete: DefaultAppComplete,
		Action:       helpCommand.Action,
		Compiled:     time.Now(), // avoid compileTime() because of the Stat()
		Writer:       os.Stdout,
	}
}

// Entry point to the cli app. Parses the arguments slice and routes to the proper flag/args combination
func (a *App) Run(arguments []string) (err error) {
	if a.Author != "" || a.Email != "" {
		a.Authors = append(a.Authors, Author{Name: a.Author, Email: a.Email})
	}

	// append help to commands
	if a.Command(helpCommand.Name) == nil && !a.HideHelp {
		a.Commands = append(a.Commands, helpCommand)
		if (HelpFlag != BoolFlag{}) {
			a.appendFlag(HelpFlag)
		}
	}

	//append version/help flags
	if a.EnableBashCompletion {
		a.appendFlag(BashCompletionFlag)
	}

	if !a.HideVersion {
		a.appendFlag(VersionFlag)
	}

	// parse flags
	set := flagSet(a.Name, a.Flags)
	set.SetOutput(ioutil.Discard)
	err = set.Parse(arguments[1:])
	nerr := normalizeFlags(a.Flags, set)
	if nerr != nil {
		fmt.Fprintln(a.Writer, nerr)
		context := NewContext(a, set, nil)
		ShowAppHelp(context)
		return nerr
	}
	context := NewContext(a, set, nil)

	if err != nil {
		fmt.Fprintln(a.Writer, "Incorrect Usage.")
		fmt.Fprintln(a.Writer)
		ShowAppHelp(context)
		return err
	}

	if checkCompletions(context) {
		return nil
	}

	if checkHelp(context) {
		return nil
	}

	if checkVersion(context) {
		return nil
	}

	if a.After != nil {
		defer func() {
			afterErr := a.After(context)
			if afterErr != nil {
				if err != nil {
					err = NewMultiError(err, afterErr)
				} else {
					err = afterErr
				}
			}
		}()
	}

	if a.Before != nil {
		err := a.Before(context)
		if err != nil {
			return err
		}
	}

	args := context.Args()
	if args.Present() {
		name := args.First()
		c := a.Command(name)
		if c != nil {
			return c.Run(context)
		}
	}

	// Run default Action
	a.Action(context)
	return nil
}

// Another entry point to the cli app, takes care of passing arguments and error handling
func (a *App) RunAndExitOnError() {
	if err := a.Run(os.Args); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}

// Invokes the subcommand given the context, parses ctx.Args() to generate command-specific flags
func (a *App) RunAsSubcommand(ctx *Context) (err error) {
	// append help to commands
	if len(a.Commands) > 0 {
		if a.Command(helpCommand.Name) == nil && !a.HideHelp {
			a.Commands = append(a.Commands, helpCommand)
			if (HelpFlag != BoolFlag{}) {
				a.appendFlag(HelpFlag)
			}
		}
	}

	// append flags
	if a.EnableBashCompletion {
		a.appendFlag(BashCompletionFlag)
	}

	// parse flags
	set := flagSet(a.Name, a.Flags)
	set.SetOutput(ioutil.Discard)
	err = set.Parse(ctx.Args().Tail())
	nerr := normalizeFlags(a.Flags, set)
	context := NewContext(a, set, ctx)

	if nerr != nil {
		fmt.Fprintln(a.Writer, nerr)
		fmt.Fprintln(a.Writer)
		if len(a.Commands) > 0 {
			ShowSubcommandHelp(context)
		} else {
			ShowCommandHelp(ctx, context.Args().First())
		}
		return nerr
	}

	if err != nil {
		fmt.Fprintln(a.Writer, "Incorrect Usage.")
		fmt.Fprintln(a.Writer)
		ShowSubcommandHelp(context)
		return err
	}

	if checkCompletions(context) {
		return nil
	}

	if len(a.Commands) > 0 {
		if checkSubcommandHelp(context) {
			return nil
		}
	} else {
		if checkCommandHelp(ctx, context.Args().First()) {
			return nil
		}
	}

	if a.After != nil {
		defer func() {
			afterErr := a.After(context)
			if afterErr != nil {
				if err != nil {
					err = NewMultiError(err, afterErr)
				} else {
					err = afterErr
				}
			}
		}()
	}

	if a.Before != nil {
		err := a.Before(context)
		if err != nil {
			return err
		}
	}

	args := context.Args()
	if args.Present() {
		name := args.First()
		c := a.Command(name)
		if c != nil {
			return c.Run(context)
		}
	}

	// Run default Action
	a.Action(context)

	return nil
}

// Returns the named command on App. Returns nil if the command does not exist
func (a *App) Command(name string) *Command {
	for _, c := range a.Commands {
		if c.HasName(name) {
			return &c
		}
	}

	return nil
}

func (a *App) hasFlag(flag Flag) bool {
	for _, f := range a.Flags {
		if flag == f {
			return true
		}
	}

	return false
}

func (a *App) appendFlag(flag Flag) {
	if !a.hasFlag(flag) {
		a.Flags = append(a.Flags, flag)
	}
}

// Author represents someone who has contributed to a cli project.
type Author struct {
	Name  string // The Authors name
	Email string // The Authors email
}

// String makes Author comply to the Stringer interface, to allow an easy print in the templating process
func (a Author) String() string {
	e := ""
	if a.Email != "" {
		e = "<" + a.Email + "> "
	}

	return fmt.Sprintf("%v %v", a.Name, e)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	log "github.com/Sirupsen/logrus"
	"github.com/gorilla/mux"
	"html/template"
	"net/http"
	"strings"
)

func hello(w http.ResponseWriter, r *http.Request) {
    t, _ := template.ParseFiles("templates/index.html")
    _ = t.ExecuteTemplate(w, "index.html", "")
}

func main() {
	r := mux.NewRouter()
	r.HandleFunc("/", hello)
	err := http.ListenAndServe(":8080", r)
	if err != nil {
		log.Fatal("ListenAndServe: ", err)
	}
}
<commit_msg>Serve static assets files<commit_after>package main

import (
	"fmt"
	log "github.com/Sirupsen/logrus"
	"github.com/gorilla/mux"
	"html/template"
	"net/http"
	"strings"
)

func hello(w http.ResponseWriter, r *http.Request) {
    t, _ := template.ParseFiles("templates/index.html")
    _ = t.ExecuteTemplate(w, "index.html", "")
}

func main() {
	r := mux.NewRouter()
	r.HandleFunc("/", hello)
	r.PathPrefix("/").Handler(http.StripPrefix("/", http.FileServer(http.Dir("assets/"))))
	err := http.ListenAndServe(":8080", r)
	if err != nil {
		log.Fatal("ListenAndServe: ", err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"path/filepath"
	"regexp"
	"strconv"

	"github.com/codegangsta/cli"
	"github.com/dustin/go-humanize"
	"github.com/mitchellh/go-homedir"
	"github.com/suchipi/domdom-go/domdom"
	"github.com/suchipi/yukkuri/download"
	"github.com/suchipi/yukkuri/unzip"
)

func check(err error) {
	if err != nil {
		fmt.Println("error: ", err)
		os.Exit(1)
	}
}

func main() {
	app := cli.NewApp()
	app.Name = "domdom-go"
	app.Usage = "Download Anime!"
	app.Version = "0.1.1"
	app.Author = "Suchipi Izumi"
	app.Email = "me@suchipi.com"

	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:   "outputdir, o",
			Value:  "~/Downloads/Anime",
			Usage:  "Directory to save into. Directories for series names will be created within this directory.",
			EnvVar: "DOMDOM_OUTPUTDIR",
		},
		cli.StringFlag{
			Name:   "key, k",
			Value:  "",
			Usage:  "DomDomSoft Anime Downloader key. Without a key, there is a limit of 5 episodes downloaded per 24 hours.",
			EnvVar: "DOMDOM_KEY",
		},
	}

	app.Commands = []cli.Command{
		{
			Name:      "search",
			ShortName: "s",
			Usage:     "Search for an anime by title",
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "regex, r, term, t",
					Value: "",
					Usage: "Search term. It will be evaluated as a regular expression.",
				},
			},
			Action: searchAction,
		},
		{
			Name:      "listepisodes",
			ShortName: "l",
			Usage:     "List available episodes for a series",
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "title, t",
					Value: "",
					Usage: "Title of series",
				},
			},
			Action: listepisodesAction,
		},
		{
			Name:      "download",
			ShortName: "d",
			Usage:     "Download episode(s)",
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "title, t",
					Value: "",
					Usage: "Title of series to download",
				},
				cli.StringFlag{
					Name:  "episode, e",
					Value: "",
					Usage: "Episode filename to download",
				},
				cli.StringFlag{
					Name:  "episodeid, i",
					Value: "",
					Usage: "Episode id to download",
				},
				cli.BoolFlag{
					Name:  "all, a",
					Usage: "Download entire series",
				},
				cli.BoolFlag{
					Name:  "redownload, r",
					Usage: "Redownload existing files",
				},
				cli.BoolFlag{
					Name:  "keep-zip-files, z",
					Usage: "Keep zip files instead of removing them after extraction",
				},
			},
			Action: downloadAction,
		},
	}

	app.Action = func(c *cli.Context) {
		cli.ShowAppHelp(c)
	}

	app.Run(os.Args)
}

func updateAction(c *cli.Context) {
	savepath, err := homedir.Expand(c.GlobalString("animelist"))
	check(err)
	if savepath == "" {
		fmt.Println(`No save location for the anime list was specified.
If you used the -l or --animelist flags, please check your input.
Otherwise, check the contents of the $DOMDOM_ANIMELIST environment variable.
You can unset the environment variable to use the application default value, which is ~/.domdom_anime_list.`)
	}

	fmt.Println("Downloading anime list...")
	err = domdom.UpdateAnimeList(savepath)
	check(err)

	fmt.Printf("Anime list was downloaded and written to %s.\n", savepath)
}

func listepisodesAction(c *cli.Context) {
	title := c.String("title")
	key := c.GlobalString("key")

	if title == "" {
		fmt.Println("Please specify a series title to list episodes of.")
		cli.ShowCommandHelp(c, "listepisodes")
		return
	}

	episodes, err := domdom.ListEpisodes(title, key)
	check(err)

	fmt.Printf("Episodes for %s:\n", title)
	for index, episode := range episodes {
		sizeInt, err := strconv.Atoi(episode.FileSize)
		check(err)
		niceSize := humanize.Bytes(uint64(sizeInt))
		fmt.Printf("%d: %s (%s)\n", index+1, episode.FileName, niceSize)
	}
}

func downloadAction(c *cli.Context) {
	title := c.String("title")
	filename := c.String("episode")
	id := c.String("episodeid")
	key := c.GlobalString("key")
	redownload := c.Bool("redownload")
	keepzipfiles := c.Bool("keep-zip-files")
	all := c.Bool("all")

	if title == "" {
		fmt.Println("Please specify a series title to download episodes from.")
		cli.ShowCommandHelp(c, "download")
		return
	}
	if filename == "" && id == "" && all == false {
		fmt.Println("Please specify an episode to download.")
		cli.ShowCommandHelp(c, "download")
		return
	}
	if key == "" {
		fmt.Println("Warning: No key was specified. You will only be able to get download links 5 times per 24 hours.")
	}

	var episodes []domdom.Episode
	var err error

	if filename != "" {
		episode, err := domdom.FindEpisodeByName(title, filename, key)
		check(err)
		episodes = make([]domdom.Episode, 1)
		episodes[0] = episode
	} else if id != "" {
		episode, err := domdom.FindEpisodeByIndex(title, id, key)
		check(err)
		episodes = make([]domdom.Episode, 1)
		episodes[0] = episode
	} else if all {
		episodes, err = domdom.ListEpisodes(title, key)
		check(err)
	}

	outputdir, err := homedir.Expand(c.GlobalString("outputdir"))
	check(err)

	for _, episode := range episodes {
		downloadEpisode(episode, outputdir, key, redownload, keepzipfiles)
	}

	fmt.Println("Download complete!")
}

func downloadEpisode(episode domdom.Episode, outputdir, key string, redownload, keepzipfiles bool) error {
	links, err := domdom.GetDownloadLinks(episode, key)
	check(err)

	outputdir = filepath.Join(outputdir, episode.SeriesName)

	if _, err := os.Stat(filepath.Join(outputdir, episode.FileName)); err == nil {
		fmt.Printf("File %s already exists... ", episode.FileName)
		if redownload {
			fmt.Print("redownload requested.\n")
		} else {
			fmt.Print("not redownloading.\n")
			return nil
		}
	}

	zip_parts := make([]string, len(links))

	fmt.Printf("Downloading file %s from series %s...\n", episode.FileName, episode.SeriesName)
	for index, link := range links {
		dl, err := download.New(link, outputdir)
		check(err)

		finalPath := filepath.Join(dl.OutputDir, dl.FileName)
		shouldDownload := true

		fmt.Printf("Part %d of %d: %s (%s)\n", index+1, len(links), dl.FileName, humanize.Bytes(dl.FileSize))

		if _, err := os.Stat(finalPath); err == nil {
			fmt.Printf("File %s exists... ", dl.FileName)
			if redownload {
				fmt.Print("redownload requested.\n")
				shouldDownload = true
			} else {
				fmt.Print("not redownloading.\n")
				shouldDownload = false
			}
		}

		if shouldDownload {
			err = dl.Run()
			check(err)
		}
		zip_parts[index] = filepath.Join(outputdir, dl.FileName)
	}

	fmt.Printf("Unzipping file %s from %d parts...\n", episode.FileName, len(zip_parts))
	err = unzip.Multiple(zip_parts, outputdir)
	check(err)

	if keepzipfiles != true {
		for _, file := range zip_parts {
			os.Remove(file)
		}
	}
	return nil
}

func searchAction(c *cli.Context) {
	term := c.String("regex")
	if term == "" {
		fmt.Println("Please specify a search term.")
		cli.ShowCommandHelp(c, "search")
		return
	}
	regex, err := regexp.Compile(term)
	check(err)
	fmt.Println("Fetching anime list...")
	animes, err := domdom.GetAnimeList()
	check(err)
	output := make([]string, 0)
	for _, anime := range animes {
		if regex.MatchString(anime.Title) {
			output = append(output, fmt.Sprintf("%s (%s episodes)", anime.Title, anime.NumFiles))
		}
	}

	for _, line := range output {
		fmt.Println(line)
	}

	fmt.Printf("%d total result(s).\n", len(output))
}<commit_msg>Use humanize.IBytes instead of humanize.Bytes<commit_after>package main

import (
	"fmt"
	"os"
	"path/filepath"
	"regexp"
	"strconv"

	"github.com/codegangsta/cli"
	"github.com/dustin/go-humanize"
	"github.com/mitchellh/go-homedir"
	"github.com/suchipi/domdom-go/domdom"
	"github.com/suchipi/yukkuri/download"
	"github.com/suchipi/yukkuri/unzip"
)

func check(err error) {
	if err != nil {
		fmt.Println("error: ", err)
		os.Exit(1)
	}
}

func main() {
	app := cli.NewApp()
	app.Name = "domdom-go"
	app.Usage = "Download Anime!"
	app.Version = "0.1.1"
	app.Author = "Suchipi Izumi"
	app.Email = "me@suchipi.com"

	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:   "outputdir, o",
			Value:  "~/Downloads/Anime",
			Usage:  "Directory to save into. Directories for series names will be created within this directory.",
			EnvVar: "DOMDOM_OUTPUTDIR",
		},
		cli.StringFlag{
			Name:   "key, k",
			Value:  "",
			Usage:  "DomDomSoft Anime Downloader key. Without a key, there is a limit of 5 episodes downloaded per 24 hours.",
			EnvVar: "DOMDOM_KEY",
		},
	}

	app.Commands = []cli.Command{
		{
			Name:      "search",
			ShortName: "s",
			Usage:     "Search for an anime by title",
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "regex, r, term, t",
					Value: "",
					Usage: "Search term. It will be evaluated as a regular expression.",
				},
			},
			Action: searchAction,
		},
		{
			Name:      "listepisodes",
			ShortName: "l",
			Usage:     "List available episodes for a series",
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "title, t",
					Value: "",
					Usage: "Title of series",
				},
			},
			Action: listepisodesAction,
		},
		{
			Name:      "download",
			ShortName: "d",
			Usage:     "Download episode(s)",
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "title, t",
					Value: "",
					Usage: "Title of series to download",
				},
				cli.StringFlag{
					Name:  "episode, e",
					Value: "",
					Usage: "Episode filename to download",
				},
				cli.StringFlag{
					Name:  "episodeid, i",
					Value: "",
					Usage: "Episode id to download",
				},
				cli.BoolFlag{
					Name:  "all, a",
					Usage: "Download entire series",
				},
				cli.BoolFlag{
					Name:  "redownload, r",
					Usage: "Redownload existing files",
				},
				cli.BoolFlag{
					Name:  "keep-zip-files, z",
					Usage: "Keep zip files instead of removing them after extraction",
				},
			},
			Action: downloadAction,
		},
	}

	app.Action = func(c *cli.Context) {
		cli.ShowAppHelp(c)
	}

	app.Run(os.Args)
}

func updateAction(c *cli.Context) {
	savepath, err := homedir.Expand(c.GlobalString("animelist"))
	check(err)
	if savepath == "" {
		fmt.Println(`No save location for the anime list was specified.
If you used the -l or --animelist flags, please check your input.
Otherwise, check the contents of the $DOMDOM_ANIMELIST environment variable.
You can unset the environment variable to use the application default value, which is ~/.domdom_anime_list.`)
	}

	fmt.Println("Downloading anime list...")
	err = domdom.UpdateAnimeList(savepath)
	check(err)

	fmt.Printf("Anime list was downloaded and written to %s.\n", savepath)
}

func listepisodesAction(c *cli.Context) {
	title := c.String("title")
	key := c.GlobalString("key")

	if title == "" {
		fmt.Println("Please specify a series title to list episodes of.")
		cli.ShowCommandHelp(c, "listepisodes")
		return
	}

	episodes, err := domdom.ListEpisodes(title, key)
	check(err)

	fmt.Printf("Episodes for %s:\n", title)
	for index, episode := range episodes {
		sizeInt, err := strconv.Atoi(episode.FileSize)
		check(err)
		niceSize := humanize.IBytes(uint64(sizeInt))
		fmt.Printf("%d: %s (%s)\n", index+1, episode.FileName, niceSize)
	}
}

func downloadAction(c *cli.Context) {
	title := c.String("title")
	filename := c.String("episode")
	id := c.String("episodeid")
	key := c.GlobalString("key")
	redownload := c.Bool("redownload")
	keepzipfiles := c.Bool("keep-zip-files")
	all := c.Bool("all")

	if title == "" {
		fmt.Println("Please specify a series title to download episodes from.")
		cli.ShowCommandHelp(c, "download")
		return
	}
	if filename == "" && id == "" && all == false {
		fmt.Println("Please specify an episode to download.")
		cli.ShowCommandHelp(c, "download")
		return
	}
	if key == "" {
		fmt.Println("Warning: No key was specified. You will only be able to get download links 5 times per 24 hours.")
	}

	var episodes []domdom.Episode
	var err error

	if filename != "" {
		episode, err := domdom.FindEpisodeByName(title, filename, key)
		check(err)
		episodes = make([]domdom.Episode, 1)
		episodes[0] = episode
	} else if id != "" {
		episode, err := domdom.FindEpisodeByIndex(title, id, key)
		check(err)
		episodes = make([]domdom.Episode, 1)
		episodes[0] = episode
	} else if all {
		episodes, err = domdom.ListEpisodes(title, key)
		check(err)
	}

	outputdir, err := homedir.Expand(c.GlobalString("outputdir"))
	check(err)

	for _, episode := range episodes {
		downloadEpisode(episode, outputdir, key, redownload, keepzipfiles)
	}

	fmt.Println("Download complete!")
}

func downloadEpisode(episode domdom.Episode, outputdir, key string, redownload, keepzipfiles bool) error {
	links, err := domdom.GetDownloadLinks(episode, key)
	check(err)

	outputdir = filepath.Join(outputdir, episode.SeriesName)

	if _, err := os.Stat(filepath.Join(outputdir, episode.FileName)); err == nil {
		fmt.Printf("File %s already exists... ", episode.FileName)
		if redownload {
			fmt.Print("redownload requested.\n")
		} else {
			fmt.Print("not redownloading.\n")
			return nil
		}
	}

	zip_parts := make([]string, len(links))

	fmt.Printf("Downloading file %s from series %s...\n", episode.FileName, episode.SeriesName)
	for index, link := range links {
		dl, err := download.New(link, outputdir)
		check(err)

		finalPath := filepath.Join(dl.OutputDir, dl.FileName)
		shouldDownload := true

		fmt.Printf("Part %d of %d: %s (%s)\n", index+1, len(links), dl.FileName, humanize.IBytes(dl.FileSize))

		if _, err := os.Stat(finalPath); err == nil {
			fmt.Printf("File %s exists... ", dl.FileName)
			if redownload {
				fmt.Print("redownload requested.\n")
				shouldDownload = true
			} else {
				fmt.Print("not redownloading.\n")
				shouldDownload = false
			}
		}

		if shouldDownload {
			err = dl.Run()
			check(err)
		}
		zip_parts[index] = filepath.Join(outputdir, dl.FileName)
	}

	fmt.Printf("Unzipping file %s from %d parts...\n", episode.FileName, len(zip_parts))
	err = unzip.Multiple(zip_parts, outputdir)
	check(err)

	if keepzipfiles != true {
		for _, file := range zip_parts {
			os.Remove(file)
		}
	}
	return nil
}

func searchAction(c *cli.Context) {
	term := c.String("regex")
	if term == "" {
		fmt.Println("Please specify a search term.")
		cli.ShowCommandHelp(c, "search")
		return
	}
	regex, err := regexp.Compile(term)
	check(err)
	fmt.Println("Fetching anime list...")
	animes, err := domdom.GetAnimeList()
	check(err)
	output := make([]string, 0)
	for _, anime := range animes {
		if regex.MatchString(anime.Title) {
			output = append(output, fmt.Sprintf("%s (%s episodes)", anime.Title, anime.NumFiles))
		}
	}

	for _, line := range output {
		fmt.Println(line)
	}

	fmt.Printf("%d total result(s).\n", len(output))
}<|endoftext|>


<commit_before>package crater

import (
	"encoding/json"
	"fmt"
	"net/http"
	"path"
	"regexp"
	"time"

	"github.com/gavruk/crater/cookie"
	"github.com/gavruk/crater/session"
)

type handlerFunc func(*Request, *Response)

// App recieves settings and handles http requests
type App struct {
	craterRequestHandler *regexpHandler
	htmlTemplates        *craterTemplate
	settings             *Settings
}

func NewApp(settings *Settings) App {
	app := App{}
	app.craterRequestHandler = newCraterHandler()
	app.htmlTemplates = &craterTemplate{}
	if settings != nil {
		app.settings = &Settings{}
		app.Settings(settings)
	} else {
		app.settings = DefaultSettings()
	}

	return app
}

// Settings recieves settings application
func (app *App) Settings(settings *Settings) {
	if settings.ViewsPath == "" {
		app.settings.ViewsPath = "."
	} else {
		app.settings.ViewsPath = settings.ViewsPath
	}
	if settings.StaticFilesPath == "" {
		app.settings.StaticFilesPath = "."
	} else {
		app.settings.StaticFilesPath = settings.StaticFilesPath
	}
	if settings.ViewExtension == "" {
		app.settings.ViewExtension = "html"
	} else {
		app.settings.ViewExtension = settings.ViewExtension
	}
}

func (app App) UseSessionStore(store session.SessionStore, timeout time.Duration) {
	sessionManager = session.NewSessionManager(store, timeout)
}

// Get handles GET requests
func (app App) Get(url string, handler handlerFunc) {
	app.craterRequestHandler.handleGet(regexp.MustCompile("^"+url+"$"), func(w http.ResponseWriter, r *http.Request) {
		req := newRequest(r, sessionManager.GetSession(w, r), cookie.NewCookieManager(w, r))
		res := newResponse(w)
		handler(req, res)

		app.sendResponse(w, r, res)
	})
}

// Post handles POST requests
func (app App) Post(url string, handler handlerFunc) {
	app.craterRequestHandler.handlePost(regexp.MustCompile("^"+url+"$"), func(w http.ResponseWriter, r *http.Request) {
		req := newRequest(r, sessionManager.GetSession(w, r), cookie.NewCookieManager(w, r))
		res := newResponse(w)
		handler(req, res)

		app.sendResponse(w, r, res)
	})
}

// Put handles PUT requests
func (app App) Put(url string, handler handlerFunc) {
	app.craterRequestHandler.handlePut(regexp.MustCompile("^"+url+"$"), func(w http.ResponseWriter, r *http.Request) {
		req := newRequest(r, sessionManager.GetSession(w, r), cookie.NewCookieManager(w, r))
		res := newResponse(w)
		handler(req, res)

		app.sendResponse(w, r, res)
	})
}

// Delete handles DELETE requests
func (app App) Delete(url string, handler handlerFunc) {
	app.craterRequestHandler.handleDelete(regexp.MustCompile("^"+url+"$"), func(w http.ResponseWriter, r *http.Request) {
		req := newRequest(r, sessionManager.GetSession(w, r), cookie.NewCookieManager(w, r))
		res := newResponse(w)
		handler(req, res)

		app.sendResponse(w, r, res)
	})
}

// NotFound overrides 404 status result
func (app App) NotFound(handler handlerFunc) {
	app.craterRequestHandler.notFoundHandler = func(w http.ResponseWriter, r *http.Request) {
		req := newRequest(r, sessionManager.GetSession(w, r), cookie.NewCookieManager(w, r))
		res := newResponse(w)
		handler(req, res)

		app.sendResponse(w, r, res)
	}
}

// HandleStaticContent handles Statis Content
func (app App) HandleStaticContent(url string) {
	app.craterRequestHandler.handleStatic(regexp.MustCompile("^"+url), url, http.Dir(app.settings.StaticFilesPath))
}

func (app App) Listen(serverURL string) {
	err := app.htmlTemplates.parseFolder(app.settings.ViewsPath, app.settings.ViewExtension)
	if err != nil {
		panic(err)
	}

	http.ListenAndServe(serverURL, app.craterRequestHandler)
}

func (app App) sendResponse(w http.ResponseWriter, r *http.Request, res *Response) {
	switch res.responseType {
	case response_template:
		app.sendTemplate(w, res.model, res.templateName)
	case response_view:
		app.sendView(w, res.model, res.viewName, app.settings.ViewExtension)
	case response_json:
		app.sendJson(w, res.model)
	case response_redirect:
		app.redirect(w, r, res.redirectUrl)
	case response_string:
		app.sendString(w, res.responseString)
	}
}

func (app App) sendJson(w http.ResponseWriter, model interface{}) {
	w.Header().Set("Content-Type", ct_JSON)
	jsonObj, _ := json.Marshal(model)
	fmt.Fprint(w, string(jsonObj))
}

func (app App) sendString(w http.ResponseWriter, str string) {
	fmt.Fprint(w, str)
}

func (app App) sendTemplate(w http.ResponseWriter, model interface{}, templateName string) {
	if templateName == "" {
		panic("crater: TemplateName cannot be empty string")
	}

	app.htmlTemplates.render(w, templateName, model)
}

func (app App) sendView(w http.ResponseWriter, model interface{}, viewName string, extension string) {
	if viewName == "" {
		panic("crater: ViewName cannot be empty string")
	}

	var filePath = path.Join(app.settings.ViewsPath, viewName+"."+extension)
	app.htmlTemplates.renderView(w, filePath, model)
}

func (app App) redirect(w http.ResponseWriter, r *http.Request, url string) {
	if url == "" {
		panic("crater: RedirectUrl cannot be empty string")
	}

	http.Redirect(w, r, url, http.StatusMovedPermanently)
}
<commit_msg>add middleware<commit_after>package crater

import (
	"encoding/json"
	"fmt"
	"net/http"
	"path"
	"regexp"
	"time"

	"github.com/gavruk/crater/cookie"
	"github.com/gavruk/crater/session"
)

type handlerFunc func(*Request, *Response)

// App recieves settings and handles http requests
type App struct {
	craterRequestHandler *regexpHandler
	htmlTemplates        *craterTemplate
	settings             *Settings
	middleware           []handlerFunc
}

func NewApp(settings *Settings) App {
	app := App{}
	app.craterRequestHandler = newCraterHandler()
	app.htmlTemplates = &craterTemplate{}
	app.middleware = make([]handlerFunc, 0)
	if settings != nil {
		app.settings = &Settings{}
		app.Settings(settings)
	} else {
		app.settings = DefaultSettings()
	}

	return app
}

// Settings recieves settings application
func (app *App) Settings(settings *Settings) {
	if settings.ViewsPath == "" {
		app.settings.ViewsPath = "."
	} else {
		app.settings.ViewsPath = settings.ViewsPath
	}
	if settings.StaticFilesPath == "" {
		app.settings.StaticFilesPath = "."
	} else {
		app.settings.StaticFilesPath = settings.StaticFilesPath
	}
	if settings.ViewExtension == "" {
		app.settings.ViewExtension = "html"
	} else {
		app.settings.ViewExtension = settings.ViewExtension
	}
}

func (app *App) UseSessionStore(store session.SessionStore, timeout time.Duration) {
	sessionManager = session.NewSessionManager(store, timeout)
}

func (app *App) Use(handler handlerFunc) {
	app.middleware = append(app.middleware, handler)
}

// Get handles GET requests
func (app *App) Get(url string, handler handlerFunc) {
	app.craterRequestHandler.handleGet(regexp.MustCompile("^"+url+"$"), func(w http.ResponseWriter, r *http.Request) {
		req := newRequest(r, sessionManager.GetSession(w, r), cookie.NewCookieManager(w, r))
		res := newResponse(w)

		if returnsResponse := app.serveMiddleware(req, res); returnsResponse {
			return
		}

		handler(req, res)

		app.sendResponse(req, res)
	})
}

// Post handles POST requests
func (app *App) Post(url string, handler handlerFunc) {
	app.craterRequestHandler.handlePost(regexp.MustCompile("^"+url+"$"), func(w http.ResponseWriter, r *http.Request) {
		req := newRequest(r, sessionManager.GetSession(w, r), cookie.NewCookieManager(w, r))
		res := newResponse(w)
		handler(req, res)

		app.sendResponse(req, res)
	})
}

// Put handles PUT requests
func (app *App) Put(url string, handler handlerFunc) {
	app.craterRequestHandler.handlePut(regexp.MustCompile("^"+url+"$"), func(w http.ResponseWriter, r *http.Request) {
		req := newRequest(r, sessionManager.GetSession(w, r), cookie.NewCookieManager(w, r))
		res := newResponse(w)
		handler(req, res)

		app.sendResponse(req, res)
	})
}

// Delete handles DELETE requests
func (app *App) Delete(url string, handler handlerFunc) {
	app.craterRequestHandler.handleDelete(regexp.MustCompile("^"+url+"$"), func(w http.ResponseWriter, r *http.Request) {
		req := newRequest(r, sessionManager.GetSession(w, r), cookie.NewCookieManager(w, r))
		res := newResponse(w)
		handler(req, res)

		app.sendResponse(req, res)
	})
}

// NotFound overrides 404 status result
func (app *App) NotFound(handler handlerFunc) {
	app.craterRequestHandler.notFoundHandler = func(w http.ResponseWriter, r *http.Request) {
		req := newRequest(r, sessionManager.GetSession(w, r), cookie.NewCookieManager(w, r))
		res := newResponse(w)
		handler(req, res)

		app.sendResponse(req, res)
	}
}

// HandleStaticContent handles Statis Content
func (app *App) HandleStaticContent(url string) {
	app.craterRequestHandler.handleStatic(regexp.MustCompile("^"+url), url, http.Dir(app.settings.StaticFilesPath))
}

func (app *App) Listen(serverURL string) {
	err := app.htmlTemplates.parseFolder(app.settings.ViewsPath, app.settings.ViewExtension)
	if err != nil {
		panic(err)
	}

	http.ListenAndServe(serverURL, app.craterRequestHandler)
}

func (app *App) sendResponse(req *Request, res *Response) {
	switch res.responseType {
	case response_template:
		app.sendTemplate(res.raw, res.model, res.templateName)
	case response_view:
		app.sendView(res.raw, res.model, res.viewName, app.settings.ViewExtension)
	case response_json:
		app.sendJson(res.raw, res.model)
	case response_redirect:
		app.redirect(res.raw, req.raw, res.redirectUrl)
	case response_string:
		app.sendString(res.raw, res.responseString)
	}
}

func (app *App) sendJson(w http.ResponseWriter, model interface{}) {
	w.Header().Set("Content-Type", ct_JSON)
	jsonObj, _ := json.Marshal(model)
	fmt.Fprint(w, string(jsonObj))
}

func (app *App) sendString(w http.ResponseWriter, str string) {
	fmt.Fprint(w, str)
}

func (app *App) sendTemplate(w http.ResponseWriter, model interface{}, templateName string) {
	if templateName == "" {
		panic("crater: TemplateName cannot be empty string")
	}

	app.htmlTemplates.render(w, templateName, model)
}

func (app *App) sendView(w http.ResponseWriter, model interface{}, viewName string, extension string) {
	if viewName == "" {
		panic("crater: ViewName cannot be empty string")
	}

	var filePath = path.Join(app.settings.ViewsPath, viewName+"."+extension)
	app.htmlTemplates.renderView(w, filePath, model)
}

func (app *App) redirect(w http.ResponseWriter, r *http.Request, url string) {
	if url == "" {
		panic("crater: RedirectUrl cannot be empty string")
	}

	http.Redirect(w, r, url, http.StatusMovedPermanently)
}

func (app *App) serveMiddleware(req *Request, res *Response) (returnsRespose bool) {
	returnsRespose = false
	for _, mw := range app.middleware {
		mw(req, res)
		if res.responseType != 0 {
			app.sendResponse(req, res)
			returnsRespose = true
			break
		}
	}
	return
}
<|endoftext|>


<commit_before>// Copyright (c) 2014, Ben Morgan. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package pacman

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"time"
)

type aurResponse struct {
	ResultCount int
	Results     aurPkgInfo
}

type aurPkgInfo struct {
	ID             uint64
	Name           string
	Version        string
	Description    string
	URL            string
	NumVotes       int
	OutofDate      bool
	Maintainer     string
	FirstSubmitted time.Time
	LastModified   time.Time
	License        string
	URLPath        string
}

func generateURL(arg string) string {
	queryURL := "https://aur.archlinux.org/rpc.php?type=info&arg=%s"
	return fmt.Sprintf(queryURL, url.QueryEscape(arg))
}

// ReadAUR reads package information from the Arch Linux User Repository
// online.  If the package cannot be found, but no unexpected error occurred,
// then (nil, nil) is returned.
//
// Note that only a few fields in the resulting Package are actually filled in,
// namely Origin, Name, Version, Description, URL, and License. This is all the
// information that we are able to retrieve.
func ReadAUR(pkgname string) (*Package, error) {
	q := generateURL(pkgname)
	resp, err := http.Get(q)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	var msg aurResponse
	dec := json.NewDecoder(resp.Body)
	err = dec.Decode(&msg)
	if err != nil {
		return nil, err
	}

	if msg.ResultCount != 1 {
		if msg.ResultCount > 1 {
			return nil, fmt.Errorf("unexpected: too many packages (%d) in resultset", msg.ResultCount)
		}
		return nil, nil
	}
	info := msg.Results
	pkg := Package{
		Origin:      AUROrigin,
		Name:        info.Name,
		Version:     info.Version,
		Description: info.Description,
		URL:         info.URL,
		License:     info.License,
	}

	return &pkg, nil
}
<commit_msg>Bugfix in ReadAUR and adding ConcurrentlyReadAUR<commit_after>// Copyright (c) 2014, Ben Morgan. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package pacman

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"sync"
)

type aurResponse struct {
	ResultCount int
	Results     aurPkgInfo
}

type aurPkgInfo struct {
	ID             uint64
	Name           string
	Version        string
	Description    string
	URL            string
	NumVotes       int
	OutOfDate      int
	Maintainer     string
	FirstSubmitted uint64
	LastModified   uint64
	License        string
	URLPath        string
}

func generateURL(arg string) string {
	queryURL := "https://aur.archlinux.org/rpc.php?type=info&arg=%s"
	return fmt.Sprintf(queryURL, url.QueryEscape(arg))
}

// ReadAUR reads package information from the Arch Linux User Repository
// online.  If the package cannot be found, but no unexpected error occurred,
// then (nil, nil) is returned.
//
// Note that only a few fields in the resulting Package are actually filled in,
// namely Origin, Name, Version, Description, URL, and License. This is all the
// information that we are able to retrieve.
func ReadAUR(pkgname string) (*Package, error) {
	q := generateURL(pkgname)
	resp, err := http.Get(q)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	var msg aurResponse
	dec := json.NewDecoder(resp.Body)
	err = dec.Decode(&msg)
	if err != nil || msg.ResultCount != 1 {
		// If there is an error at this stage, treat it like 'not found'.
		return nil, nil
	}

	info := msg.Results
	pkg := Package{
		Origin:      AUROrigin,
		Name:        info.Name,
		Version:     info.Version,
		Description: info.Description,
		URL:         info.URL,
		License:     info.License,
	}

	return &pkg, nil
}

func ConcurrentlyReadAUR(pkgnames []string, n int, ch chan<- error) map[string]*Package {
	var wg sync.WaitGroup
	var mu sync.Mutex
	jobs := make(chan string)
	pkgs := make(map[string]*Package, len(pkgnames))

	jobber := func() {
		for j := range jobs {
			p, err := ReadAUR(j)
			if err != nil {
				if ch != nil {
					ch <- err
				}
				continue
			}

			mu.Lock()
			pkgs[j] = p
			mu.Unlock()
		}
		wg.Done()
	}

	// Start n concurrent job takers
	for i := 0; i < n; i++ {
		wg.Add(1)
		go jobber()
	}

	// Start a producer of jobs
	go func() {
		for _, q := range pkgnames {
			jobs <- q
		}
		close(jobs)
	}()

	wg.Wait()
	return pkgs
}
<|endoftext|>


<commit_before>/*
   Copyright 2020 Docker Compose CLI authors

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

package api

import (
	"github.com/pkg/errors"
)

const (
	//ExitCodeLoginRequired exit code when command cannot execute because it requires cloud login
	// This will be used by VSCode to detect when creating context if the user needs to login first
	ExitCodeLoginRequired = 5
)

var (
	// ErrNotFound is returned when an object is not found
	ErrNotFound = errors.New("not found")
	// ErrAlreadyExists is returned when an object already exists
	ErrAlreadyExists = errors.New("already exists")
	// ErrForbidden is returned when an operation is not permitted
	ErrForbidden = errors.New("forbidden")
	// ErrUnknown is returned when the error type is unmapped
	ErrUnknown = errors.New("unknown")
	// ErrLoginFailed is returned when login failed
	ErrLoginFailed = errors.New("login failed")
	// ErrLoginRequired is returned when login is required for a specific action
	ErrLoginRequired = errors.New("login required")
	// ErrNotImplemented is returned when a backend doesn't implement
	// an action
	ErrNotImplemented = errors.New("not implemented")
	// ErrCanceled is returned when the command was canceled by user
	ErrCanceled = errors.New("canceled")
	// ErrParsingFailed is returned when a string cannot be parsed
	ErrParsingFailed = errors.New("parsing failed")
	// ErrWrongContextType is returned when the caller tries to get a context
	// with the wrong type
	ErrWrongContextType = errors.New("wrong context type")
)

// IsNotFoundError returns true if the unwrapped error is ErrNotFound
func IsNotFoundError(err error) bool {
	return errors.Is(err, ErrNotFound)
}

// IsAlreadyExistsError returns true if the unwrapped error is ErrAlreadyExists
func IsAlreadyExistsError(err error) bool {
	return errors.Is(err, ErrAlreadyExists)
}

// IsForbiddenError returns true if the unwrapped error is ErrForbidden
func IsForbiddenError(err error) bool {
	return errors.Is(err, ErrForbidden)
}

// IsUnknownError returns true if the unwrapped error is ErrUnknown
func IsUnknownError(err error) bool {
	return errors.Is(err, ErrUnknown)
}

// IsErrNotImplemented returns true if the unwrapped error is ErrNotImplemented
func IsErrNotImplemented(err error) bool {
	return errors.Is(err, ErrNotImplemented)
}

// IsErrParsingFailed returns true if the unwrapped error is ErrParsingFailed
func IsErrParsingFailed(err error) bool {
	return errors.Is(err, ErrParsingFailed)
}

// IsErrCanceled returns true if the unwrapped error is ErrCanceled
func IsErrCanceled(err error) bool {
	return errors.Is(err, ErrCanceled)
}
<commit_msg>Add UnsupportedFlag to the declared errors<commit_after>/*
   Copyright 2020 Docker Compose CLI authors

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

package api

import (
	"github.com/pkg/errors"
)

const (
	//ExitCodeLoginRequired exit code when command cannot execute because it requires cloud login
	// This will be used by VSCode to detect when creating context if the user needs to login first
	ExitCodeLoginRequired = 5
)

var (
	// ErrNotFound is returned when an object is not found
	ErrNotFound = errors.New("not found")
	// ErrAlreadyExists is returned when an object already exists
	ErrAlreadyExists = errors.New("already exists")
	// ErrForbidden is returned when an operation is not permitted
	ErrForbidden = errors.New("forbidden")
	// ErrUnknown is returned when the error type is unmapped
	ErrUnknown = errors.New("unknown")
	// ErrLoginFailed is returned when login failed
	ErrLoginFailed = errors.New("login failed")
	// ErrLoginRequired is returned when login is required for a specific action
	ErrLoginRequired = errors.New("login required")
	// ErrNotImplemented is returned when a backend doesn't implement
	// an action
	ErrNotImplemented = errors.New("not implemented")
	// ErrUnsupportedFlag is returned when a backend doesn't support a flag
	ErrUnsupportedFlag = errors.New("unsupported flag")
	// ErrCanceled is returned when the command was canceled by user
	ErrCanceled = errors.New("canceled")
	// ErrParsingFailed is returned when a string cannot be parsed
	ErrParsingFailed = errors.New("parsing failed")
	// ErrWrongContextType is returned when the caller tries to get a context
	// with the wrong type
	ErrWrongContextType = errors.New("wrong context type")
)

// IsNotFoundError returns true if the unwrapped error is ErrNotFound
func IsNotFoundError(err error) bool {
	return errors.Is(err, ErrNotFound)
}

// IsAlreadyExistsError returns true if the unwrapped error is ErrAlreadyExists
func IsAlreadyExistsError(err error) bool {
	return errors.Is(err, ErrAlreadyExists)
}

// IsForbiddenError returns true if the unwrapped error is ErrForbidden
func IsForbiddenError(err error) bool {
	return errors.Is(err, ErrForbidden)
}

// IsUnknownError returns true if the unwrapped error is ErrUnknown
func IsUnknownError(err error) bool {
	return errors.Is(err, ErrUnknown)
}

// IsErrUnsupportedFlag returns true if the unwrapped error is ErrUnsupportedFlag
func IsErrUnsupportedFlag(err error) bool {
	return errors.Is(err, ErrUnsupportedFlag)
}

// IsErrNotImplemented returns true if the unwrapped error is ErrNotImplemented
func IsErrNotImplemented(err error) bool {
	return errors.Is(err, ErrNotImplemented)
}

// IsErrParsingFailed returns true if the unwrapped error is ErrParsingFailed
func IsErrParsingFailed(err error) bool {
	return errors.Is(err, ErrParsingFailed)
}

// IsErrCanceled returns true if the unwrapped error is ErrCanceled
func IsErrCanceled(err error) bool {
	return errors.Is(err, ErrCanceled)
}
<|endoftext|>


<commit_before>// Package k8s is a facade over (super-terrible, very difficult to understand)
// client-go to provide a higher-level interface to Kubernetes, with support
// for simple, high-level APIs for watching resources (including from stable,
// long-running processes) and implementing basic controllers.
//
// It is intended to offer the same API for (nearly) every Kubernetes resource,
// including easy CRD access without codegen.
package k8s

import (
	"fmt"
	"strings"

	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime/schema"

	"k8s.io/client-go/discovery"
	"k8s.io/client-go/dynamic"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/clientcmd"

	"github.com/pkg/errors"
)

// KubeInfo holds the data required to talk to a cluster
type KubeInfo struct {
	Kubeconfig   string
	Context      string
	Namespace    string
	clientConfig clientcmd.ClientConfig
}

// NewKubeInfo returns a useable KubeInfo, handling optional
// kubeconfig, context, and namespace.
func NewKubeInfo(configfile, context, namespace string) (*KubeInfo, error) {
	// Find the correct kube config file
	configfilesearch := clientcmd.NewDefaultClientConfigLoadingRules()
	if len(configfile) != 0 {
		configfilesearch.ExplicitPath = configfile
	}

	// Possibly override context and namespace
	overrides := &clientcmd.ConfigOverrides{}
	if len(context) != 0 {
		overrides.CurrentContext = context
	}
	if len(namespace) != 0 {
		overrides.Context.Namespace = namespace
	}

	// Construct the config
	kubeconfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(configfilesearch, overrides)

	// Extract resulting context and namespace
	resultContext := context
	if len(context) == 0 {
		apiconfig, err := kubeconfig.RawConfig()
		if err != nil {
			return nil, err
		}
		resultContext = apiconfig.CurrentContext
	}

	resultNamespace, _, err := kubeconfig.Namespace()
	if err != nil {
		return nil, err
	}

	res := KubeInfo{
		configfile,
		resultContext,
		resultNamespace,
		kubeconfig,
	}

	return &res, nil
}

// GetRestConfig returns a REST config
func (info *KubeInfo) GetRestConfig() (*rest.Config, error) {
	/*
		// Do the right thing if you're running in a cluster
		if os.Getenv("KUBERNETES_SERVICE_HOST") != "" {
			inClusterConfig, err := rest.InClusterConfig()
			if err != nil {
				return nil, err
			}
			return inClusterConfig, nil
		}
	*/

	config, err := info.clientConfig.ClientConfig()
	if err != nil {
		return nil, err
	}
	return config, nil
}

// GetKubectl returns the arguments for a runnable kubectl command that talks to
// the same cluster as the associated ClientConfig.
func (info *KubeInfo) GetKubectl(args string) string {
	res := []string{"kubectl"}
	if len(info.Kubeconfig) != 0 {
		res = append(res, "--kubeconfig", info.Kubeconfig)
	}
	res = append(res, "--context", info.Context, "--namespace", info.Namespace)
	res = append(res, args)
	return strings.Join(res[1:], " ") // Drop leading "kubectl" because reasons...
}

// Client is the top-level handle to the Kubernetes cluster.
type Client struct {
	config    *rest.Config
	resources []*v1.APIResourceList
}

// NewClient constructs a k8s.Client, optionally using a previously-constructed
// KubeInfo.
func NewClient(info *KubeInfo) *Client {
	if info == nil {
		var err error
		info, err = NewKubeInfo("", "", "") // Empty file/ctx/ns for defaults
		if err != nil {
			panic(err)
		}
	}
	config, err := info.GetRestConfig()
	if err != nil {
		panic(fmt.Sprintf("Failed to get REST config: %v", err))
	}

	disco, err := discovery.NewDiscoveryClientForConfig(config)
	if err != nil {
		panic(err)
	}

	resources, err := disco.ServerResources()
	if err != nil {
		panic(err)
	}

	return &Client{
		config:    config,
		resources: resources,
	}
}

// ResourceType describes a Kubernetes resource type in a particular cluster.
// See ResolveResourceType() for more information.
//
// It is equivalent to a k8s.io/apimachinery/pkg/api/meta.RESTMapping
type ResourceType struct {
	Group      string
	Version    string
	Name       string // lowercase plural
	Kind       string // uppercase singular
	Namespaced bool
}

// ResolveResourceType takes the name of a resource type (singular,
// plural, or an abbreviation; like you might pass to `kubectl get`)
// and returns cluster-specific canonical information about that
// resource type.
//
// For example, with Kubernetes v1.10.5:
//   "pod"        --> {Group: "",           Version: "v1",      Name: "pods",        Kind: "Pod",        Namespaced: true}
//   "deployment" --> {Group: "extensions", Version: "v1beta1", Name: "deployments", Kind: "Deployment", Namespaced: true}
//
// Newer versions of Kubernetes might instead put "pod" in the "core"
// group, or put "deployment" in apps/v1 instead of
// extensions/v1beta1.  Because of discrepancies between different
// clusters, it may be a good idea to use this even for internal
// callers, rather than treating it purely as a UI concern.
//
// BUG(lukeshu): ResolveResourceType currently only takes the type name, it should
// accept TYPE[[.VERSION].GROUP], like `kubectl`.
//
// BUG(lukeshu): ResolveResourceType currently returns the first
// match.  In the event of multiple resource types with the same name
// (multiple API groups, multiple versions), it should do something
// more intelligent than that; it should at least pay attention to the
// API group's PreferredVersion.
//
// Should be equivalent to
// k8s.io/cli-runtime/pkg/genericclioptions/resource.Builder.mappingFor(),
// which calls
// k8s.io/apimachinery/pkg/runtime/schema.ParseResourceArg() and
// k8s.io/client-go/restmapper.shortcutExpander.expandResourceShortcut()
func (c *Client) ResolveResourceType(resource string) ResourceType {
	if resource == "" {
		panic("empty resource string")
	}
	lresource := strings.ToLower(resource)
	for _, rl := range c.resources {
		for _, r := range rl.APIResources {
			candidates := []string{
				r.Name,         // lowercase plural
				r.Kind,         // uppercase singular
				r.SingularName, // lowercase singular
			}
			candidates = append(candidates, r.ShortNames...)

			for _, c := range candidates {
				if lresource == strings.ToLower(c) {
					var group string
					var version string
					parts := strings.Split(rl.GroupVersion, "/")
					switch len(parts) {
					case 1:
						group = ""
						version = parts[0]
					case 2:
						group = parts[0]
						version = parts[1]
					default:
						panic("unrecognized GroupVersion")
					}
					return ResourceType{group, version, r.Name, r.Kind, r.Namespaced}
				}
			}
		}
	}
	panic(fmt.Sprintf("unrecognized resource: %s", resource))
}

// List calls ListNamespace(...) with the empty string as the namespace, which
// means all namespaces if the resource is namespaced.
func (c *Client) List(resource string) ([]Resource, error) {
	return c.ListNamespace("", resource)
}

// ListNamespace returns a slice of Resources.
// If the resource is not namespaced, the namespace must be the empty string.
// If the resource is namespaced, the empty string lists across all namespaces.
func (c *Client) ListNamespace(namespace, resource string) ([]Resource, error) {
	return c.SelectiveList(namespace, resource, "", "")
}

func (c *Client) SelectiveList(namespace, resource, fieldSelector, labelSelector string) ([]Resource, error) {
	ri := c.ResolveResourceType(resource)

	dyn, err := dynamic.NewForConfig(c.config)
	if err != nil {
		return nil, errors.Wrap(err, "failed to create dynamic context")
	}

	cli := dyn.Resource(schema.GroupVersionResource{
		Group:    ri.Group,
		Version:  ri.Version,
		Resource: ri.Name,
	})

	var filtered dynamic.ResourceInterface
	if namespace != "" {
		filtered = cli.Namespace(namespace)
	} else {
		filtered = cli
	}

	uns, err := filtered.List(v1.ListOptions{
		FieldSelector: fieldSelector,
		LabelSelector: labelSelector,
	})
	if err != nil {
		return nil, err
	}

	result := make([]Resource, len(uns.Items))
	for idx, un := range uns.Items {
		result[idx] = un.UnstructuredContent()
	}
	return result, nil
}
<commit_msg>add KubeInfo.GetKubectlArray<commit_after>// Package k8s is a facade over (super-terrible, very difficult to understand)
// client-go to provide a higher-level interface to Kubernetes, with support
// for simple, high-level APIs for watching resources (including from stable,
// long-running processes) and implementing basic controllers.
//
// It is intended to offer the same API for (nearly) every Kubernetes resource,
// including easy CRD access without codegen.
package k8s

import (
	"fmt"
	"strings"

	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime/schema"

	"k8s.io/client-go/discovery"
	"k8s.io/client-go/dynamic"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/clientcmd"

	"github.com/google/shlex"
	"github.com/pkg/errors"
)

// KubeInfo holds the data required to talk to a cluster
type KubeInfo struct {
	Kubeconfig   string
	Context      string
	Namespace    string
	clientConfig clientcmd.ClientConfig
}

// NewKubeInfo returns a useable KubeInfo, handling optional
// kubeconfig, context, and namespace.
func NewKubeInfo(configfile, context, namespace string) (*KubeInfo, error) {
	// Find the correct kube config file
	configfilesearch := clientcmd.NewDefaultClientConfigLoadingRules()
	if len(configfile) != 0 {
		configfilesearch.ExplicitPath = configfile
	}

	// Possibly override context and namespace
	overrides := &clientcmd.ConfigOverrides{}
	if len(context) != 0 {
		overrides.CurrentContext = context
	}
	if len(namespace) != 0 {
		overrides.Context.Namespace = namespace
	}

	// Construct the config
	kubeconfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(configfilesearch, overrides)

	// Extract resulting context and namespace
	resultContext := context
	if len(context) == 0 {
		apiconfig, err := kubeconfig.RawConfig()
		if err != nil {
			return nil, err
		}
		resultContext = apiconfig.CurrentContext
	}

	resultNamespace, _, err := kubeconfig.Namespace()
	if err != nil {
		return nil, err
	}

	res := KubeInfo{
		configfile,
		resultContext,
		resultNamespace,
		kubeconfig,
	}

	return &res, nil
}

// GetRestConfig returns a REST config
func (info *KubeInfo) GetRestConfig() (*rest.Config, error) {
	/*
		// Do the right thing if you're running in a cluster
		if os.Getenv("KUBERNETES_SERVICE_HOST") != "" {
			inClusterConfig, err := rest.InClusterConfig()
			if err != nil {
				return nil, err
			}
			return inClusterConfig, nil
		}
	*/

	config, err := info.clientConfig.ClientConfig()
	if err != nil {
		return nil, err
	}
	return config, nil
}

// GetKubectl returns the arguments for a runnable kubectl command that talks to
// the same cluster as the associated ClientConfig.
func (info *KubeInfo) GetKubectl(args string) string {
	parts, err := shlex.Split(args)
	if err != nil {
		panic(err)
	}
	return strings.Join(info.GetKubectlArray(parts...), " ")
}

func (info *KubeInfo) GetKubectlArray(args ...string) []string {
	res := []string{"kubectl"}
	if len(info.Kubeconfig) != 0 {
		res = append(res, "--kubeconfig", info.Kubeconfig)
	}
	res = append(res, "--context", info.Context, "--namespace", info.Namespace)
	res = append(res, args...)
	return res[1:] // Drop leading "kubectl" because reasons...
}

// Client is the top-level handle to the Kubernetes cluster.
type Client struct {
	config    *rest.Config
	resources []*v1.APIResourceList
}

// NewClient constructs a k8s.Client, optionally using a previously-constructed
// KubeInfo.
func NewClient(info *KubeInfo) *Client {
	if info == nil {
		var err error
		info, err = NewKubeInfo("", "", "") // Empty file/ctx/ns for defaults
		if err != nil {
			panic(err)
		}
	}
	config, err := info.GetRestConfig()
	if err != nil {
		panic(fmt.Sprintf("Failed to get REST config: %v", err))
	}

	disco, err := discovery.NewDiscoveryClientForConfig(config)
	if err != nil {
		panic(err)
	}

	resources, err := disco.ServerResources()
	if err != nil {
		panic(err)
	}

	return &Client{
		config:    config,
		resources: resources,
	}
}

// ResourceType describes a Kubernetes resource type in a particular cluster.
// See ResolveResourceType() for more information.
//
// It is equivalent to a k8s.io/apimachinery/pkg/api/meta.RESTMapping
type ResourceType struct {
	Group      string
	Version    string
	Name       string // lowercase plural
	Kind       string // uppercase singular
	Namespaced bool
}

// ResolveResourceType takes the name of a resource type (singular,
// plural, or an abbreviation; like you might pass to `kubectl get`)
// and returns cluster-specific canonical information about that
// resource type.
//
// For example, with Kubernetes v1.10.5:
//   "pod"        --> {Group: "",           Version: "v1",      Name: "pods",        Kind: "Pod",        Namespaced: true}
//   "deployment" --> {Group: "extensions", Version: "v1beta1", Name: "deployments", Kind: "Deployment", Namespaced: true}
//
// Newer versions of Kubernetes might instead put "pod" in the "core"
// group, or put "deployment" in apps/v1 instead of
// extensions/v1beta1.  Because of discrepancies between different
// clusters, it may be a good idea to use this even for internal
// callers, rather than treating it purely as a UI concern.
//
// BUG(lukeshu): ResolveResourceType currently only takes the type name, it should
// accept TYPE[[.VERSION].GROUP], like `kubectl`.
//
// BUG(lukeshu): ResolveResourceType currently returns the first
// match.  In the event of multiple resource types with the same name
// (multiple API groups, multiple versions), it should do something
// more intelligent than that; it should at least pay attention to the
// API group's PreferredVersion.
//
// Should be equivalent to
// k8s.io/cli-runtime/pkg/genericclioptions/resource.Builder.mappingFor(),
// which calls
// k8s.io/apimachinery/pkg/runtime/schema.ParseResourceArg() and
// k8s.io/client-go/restmapper.shortcutExpander.expandResourceShortcut()
func (c *Client) ResolveResourceType(resource string) ResourceType {
	if resource == "" {
		panic("empty resource string")
	}
	lresource := strings.ToLower(resource)
	for _, rl := range c.resources {
		for _, r := range rl.APIResources {
			candidates := []string{
				r.Name,         // lowercase plural
				r.Kind,         // uppercase singular
				r.SingularName, // lowercase singular
			}
			candidates = append(candidates, r.ShortNames...)

			for _, c := range candidates {
				if lresource == strings.ToLower(c) {
					var group string
					var version string
					parts := strings.Split(rl.GroupVersion, "/")
					switch len(parts) {
					case 1:
						group = ""
						version = parts[0]
					case 2:
						group = parts[0]
						version = parts[1]
					default:
						panic("unrecognized GroupVersion")
					}
					return ResourceType{group, version, r.Name, r.Kind, r.Namespaced}
				}
			}
		}
	}
	panic(fmt.Sprintf("unrecognized resource: %s", resource))
}

// List calls ListNamespace(...) with the empty string as the namespace, which
// means all namespaces if the resource is namespaced.
func (c *Client) List(resource string) ([]Resource, error) {
	return c.ListNamespace("", resource)
}

// ListNamespace returns a slice of Resources.
// If the resource is not namespaced, the namespace must be the empty string.
// If the resource is namespaced, the empty string lists across all namespaces.
func (c *Client) ListNamespace(namespace, resource string) ([]Resource, error) {
	return c.SelectiveList(namespace, resource, "", "")
}

func (c *Client) SelectiveList(namespace, resource, fieldSelector, labelSelector string) ([]Resource, error) {
	ri := c.ResolveResourceType(resource)

	dyn, err := dynamic.NewForConfig(c.config)
	if err != nil {
		return nil, errors.Wrap(err, "failed to create dynamic context")
	}

	cli := dyn.Resource(schema.GroupVersionResource{
		Group:    ri.Group,
		Version:  ri.Version,
		Resource: ri.Name,
	})

	var filtered dynamic.ResourceInterface
	if namespace != "" {
		filtered = cli.Namespace(namespace)
	} else {
		filtered = cli
	}

	uns, err := filtered.List(v1.ListOptions{
		FieldSelector: fieldSelector,
		LabelSelector: labelSelector,
	})
	if err != nil {
		return nil, err
	}

	result := make([]Resource, len(uns.Items))
	for idx, un := range uns.Items {
		result[idx] = un.UnstructuredContent()
	}
	return result, nil
}
<|endoftext|>


<commit_before>/* This file is part of VoltDB.
 * Copyright (C) 2008-2016 VoltDB Inc.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with VoltDB.  If not, see <http://www.gnu.org/licenses/>.
 */

package voltdbclient

import (
	"database/sql/driver"
	"errors"
	"fmt"
	"log"
	"reflect"
	"sync"
	"sync/atomic"
	"time"
)

// the set of currently active connections
type distributer struct {
	handle int64
	ncs    []*nodeConn
	numNC  int32
	// next conn to look at when finding by round robin.
	ncIndex int32
	open    atomic.Value
	h       *hashinater
}

func newDistributer() *distributer {
	var d = new(distributer)
	d.handle = 0
	d.ncIndex = 0
	d.open = atomic.Value{}
	d.open.Store(true)
	d.h = newHashinater()
	return d
}

func (d *distributer) setConns(ncs []*nodeConn) {
	d.ncs = ncs
	d.numNC = int32(len(ncs))
}

// Begin starts a transaction.  VoltDB runs in auto commit mode, and so Begin
// returns an error.
func (d *distributer) Begin() (driver.Tx, error) {
	d.assertOpen()
	return nil, errors.New("VoltDB does not support transactions, VoltDB autocommits")
}

// Close closes the connection to the VoltDB server.  Connections to the server
// are meant to be long lived; it should not be necessary to continually close
// and reopen connections.  Close would typically be called using a defer.
// Operations using a closed connection cause a panic.
func (d *distributer) Close() (err error) {
	if d.isClosed() {
		return
	}
	d.setClosed()

	// once this is closed there shouldn't be any additional activity against
	// the connections.  They're touched here without getting a lock.
	for _, nc := range d.ncs {
		err := nc.close()
		if err != nil {
			log.Println("Failed to close connection with %v", err)
		}
	}
	d.ncs = nil
	return nil
}

// Drain blocks until all outstanding asynchronous requests have been satisfied.
// Asynchronous requests are processed in a background thread; this call blocks the
// current thread until that background thread has finished with all asynchronous requests.
func (d *distributer) Drain() {
	d.assertOpen()

	// drain can't work if the set of connections can change
	// while it's running.  Hold the lock for the duration, some
	// asyncs may time out.  The user thread is blocked on drain.
	for _, nc := range d.ncs {
		nc.drain()
	}
}

func (d *distributer) assertOpen() {
	if !(d.open.Load().(bool)) {
		panic("Tried to use closed connection pool")
	}
}

func (d *distributer) isClosed() bool {
	return !(d.open.Load().(bool))
}

func (d *distributer) setClosed() {
	d.open.Store(false)
}

// Exec executes a query that doesn't return rows, such as an INSERT or UPDATE.
// Exec is available on both VoltConn and on VoltStatement.  Uses DEFAULT_QUERY_TIMEOUT.
func (d *distributer) Exec(query string, args []driver.Value) (driver.Result, error) {
	return d.ExecTimeout(query, args, DEFAULT_QUERY_TIMEOUT)
}

// Exec executes a query that doesn't return rows, such as an INSERT or UPDATE.
// Exec is available on both VoltConn and on VoltStatement.  Specifies a duration for timeout.
func (d *distributer) ExecTimeout(query string, args []driver.Value, timeout time.Duration) (driver.Result, error) {
	pi := newProcedureInvocation(d.getNextHandle(), false, query, args, timeout)
	nc, err := d.getConn(pi)
	if err != nil {
		return nil, err
	}
	return nc.exec(pi)
}

// Exec executes a query that doesn't return rows, such as an INSERT or UPDATE.
// ExecAsync is analogous to Exec but is run asynchronously.  That is, an
// invocation of this method blocks only until a request is sent to the VoltDB
// server.  Uses DEFAULT_QUERY_TIMEOUT.
func (d *distributer) ExecAsync(resCons AsyncResponseConsumer, query string, args []driver.Value) error {
	return d.ExecAsyncTimeout(resCons, query, args, DEFAULT_QUERY_TIMEOUT)
}

// Exec executes a query that doesn't return rows, such as an INSERT or UPDATE.
// ExecAsync is analogous to Exec but is run asynchronously.  That is, an
// invocation of this method blocks only until a request is sent to the VoltDB
// server.  Specifies a duration for timeout.
func (d *distributer) ExecAsyncTimeout(resCons AsyncResponseConsumer, query string, args []driver.Value, timeout time.Duration) error {
	pi := newProcedureInvocation(d.getNextHandle(), false, query, args, timeout)
	nc, err := d.getConn(pi)
	if err != nil {
		return err
	}
	return nc.execAsync(resCons, pi)
}

// Prepare creates a prepared statement for later queries or executions.
// The Statement returned by Prepare is bound to this VoltConn.
func (d *distributer) Prepare(query string) (driver.Stmt, error) {
	stmt := newVoltStatement(d, query)
	return *stmt, nil
}

// Query executes a query that returns rows, typically a SELECT. The args are for any placeholder parameters in the query.
// Uses DEFAULT_QUERY_TIMEOUT.
func (d *distributer) Query(query string, args []driver.Value) (driver.Rows, error) {
	return d.QueryTimeout(query, args, DEFAULT_QUERY_TIMEOUT)
}

// Query executes a query that returns rows, typically a SELECT. The args are for any placeholder parameters in the query.
// Specifies a duration for timeout.
func (d *distributer) QueryTimeout(query string, args []driver.Value, timeout time.Duration) (driver.Rows, error) {
	pi := newProcedureInvocation(d.getNextHandle(), true, query, args, timeout)
	nc, err := d.getConn(pi)
	if err != nil {
		return nil, err
	}
	return nc.query(pi)
}

// QueryAsync executes a query asynchronously.  The invoking thread will block
// until the query is sent over the network to the server.  The eventual
// response will be handled by the given AsyncResponseConsumer, this processing
// happens in the 'response' thread.  Uses DEFAULT_QUERY_TIMEOUT.
func (d *distributer) QueryAsync(rowsCons AsyncResponseConsumer, query string, args []driver.Value) error {
	return d.QueryAsyncTimeout(rowsCons, query, args, DEFAULT_QUERY_TIMEOUT)
}

// QueryAsync executes a query asynchronously.  The invoking thread will block
// until the query is sent over the network to the server.  The eventual
// response will be handled by the given AsyncResponseConsumer, this processing
// happens in the 'response' thread.  Specifies a duration for timeout.
func (d *distributer) QueryAsyncTimeout(rowsCons AsyncResponseConsumer, query string, args []driver.Value, timeout time.Duration) error {
	pi := newProcedureInvocation(d.getNextHandle(), true, query, args, timeout)
	nc, err := d.getConn(pi)
	if err != nil {
		return err
	}
	return nc.queryAsync(rowsCons, pi)
}

// Get a connection from the hashinator.  If not, get one by round robin.  If not return nil.
func (d *distributer) getConn(pi *procedureInvocation) (nc *nodeConn, err error) {

	d.assertOpen()
	nc = d.h.getConn(pi)
	if nc == nil {
		nc, err = d.getConnByRR(pi.timeout)
	}
	if err != nil {
		return nil, err
	}
	return nc, nil
}

func (d *distributer) getConnByRR(timeout time.Duration) (*nodeConn, error) {
	start := time.Now()
	var nc *nodeConn
	for {
		var i int32
		for {
			currIndex := d.ncIndex
			if currIndex == d.numNC-1 {
				i = 0
				if atomic.CompareAndSwapInt32(&d.ncIndex, currIndex, 0) {
					break
				}
			} else {
				i = currIndex + 1
				if atomic.CompareAndSwapInt32(&d.ncIndex, currIndex, i) {
					break
				}
			}
		}
		nc = d.ncs[i]
		if nc.isOpen() && !nc.hasBP() {
			break
		}
	}
	if time.Now().Sub(start) > timeout {
		return nil, errors.New("timeout")
	} else {
		return nc, nil
	}
}

func (d *distributer) getNextHandle() int64 {
	var nextHandle int64
	for {
		currHandle := d.handle
		nextHandle = currHandle + 1
		if atomic.CompareAndSwapInt64(&d.handle, currHandle, nextHandle) {
			break
		}
	}
	return nextHandle
}

type procedureInvocation struct {
	handle  int64
	isQuery bool // as opposed to an exec.
	query   string
	params  []driver.Value
	timeout time.Duration
	slen    int // length of pi once serialized
}

func newProcedureInvocation(handle int64, isQuery bool, query string, params []driver.Value, timeout time.Duration) *procedureInvocation {
	var pi = new(procedureInvocation)
	pi.handle = handle
	pi.isQuery = isQuery
	pi.query = query
	pi.params = params
	pi.timeout = timeout
	pi.slen = -1
	return pi
}

func (pi *procedureInvocation) getLen() int {
	if pi.slen == -1 {
		pi.slen = pi.calcLen()
	}
	return pi.slen
}

func (pi *procedureInvocation) calcLen() int {
	// fixed - 1 for batch timeout type, 4 for str length (proc name), 8 for handle, 2 for paramCount
	var slen int = 15
	slen += len(pi.query)
	for _, param := range pi.params {
		slen += pi.calcParamLen(param)
	}
	return slen
}

func (pi *procedureInvocation) calcParamLen(param interface{}) int {
	// add one to each because the type itself takes one byte
	v := reflect.ValueOf(param)
	switch v.Kind() {
	case reflect.Bool:
		return 2
	case reflect.Int8:
		return 2
	case reflect.Int16:
		return 3
	case reflect.Int32:
		return 5
	case reflect.Int64:
		return 9
	case reflect.Float64:
		return 9
	case reflect.String:
		return 5 + v.Len()
	case reflect.Slice:
		return 1 + v.Len()
	case reflect.Struct:
		panic("Can't marshal a struct")
	case reflect.Ptr:
		panic("Can't marshal a pointer")
	default:
		panic(fmt.Sprintf("Can't marshal %v-type parameters", v.Kind()))
	}
}
<commit_msg>ENG-10558 remove unneeded import<commit_after>/* This file is part of VoltDB.
 * Copyright (C) 2008-2016 VoltDB Inc.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with VoltDB.  If not, see <http://www.gnu.org/licenses/>.
 */

package voltdbclient

import (
	"database/sql/driver"
	"errors"
	"fmt"
	"log"
	"reflect"
	"sync/atomic"
	"time"
)

// the set of currently active connections
type distributer struct {
	handle int64
	ncs    []*nodeConn
	numNC  int32
	// next conn to look at when finding by round robin.
	ncIndex int32
	open    atomic.Value
	h       *hashinater
}

func newDistributer() *distributer {
	var d = new(distributer)
	d.handle = 0
	d.ncIndex = 0
	d.open = atomic.Value{}
	d.open.Store(true)
	d.h = newHashinater()
	return d
}

func (d *distributer) setConns(ncs []*nodeConn) {
	d.ncs = ncs
	d.numNC = int32(len(ncs))
}

// Begin starts a transaction.  VoltDB runs in auto commit mode, and so Begin
// returns an error.
func (d *distributer) Begin() (driver.Tx, error) {
	d.assertOpen()
	return nil, errors.New("VoltDB does not support transactions, VoltDB autocommits")
}

// Close closes the connection to the VoltDB server.  Connections to the server
// are meant to be long lived; it should not be necessary to continually close
// and reopen connections.  Close would typically be called using a defer.
// Operations using a closed connection cause a panic.
func (d *distributer) Close() (err error) {
	if d.isClosed() {
		return
	}
	d.setClosed()

	// once this is closed there shouldn't be any additional activity against
	// the connections.  They're touched here without getting a lock.
	for _, nc := range d.ncs {
		err := nc.close()
		if err != nil {
			log.Println("Failed to close connection with %v", err)
		}
	}
	d.ncs = nil
	return nil
}

// Drain blocks until all outstanding asynchronous requests have been satisfied.
// Asynchronous requests are processed in a background thread; this call blocks the
// current thread until that background thread has finished with all asynchronous requests.
func (d *distributer) Drain() {
	d.assertOpen()

	// drain can't work if the set of connections can change
	// while it's running.  Hold the lock for the duration, some
	// asyncs may time out.  The user thread is blocked on drain.
	for _, nc := range d.ncs {
		nc.drain()
	}
}

func (d *distributer) assertOpen() {
	if !(d.open.Load().(bool)) {
		panic("Tried to use closed connection pool")
	}
}

func (d *distributer) isClosed() bool {
	return !(d.open.Load().(bool))
}

func (d *distributer) setClosed() {
	d.open.Store(false)
}

// Exec executes a query that doesn't return rows, such as an INSERT or UPDATE.
// Exec is available on both VoltConn and on VoltStatement.  Uses DEFAULT_QUERY_TIMEOUT.
func (d *distributer) Exec(query string, args []driver.Value) (driver.Result, error) {
	return d.ExecTimeout(query, args, DEFAULT_QUERY_TIMEOUT)
}

// Exec executes a query that doesn't return rows, such as an INSERT or UPDATE.
// Exec is available on both VoltConn and on VoltStatement.  Specifies a duration for timeout.
func (d *distributer) ExecTimeout(query string, args []driver.Value, timeout time.Duration) (driver.Result, error) {
	pi := newProcedureInvocation(d.getNextHandle(), false, query, args, timeout)
	nc, err := d.getConn(pi)
	if err != nil {
		return nil, err
	}
	return nc.exec(pi)
}

// Exec executes a query that doesn't return rows, such as an INSERT or UPDATE.
// ExecAsync is analogous to Exec but is run asynchronously.  That is, an
// invocation of this method blocks only until a request is sent to the VoltDB
// server.  Uses DEFAULT_QUERY_TIMEOUT.
func (d *distributer) ExecAsync(resCons AsyncResponseConsumer, query string, args []driver.Value) error {
	return d.ExecAsyncTimeout(resCons, query, args, DEFAULT_QUERY_TIMEOUT)
}

// Exec executes a query that doesn't return rows, such as an INSERT or UPDATE.
// ExecAsync is analogous to Exec but is run asynchronously.  That is, an
// invocation of this method blocks only until a request is sent to the VoltDB
// server.  Specifies a duration for timeout.
func (d *distributer) ExecAsyncTimeout(resCons AsyncResponseConsumer, query string, args []driver.Value, timeout time.Duration) error {
	pi := newProcedureInvocation(d.getNextHandle(), false, query, args, timeout)
	nc, err := d.getConn(pi)
	if err != nil {
		return err
	}
	return nc.execAsync(resCons, pi)
}

// Prepare creates a prepared statement for later queries or executions.
// The Statement returned by Prepare is bound to this VoltConn.
func (d *distributer) Prepare(query string) (driver.Stmt, error) {
	stmt := newVoltStatement(d, query)
	return *stmt, nil
}

// Query executes a query that returns rows, typically a SELECT. The args are for any placeholder parameters in the query.
// Uses DEFAULT_QUERY_TIMEOUT.
func (d *distributer) Query(query string, args []driver.Value) (driver.Rows, error) {
	return d.QueryTimeout(query, args, DEFAULT_QUERY_TIMEOUT)
}

// Query executes a query that returns rows, typically a SELECT. The args are for any placeholder parameters in the query.
// Specifies a duration for timeout.
func (d *distributer) QueryTimeout(query string, args []driver.Value, timeout time.Duration) (driver.Rows, error) {
	pi := newProcedureInvocation(d.getNextHandle(), true, query, args, timeout)
	nc, err := d.getConn(pi)
	if err != nil {
		return nil, err
	}
	return nc.query(pi)
}

// QueryAsync executes a query asynchronously.  The invoking thread will block
// until the query is sent over the network to the server.  The eventual
// response will be handled by the given AsyncResponseConsumer, this processing
// happens in the 'response' thread.  Uses DEFAULT_QUERY_TIMEOUT.
func (d *distributer) QueryAsync(rowsCons AsyncResponseConsumer, query string, args []driver.Value) error {
	return d.QueryAsyncTimeout(rowsCons, query, args, DEFAULT_QUERY_TIMEOUT)
}

// QueryAsync executes a query asynchronously.  The invoking thread will block
// until the query is sent over the network to the server.  The eventual
// response will be handled by the given AsyncResponseConsumer, this processing
// happens in the 'response' thread.  Specifies a duration for timeout.
func (d *distributer) QueryAsyncTimeout(rowsCons AsyncResponseConsumer, query string, args []driver.Value, timeout time.Duration) error {
	pi := newProcedureInvocation(d.getNextHandle(), true, query, args, timeout)
	nc, err := d.getConn(pi)
	if err != nil {
		return err
	}
	return nc.queryAsync(rowsCons, pi)
}

// Get a connection from the hashinator.  If not, get one by round robin.  If not return nil.
func (d *distributer) getConn(pi *procedureInvocation) (nc *nodeConn, err error) {

	d.assertOpen()
	nc = d.h.getConn(pi)
	if nc == nil {
		nc, err = d.getConnByRR(pi.timeout)
	}
	if err != nil {
		return nil, err
	}
	return nc, nil
}

func (d *distributer) getConnByRR(timeout time.Duration) (*nodeConn, error) {
	start := time.Now()
	var nc *nodeConn
	for {
		var i int32
		for {
			currIndex := d.ncIndex
			if currIndex == d.numNC-1 {
				i = 0
				if atomic.CompareAndSwapInt32(&d.ncIndex, currIndex, 0) {
					break
				}
			} else {
				i = currIndex + 1
				if atomic.CompareAndSwapInt32(&d.ncIndex, currIndex, i) {
					break
				}
			}
		}
		nc = d.ncs[i]
		if nc.isOpen() && !nc.hasBP() {
			break
		}
	}
	if time.Now().Sub(start) > timeout {
		return nil, errors.New("timeout")
	} else {
		return nc, nil
	}
}

func (d *distributer) getNextHandle() int64 {
	var nextHandle int64
	for {
		currHandle := d.handle
		nextHandle = currHandle + 1
		if atomic.CompareAndSwapInt64(&d.handle, currHandle, nextHandle) {
			break
		}
	}
	return nextHandle
}

type procedureInvocation struct {
	handle  int64
	isQuery bool // as opposed to an exec.
	query   string
	params  []driver.Value
	timeout time.Duration
	slen    int // length of pi once serialized
}

func newProcedureInvocation(handle int64, isQuery bool, query string, params []driver.Value, timeout time.Duration) *procedureInvocation {
	var pi = new(procedureInvocation)
	pi.handle = handle
	pi.isQuery = isQuery
	pi.query = query
	pi.params = params
	pi.timeout = timeout
	pi.slen = -1
	return pi
}

func (pi *procedureInvocation) getLen() int {
	if pi.slen == -1 {
		pi.slen = pi.calcLen()
	}
	return pi.slen
}

func (pi *procedureInvocation) calcLen() int {
	// fixed - 1 for batch timeout type, 4 for str length (proc name), 8 for handle, 2 for paramCount
	var slen int = 15
	slen += len(pi.query)
	for _, param := range pi.params {
		slen += pi.calcParamLen(param)
	}
	return slen
}

func (pi *procedureInvocation) calcParamLen(param interface{}) int {
	// add one to each because the type itself takes one byte
	v := reflect.ValueOf(param)
	switch v.Kind() {
	case reflect.Bool:
		return 2
	case reflect.Int8:
		return 2
	case reflect.Int16:
		return 3
	case reflect.Int32:
		return 5
	case reflect.Int64:
		return 9
	case reflect.Float64:
		return 9
	case reflect.String:
		return 5 + v.Len()
	case reflect.Slice:
		return 1 + v.Len()
	case reflect.Struct:
		panic("Can't marshal a struct")
	case reflect.Ptr:
		panic("Can't marshal a pointer")
	default:
		panic(fmt.Sprintf("Can't marshal %v-type parameters", v.Kind()))
	}
}
<|endoftext|>


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

package backups

import (
	"io"
	"time"

	"github.com/juju/errors"
	"github.com/juju/utils"

	"github.com/juju/juju/apiserver/params"
	"github.com/juju/juju/rpc"
)

var (
	// restoreStrategy is the attempt strategy for api server calls re-attempts in case
	// the server is upgrading.
	restoreStrategy = utils.AttemptStrategy{
		Delay: 10 * time.Second,
		Min:   10,
	}
)

// ClientConnection type represents a function capable of spawning a new Client connection
// it is used to pass around connection factories when necessary.
type ClientConnection func() (*Client, func() error, error)

// closerfunc is a function that allows you to close a client connection.
type closerFunc func() error

func prepareAttempt(client *Client, closer closerFunc) (error, error) {
	var remoteError error
	defer closer()
	err := client.facade.FacadeCall("PrepareRestore", nil, &remoteError)
	return err, remoteError
}

func prepareRestore(newClient ClientConnection) error {
	var err, remoteError error

	// PrepareRestore puts the server into a state that only allows
	// for restore to be called. This is to avoid the data loss if
	// users try to perform actions that are going to be overwritten
	// by restore.
	for a := restoreStrategy.Start(); a.Next(); {
		logger.Debugf("Will attempt to call 'PrepareRestore'")
		client, clientCloser, clientErr := newClient()
		if clientErr != nil {
			return errors.Trace(clientErr)
		}
		if err, remoteError = prepareAttempt(client, clientCloser); err == nil {
			return nil
		}
		if !params.IsCodeUpgradeInProgress(remoteError) {
			return errors.Annotatef(err, "could not start prepare restore mode, server returned: %v", remoteError)
		}
	}
	return errors.Annotatef(err, "could not start restore process: %v", remoteError)
}

// RestoreReader restores the contents of backupFile as backup.
func (c *Client) RestoreReader(r io.Reader, meta *params.BackupsMetadataResult, newClient ClientConnection) error {
	if err := prepareRestore(newClient); err != nil {
		return errors.Trace(err)
	}
	logger.Debugf("Server is now in 'about to restore' mode, proceeding to upload the backup file")

	// Upload
	backupId, err := c.Upload(r, *meta)
	if err != nil {
		finishRestore(newClient)
		return errors.Annotatef(err, "cannot upload backup file")
	}
	return c.restore(backupId, newClient)
}

// Restore performs restore using a backup id corresponding to a backup stored in the server.
func (c *Client) Restore(backupId string, newClient ClientConnection) error {
	if err := prepareRestore(newClient); err != nil {
		return errors.Trace(err)
	}
	logger.Debugf("Server in 'about to restore' mode")
	return c.restore(backupId, newClient)
}

func restoreAttempt(client *Client, closer closerFunc, restoreArgs params.RestoreArgs) (error, error) {
	var remoteError error
	defer closer()
	err := client.facade.FacadeCall("Restore", restoreArgs, &remoteError)
	return err, remoteError
}

// restore is responsible for triggering the whole restore process in a remote
// machine. The backup information for the process should already be in the
// server and loaded in the backup storage under the backupId id.
// It takes backupId as the identifier for the remote backup file and a
// client connection factory newClient (newClient should no longer be
// necessary when lp:1399722 is sorted out).
func (c *Client) restore(backupId string, newClient ClientConnection) error {
	var err, remoteError error

	// Restore
	restoreArgs := params.RestoreArgs{
		BackupId: backupId,
	}

	for a := restoreStrategy.Start(); a.Next(); {
		logger.Debugf("Attempting Restore of %q", backupId)
		restoreClient, restoreClientCloser, err := newClient()
		if err != nil {
			return errors.Trace(err)
		}
		defer restoreClientCloser()

		err, remoteError = restoreAttempt(restoreClient, restoreClientCloser, restoreArgs)

		// This signals that Restore almost certainly finished and
		// triggered Exit.
		if err == rpc.ErrShutdown && remoteError == nil {
			break
		}
		if err != nil && !params.IsCodeUpgradeInProgress(remoteError) {
			finishRestore(newClient)
			return errors.Annotatef(err, "cannot perform restore: %v", remoteError)
		}
	}
	if err != rpc.ErrShutdown {
		return errors.Annotatef(err, "cannot perform restore: %v", remoteError)
	}

	err = finishRestore(newClient)
	if err != nil {
		return errors.Annotatef(err, "could not finish restore process: %v", remoteError)
	}
	return nil
}

func finishAttempt(client *Client, closer closerFunc) (error, error) {
	var remoteError error
	defer closer()
	err := client.facade.FacadeCall("finishRestore", nil, &remoteError)
	return err, remoteError
}

// finishRestore since Restore call will end up with a reset
// state server, finish restore will check that the the newly
// placed state server has the mark of restore complete.
// upstart should have restarted the api server so we reconnect.
func finishRestore(newClient ClientConnection) error {
	var err, remoteError error
	for a := restoreStrategy.Start(); a.Next(); {
		logger.Debugf("Attempting finishRestore")
		finishClient, finishClientCloser, err := newClient()
		if err != nil {
			return errors.Trace(err)
		}

		if err, remoteError = finishAttempt(finishClient, finishClientCloser); err == nil {
			return nil
		}
		if !params.IsCodeUpgradeInProgress(remoteError) {
			return errors.Annotatef(err, "cannot complete restore: %v", remoteError)
		}
	}
	return errors.Annotatef(err, "cannot complete restore: %v", remoteError)
}
<commit_msg>Add finishrestore to err output<commit_after>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package backups

import (
	"io"
	"time"

	"github.com/juju/errors"
	"github.com/juju/utils"

	"github.com/juju/juju/apiserver/params"
	"github.com/juju/juju/rpc"
)

var (
	// restoreStrategy is the attempt strategy for api server calls re-attempts in case
	// the server is upgrading.
	restoreStrategy = utils.AttemptStrategy{
		Delay: 10 * time.Second,
		Min:   10,
	}
)

// ClientConnection type represents a function capable of spawning a new Client connection
// it is used to pass around connection factories when necessary.
type ClientConnection func() (*Client, func() error, error)

// closerfunc is a function that allows you to close a client connection.
type closerFunc func() error

func prepareAttempt(client *Client, closer closerFunc) (error, error) {
	var remoteError error
	defer closer()
	err := client.facade.FacadeCall("PrepareRestore", nil, &remoteError)
	return err, remoteError
}

func prepareRestore(newClient ClientConnection) error {
	var err, remoteError error

	// PrepareRestore puts the server into a state that only allows
	// for restore to be called. This is to avoid the data loss if
	// users try to perform actions that are going to be overwritten
	// by restore.
	for a := restoreStrategy.Start(); a.Next(); {
		logger.Debugf("Will attempt to call 'PrepareRestore'")
		client, clientCloser, clientErr := newClient()
		if clientErr != nil {
			return errors.Trace(clientErr)
		}
		if err, remoteError = prepareAttempt(client, clientCloser); err == nil {
			return nil
		}
		if !params.IsCodeUpgradeInProgress(remoteError) {
			return errors.Annotatef(err, "could not start prepare restore mode, server returned: %v", remoteError)
		}
	}
	return errors.Annotatef(err, "could not start restore process: %v", remoteError)
}

// RestoreReader restores the contents of backupFile as backup.
func (c *Client) RestoreReader(r io.Reader, meta *params.BackupsMetadataResult, newClient ClientConnection) error {
	if err := prepareRestore(newClient); err != nil {
		return errors.Trace(err)
	}
	logger.Debugf("Server is now in 'about to restore' mode, proceeding to upload the backup file")

	// Upload
	backupId, err := c.Upload(r, *meta)
	if err != nil {
		finishRestore(newClient)
		return errors.Annotatef(err, "cannot upload backup file")
	}
	return c.restore(backupId, newClient)
}

// Restore performs restore using a backup id corresponding to a backup stored in the server.
func (c *Client) Restore(backupId string, newClient ClientConnection) error {
	if err := prepareRestore(newClient); err != nil {
		return errors.Trace(err)
	}
	logger.Debugf("Server in 'about to restore' mode")
	return c.restore(backupId, newClient)
}

func restoreAttempt(client *Client, closer closerFunc, restoreArgs params.RestoreArgs) (error, error) {
	var remoteError error
	defer closer()
	err := client.facade.FacadeCall("Restore", restoreArgs, &remoteError)
	return err, remoteError
}

// restore is responsible for triggering the whole restore process in a remote
// machine. The backup information for the process should already be in the
// server and loaded in the backup storage under the backupId id.
// It takes backupId as the identifier for the remote backup file and a
// client connection factory newClient (newClient should no longer be
// necessary when lp:1399722 is sorted out).
func (c *Client) restore(backupId string, newClient ClientConnection) error {
	var err, remoteError error

	// Restore
	restoreArgs := params.RestoreArgs{
		BackupId: backupId,
	}

	for a := restoreStrategy.Start(); a.Next(); {
		logger.Debugf("Attempting Restore of %q", backupId)
		restoreClient, restoreClientCloser, err := newClient()
		if err != nil {
			return errors.Trace(err)
		}
		defer restoreClientCloser()

		err, remoteError = restoreAttempt(restoreClient, restoreClientCloser, restoreArgs)

		// This signals that Restore almost certainly finished and
		// triggered Exit.
		if err == rpc.ErrShutdown && remoteError == nil {
			break
		}
		if err != nil && !params.IsCodeUpgradeInProgress(remoteError) {
			finishRestore(newClient)
			return errors.Annotatef(err, "cannot perform restore: %v", remoteError)
		}
	}
	if err != rpc.ErrShutdown {
		finishRestore(newClient)
		return errors.Annotatef(err, "cannot perform restore: %v", remoteError)
	}

	err = finishRestore(newClient)
	if err != nil {
		return errors.Annotatef(err, "could not finish restore process: %v", remoteError)
	}
	return nil
}

func finishAttempt(client *Client, closer closerFunc) (error, error) {
	var remoteError error
	defer closer()
	err := client.facade.FacadeCall("finishRestore", nil, &remoteError)
	return err, remoteError
}

// finishRestore since Restore call will end up with a reset
// state server, finish restore will check that the the newly
// placed state server has the mark of restore complete.
// upstart should have restarted the api server so we reconnect.
func finishRestore(newClient ClientConnection) error {
	var err, remoteError error
	for a := restoreStrategy.Start(); a.Next(); {
		logger.Debugf("Attempting finishRestore")
		finishClient, finishClientCloser, err := newClient()
		if err != nil {
			return errors.Trace(err)
		}

		if err, remoteError = finishAttempt(finishClient, finishClientCloser); err == nil {
			return nil
		}
		if !params.IsCodeUpgradeInProgress(remoteError) {
			return errors.Annotatef(err, "cannot complete restore: %v", remoteError)
		}
	}
	return errors.Annotatef(err, "cannot complete restore: %v", remoteError)
}
<|endoftext|>


<commit_before>package version

// Version of Functions
var Version = "0.3.321"
<commit_msg>fnserver: 0.3.322 release [skip ci]<commit_after>package version

// Version of Functions
var Version = "0.3.322"
<|endoftext|>