<commit_before>package stats

// Float64Data is a named type for []float64 with helper methods
type Float64Data []float64

// Get item in slice
func (f Float64Data) Get(i int) float64 { return f[i] }

// Len returns length of slice
func (f Float64Data) Len() int { return len(f) }

// Less returns if one number is less than another
func (f Float64Data) Less(i, j int) bool { return f[i] < f[j] }

// Swap switches out two numbers in slice
func (f Float64Data) Swap(i, j int) { f[i], f[j] = f[j], f[i] }

// Min returns the minimum number in the data
func (f Float64Data) Min() (float64, error) { return Min(f) }

// Max returns the maximum number in the data
func (f Float64Data) Max() (float64, error) { return Max(f) }

// Sum returns the total of all the numbers in the data
func (f Float64Data) Sum() (float64, error) { return Sum(f) }

// Mean returns the mean of the data
func (f Float64Data) Mean() (float64, error) { return Mean(f) }

// Median returns the median of the data
func (f Float64Data) Median() (float64, error) { return Median(f) }

// Mode returns the mode of the data
func (f Float64Data) Mode() ([]float64, error) { return Mode(f) }

// GeometricMean returns the median of the data
func (f Float64Data) GeometricMean() (float64, error) { return GeometricMean(f) }

// HarmonicMean returns the mode of the data
func (f Float64Data) HarmonicMean() (float64, error) { return HarmonicMean(f) }

// MedianAbsoluteDeviation the median of the absolute deviations from the dataset median
func (f Float64Data) MedianAbsoluteDeviation() (float64, error) {
	return MedianAbsoluteDeviation(f)
}

// MedianAbsoluteDeviationPopulation finds the median of the absolute deviations from the population median
func (f Float64Data) MedianAbsoluteDeviationPopulation() (float64, error) {
	return MedianAbsoluteDeviationPopulation(f)
}

// StandardDeviation the amount of variation in the dataset
func (f Float64Data) StandardDeviation() (float64, error) {
	return StandardDeviation(f)
}

// StandardDeviationPopulation finds the amount of variation from the population
func (f Float64Data) StandardDeviationPopulation() (float64, error) {
	return StandardDeviationPopulation(f)
}

// StandardDeviationSample finds the amount of variation from a sample
func (f Float64Data) StandardDeviationSample() (float64, error) {
	return StandardDeviationSample(f)
}

// QuartileOutliers finds the mild and extreme outliers
func (f Float64Data) QuartileOutliers() (Outliers, error) {
	return QuartileOutliers(f)
}

// Percentile finds the relative standing in a slice of floats
func (f Float64Data) Percentile(p float64) (float64, error) {
	return Percentile(f, p)
}

// PercentileNearestRank finds the relative standing using the Nearest Rank method
func (f Float64Data) PercentileNearestRank(p float64) (float64, error) {
	return PercentileNearestRank(f, p)
}

// Correlation describes the degree of relationship between two sets of data
func (f Float64Data) Correlation(d Float64Data) (float64, error) {
	return Correlation(f, d)
}

// Pearson calculates the Pearson product-moment correlation coefficient between two variables.
func (f Float64Data) Pearson(d Float64Data) (float64, error) {
	return Pearson(f, d)
}

// Quartile returns the three quartile points from a slice of data
func (f Float64Data) Quartile(d Float64Data) (Quartiles, error) {
	return Quartile(d)
}

// InterQuartileRange finds the range between Q1 and Q3
func (f Float64Data) InterQuartileRange(d Float64Data) (float64, error) {
	return InterQuartileRange(d)
}

// Midhinge finds the average of the first and third quartiles
func (f Float64Data) Midhinge(d Float64Data) (float64, error) {
	return Midhinge(d)
}

// Trimean finds the average of the median and the midhinge
func (f Float64Data) Trimean(d Float64Data) (float64, error) {
	return Trimean(d)
}

// Sample returns sample from input with replacement or without
func (f Float64Data) Sample(n int, r bool) ([]float64, error) {
	return Sample(f, n, r)
}

// Variance the amount of variation in the dataset
func (f Float64Data) Variance() (float64, error) {
	return Variance(f)
}

// PopulationVariance finds the amount of variance within a population
func (f Float64Data) PopulationVariance() (float64, error) {
	return PopulationVariance(f)
}

// SampleVariance finds the amount of variance within a sample
func (f Float64Data) SampleVariance() (float64, error) {
	return SampleVariance(f)
}

// Covariance is a measure of how much two sets of data change
func (f Float64Data) Covariance(d Float64Data) (float64, error) {
	return Covariance(f, d)
}

// CovariancePopulation computes covariance for entire population between two variables.
func (f Float64Data) CovariancePopulation(d Float64Data) (float64, error) {
	return CovariancePopulation(f, d)
}
<commit_msg>Retrieve InterQuartileRange for the Float64Data.<commit_after>package stats

// Float64Data is a named type for []float64 with helper methods
type Float64Data []float64

// Get item in slice
func (f Float64Data) Get(i int) float64 { return f[i] }

// Len returns length of slice
func (f Float64Data) Len() int { return len(f) }

// Less returns if one number is less than another
func (f Float64Data) Less(i, j int) bool { return f[i] < f[j] }

// Swap switches out two numbers in slice
func (f Float64Data) Swap(i, j int) { f[i], f[j] = f[j], f[i] }

// Min returns the minimum number in the data
func (f Float64Data) Min() (float64, error) { return Min(f) }

// Max returns the maximum number in the data
func (f Float64Data) Max() (float64, error) { return Max(f) }

// Sum returns the total of all the numbers in the data
func (f Float64Data) Sum() (float64, error) { return Sum(f) }

// Mean returns the mean of the data
func (f Float64Data) Mean() (float64, error) { return Mean(f) }

// Median returns the median of the data
func (f Float64Data) Median() (float64, error) { return Median(f) }

// Mode returns the mode of the data
func (f Float64Data) Mode() ([]float64, error) { return Mode(f) }

// GeometricMean returns the median of the data
func (f Float64Data) GeometricMean() (float64, error) { return GeometricMean(f) }

// HarmonicMean returns the mode of the data
func (f Float64Data) HarmonicMean() (float64, error) { return HarmonicMean(f) }

// MedianAbsoluteDeviation the median of the absolute deviations from the dataset median
func (f Float64Data) MedianAbsoluteDeviation() (float64, error) {
	return MedianAbsoluteDeviation(f)
}

// MedianAbsoluteDeviationPopulation finds the median of the absolute deviations from the population median
func (f Float64Data) MedianAbsoluteDeviationPopulation() (float64, error) {
	return MedianAbsoluteDeviationPopulation(f)
}

// StandardDeviation the amount of variation in the dataset
func (f Float64Data) StandardDeviation() (float64, error) {
	return StandardDeviation(f)
}

// StandardDeviationPopulation finds the amount of variation from the population
func (f Float64Data) StandardDeviationPopulation() (float64, error) {
	return StandardDeviationPopulation(f)
}

// StandardDeviationSample finds the amount of variation from a sample
func (f Float64Data) StandardDeviationSample() (float64, error) {
	return StandardDeviationSample(f)
}

// QuartileOutliers finds the mild and extreme outliers
func (f Float64Data) QuartileOutliers() (Outliers, error) {
	return QuartileOutliers(f)
}

// Percentile finds the relative standing in a slice of floats
func (f Float64Data) Percentile(p float64) (float64, error) {
	return Percentile(f, p)
}

// PercentileNearestRank finds the relative standing using the Nearest Rank method
func (f Float64Data) PercentileNearestRank(p float64) (float64, error) {
	return PercentileNearestRank(f, p)
}

// Correlation describes the degree of relationship between two sets of data
func (f Float64Data) Correlation(d Float64Data) (float64, error) {
	return Correlation(f, d)
}

// Pearson calculates the Pearson product-moment correlation coefficient between two variables.
func (f Float64Data) Pearson(d Float64Data) (float64, error) {
	return Pearson(f, d)
}

// Quartile returns the three quartile points from a slice of data
func (f Float64Data) Quartile(d Float64Data) (Quartiles, error) {
	return Quartile(d)
}

// InterQuartileRange finds the range between Q1 and Q3
func (f Float64Data) InterQuartileRange() (float64, error) {
	return InterQuartileRange(f)
}

// Midhinge finds the average of the first and third quartiles
func (f Float64Data) Midhinge(d Float64Data) (float64, error) {
	return Midhinge(d)
}

// Trimean finds the average of the median and the midhinge
func (f Float64Data) Trimean(d Float64Data) (float64, error) {
	return Trimean(d)
}

// Sample returns sample from input with replacement or without
func (f Float64Data) Sample(n int, r bool) ([]float64, error) {
	return Sample(f, n, r)
}

// Variance the amount of variation in the dataset
func (f Float64Data) Variance() (float64, error) {
	return Variance(f)
}

// PopulationVariance finds the amount of variance within a population
func (f Float64Data) PopulationVariance() (float64, error) {
	return PopulationVariance(f)
}

// SampleVariance finds the amount of variance within a sample
func (f Float64Data) SampleVariance() (float64, error) {
	return SampleVariance(f)
}

// Covariance is a measure of how much two sets of data change
func (f Float64Data) Covariance(d Float64Data) (float64, error) {
	return Covariance(f, d)
}

// CovariancePopulation computes covariance for entire population between two variables.
func (f Float64Data) CovariancePopulation(d Float64Data) (float64, error) {
	return CovariancePopulation(f, d)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"math/rand"
	"net/http"
	"os"
	"strings"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/gin-gonic/gin"
	"github.com/moul/bolosseum"
	"github.com/moul/bolosseum/bots"
	"github.com/moul/bolosseum/bots/filebot"
	"github.com/moul/bolosseum/bots/httpbot"
	"github.com/moul/bolosseum/bots/stupidbot"
	"github.com/moul/bolosseum/games"
	"github.com/moul/bolosseum/games/coinflip"
	"github.com/moul/bolosseum/games/connectfour"
	"github.com/moul/bolosseum/games/russianbullet"
	"github.com/moul/bolosseum/games/tictactoe"
	"github.com/moul/bolosseum/stupid-ias"
	"github.com/moul/bolosseum/stupid-ias/coinflip"
	"github.com/moul/bolosseum/stupid-ias/connectfour"
	"github.com/moul/bolosseum/stupid-ias/tictactoe"
	"github.com/urfave/cli"
)

var availableGames = []string{
	"coinflip",
	"connectfour",
	"russianbullet",
	"tictactoe",
}

func getGame(gameName string) (games.Game, error) {
	switch gameName {
	case "coinflip":
		return coinflip.NewGame()
	case "connectfour":
		return connectfour.NewGame()
	case "russianbullet":
		return russianbullet.NewGame()
	case "tictactoe":
		return tictactoe.NewGame()
	default:
		return nil, fmt.Errorf("unknown game %q", gameName)
	}
}

func getStupidIA(iaPath string) (stupidias.StupidIA, error) {
	logrus.Warnf("Getting stupid IA %q", iaPath)
	switch iaPath {
	case "connectfour":
		return stupidconnectfour.NewIA()
	case "coinflip":
		return stupidcoinflip.NewIA()
	case "tictactoe":
		return stupidtictactoe.NewIA()
	default:
		return nil, fmt.Errorf("unknown stupid IA %q", iaPath)
	}
}

func getBot(botPath string, game games.Game) (bots.Bot, error) {
	logrus.Warnf("Getting bot %q", botPath)

	if botPath == "stupid" {
		botPath = fmt.Sprintf("stupid://%s", game.Name())
	}

	splt := strings.Split(botPath, "://")
	if len(splt) != 2 {
		return nil, fmt.Errorf("invalid bot path")
	}

	scheme := splt[0]
	path := splt[1]

	switch scheme {
	case "file":
		return filebot.NewBot(path)
	case "http+get":
		return httpbot.NewBot(path, "GET", "http")
	case "http+post", "http":
		return httpbot.NewBot(path, "POST", "http")
	case "https+get":
		return httpbot.NewBot(path, "GET", "https")
	case "https+post", "https":
		return httpbot.NewBot(path, "POST", "https")
	case "stupid":
		ia, err := getStupidIA(path)
		if err != nil {
			return nil, err
		}
		return stupidbot.NewStupidBot(path, ia)
	default:
		return nil, fmt.Errorf("invalid bot scheme: %q (%q)", scheme, path)
	}
}

func main() {
	// seed random
	rand.Seed(time.Now().UTC().UnixNano())

	// configure CLI
	app := cli.NewApp()
	app.Name = "bolosseum"
	app.Usage = "colosseum for bots"
	app.Version = bolosseum.VERSION

	app.Commands = []cli.Command{
		{
			Name:   "run",
			Usage:  "Start a battle",
			Action: run,
		},
		{
			Name:   "list-games",
			Usage:  "List games",
			Action: listGames,
		},
		{
			Name:   "server",
			Usage:  "Start a bolosseum web server",
			Action: server,
		},
	}
	if err := app.Run(os.Args); err != nil {
		logrus.Fatalf("%v", err)
	}
}

var indexHTML = `

    <title>Bolosseum</title>

Bolosseum

`

func server(c *cli.Context) error {
	r := gin.Default()
	r.LoadHTMLGlob("web/*")
	r.GET("/", func(c *gin.Context) {
		//c.Header("Content-Type", "text/html")
		//c.String(http.StatusOK, indexHTML)
		c.HTML(http.StatusOK, "index.tmpl", nil)
	})
	r.POST("/run", func(c *gin.Context) {
		gameName := c.PostForm("game")
		bot1URL := c.PostForm("bot1")
		bot2URL := c.PostForm("bot2")

		if gameName == "" || bot1URL == "" || bot2URL == "" {
			c.JSON(http.StatusNotFound, gin.H{
				"error": "Missing parameters",
			})
			return
		}

		// initialize game
		logrus.Warnf("Initializing game %q", gameName)
		game, err := getGame(gameName)
		if err != nil {
			c.JSON(http.StatusNotFound, gin.H{
				"error": "No such game",
			})
			return
		}
		logrus.Warnf("Game: %q: %q", game.Name(), game)

		args := []string{bot1URL, bot2URL}

		if err = game.CheckArgs(args); err != nil {
			c.JSON(http.StatusNotFound, gin.H{
				"error":  "Invalid parameters",
				"detail": err,
			})
		}

		// initialize bots
		hasError := false
		for _, botPath := range args {
			bot, err := getBot(botPath, game)
			if err != nil {
				hasError = true
				logrus.Errorf("Failed to initialize bot %q", bot)
			} else {
				logrus.Warnf("Registering bot %q", bot.Path())
				game.RegisterBot(bot)
			}
		}
		if hasError {
			c.JSON(http.StatusNotFound, gin.H{
				"error":  "bot registering error",
				"detail": err,
			})
			return
		}

		// run
		if err = game.Run("gameid"); err != nil {
			logrus.Errorf("Run error: %v", err)
		}

		// print ascii output
		output := game.GetAsciiOutput()
		if len(output) > 0 {
			fmt.Printf("%s", output)
		}

		c.JSON(http.StatusOK, gin.H{
			"output": string(output),
		})
	})
	return r.Run(":9000")
}

func listGames(c *cli.Context) error {
	fmt.Println("Games:")
	for _, game := range availableGames {
		fmt.Printf("- %s\n", game)
	}
	return nil
}

func run(c *cli.Context) error {
	args := c.Args()
	if len(args) < 1 {
		return cli.NewExitError("You need to specify the game", -1)
	}

	// initialize game
	logrus.Warnf("Initializing game %q", args[0])
	game, err := getGame(args[0])
	if err != nil {
		return cli.NewExitError(fmt.Sprintf("No such game %q", args[0]), -1)
	}
	logrus.Warnf("Game: %q: %q", game.Name(), game)

	if err = game.CheckArgs(args[1:]); err != nil {
		return cli.NewExitError(fmt.Sprintf("%v", err), -1)
	}

	// initialize bots
	hasError := false
	for _, botPath := range args[1:] {
		bot, err := getBot(botPath, game)
		if err != nil {
			hasError = true
			logrus.Errorf("Failed to initialize bot %q", bot)
		} else {
			logrus.Warnf("Registering bot %q", bot.Path())
			game.RegisterBot(bot)
		}
	}
	if hasError {
		return cli.NewExitError("Invalid bots", -1)
	}

	// run
	if err = game.Run("gameid"); err != nil {
		logrus.Errorf("Run error: %v", err)
	}

	// print ascii output
	if output := game.GetAsciiOutput(); len(output) > 0 {
		fmt.Printf("%s", output)
	}

	return nil
}
<commit_msg>Unescape URL inputs<commit_after>package main

import (
	"fmt"
	"math/rand"
	"net/http"
	"net/url"
	"os"
	"strings"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/gin-gonic/gin"
	"github.com/moul/bolosseum"
	"github.com/moul/bolosseum/bots"
	"github.com/moul/bolosseum/bots/filebot"
	"github.com/moul/bolosseum/bots/httpbot"
	"github.com/moul/bolosseum/bots/stupidbot"
	"github.com/moul/bolosseum/games"
	"github.com/moul/bolosseum/games/coinflip"
	"github.com/moul/bolosseum/games/connectfour"
	"github.com/moul/bolosseum/games/russianbullet"
	"github.com/moul/bolosseum/games/tictactoe"
	"github.com/moul/bolosseum/stupid-ias"
	"github.com/moul/bolosseum/stupid-ias/coinflip"
	"github.com/moul/bolosseum/stupid-ias/connectfour"
	"github.com/moul/bolosseum/stupid-ias/tictactoe"
	"github.com/urfave/cli"
)

var availableGames = []string{
	"coinflip",
	"connectfour",
	"russianbullet",
	"tictactoe",
}

func getGame(gameName string) (games.Game, error) {
	switch gameName {
	case "coinflip":
		return coinflip.NewGame()
	case "connectfour":
		return connectfour.NewGame()
	case "russianbullet":
		return russianbullet.NewGame()
	case "tictactoe":
		return tictactoe.NewGame()
	default:
		return nil, fmt.Errorf("unknown game %q", gameName)
	}
}

func getStupidIA(iaPath string) (stupidias.StupidIA, error) {
	logrus.Warnf("Getting stupid IA %q", iaPath)
	switch iaPath {
	case "connectfour":
		return stupidconnectfour.NewIA()
	case "coinflip":
		return stupidcoinflip.NewIA()
	case "tictactoe":
		return stupidtictactoe.NewIA()
	default:
		return nil, fmt.Errorf("unknown stupid IA %q", iaPath)
	}
}

func getBot(botPath string, game games.Game) (bots.Bot, error) {
	logrus.Warnf("Getting bot %q", botPath)

	if botPath == "stupid" {
		botPath = fmt.Sprintf("stupid://%s", game.Name())
	}

	splt := strings.Split(botPath, "://")
	if len(splt) != 2 {
		return nil, fmt.Errorf("invalid bot path")
	}

	scheme := splt[0]
	path := splt[1]

	switch scheme {
	case "file":
		return filebot.NewBot(path)
	case "http+get":
		return httpbot.NewBot(path, "GET", "http")
	case "http+post", "http":
		return httpbot.NewBot(path, "POST", "http")
	case "https+get":
		return httpbot.NewBot(path, "GET", "https")
	case "https+post", "https":
		return httpbot.NewBot(path, "POST", "https")
	case "stupid":
		ia, err := getStupidIA(path)
		if err != nil {
			return nil, err
		}
		return stupidbot.NewStupidBot(path, ia)
	default:
		return nil, fmt.Errorf("invalid bot scheme: %q (%q)", scheme, path)
	}
}

func main() {
	// seed random
	rand.Seed(time.Now().UTC().UnixNano())

	// configure CLI
	app := cli.NewApp()
	app.Name = "bolosseum"
	app.Usage = "colosseum for bots"
	app.Version = bolosseum.VERSION

	app.Commands = []cli.Command{
		{
			Name:   "run",
			Usage:  "Start a battle",
			Action: run,
		},
		{
			Name:   "list-games",
			Usage:  "List games",
			Action: listGames,
		},
		{
			Name:   "server",
			Usage:  "Start a bolosseum web server",
			Action: server,
		},
	}
	if err := app.Run(os.Args); err != nil {
		logrus.Fatalf("%v", err)
	}
}

var indexHTML = `

    <title>Bolosseum</title>

Bolosseum

`

func server(c *cli.Context) error {
	r := gin.Default()
	r.LoadHTMLGlob("web/*")
	r.GET("/", func(c *gin.Context) {
		//c.Header("Content-Type", "text/html")
		//c.String(http.StatusOK, indexHTML)
		c.HTML(http.StatusOK, "index.tmpl", nil)
	})
	r.POST("/run", func(c *gin.Context) {
		gameName := c.PostForm("game")
		bot1URL, err := url.QueryUnescape(c.PostForm("bot1"))
		if err != nil {
			c.JSON(http.StatusNotFound, gin.H{
				"error":  "Invalid bot1 parameter",
				"detail": err,
			})
			return
		}
		bot2URL, err := url.QueryUnescape(c.PostForm("bot2"))
		if err != nil {
			c.JSON(http.StatusNotFound, gin.H{
				"error":  "Invalid bot2 parameter",
				"detail": err,
			})
			return
		}

		if gameName == "" || bot1URL == "" || bot2URL == "" {
			c.JSON(http.StatusNotFound, gin.H{
				"error": "Missing parameters",
			})
			return
		}

		// initialize game
		logrus.Warnf("Initializing game %q", gameName)
		game, err := getGame(gameName)
		if err != nil {
			c.JSON(http.StatusNotFound, gin.H{
				"error": "No such game",
			})
			return
		}
		logrus.Warnf("Game: %q: %q", game.Name(), game)

		args := []string{bot1URL, bot2URL}

		if err = game.CheckArgs(args); err != nil {
			c.JSON(http.StatusNotFound, gin.H{
				"error":  "Invalid parameters",
				"detail": err,
			})
		}

		// initialize bots
		hasError := false
		for _, botPath := range args {
			bot, err := getBot(botPath, game)
			if err != nil {
				hasError = true
				logrus.Errorf("Failed to initialize bot %q", bot)
			} else {
				logrus.Warnf("Registering bot %q", bot.Path())
				game.RegisterBot(bot)
			}
		}
		if hasError {
			c.JSON(http.StatusNotFound, gin.H{
				"error":  "bot registering error",
				"detail": err,
			})
			return
		}

		// run
		if err = game.Run("gameid"); err != nil {
			logrus.Errorf("Run error: %v", err)
		}

		// print ascii output
		output := game.GetAsciiOutput()
		if len(output) > 0 {
			fmt.Printf("%s", output)
		}

		c.JSON(http.StatusOK, gin.H{
			"output": string(output),
		})
	})
	return r.Run(":9000")
}

func listGames(c *cli.Context) error {
	fmt.Println("Games:")
	for _, game := range availableGames {
		fmt.Printf("- %s\n", game)
	}
	return nil
}

func run(c *cli.Context) error {
	args := c.Args()
	if len(args) < 1 {
		return cli.NewExitError("You need to specify the game", -1)
	}

	// initialize game
	logrus.Warnf("Initializing game %q", args[0])
	game, err := getGame(args[0])
	if err != nil {
		return cli.NewExitError(fmt.Sprintf("No such game %q", args[0]), -1)
	}
	logrus.Warnf("Game: %q: %q", game.Name(), game)

	if err = game.CheckArgs(args[1:]); err != nil {
		return cli.NewExitError(fmt.Sprintf("%v", err), -1)
	}

	// initialize bots
	hasError := false
	for _, botPath := range args[1:] {
		bot, err := getBot(botPath, game)
		if err != nil {
			hasError = true
			logrus.Errorf("Failed to initialize bot %q", bot)
		} else {
			logrus.Warnf("Registering bot %q", bot.Path())
			game.RegisterBot(bot)
		}
	}
	if hasError {
		return cli.NewExitError("Invalid bots", -1)
	}

	// run
	if err = game.Run("gameid"); err != nil {
		logrus.Errorf("Run error: %v", err)
	}

	// print ascii output
	if output := game.GetAsciiOutput(); len(output) > 0 {
		fmt.Printf("%s", output)
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"net/http"
	"os"
	"os/signal"
	"runtime"
	"strings"
	"syscall"

	"github.com/kelseyhightower/envconfig"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promauto"
	"github.com/prometheus/client_golang/prometheus/promhttp"

	"nimona.io/internal/version"
	"nimona.io/pkg/context"
	"nimona.io/pkg/crypto"
	"nimona.io/pkg/hyperspace/provider"
	"nimona.io/pkg/localpeer"
	"nimona.io/pkg/log"
	"nimona.io/pkg/network"
	"nimona.io/pkg/peer"
)

// nolint: lll
type config struct {
	Peer struct {
		PrivateKey      crypto.PrivateKey `envconfig:"PRIVATE_KEY"`
		BindAddress     string            `envconfig:"BIND_ADDRESS" default:"0.0.0.0:0"`
		AnnounceAddress string            `envconfig:"ANNOUNCE_ADDRESS"`
		Bootstraps      []peer.Shorthand  `envconfig:"BOOTSTRAPS"`
	} `envconfig:"PEER"`
	Metrics struct {
		BindAddress string `envconfig:"BIND_ADDRESS" default:"0.0.0.0:0"`
	} `envconfig:"METRICS"`
}

func main() {
	ctx := context.New(
		context.WithCorrelationID("nimona"),
	)

	logger := log.FromContext(ctx).With(
		log.String("build.version", version.Version),
		log.String("build.commit", version.Commit),
		log.String("build.timestamp", version.Date),
	)

	cfg := &config{}
	if err := envconfig.Process("nimona", cfg); err != nil {
		logger.Fatal("error processing config", log.Error(err))
	}

	if cfg.Peer.PrivateKey.IsEmpty() {
		logger.Fatal("missing peer private key")
	}

	// construct local peer
	local := localpeer.New()
	// attach peer private key from config
	local.PutPrimaryPeerKey(cfg.Peer.PrivateKey)

	// construct new network
	net := network.New(
		ctx,
		network.WithLocalPeer(local),
	)

	// start listening
	lis, err := net.Listen(
		ctx,
		cfg.Peer.BindAddress,
		network.ListenOnLocalIPs,
		network.ListenOnPrivateIPs,
	)
	if err != nil {
		logger.Fatal("error while listening", log.Error(err))
	}

	// add announce address
	if cfg.Peer.AnnounceAddress != "" {
		local.PutAddresses("tcps:" + cfg.Peer.AnnounceAddress)
	}

	// convert shorthands into connection infos
	bootstrapProviders := []*peer.ConnectionInfo{}
	for _, s := range cfg.Peer.Bootstraps {
		bootstrapPeer, err := s.ConnectionInfo()
		if err != nil {
			logger.Fatal("error parsing bootstrap peer", log.Error(err))
		}
		bootstrapProviders = append(bootstrapProviders, bootstrapPeer)
	}

	// construct new hyperspace provider
	_, err = provider.New(
		ctx,
		net,
		bootstrapProviders,
	)
	if err != nil {
		logger.Fatal("error while constructing provider", log.Error(err))
	}

	logger = logger.With(
		log.String("peer.privateKey", local.GetPrimaryPeerKey().String()),
		log.String("peer.publicKey", local.GetPrimaryPeerKey().PublicKey().String()),
		log.Strings("peer.addresses", local.GetAddresses()),
	)

	logger.Info("bootstrap node ready")

	go func() {
		promauto.NewGaugeFunc(
			prometheus.GaugeOpts{
				Name: "build_info",
				Help: "Build info",
				ConstLabels: prometheus.Labels{
					"commit":     version.Commit,
					"build_date": version.Date,
					"version":    version.Version,
					"goversion": strings.Replace(
						runtime.Version(),
						"go", "v", 1,
					),
				},
			},
			func() float64 { return 1 },
		)
		logger.Info(
			"serving metrics",
			log.String("address", cfg.Metrics.BindAddress),
		)
		http.Handle("/metrics", promhttp.Handler())
		err := http.ListenAndServe(cfg.Metrics.BindAddress, nil)
		if err != nil {
			logger.Warn("error serving metrics", log.Error(err))
			return
		}
	}()

	// register for termination signals
	sigs := make(chan os.Signal, 1)
	signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)

	// and wait for one
	<-sigs

	// finally terminate everything
	logger.Info("shutting down")
	lis.Close() // nolint: errcheck
}
<commit_msg>feat(cmd/bootstrap): add resolver so providers update each other<commit_after>package main

import (
	"net/http"
	"os"
	"os/signal"
	"runtime"
	"strings"
	"syscall"

	"github.com/kelseyhightower/envconfig"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promauto"
	"github.com/prometheus/client_golang/prometheus/promhttp"

	"nimona.io/internal/version"
	"nimona.io/pkg/context"
	"nimona.io/pkg/crypto"
	"nimona.io/pkg/hyperspace/provider"
	"nimona.io/pkg/hyperspace/resolver"
	"nimona.io/pkg/localpeer"
	"nimona.io/pkg/log"
	"nimona.io/pkg/network"
	"nimona.io/pkg/peer"
)

// nolint: lll
type config struct {
	Peer struct {
		PrivateKey      crypto.PrivateKey `envconfig:"PRIVATE_KEY"`
		BindAddress     string            `envconfig:"BIND_ADDRESS" default:"0.0.0.0:0"`
		AnnounceAddress string            `envconfig:"ANNOUNCE_ADDRESS"`
		Bootstraps      []peer.Shorthand  `envconfig:"BOOTSTRAPS"`
	} `envconfig:"PEER"`
	Metrics struct {
		BindAddress string `envconfig:"BIND_ADDRESS" default:"0.0.0.0:0"`
	} `envconfig:"METRICS"`
}

func main() {
	ctx := context.New(
		context.WithCorrelationID("nimona"),
	)

	logger := log.FromContext(ctx).With(
		log.String("build.version", version.Version),
		log.String("build.commit", version.Commit),
		log.String("build.timestamp", version.Date),
	)

	cfg := &config{}
	if err := envconfig.Process("nimona", cfg); err != nil {
		logger.Fatal("error processing config", log.Error(err))
	}

	if cfg.Peer.PrivateKey.IsEmpty() {
		logger.Fatal("missing peer private key")
	}

	// construct local peer
	local := localpeer.New()
	// attach peer private key from config
	local.PutPrimaryPeerKey(cfg.Peer.PrivateKey)

	// construct new network
	net := network.New(
		ctx,
		network.WithLocalPeer(local),
	)

	// start listening
	lis, err := net.Listen(
		ctx,
		cfg.Peer.BindAddress,
		network.ListenOnLocalIPs,
		network.ListenOnPrivateIPs,
	)
	if err != nil {
		logger.Fatal("error while listening", log.Error(err))
	}

	// add announce address
	if cfg.Peer.AnnounceAddress != "" {
		local.PutAddresses("tcps:" + cfg.Peer.AnnounceAddress)
	}

	// convert shorthands into connection infos
	bootstrapProviders := []*peer.ConnectionInfo{}
	for _, s := range cfg.Peer.Bootstraps {
		bootstrapPeer, err := s.ConnectionInfo()
		if err != nil {
			logger.Fatal("error parsing bootstrap peer", log.Error(err))
		}
		bootstrapProviders = append(bootstrapProviders, bootstrapPeer)
	}

	// construct new hyperspace provider
	_, err = provider.New(
		ctx,
		net,
		bootstrapProviders,
	)
	if err != nil {
		logger.Fatal("error while constructing provider", log.Error(err))
	}

	logger = logger.With(
		log.String("peer.privateKey", local.GetPrimaryPeerKey().String()),
		log.String("peer.publicKey", local.GetPrimaryPeerKey().PublicKey().String()),
		log.Strings("peer.addresses", local.GetAddresses()),
	)

	// construct a resolver so the providers keep each other updated about
	// themselves
	resolver.New(
		ctx,
		net,
		resolver.WithBoostrapPeers(bootstrapProviders...),
	)

	logger.Info("bootstrap node ready")

	go func() {
		promauto.NewGaugeFunc(
			prometheus.GaugeOpts{
				Name: "build_info",
				Help: "Build info",
				ConstLabels: prometheus.Labels{
					"commit":     version.Commit,
					"build_date": version.Date,
					"version":    version.Version,
					"goversion": strings.Replace(
						runtime.Version(),
						"go", "v", 1,
					),
				},
			},
			func() float64 { return 1 },
		)
		logger.Info(
			"serving metrics",
			log.String("address", cfg.Metrics.BindAddress),
		)
		http.Handle("/metrics", promhttp.Handler())
		err := http.ListenAndServe(cfg.Metrics.BindAddress, nil)
		if err != nil {
			logger.Warn("error serving metrics", log.Error(err))
			return
		}
	}()

	// register for termination signals
	sigs := make(chan os.Signal, 1)
	signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)

	// and wait for one
	<-sigs

	// finally terminate everything
	logger.Info("shutting down")
	lis.Close() // nolint: errcheck
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"encoding/json"
	"io"
	"os"

	"github.com/containerd/console"
	"github.com/moby/buildkit/client"
	"github.com/moby/buildkit/client/llb"
	"github.com/moby/buildkit/cmd/buildctl/build"
	bccommon "github.com/moby/buildkit/cmd/buildctl/common"
	"github.com/moby/buildkit/session"
	"github.com/moby/buildkit/session/auth/authprovider"
	"github.com/moby/buildkit/session/sshforward/sshprovider"
	"github.com/moby/buildkit/solver/pb"
	"github.com/moby/buildkit/util/progress/progressui"
	"github.com/opencontainers/go-digest"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
	"github.com/urfave/cli"
	"golang.org/x/sync/errgroup"
)

var buildCommand = cli.Command{
	Name:    "build",
	Aliases: []string{"b"},
	Usage:   "build",
	UsageText: `
	To build and push an image using Dockerfile:
	  $ buildctl build --frontend dockerfile.v0 --opt target=foo --opt build-arg:foo=bar --local context=. --local dockerfile=. --output type=image,name=docker.io/username/image,push=true
	`,
	Action: buildAction,
	Flags: []cli.Flag{
		cli.StringSliceFlag{
			Name:  "output,o",
			Usage: "Define exports for build result, e.g. --output type=image,name=docker.io/username/image,push=true",
		},
		cli.StringFlag{
			Name:   "exporter",
			Usage:  "Define exporter for build result (DEPRECATED: use --export type=<type>[,<opt>=<optval>]",
			Hidden: true,
		},
		cli.StringSliceFlag{
			Name:   "exporter-opt",
			Usage:  "Define custom options for exporter (DEPRECATED: use --output type=<type>[,<opt>=<optval>]",
			Hidden: true,
		},
		cli.StringFlag{
			Name:  "progress",
			Usage: "Set type of progress (auto, plain, tty). Use plain to show container output",
			Value: "auto",
		},
		cli.StringFlag{
			Name:  "trace",
			Usage: "Path to trace file. Defaults to no tracing.",
		},
		cli.StringSliceFlag{
			Name:  "local",
			Usage: "Allow build access to the local directory",
		},
		cli.StringFlag{
			Name:  "frontend",
			Usage: "Define frontend used for build",
		},
		cli.StringSliceFlag{
			Name:  "opt",
			Usage: "Define custom options for frontend, e.g. --opt target=foo --opt build-arg:foo=bar",
		},
		cli.StringSliceFlag{
			Name:   "frontend-opt",
			Usage:  "Define custom options for frontend, e.g. --frontend-opt target=foo --frontend-opt build-arg:foo=bar (DEPRECATED: use --opt)",
			Hidden: true,
		},
		cli.BoolFlag{
			Name:  "no-cache",
			Usage: "Disable cache for all the vertices",
		},
		cli.StringSliceFlag{
			Name:  "export-cache",
			Usage: "Export build cache, e.g. type=registry,ref=example.com/foo/bar, or type=local,dest=path/to/dir",
		},
		cli.StringSliceFlag{
			Name:   "export-cache-opt",
			Usage:  "Define custom options for cache exporting (DEPRECATED: use --export-cache type=<type>,<opt>=<optval>[,<opt>=<optval>]",
			Hidden: true,
		},
		cli.StringSliceFlag{
			Name:  "import-cache",
			Usage: "Import build cache, e.g. type=registry,ref=example.com/foo/bar, or type=local,src=path/to/dir",
		},
		cli.StringSliceFlag{
			Name:  "secret",
			Usage: "Secret value exposed to the build. Format id=secretname,src=filepath",
		},
		cli.StringSliceFlag{
			Name:  "allow",
			Usage: "Allow extra privileged entitlement, e.g. network.host, security.insecure",
		},
		cli.StringSliceFlag{
			Name:  "ssh",
			Usage: "Allow forwarding SSH agent to the builder. Format default|<id>[=<socket>|<key>[,<key>]]",
		},
	},
}

func read(r io.Reader, clicontext *cli.Context) (*llb.Definition, error) {
	def, err := llb.ReadFrom(r)
	if err != nil {
		return nil, errors.Wrap(err, "failed to parse input")
	}
	if clicontext.Bool("no-cache") {
		for _, dt := range def.Def {
			var op pb.Op
			if err := (&op).Unmarshal(dt); err != nil {
				return nil, errors.Wrap(err, "failed to parse llb proto op")
			}
			dgst := digest.FromBytes(dt)
			opMetadata, ok := def.Metadata[dgst]
			if !ok {
				opMetadata = pb.OpMetadata{}
			}
			c := llb.Constraints{Metadata: opMetadata}
			llb.IgnoreCache(&c)
			def.Metadata[dgst] = c.Metadata
		}
	}
	return def, nil
}

func openTraceFile(clicontext *cli.Context) (*os.File, error) {
	if traceFileName := clicontext.String("trace"); traceFileName != "" {
		return os.OpenFile(traceFileName, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0600)
	}
	return nil, nil
}

func buildAction(clicontext *cli.Context) error {
	c, err := bccommon.ResolveClient(clicontext)
	if err != nil {
		return err
	}

	traceFile, err := openTraceFile(clicontext)
	if err != nil {
		return err
	}
	var traceEnc *json.Encoder
	if traceFile != nil {
		defer traceFile.Close()
		traceEnc = json.NewEncoder(traceFile)

		logrus.Infof("tracing logs to %s", traceFile.Name())
	}

	attachable := []session.Attachable{authprovider.NewDockerAuthProvider()}

	if ssh := clicontext.StringSlice("ssh"); len(ssh) > 0 {
		configs, err := build.ParseSSH(ssh)
		if err != nil {
			return err
		}
		sp, err := sshprovider.NewSSHAgentProvider(configs)
		if err != nil {
			return err
		}
		attachable = append(attachable, sp)
	}

	if secrets := clicontext.StringSlice("secret"); len(secrets) > 0 {
		secretProvider, err := build.ParseSecret(secrets)
		if err != nil {
			return err
		}
		attachable = append(attachable, secretProvider)
	}

	allowed, err := build.ParseAllow(clicontext.StringSlice("allow"))
	if err != nil {
		return err
	}

	var exports []client.ExportEntry
	if legacyExporter := clicontext.String("exporter"); legacyExporter != "" {
		logrus.Warnf("--exporter <exporter> is deprecated. Please use --output type=<exporter>[,<opt>=<optval>] instead.")
		if len(clicontext.StringSlice("output")) > 0 {
			return errors.New("--exporter cannot be used with --output")
		}
		exports, err = build.ParseLegacyExporter(clicontext.String("exporter"), clicontext.StringSlice("exporter-opt"))
	} else {
		exports, err = build.ParseOutput(clicontext.StringSlice("output"))
	}
	if err != nil {
		return err
	}

	cacheExports, err := build.ParseExportCache(clicontext.StringSlice("export-cache"), clicontext.StringSlice("export-cache-opt"))
	if err != nil {
		return err
	}
	cacheImports, err := build.ParseImportCache(clicontext.StringSlice("import-cache"))
	if err != nil {
		return err
	}

	ch := make(chan *client.SolveStatus)
	eg, ctx := errgroup.WithContext(bccommon.CommandContext(clicontext))

	solveOpt := client.SolveOpt{
		Exports: exports,
		// LocalDirs is set later
		Frontend: clicontext.String("frontend"),
		// FrontendAttrs is set later
		CacheExports:        cacheExports,
		CacheImports:        cacheImports,
		Session:             attachable,
		AllowedEntitlements: allowed,
	}

	solveOpt.FrontendAttrs, err = build.ParseOpt(clicontext.StringSlice("opt"), clicontext.StringSlice("frontend-opt"))
	if err != nil {
		return errors.Wrap(err, "invalid opt")
	}

	solveOpt.LocalDirs, err = build.ParseLocal(clicontext.StringSlice("local"))
	if err != nil {
		return errors.Wrap(err, "invalid local")
	}

	var def *llb.Definition
	if clicontext.String("frontend") == "" {
		if fi, _ := os.Stdin.Stat(); (fi.Mode() & os.ModeCharDevice) != 0 {
			return errors.Errorf("please specify --frontend or pipe LLB definition to stdin")
		}
		def, err = read(os.Stdin, clicontext)
		if err != nil {
			return err
		}
		if len(def.Def) == 0 {
			return errors.Errorf("empty definition sent to build. Specify --frontend instead?")
		}
	} else {
		if clicontext.Bool("no-cache") {
			solveOpt.FrontendAttrs["no-cache"] = ""
		}
	}

	eg.Go(func() error {
		resp, err := c.Solve(ctx, def, solveOpt, ch)
		if err != nil {
			return err
		}
		for k, v := range resp.ExporterResponse {
			logrus.Debugf("exporter response: %s=%s", k, v)
		}
		return err
	})

	displayCh := ch
	if traceEnc != nil {
		displayCh = make(chan *client.SolveStatus)
		eg.Go(func() error {
			defer close(displayCh)
			for s := range ch {
				if err := traceEnc.Encode(s); err != nil {
					logrus.Error(err)
				}
				displayCh <- s
			}
			return nil
		})
	}

	eg.Go(func() error {
		var c console.Console
		progressOpt := clicontext.String("progress")

		switch progressOpt {
		case "auto", "tty":
			cf, err := console.ConsoleFromFile(os.Stderr)
			if err != nil && progressOpt == "tty" {
				return err
			}
			c = cf
		case "plain":
		default:
			return errors.Errorf("invalid progress value : %s", progressOpt)
		}

		// not using shared context to not disrupt display but let is finish reporting errors
		return progressui.DisplaySolveStatus(context.TODO(), "", c, os.Stdout, displayCh)
	})

	return eg.Wait()
}
<commit_msg>buildctl: print plain progress to stderr<commit_after>package main

import (
	"context"
	"encoding/json"
	"io"
	"os"

	"github.com/containerd/console"
	"github.com/moby/buildkit/client"
	"github.com/moby/buildkit/client/llb"
	"github.com/moby/buildkit/cmd/buildctl/build"
	bccommon "github.com/moby/buildkit/cmd/buildctl/common"
	"github.com/moby/buildkit/session"
	"github.com/moby/buildkit/session/auth/authprovider"
	"github.com/moby/buildkit/session/sshforward/sshprovider"
	"github.com/moby/buildkit/solver/pb"
	"github.com/moby/buildkit/util/progress/progressui"
	"github.com/opencontainers/go-digest"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
	"github.com/urfave/cli"
	"golang.org/x/sync/errgroup"
)

var buildCommand = cli.Command{
	Name:    "build",
	Aliases: []string{"b"},
	Usage:   "build",
	UsageText: `
	To build and push an image using Dockerfile:
	  $ buildctl build --frontend dockerfile.v0 --opt target=foo --opt build-arg:foo=bar --local context=. --local dockerfile=. --output type=image,name=docker.io/username/image,push=true
	`,
	Action: buildAction,
	Flags: []cli.Flag{
		cli.StringSliceFlag{
			Name:  "output,o",
			Usage: "Define exports for build result, e.g. --output type=image,name=docker.io/username/image,push=true",
		},
		cli.StringFlag{
			Name:   "exporter",
			Usage:  "Define exporter for build result (DEPRECATED: use --export type=<type>[,<opt>=<optval>]",
			Hidden: true,
		},
		cli.StringSliceFlag{
			Name:   "exporter-opt",
			Usage:  "Define custom options for exporter (DEPRECATED: use --output type=<type>[,<opt>=<optval>]",
			Hidden: true,
		},
		cli.StringFlag{
			Name:  "progress",
			Usage: "Set type of progress (auto, plain, tty). Use plain to show container output",
			Value: "auto",
		},
		cli.StringFlag{
			Name:  "trace",
			Usage: "Path to trace file. Defaults to no tracing.",
		},
		cli.StringSliceFlag{
			Name:  "local",
			Usage: "Allow build access to the local directory",
		},
		cli.StringFlag{
			Name:  "frontend",
			Usage: "Define frontend used for build",
		},
		cli.StringSliceFlag{
			Name:  "opt",
			Usage: "Define custom options for frontend, e.g. --opt target=foo --opt build-arg:foo=bar",
		},
		cli.StringSliceFlag{
			Name:   "frontend-opt",
			Usage:  "Define custom options for frontend, e.g. --frontend-opt target=foo --frontend-opt build-arg:foo=bar (DEPRECATED: use --opt)",
			Hidden: true,
		},
		cli.BoolFlag{
			Name:  "no-cache",
			Usage: "Disable cache for all the vertices",
		},
		cli.StringSliceFlag{
			Name:  "export-cache",
			Usage: "Export build cache, e.g. type=registry,ref=example.com/foo/bar, or type=local,dest=path/to/dir",
		},
		cli.StringSliceFlag{
			Name:   "export-cache-opt",
			Usage:  "Define custom options for cache exporting (DEPRECATED: use --export-cache type=<type>,<opt>=<optval>[,<opt>=<optval>]",
			Hidden: true,
		},
		cli.StringSliceFlag{
			Name:  "import-cache",
			Usage: "Import build cache, e.g. type=registry,ref=example.com/foo/bar, or type=local,src=path/to/dir",
		},
		cli.StringSliceFlag{
			Name:  "secret",
			Usage: "Secret value exposed to the build. Format id=secretname,src=filepath",
		},
		cli.StringSliceFlag{
			Name:  "allow",
			Usage: "Allow extra privileged entitlement, e.g. network.host, security.insecure",
		},
		cli.StringSliceFlag{
			Name:  "ssh",
			Usage: "Allow forwarding SSH agent to the builder. Format default|<id>[=<socket>|<key>[,<key>]]",
		},
	},
}

func read(r io.Reader, clicontext *cli.Context) (*llb.Definition, error) {
	def, err := llb.ReadFrom(r)
	if err != nil {
		return nil, errors.Wrap(err, "failed to parse input")
	}
	if clicontext.Bool("no-cache") {
		for _, dt := range def.Def {
			var op pb.Op
			if err := (&op).Unmarshal(dt); err != nil {
				return nil, errors.Wrap(err, "failed to parse llb proto op")
			}
			dgst := digest.FromBytes(dt)
			opMetadata, ok := def.Metadata[dgst]
			if !ok {
				opMetadata = pb.OpMetadata{}
			}
			c := llb.Constraints{Metadata: opMetadata}
			llb.IgnoreCache(&c)
			def.Metadata[dgst] = c.Metadata
		}
	}
	return def, nil
}

func openTraceFile(clicontext *cli.Context) (*os.File, error) {
	if traceFileName := clicontext.String("trace"); traceFileName != "" {
		return os.OpenFile(traceFileName, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0600)
	}
	return nil, nil
}

func buildAction(clicontext *cli.Context) error {
	c, err := bccommon.ResolveClient(clicontext)
	if err != nil {
		return err
	}

	traceFile, err := openTraceFile(clicontext)
	if err != nil {
		return err
	}
	var traceEnc *json.Encoder
	if traceFile != nil {
		defer traceFile.Close()
		traceEnc = json.NewEncoder(traceFile)

		logrus.Infof("tracing logs to %s", traceFile.Name())
	}

	attachable := []session.Attachable{authprovider.NewDockerAuthProvider()}

	if ssh := clicontext.StringSlice("ssh"); len(ssh) > 0 {
		configs, err := build.ParseSSH(ssh)
		if err != nil {
			return err
		}
		sp, err := sshprovider.NewSSHAgentProvider(configs)
		if err != nil {
			return err
		}
		attachable = append(attachable, sp)
	}

	if secrets := clicontext.StringSlice("secret"); len(secrets) > 0 {
		secretProvider, err := build.ParseSecret(secrets)
		if err != nil {
			return err
		}
		attachable = append(attachable, secretProvider)
	}

	allowed, err := build.ParseAllow(clicontext.StringSlice("allow"))
	if err != nil {
		return err
	}

	var exports []client.ExportEntry
	if legacyExporter := clicontext.String("exporter"); legacyExporter != "" {
		logrus.Warnf("--exporter <exporter> is deprecated. Please use --output type=<exporter>[,<opt>=<optval>] instead.")
		if len(clicontext.StringSlice("output")) > 0 {
			return errors.New("--exporter cannot be used with --output")
		}
		exports, err = build.ParseLegacyExporter(clicontext.String("exporter"), clicontext.StringSlice("exporter-opt"))
	} else {
		exports, err = build.ParseOutput(clicontext.StringSlice("output"))
	}
	if err != nil {
		return err
	}

	cacheExports, err := build.ParseExportCache(clicontext.StringSlice("export-cache"), clicontext.StringSlice("export-cache-opt"))
	if err != nil {
		return err
	}
	cacheImports, err := build.ParseImportCache(clicontext.StringSlice("import-cache"))
	if err != nil {
		return err
	}

	ch := make(chan *client.SolveStatus)
	eg, ctx := errgroup.WithContext(bccommon.CommandContext(clicontext))

	solveOpt := client.SolveOpt{
		Exports: exports,
		// LocalDirs is set later
		Frontend: clicontext.String("frontend"),
		// FrontendAttrs is set later
		CacheExports:        cacheExports,
		CacheImports:        cacheImports,
		Session:             attachable,
		AllowedEntitlements: allowed,
	}

	solveOpt.FrontendAttrs, err = build.ParseOpt(clicontext.StringSlice("opt"), clicontext.StringSlice("frontend-opt"))
	if err != nil {
		return errors.Wrap(err, "invalid opt")
	}

	solveOpt.LocalDirs, err = build.ParseLocal(clicontext.StringSlice("local"))
	if err != nil {
		return errors.Wrap(err, "invalid local")
	}

	var def *llb.Definition
	if clicontext.String("frontend") == "" {
		if fi, _ := os.Stdin.Stat(); (fi.Mode() & os.ModeCharDevice) != 0 {
			return errors.Errorf("please specify --frontend or pipe LLB definition to stdin")
		}
		def, err = read(os.Stdin, clicontext)
		if err != nil {
			return err
		}
		if len(def.Def) == 0 {
			return errors.Errorf("empty definition sent to build. Specify --frontend instead?")
		}
	} else {
		if clicontext.Bool("no-cache") {
			solveOpt.FrontendAttrs["no-cache"] = ""
		}
	}

	eg.Go(func() error {
		resp, err := c.Solve(ctx, def, solveOpt, ch)
		if err != nil {
			return err
		}
		for k, v := range resp.ExporterResponse {
			logrus.Debugf("exporter response: %s=%s", k, v)
		}
		return err
	})

	displayCh := ch
	if traceEnc != nil {
		displayCh = make(chan *client.SolveStatus)
		eg.Go(func() error {
			defer close(displayCh)
			for s := range ch {
				if err := traceEnc.Encode(s); err != nil {
					logrus.Error(err)
				}
				displayCh <- s
			}
			return nil
		})
	}

	eg.Go(func() error {
		var c console.Console
		progressOpt := clicontext.String("progress")

		switch progressOpt {
		case "auto", "tty":
			cf, err := console.ConsoleFromFile(os.Stderr)
			if err != nil && progressOpt == "tty" {
				return err
			}
			c = cf
		case "plain":
		default:
			return errors.Errorf("invalid progress value : %s", progressOpt)
		}

		// not using shared context to not disrupt display but let is finish reporting errors
		return progressui.DisplaySolveStatus(context.TODO(), "", c, os.Stderr, displayCh)
	})

	return eg.Wait()
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/BytemarkHosting/bytemark-client/cmd/bytemark/app"
	"github.com/BytemarkHosting/bytemark-client/cmd/bytemark/app/args"
	"github.com/BytemarkHosting/bytemark-client/cmd/bytemark/app/with"
	"github.com/BytemarkHosting/bytemark-client/util/log"
	"github.com/urfave/cli"
)

func init() {
	commands = append(commands, cli.Command{
		Name:        "reset",
		Usage:       "restart a server as though the reset button had been pushed",
		UsageText:   "bytemark reset <server>",
		Description: "For cloud servers, this does not cause the qemu process to be restarted. This means that the server will remain on the same head and will not notice hardware changes.",
		Flags: []cli.Flag{
			cli.GenericFlag{
				Name:  "server",
				Usage: "the server to reset",
				Value: new(app.VirtualMachineNameFlag),
			},
		},
		Action: app.Action(args.Optional("server"), with.RequiredFlags("server"), with.Auth, func(c *app.Context) (err error) {
			vmName := c.VirtualMachineName("server")
			log.Logf("Attempting to reset %v...\r\n", vmName)
			err = c.Client().ResetVirtualMachine(vmName)
			if err != nil {
				return err
			}

			log.Errorf("%v reset successfully.\r\n", vmName)
			return
		}),
	}, cli.Command{
		Name:        "restart",
		Usage:       "power off a server and start it again",
		UsageText:   "bytemark restart <server>",
		Description: "This command will power down a server, cleanly if possible, and then start it back up again. For cloud servers this can cause the server to be started on a different head to the one it was running on, but this is not guaranteed.",
		Flags: []cli.Flag{
			cli.GenericFlag{
				Name:  "server",
				Usage: "the server to restart",
				Value: new(app.VirtualMachineNameFlag),
			},
		},
		Action: app.Action(args.Optional("server"), with.RequiredFlags("server"), with.Auth, func(c *app.Context) (err error) {
			vmName := c.VirtualMachineName("server")
			log.Logf("Attempting to restart %v...\r\n", vmName)
			err = c.Client().RestartVirtualMachine(vmName)
			if err != nil {
				return
			}

			log.Logf("%s restarted successfully.\r\n", vmName)
			return
		}),
	}, cli.Command{
		Name:        "shutdown",
		Usage:       "cleanly shut down a server",
		UsageText:   "bytemark shutdown <server>",
		Description: "This command sends the ACPI shutdown signal to the server, causing a clean shut down. This is like pressing the power button on a computer you have physical access to.",
		Flags: []cli.Flag{
			cli.GenericFlag{
				Name:  "server",
				Usage: "the server to shutdown",
				Value: new(app.VirtualMachineNameFlag),
			},
		},
		Action: app.Action(args.Optional("server"), with.RequiredFlags("server"), with.Auth, func(c *app.Context) (err error) {
			vmName := c.VirtualMachineName("server")
			log.Logf("Attempting to shutdown %v...\r\n", vmName)
			err = c.Client().ShutdownVirtualMachine(vmName, true)
			if err != nil {
				return
			}

			log.Logf("%s was shutdown successfully.\r\n", vmName)
			return
		}),
	}, cli.Command{
		Name:        "start",
		Usage:       "start a stopped server",
		UsageText:   "bytemark start <server>",
		Description: "This command will start a server that is not currently running.",
		Flags: []cli.Flag{
			cli.GenericFlag{
				Name:  "server",
				Usage: "the server to start",
				Value: new(app.VirtualMachineNameFlag),
			},
		},
		Action: app.Action(args.Optional("server"), with.RequiredFlags("server"), with.Auth, func(c *app.Context) (err error) {
			vmName := c.VirtualMachineName("server")
			log.Logf("Attempting to start %s...\r\n", vmName)
			err = c.Client().StartVirtualMachine(vmName)
			if err != nil {
				return
			}

			log.Logf("%s started successfully.\r\n", vmName)
			return
		}),
	}, cli.Command{
		Name:        "stop",
		Usage:       "stop a server, as though pulling the power cable out",
		UsageText:   "bytemark stop <server>",
		Description: "This command will instantly power down a server. Note that this may cause data loss, particularly on servers with unjournaled file systems (e.g. ext2)",
		Flags: []cli.Flag{
			cli.GenericFlag{
				Name:  "server",
				Usage: "the server to stop",
				Value: new(app.VirtualMachineNameFlag),
			},
		},
		Action: app.Action(args.Optional("server"), with.RequiredFlags("server"), with.Auth, func(c *app.Context) (err error) {
			vmName := c.VirtualMachineName("server")
			log.Logf("Attempting to stop %s...\r\n", vmName)
			err = c.Client().StopVirtualMachine(vmName)
			if err != nil {
				return
			}

			log.Logf("%s stopped successfully.\r\n", vmName)
			return
		}),
	})
}
<commit_msg>Rewrite restart command to actually do a safe shutdown & rewrite shutdown for shiggles<commit_after>package main

import (
	"fmt"
	"time"

	"github.com/BytemarkHosting/bytemark-client/cmd/bytemark/app"
	"github.com/BytemarkHosting/bytemark-client/cmd/bytemark/app/args"
	"github.com/BytemarkHosting/bytemark-client/cmd/bytemark/app/with"
	"github.com/BytemarkHosting/bytemark-client/lib"
	"github.com/BytemarkHosting/bytemark-client/lib/brain"
	"github.com/BytemarkHosting/bytemark-client/util/log"
	"github.com/urfave/cli"
)

func init() {
	commands = append(commands, cli.Command{
		Name:        "reset",
		Usage:       "restart a server as though the reset button had been pushed",
		UsageText:   "bytemark reset <server>",
		Description: "For cloud servers, this does not cause the qemu process to be restarted. This means that the server will remain on the same head and will not notice hardware changes.",
		Flags: []cli.Flag{
			cli.GenericFlag{
				Name:  "server",
				Usage: "the server to reset",
				Value: new(app.VirtualMachineNameFlag),
			},
		},
		Action: app.Action(args.Optional("server"), with.RequiredFlags("server"), with.Auth, func(c *app.Context) (err error) {
			vmName := c.VirtualMachineName("server")
			log.Logf("Attempting to reset %v...\r\n", vmName)
			err = c.Client().ResetVirtualMachine(vmName)
			if err != nil {
				return err
			}

			log.Errorf("%v reset successfully.\r\n", vmName)
			return
		}),
	}, cli.Command{
		Name:        "restart",
		Usage:       "power off a server and start it again",
		UsageText:   "bytemark restart <server>",
		Description: "This command will power down a server and then start it back up again.",
		Flags: []cli.Flag{
			cli.GenericFlag{
				Name:  "server",
				Usage: "the server to restart",
				Value: new(app.VirtualMachineNameFlag),
			},
		},
		Action: app.Action(args.Optional("server"), with.RequiredFlags("server"), with.Auth, func(c *app.Context) (err error) {
			vmName := c.VirtualMachineName("server")
			fmt.Fprintf(c.App().Writer, "Shutting down %v...", vmName)
			err = c.Client().ShutdownVirtualMachine(vmName, true)
			if err != nil {
				return
			}
			err = waitForShutdown(c, vmName)
			if err != nil {
				return
			}

			c.Log("Done!\n\nStarting %s back up.", vmName)
			c.Client().StartVirtualMachine(vmName)

			return
		}),
	}, cli.Command{
		Name:        "shutdown",
		Usage:       "cleanly shut down a server",
		UsageText:   "bytemark shutdown <server>",
		Description: "This command sends the ACPI shutdown signal to the server, causing a clean shut down. This is like pressing the power button on a computer you have physical access to.",
		Flags: []cli.Flag{
			cli.GenericFlag{
				Name:  "server",
				Usage: "the server to shutdown",
				Value: new(app.VirtualMachineNameFlag),
			},
		},
		Action: app.Action(args.Optional("server"), with.RequiredFlags("server"), with.Auth, func(c *app.Context) (err error) {
			vmName := c.VirtualMachineName("server")
			fmt.Fprintf(c.App().Writer, "Shutting down %v...", vmName)
			err = c.Client().ShutdownVirtualMachine(vmName, true)
			if err != nil {
				return
			}

			err = waitForShutdown(c, vmName)
			if err != nil {
				return
			}

			c.Log("Done!", vmName)
			return
		}),
	}, cli.Command{
		Name:        "start",
		Usage:       "start a stopped server",
		UsageText:   "bytemark start <server>",
		Description: "This command will start a server that is not currently running.",
		Flags: []cli.Flag{
			cli.GenericFlag{
				Name:  "server",
				Usage: "the server to start",
				Value: new(app.VirtualMachineNameFlag),
			},
		},
		Action: app.Action(args.Optional("server"), with.RequiredFlags("server"), with.Auth, func(c *app.Context) (err error) {
			vmName := c.VirtualMachineName("server")
			log.Logf("Attempting to start %s...\r\n", vmName)
			err = c.Client().StartVirtualMachine(vmName)
			if err != nil {
				return
			}

			log.Logf("%s started successfully.\r\n", vmName)
			return
		}),
	}, cli.Command{
		Name:        "stop",
		Usage:       "stop a server, as though pulling the power cable out",
		UsageText:   "bytemark stop <server>",
		Description: "This command will instantly power down a server. Note that this may cause data loss, particularly on servers with unjournaled file systems (e.g. ext2)",
		Flags: []cli.Flag{
			cli.GenericFlag{
				Name:  "server",
				Usage: "the server to stop",
				Value: new(app.VirtualMachineNameFlag),
			},
		},
		Action: app.Action(args.Optional("server"), with.RequiredFlags("server"), with.Auth, func(c *app.Context) (err error) {
			vmName := c.VirtualMachineName("server")
			log.Logf("Attempting to stop %s...\r\n", vmName)
			err = c.Client().StopVirtualMachine(vmName)
			if err != nil {
				return
			}

			log.Logf("%s stopped successfully.\r\n", vmName)
			return
		}),
	})
}
func waitForShutdown(c *app.Context, name lib.VirtualMachineName) (err error) {
	vm := brain.VirtualMachine{PowerOn: true}

	for vm.PowerOn {
		time.Sleep(5 * time.Second)
		fmt.Fprint(c.App().Writer, ".")

		vm, err = c.Client().GetVirtualMachine(name)
		if err != nil {
			return
		}
	}
	return
}
<|endoftext|>


<commit_before>package apiservermsgs

/*
Copyright 2017 Crunchy Data Solutions, Inc.
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.
*/

import ()

const PGO_VERSION = "3.5.0-rc9"

// Ok status
const Ok = "ok"
const Error = "error"

// Status ...
type Status struct {
	Code string
	Msg  string
}

type BasicAuthCredentials struct {
	Username     string
	Password     string
	APIServerURL string
}
<commit_msg>make version number 3.5.0 before push<commit_after>package apiservermsgs

/*
Copyright 2017 Crunchy Data Solutions, Inc.
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.
*/

import ()

const PGO_VERSION = "3.5.0"

// Ok status
const Ok = "ok"
const Error = "error"

// Status ...
type Status struct {
	Code string
	Msg  string
}

type BasicAuthCredentials struct {
	Username     string
	Password     string
	APIServerURL string
}
<|endoftext|>


<commit_before>package services_state

import (
	"encoding/json"
	"fmt"
	"log"
	"os"
	"time"

	"github.com/hashicorp/memberlist"
	"github.com/newrelic/bosun/service"
)

// services_state handles all of the eventual-consistency mechanisms for
// service discovery state. The ServicesState struct has a mapping of
// servers to Service lists and manages the lifecycle.

const (
	TOMBSTONE_LIFESPAN = 3 * time.Hour          // How long we keep tombstones around
	TOMBSTONE_COUNT = 10                        // Send tombstones at 1 per second 10 times
	TOMBSTONE_SLEEP_INTERVAL = 2 * time.Second  // Sleep between local service checks
	ALIVE_LIFESPAN = 20 * time.Second           // Down if not heard from in 20 seconds
	ALIVE_SLEEP_INTERVAL = 2 * time.Second      // Sleep between local service checks
)

// Holds the state about one server in our cluster
type Server struct {
	Name string
	Services map[string]*service.Service
	LastUpdated time.Time
}

// Returns a pointer to a properly configured Server
func NewServer(name string) *Server {
	var server Server
	server.Name = name
	// Pre-create for 5 services per host
	server.Services = make(map[string]*service.Service, 5)
	server.LastUpdated = time.Unix(0, 0)

	return &server
}

// Holds the state about all the servers in the cluster
type ServicesState struct {
	Servers map[string]*Server
	HostnameFn func() (string, error)
	Broadcasts chan [][]byte
}

// Returns a pointer to a properly configured ServicesState
func NewServicesState() *ServicesState {
	var state ServicesState
	state.Servers    = make(map[string]*Server, 5)
	state.HostnameFn = os.Hostname
	state.Broadcasts = make(chan [][]byte)
	return &state
}

// Return a Marshaled/Encoded byte array that can be deocoded with
// services_state.Decode()
func (state *ServicesState) Encode() []byte {
	jsonData, err := json.Marshal(state.Servers)
	if err != nil {
		log.Println("ERROR: Failed to Marshal state")
		return []byte{}
	}

	return jsonData
}

// Shortcut for checking if the Servers map has an entry for this
// hostname.
func (state *ServicesState) HasServer(hostname string) bool {
	if state.Servers[hostname] != nil {
		return true
	}

	return false
}

// A server has left the cluster, so tombstone all of its records
func (state *ServicesState) ExpireServer(hostname string, quit chan bool) {
	if !state.HasServer(hostname) {
		log.Printf("No records to expire for %s\n", hostname)
		return
	}

	log.Printf("Expiring %s\n", hostname)

	tombstones := make([][]byte, 0, len(state.Servers[hostname].Services))

	for _, svc := range state.Servers[hostname].Services {
		svc.Tombstone()

		encoded, err := svc.Encode()
		if err != nil {
			log.Printf("ERROR encoding service: (%s)", err.Error())
			continue
		}

		tombstones = append(tombstones, encoded)
	}

	state.SendTombstones(tombstones, quit)
}

// Take a service and merge it into our state. Correctly handle
// timestamps so we only add things newer than what we already
// know about.
func (state *ServicesState) AddServiceEntry(entry service.Service) {

	if !state.HasServer(entry.Hostname) {
		state.Servers[entry.Hostname] = NewServer(entry.Hostname)
	}

	server := state.Servers[entry.Hostname]
	// Only apply changes that are newer
	if server.Services[entry.ID] == nil ||
			entry.Invalidates(server.Services[entry.ID]) {
		server.Services[entry.ID] = &entry
		server.LastUpdated = entry.Updated
	}
}

// Merge a complete state struct into this one. Usually used on
// node startup and during anti-entropy operations.
func (state *ServicesState) Merge(otherState *ServicesState) {
	for _, server := range otherState.Servers {
		for _, service := range server.Services {
			state.AddServiceEntry(*service)
		}
	}
}

// Pretty-print(ish) a services state struct so a human can read
// it on the terminal. Makes for awesome web apps.
func (state *ServicesState) Format(list *memberlist.Memberlist) string {
	var output string

	output += "Services ------------------------------\n"
	for hostname, server := range state.Servers {
		output += fmt.Sprintf("  %s: (%s)\n", hostname, server.LastUpdated.String())
		for _, service := range server.Services {
			output += service.Format()
		}
		output += "\n"
	}

	// Don't show member list
	if list == nil {
		return output
	}

	output += "\nCluster Hosts -------------------------\n"
	for _, host := range list.Members() {
		output += fmt.Sprintf("    %s\n", host.Name)
	}

	output += "---------------------------------------"

	return output
}

// Print the formatted struct
func (state *ServicesState) Print(list *memberlist.Memberlist) {
	log.Println(state.Format(list))
}

// Loops forever, keeping transmitting info about our containers
// on the broadcast channel. Intended to run as a background goroutine.
func (state *ServicesState) BroadcastServices(fn func() []service.Service, quit chan bool) {
	for ;; {
		containerList := fn()
		prepared := make([][]byte, 0, len(containerList))

		for _, container := range containerList {
			state.AddServiceEntry(container)
			encoded, err := container.Encode()
			if err != nil {
				log.Printf("ERROR encoding container: (%s)", err.Error())
				continue
			}

			prepared = append(prepared, encoded)
		}

		if len(prepared) > 0 {
			state.Broadcasts <- prepared // Put it on the wire
		} else {
			// We expect there to always be _something_ in the channel
			// once we've run.
			state.Broadcasts <- nil
		}

		// Now that we're finished, see if we're supposed to exit
		select {
			case <- quit:
				return
			default:
		}

		time.Sleep(ALIVE_SLEEP_INTERVAL)
	}
}

// Actually transmit an encoded tombstone record into the channel. Runs a
// background goroutine that continues the broadcast for 10 seconds so we
// have a pretty good idea that it was delivered.
func (state *ServicesState) SendTombstones(tombstones [][]byte, quit chan bool) {
	state.Broadcasts <- tombstones // Put it on the wire

	// Announce these every second for awhile
	go func() {
		for i := 0; i < TOMBSTONE_COUNT; i++ {
			state.Broadcasts <- tombstones

			select {
			case <- quit:
				return
			default:
			}

			time.Sleep(1 * time.Second)
		}
	}()
}

func (state *ServicesState) BroadcastTombstones(fn func() []service.Service, quit chan bool) {
	hostname, _ := state.HostnameFn()
	propagateQuit := make(chan bool)

	for ;; {
		containerList := fn()
		// Tell people about our dead services
		otherTombstones := state.TombstoneOthersServices()
		tombstones      := state.TombstoneServices(hostname, containerList)

		tombstones = append(tombstones, otherTombstones...)

		if tombstones != nil && len(tombstones) > 0 {
			state.SendTombstones(tombstones, propagateQuit)
		} else {
			// We expect there to always be _something_ in the channel
			// once we've run.
			state.Broadcasts <- nil
		}

		// Now that we're finished, see if we're supposed to exit
		select {
			case <- quit:
				propagateQuit <- true
				return
			default:
		}

		time.Sleep(TOMBSTONE_SLEEP_INTERVAL)
	}
}

func (state *ServicesState) TombstoneOthersServices() [][]byte {
	result := make([][]byte, 0, 1)

	// Manage tombstone life so we don't keep them forever. We have to do this
	// even for hosts that aren't running services now, because they might have
	// been. Make sure we don't keep alive services around for very much
	// time at all.
	state.EachService(func(hostname *string, id *string, svc *service.Service) {
		if svc.IsTombstone() &&
				svc.Updated.Before(time.Now().UTC().Add(0 - TOMBSTONE_LIFESPAN)) {
			delete(state.Servers[*hostname].Services, *id)
			// If this is the last service, remove the server
			if len(state.Servers[*hostname].Services) < 1 {
				delete(state.Servers, *hostname)
			}
		}

		if svc.IsAlive() &&
				svc.Updated.Before(time.Now().UTC().Add(0 - ALIVE_LIFESPAN)) {

			log.Printf("Found expired service %s from %s, tombstoning",
				svc.Name, svc.Hostname,
			)

			// Because we don't know that other hosts haven't gotten a newer
			// message that we missed, we'll tombstone them with the original
			// timestamp + 1 second. This way we don't invalidate newer records
			// we didn't see. This might happen when any node is removed from
			// cluster and re-joins, for example. So we can't use svc.Tombstone()
			// which updates the timestamp to Now().UTC()
			svc.Status = service.TOMBSTONE
			svc.Updated = svc.Updated.Add(time.Second)

			encoded, err := svc.Encode()

			if err != nil {
				log.Printf("ERROR encoding container: (%s)", err.Error())
			}

			result = append(result, encoded)
		}
	})

	return result
}

func (state *ServicesState) TombstoneServices(hostname string, containerList []service.Service) [][]byte {

	if !state.HasServer(hostname) {
		println("TombstoneServices(): New host or not running services, skipping.")
		return nil
	}
	// Build a map from the list first
	mapping := makeServiceMapping(containerList)

	result := make([][]byte, 0, len(containerList))

	// Copy this so we can change the real list in the loop
	services := state.Servers[hostname].Services

	// Tombstone our own services that went away
	for id, svc := range services {
		if mapping[id] == nil && svc.Status == service.ALIVE {
			log.Printf("Tombstoning %s\n", svc.ID)
			svc.Tombstone()
			// Tombstone each record twice to help with receipt
			encoded, err := svc.Encode()
			if err != nil {
				log.Printf("ERROR encoding container: (%s)", err.Error())
			}

			for i := 0; i < 2; i++ {
				result = append(result, encoded)
			}
		}
	}

	return result
}

func (state *ServicesState) EachService(fn func(hostname *string, serviceId *string, svc *service.Service)) {
	for hostname, _ := range state.Servers {
		for id, svc := range state.Servers[hostname].Services {
			fn(&hostname, &id, svc)
		}
	}
}

func (state *ServicesState) ByService() map[string]map[string]*service.Service {
	serviceMap := make(map[string]map[string]*service.Service)

	state.EachService(
		func(hostname *string, serviceId *string, svc *service.Service) {
			if _, ok := serviceMap[svc.Image]; !ok {
				serviceMap[svc.Image] = make(map[string]*service.Service)
			}
			serviceMap[svc.Image][*serviceId] = svc
		},
	)

	return serviceMap
}

func makeServiceMapping(containerList []service.Service) map[string]*service.Service {
	mapping := make(map[string]*service.Service, len(containerList))
	for _, container := range containerList {
		mapping[container.ID] = &container
	}

	return mapping
}

// Take a byte slice and return a properly reconstituted state struct
func Decode(data []byte) (*ServicesState, error) {
	newState := NewServicesState()
	err := json.Unmarshal(data, &newState.Servers)
	if err != nil {
		log.Printf("Error decoding state! (%s)", err.Error())
	}

	return newState, err
}
<commit_msg>Change ByService() to use sorted results.<commit_after>package services_state

import (
	"encoding/json"
	"fmt"
	"log"
	"os"
	"time"

	"github.com/hashicorp/memberlist"
	"github.com/newrelic/bosun/service"
)

// services_state handles all of the eventual-consistency mechanisms for
// service discovery state. The ServicesState struct has a mapping of
// servers to Service lists and manages the lifecycle.

const (
	TOMBSTONE_LIFESPAN = 3 * time.Hour          // How long we keep tombstones around
	TOMBSTONE_COUNT = 10                        // Send tombstones at 1 per second 10 times
	TOMBSTONE_SLEEP_INTERVAL = 2 * time.Second  // Sleep between local service checks
	ALIVE_LIFESPAN = 20 * time.Second           // Down if not heard from in 20 seconds
	ALIVE_SLEEP_INTERVAL = 2 * time.Second      // Sleep between local service checks
)

// Holds the state about one server in our cluster
type Server struct {
	Name string
	Services map[string]*service.Service
	LastUpdated time.Time
}

// Returns a pointer to a properly configured Server
func NewServer(name string) *Server {
	var server Server
	server.Name = name
	// Pre-create for 5 services per host
	server.Services = make(map[string]*service.Service, 5)
	server.LastUpdated = time.Unix(0, 0)

	return &server
}

// Holds the state about all the servers in the cluster
type ServicesState struct {
	Servers map[string]*Server
	HostnameFn func() (string, error)
	Broadcasts chan [][]byte
}

// Returns a pointer to a properly configured ServicesState
func NewServicesState() *ServicesState {
	var state ServicesState
	state.Servers    = make(map[string]*Server, 5)
	state.HostnameFn = os.Hostname
	state.Broadcasts = make(chan [][]byte)
	return &state
}

// Return a Marshaled/Encoded byte array that can be deocoded with
// services_state.Decode()
func (state *ServicesState) Encode() []byte {
	jsonData, err := json.Marshal(state.Servers)
	if err != nil {
		log.Println("ERROR: Failed to Marshal state")
		return []byte{}
	}

	return jsonData
}

// Shortcut for checking if the Servers map has an entry for this
// hostname.
func (state *ServicesState) HasServer(hostname string) bool {
	if state.Servers[hostname] != nil {
		return true
	}

	return false
}

// A server has left the cluster, so tombstone all of its records
func (state *ServicesState) ExpireServer(hostname string, quit chan bool) {
	if !state.HasServer(hostname) {
		log.Printf("No records to expire for %s\n", hostname)
		return
	}

	log.Printf("Expiring %s\n", hostname)

	tombstones := make([][]byte, 0, len(state.Servers[hostname].Services))

	for _, svc := range state.Servers[hostname].Services {
		svc.Tombstone()

		encoded, err := svc.Encode()
		if err != nil {
			log.Printf("ERROR encoding service: (%s)", err.Error())
			continue
		}

		tombstones = append(tombstones, encoded)
	}

	state.SendTombstones(tombstones, quit)
}

// Take a service and merge it into our state. Correctly handle
// timestamps so we only add things newer than what we already
// know about.
func (state *ServicesState) AddServiceEntry(entry service.Service) {

	if !state.HasServer(entry.Hostname) {
		state.Servers[entry.Hostname] = NewServer(entry.Hostname)
	}

	server := state.Servers[entry.Hostname]
	// Only apply changes that are newer
	if server.Services[entry.ID] == nil ||
			entry.Invalidates(server.Services[entry.ID]) {
		server.Services[entry.ID] = &entry
		server.LastUpdated = entry.Updated
	}
}

// Merge a complete state struct into this one. Usually used on
// node startup and during anti-entropy operations.
func (state *ServicesState) Merge(otherState *ServicesState) {
	for _, server := range otherState.Servers {
		for _, service := range server.Services {
			state.AddServiceEntry(*service)
		}
	}
}

// Pretty-print(ish) a services state struct so a human can read
// it on the terminal. Makes for awesome web apps.
func (state *ServicesState) Format(list *memberlist.Memberlist) string {
	var output string

	output += "Services ------------------------------\n"
	for hostname, server := range state.Servers {
		output += fmt.Sprintf("  %s: (%s)\n", hostname, server.LastUpdated.String())
		for _, service := range server.Services {
			output += service.Format()
		}
		output += "\n"
	}

	// Don't show member list
	if list == nil {
		return output
	}

	output += "\nCluster Hosts -------------------------\n"
	for _, host := range list.Members() {
		output += fmt.Sprintf("    %s\n", host.Name)
	}

	output += "---------------------------------------"

	return output
}

// Print the formatted struct
func (state *ServicesState) Print(list *memberlist.Memberlist) {
	log.Println(state.Format(list))
}

// Loops forever, keeping transmitting info about our containers
// on the broadcast channel. Intended to run as a background goroutine.
func (state *ServicesState) BroadcastServices(fn func() []service.Service, quit chan bool) {
	for ;; {
		containerList := fn()
		prepared := make([][]byte, 0, len(containerList))

		for _, container := range containerList {
			state.AddServiceEntry(container)
			encoded, err := container.Encode()
			if err != nil {
				log.Printf("ERROR encoding container: (%s)", err.Error())
				continue
			}

			prepared = append(prepared, encoded)
		}

		if len(prepared) > 0 {
			state.Broadcasts <- prepared // Put it on the wire
		} else {
			// We expect there to always be _something_ in the channel
			// once we've run.
			state.Broadcasts <- nil
		}

		// Now that we're finished, see if we're supposed to exit
		select {
			case <- quit:
				return
			default:
		}

		time.Sleep(ALIVE_SLEEP_INTERVAL)
	}
}

// Actually transmit an encoded tombstone record into the channel. Runs a
// background goroutine that continues the broadcast for 10 seconds so we
// have a pretty good idea that it was delivered.
func (state *ServicesState) SendTombstones(tombstones [][]byte, quit chan bool) {
	state.Broadcasts <- tombstones // Put it on the wire

	// Announce these every second for awhile
	go func() {
		for i := 0; i < TOMBSTONE_COUNT; i++ {
			state.Broadcasts <- tombstones

			select {
			case <- quit:
				return
			default:
			}

			time.Sleep(1 * time.Second)
		}
	}()
}

func (state *ServicesState) BroadcastTombstones(fn func() []service.Service, quit chan bool) {
	hostname, _ := state.HostnameFn()
	propagateQuit := make(chan bool)

	for ;; {
		containerList := fn()
		// Tell people about our dead services
		otherTombstones := state.TombstoneOthersServices()
		tombstones      := state.TombstoneServices(hostname, containerList)

		tombstones = append(tombstones, otherTombstones...)

		if tombstones != nil && len(tombstones) > 0 {
			state.SendTombstones(tombstones, propagateQuit)
		} else {
			// We expect there to always be _something_ in the channel
			// once we've run.
			state.Broadcasts <- nil
		}

		// Now that we're finished, see if we're supposed to exit
		select {
			case <- quit:
				propagateQuit <- true
				return
			default:
		}

		time.Sleep(TOMBSTONE_SLEEP_INTERVAL)
	}
}

func (state *ServicesState) TombstoneOthersServices() [][]byte {
	result := make([][]byte, 0, 1)

	// Manage tombstone life so we don't keep them forever. We have to do this
	// even for hosts that aren't running services now, because they might have
	// been. Make sure we don't keep alive services around for very much
	// time at all.
	state.EachService(func(hostname *string, id *string, svc *service.Service) {
		if svc.IsTombstone() &&
				svc.Updated.Before(time.Now().UTC().Add(0 - TOMBSTONE_LIFESPAN)) {
			delete(state.Servers[*hostname].Services, *id)
			// If this is the last service, remove the server
			if len(state.Servers[*hostname].Services) < 1 {
				delete(state.Servers, *hostname)
			}
		}

		if svc.IsAlive() &&
				svc.Updated.Before(time.Now().UTC().Add(0 - ALIVE_LIFESPAN)) {

			log.Printf("Found expired service %s from %s, tombstoning",
				svc.Name, svc.Hostname,
			)

			// Because we don't know that other hosts haven't gotten a newer
			// message that we missed, we'll tombstone them with the original
			// timestamp + 1 second. This way we don't invalidate newer records
			// we didn't see. This might happen when any node is removed from
			// cluster and re-joins, for example. So we can't use svc.Tombstone()
			// which updates the timestamp to Now().UTC()
			svc.Status = service.TOMBSTONE
			svc.Updated = svc.Updated.Add(time.Second)

			encoded, err := svc.Encode()

			if err != nil {
				log.Printf("ERROR encoding container: (%s)", err.Error())
			}

			result = append(result, encoded)
		}
	})

	return result
}

func (state *ServicesState) TombstoneServices(hostname string, containerList []service.Service) [][]byte {

	if !state.HasServer(hostname) {
		println("TombstoneServices(): New host or not running services, skipping.")
		return nil
	}
	// Build a map from the list first
	mapping := makeServiceMapping(containerList)

	result := make([][]byte, 0, len(containerList))

	// Copy this so we can change the real list in the loop
	services := state.Servers[hostname].Services

	// Tombstone our own services that went away
	for id, svc := range services {
		if mapping[id] == nil && svc.Status == service.ALIVE {
			log.Printf("Tombstoning %s\n", svc.ID)
			svc.Tombstone()
			// Tombstone each record twice to help with receipt
			encoded, err := svc.Encode()
			if err != nil {
				log.Printf("ERROR encoding container: (%s)", err.Error())
			}

			for i := 0; i < 2; i++ {
				result = append(result, encoded)
			}
		}
	}

	return result
}

func (state *ServicesState) EachService(fn func(hostname *string, serviceId *string, svc *service.Service)) {
	for hostname, _ := range state.Servers {
		for id, svc := range state.Servers[hostname].Services {
			fn(&hostname, &id, svc)
		}
	}
}

func (state *ServicesState) ByService() map[string]map[string]*service.Service {
	serviceMap := make(map[string]map[string]*service.Service)

	state.EachServiceSorted(
		func(hostname *string, serviceId *string, svc *service.Service) {
			if _, ok := serviceMap[svc.Image]; !ok {
				serviceMap[svc.Image] = make(map[string]*service.Service)
			}
			serviceMap[svc.Image][*serviceId] = svc
		},
	)

	return serviceMap
}

func makeServiceMapping(containerList []service.Service) map[string]*service.Service {
	mapping := make(map[string]*service.Service, len(containerList))
	for _, container := range containerList {
		mapping[container.ID] = &container
	}

	return mapping
}

// Take a byte slice and return a properly reconstituted state struct
func Decode(data []byte) (*ServicesState, error) {
	newState := NewServicesState()
	err := json.Unmarshal(data, &newState.Servers)
	if err != nil {
		log.Printf("Error decoding state! (%s)", err.Error())
	}

	return newState, err
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/docker/docker/daemon/config"
	"github.com/docker/docker/opts"
	"github.com/docker/docker/registry"
	"github.com/spf13/pflag"
)

// defaultTrustKeyFile is the default filename for the trust key
const defaultTrustKeyFile = "key.json"

// installCommonConfigFlags adds flags to the pflag.FlagSet to configure the daemon
func installCommonConfigFlags(conf *config.Config, flags *pflag.FlagSet) error {
	var maxConcurrentDownloads, maxConcurrentUploads, maxDownloadAttempts int
	defaultPidFile, err := getDefaultPidFile()
	if err != nil {
		return err
	}
	defaultDataRoot, err := getDefaultDataRoot()
	if err != nil {
		return err
	}
	defaultExecRoot, err := getDefaultExecRoot()
	if err != nil {
		return err
	}

	installRegistryServiceFlags(&conf.ServiceOptions, flags)

	flags.Var(opts.NewNamedListOptsRef("storage-opts", &conf.GraphOptions, nil), "storage-opt", "Storage driver options")
	flags.Var(opts.NewNamedListOptsRef("authorization-plugins", &conf.AuthorizationPlugins, nil), "authorization-plugin", "Authorization plugins to load")
	flags.Var(opts.NewNamedListOptsRef("exec-opts", &conf.ExecOptions, nil), "exec-opt", "Runtime execution options")
	flags.StringVarP(&conf.Pidfile, "pidfile", "p", defaultPidFile, "Path to use for daemon PID file")
	flags.StringVar(&conf.Root, "data-root", defaultDataRoot, "Root directory of persistent Docker state")
	flags.StringVar(&conf.ExecRoot, "exec-root", defaultExecRoot, "Root directory for execution state files")
	flags.StringVar(&conf.ContainerdAddr, "containerd", "", "containerd grpc address")
	flags.BoolVar(&conf.CriContainerd, "cri-containerd", false, "start containerd with cri")

	flags.IntVar(&conf.Mtu, "mtu", 0, "Set the containers network MTU")
	flags.IntVar(&conf.NetworkControlPlaneMTU, "network-control-plane-mtu", config.DefaultNetworkMtu, "Network Control plane MTU")
	flags.IntVar(&conf.NetworkDiagnosticPort, "network-diagnostic-port", 0, "TCP port number of the network diagnostic server")
	_ = flags.MarkHidden("network-diagnostic-port")

	flags.BoolVar(&conf.RawLogs, "raw-logs", false, "Full timestamps without ANSI coloring")
	flags.Var(opts.NewListOptsRef(&conf.DNS, opts.ValidateIPAddress), "dns", "DNS server to use")
	flags.Var(opts.NewNamedListOptsRef("dns-opts", &conf.DNSOptions, nil), "dns-opt", "DNS options to use")
	flags.Var(opts.NewListOptsRef(&conf.DNSSearch, opts.ValidateDNSSearch), "dns-search", "DNS search domains to use")
	flags.Var(opts.NewIPOpt(&conf.HostGatewayIP, ""), "host-gateway-ip", "IP address that the special 'host-gateway' string in --add-host resolves to. Defaults to the IP address of the default bridge")
	flags.Var(opts.NewNamedListOptsRef("labels", &conf.Labels, opts.ValidateLabel), "label", "Set key=value labels to the daemon")
	flags.StringVar(&conf.LogConfig.Type, "log-driver", "json-file", "Default driver for container logs")
	flags.Var(opts.NewNamedMapOpts("log-opts", conf.LogConfig.Config, nil), "log-opt", "Default log driver options for containers")

	flags.StringVar(&conf.CorsHeaders, "api-cors-header", "", "Set CORS headers in the Engine API")
	flags.IntVar(&maxConcurrentDownloads, "max-concurrent-downloads", config.DefaultMaxConcurrentDownloads, "Set the max concurrent downloads for each pull")
	flags.IntVar(&maxConcurrentUploads, "max-concurrent-uploads", config.DefaultMaxConcurrentUploads, "Set the max concurrent uploads for each push")
	flags.IntVar(&maxDownloadAttempts, "max-download-attempts", config.DefaultDownloadAttempts, "Set the max download attempts for each pull")
	flags.IntVar(&conf.ShutdownTimeout, "shutdown-timeout", config.DefaultShutdownTimeout, "Set the default shutdown timeout")

	flags.StringVar(&conf.SwarmDefaultAdvertiseAddr, "swarm-default-advertise-addr", "", "Set default address or interface for swarm advertised address")
	flags.BoolVar(&conf.Experimental, "experimental", false, "Enable experimental features")
	flags.StringVar(&conf.MetricsAddress, "metrics-addr", "", "Set default address and port to serve the metrics api on")
	flags.Var(opts.NewNamedListOptsRef("node-generic-resources", &conf.NodeGenericResources, opts.ValidateSingleGenericResource), "node-generic-resource", "Advertise user-defined resource")

	conf.MaxConcurrentDownloads = &maxConcurrentDownloads
	conf.MaxConcurrentUploads = &maxConcurrentUploads
	conf.MaxDownloadAttempts = &maxDownloadAttempts

	flags.StringVar(&conf.ContainerdNamespace, "containerd-namespace", config.DefaultContainersNamespace, "Containerd namespace to use")
	flags.StringVar(&conf.ContainerdPluginNamespace, "containerd-plugins-namespace", config.DefaultPluginNamespace, "Containerd namespace to use for plugins")
	flags.StringVar(&conf.DefaultRuntime, "default-runtime", config.StockRuntimeName, "Default OCI runtime for containers")

	flags.StringVar(&conf.HTTPProxy, "http-proxy", "", "HTTP proxy URL to use for outgoing traffic")
	flags.StringVar(&conf.HTTPSProxy, "https-proxy", "", "HTTPS proxy URL to use for outgoing traffic")
	flags.StringVar(&conf.NoProxy, "no-proxy", "", "Comma-separated list of hosts or IP addresses for which the proxy is skipped")

	// Deprecated flags / options

	// "--graph" is "soft-deprecated" in favor of "data-root". This flag was added
	// before Docker 1.0, so won't be removed, only hidden, to discourage its usage.
	flags.StringVarP(&conf.Root, "graph", "g", defaultDataRoot, "Root of the Docker runtime")
	_ = flags.MarkHidden("graph")
	flags.BoolVarP(&conf.AutoRestart, "restart", "r", true, "--restart on the daemon has been deprecated in favor of --restart policies on docker run")
	_ = flags.MarkDeprecated("restart", "Please use a restart policy on docker run")
	flags.StringVar(&conf.ClusterAdvertise, "cluster-advertise", "", "Address or interface name to advertise")
	_ = flags.MarkDeprecated("cluster-advertise", "Swarm classic is deprecated. Please use Swarm-mode (docker swarm init)")
	flags.StringVar(&conf.ClusterStore, "cluster-store", "", "URL of the distributed storage backend")
	_ = flags.MarkDeprecated("cluster-store", "Swarm classic is deprecated. Please use Swarm-mode (docker swarm init)")
	flags.Var(opts.NewNamedMapOpts("cluster-store-opts", conf.ClusterOpts, nil), "cluster-store-opt", "Set cluster store options")
	_ = flags.MarkDeprecated("cluster-store-opt", "Swarm classic is deprecated. Please use Swarm-mode (docker swarm init)")

	return nil
}

func installRegistryServiceFlags(options *registry.ServiceOptions, flags *pflag.FlagSet) {
	ana := opts.NewNamedListOptsRef("allow-nondistributable-artifacts", &options.AllowNondistributableArtifacts, registry.ValidateIndexName)
	mirrors := opts.NewNamedListOptsRef("registry-mirrors", &options.Mirrors, registry.ValidateMirror)
	insecureRegistries := opts.NewNamedListOptsRef("insecure-registries", &options.InsecureRegistries, registry.ValidateIndexName)

	flags.Var(ana, "allow-nondistributable-artifacts", "Allow push of nondistributable artifacts to registry")
	flags.Var(mirrors, "registry-mirror", "Preferred Docker registry mirror")
	flags.Var(insecureRegistries, "insecure-registry", "Enable insecure registry communication")
}
<commit_msg>cmd/dockerd: remove some intermediate variables<commit_after>package main

import (
	"github.com/docker/docker/daemon/config"
	"github.com/docker/docker/opts"
	"github.com/docker/docker/registry"
	"github.com/spf13/pflag"
)

// defaultTrustKeyFile is the default filename for the trust key
const defaultTrustKeyFile = "key.json"

// installCommonConfigFlags adds flags to the pflag.FlagSet to configure the daemon
func installCommonConfigFlags(conf *config.Config, flags *pflag.FlagSet) error {
	var maxConcurrentDownloads, maxConcurrentUploads, maxDownloadAttempts int
	var err error
	conf.Pidfile, err = getDefaultPidFile()
	if err != nil {
		return err
	}
	conf.Root, err = getDefaultDataRoot()
	if err != nil {
		return err
	}
	conf.ExecRoot, err = getDefaultExecRoot()
	if err != nil {
		return err
	}

	installRegistryServiceFlags(&conf.ServiceOptions, flags)

	flags.Var(opts.NewNamedListOptsRef("storage-opts", &conf.GraphOptions, nil), "storage-opt", "Storage driver options")
	flags.Var(opts.NewNamedListOptsRef("authorization-plugins", &conf.AuthorizationPlugins, nil), "authorization-plugin", "Authorization plugins to load")
	flags.Var(opts.NewNamedListOptsRef("exec-opts", &conf.ExecOptions, nil), "exec-opt", "Runtime execution options")
	flags.StringVarP(&conf.Pidfile, "pidfile", "p", conf.Pidfile, "Path to use for daemon PID file")
	flags.StringVar(&conf.Root, "data-root", conf.Root, "Root directory of persistent Docker state")
	flags.StringVar(&conf.ExecRoot, "exec-root", conf.ExecRoot, "Root directory for execution state files")
	flags.StringVar(&conf.ContainerdAddr, "containerd", "", "containerd grpc address")
	flags.BoolVar(&conf.CriContainerd, "cri-containerd", false, "start containerd with cri")

	flags.IntVar(&conf.Mtu, "mtu", 0, "Set the containers network MTU")
	flags.IntVar(&conf.NetworkControlPlaneMTU, "network-control-plane-mtu", config.DefaultNetworkMtu, "Network Control plane MTU")
	flags.IntVar(&conf.NetworkDiagnosticPort, "network-diagnostic-port", 0, "TCP port number of the network diagnostic server")
	_ = flags.MarkHidden("network-diagnostic-port")

	flags.BoolVar(&conf.RawLogs, "raw-logs", false, "Full timestamps without ANSI coloring")
	flags.Var(opts.NewListOptsRef(&conf.DNS, opts.ValidateIPAddress), "dns", "DNS server to use")
	flags.Var(opts.NewNamedListOptsRef("dns-opts", &conf.DNSOptions, nil), "dns-opt", "DNS options to use")
	flags.Var(opts.NewListOptsRef(&conf.DNSSearch, opts.ValidateDNSSearch), "dns-search", "DNS search domains to use")
	flags.Var(opts.NewIPOpt(&conf.HostGatewayIP, ""), "host-gateway-ip", "IP address that the special 'host-gateway' string in --add-host resolves to. Defaults to the IP address of the default bridge")
	flags.Var(opts.NewNamedListOptsRef("labels", &conf.Labels, opts.ValidateLabel), "label", "Set key=value labels to the daemon")
	flags.StringVar(&conf.LogConfig.Type, "log-driver", "json-file", "Default driver for container logs")
	flags.Var(opts.NewNamedMapOpts("log-opts", conf.LogConfig.Config, nil), "log-opt", "Default log driver options for containers")

	flags.StringVar(&conf.CorsHeaders, "api-cors-header", "", "Set CORS headers in the Engine API")
	flags.IntVar(&maxConcurrentDownloads, "max-concurrent-downloads", config.DefaultMaxConcurrentDownloads, "Set the max concurrent downloads for each pull")
	flags.IntVar(&maxConcurrentUploads, "max-concurrent-uploads", config.DefaultMaxConcurrentUploads, "Set the max concurrent uploads for each push")
	flags.IntVar(&maxDownloadAttempts, "max-download-attempts", config.DefaultDownloadAttempts, "Set the max download attempts for each pull")
	flags.IntVar(&conf.ShutdownTimeout, "shutdown-timeout", config.DefaultShutdownTimeout, "Set the default shutdown timeout")

	flags.StringVar(&conf.SwarmDefaultAdvertiseAddr, "swarm-default-advertise-addr", "", "Set default address or interface for swarm advertised address")
	flags.BoolVar(&conf.Experimental, "experimental", false, "Enable experimental features")
	flags.StringVar(&conf.MetricsAddress, "metrics-addr", "", "Set default address and port to serve the metrics api on")
	flags.Var(opts.NewNamedListOptsRef("node-generic-resources", &conf.NodeGenericResources, opts.ValidateSingleGenericResource), "node-generic-resource", "Advertise user-defined resource")

	conf.MaxConcurrentDownloads = &maxConcurrentDownloads
	conf.MaxConcurrentUploads = &maxConcurrentUploads
	conf.MaxDownloadAttempts = &maxDownloadAttempts

	flags.StringVar(&conf.ContainerdNamespace, "containerd-namespace", config.DefaultContainersNamespace, "Containerd namespace to use")
	flags.StringVar(&conf.ContainerdPluginNamespace, "containerd-plugins-namespace", config.DefaultPluginNamespace, "Containerd namespace to use for plugins")
	flags.StringVar(&conf.DefaultRuntime, "default-runtime", config.StockRuntimeName, "Default OCI runtime for containers")

	flags.StringVar(&conf.HTTPProxy, "http-proxy", "", "HTTP proxy URL to use for outgoing traffic")
	flags.StringVar(&conf.HTTPSProxy, "https-proxy", "", "HTTPS proxy URL to use for outgoing traffic")
	flags.StringVar(&conf.NoProxy, "no-proxy", "", "Comma-separated list of hosts or IP addresses for which the proxy is skipped")

	// Deprecated flags / options

	// "--graph" is "soft-deprecated" in favor of "data-root". This flag was added
	// before Docker 1.0, so won't be removed, only hidden, to discourage its usage.
	flags.StringVarP(&conf.Root, "graph", "g", conf.Root, "Root of the Docker runtime")
	_ = flags.MarkHidden("graph")
	flags.BoolVarP(&conf.AutoRestart, "restart", "r", true, "--restart on the daemon has been deprecated in favor of --restart policies on docker run")
	_ = flags.MarkDeprecated("restart", "Please use a restart policy on docker run")
	flags.StringVar(&conf.ClusterAdvertise, "cluster-advertise", "", "Address or interface name to advertise")
	_ = flags.MarkDeprecated("cluster-advertise", "Swarm classic is deprecated. Please use Swarm-mode (docker swarm init)")
	flags.StringVar(&conf.ClusterStore, "cluster-store", "", "URL of the distributed storage backend")
	_ = flags.MarkDeprecated("cluster-store", "Swarm classic is deprecated. Please use Swarm-mode (docker swarm init)")
	flags.Var(opts.NewNamedMapOpts("cluster-store-opts", conf.ClusterOpts, nil), "cluster-store-opt", "Set cluster store options")
	_ = flags.MarkDeprecated("cluster-store-opt", "Swarm classic is deprecated. Please use Swarm-mode (docker swarm init)")

	return nil
}

func installRegistryServiceFlags(options *registry.ServiceOptions, flags *pflag.FlagSet) {
	ana := opts.NewNamedListOptsRef("allow-nondistributable-artifacts", &options.AllowNondistributableArtifacts, registry.ValidateIndexName)
	mirrors := opts.NewNamedListOptsRef("registry-mirrors", &options.Mirrors, registry.ValidateMirror)
	insecureRegistries := opts.NewNamedListOptsRef("insecure-registries", &options.InsecureRegistries, registry.ValidateIndexName)

	flags.Var(ana, "allow-nondistributable-artifacts", "Allow push of nondistributable artifacts to registry")
	flags.Var(mirrors, "registry-mirror", "Preferred Docker registry mirror")
	flags.Var(insecureRegistries, "insecure-registry", "Enable insecure registry communication")
}
<|endoftext|>