<commit_before>// Copyright © 2017 Marcus Franke <marcus.franke@gmail.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package cmd

import (
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"

	"github.com/spf13/cobra"
	"github.com/spf13/viper"
	"gopkg.in/russross/blackfriday.v2"
)

var (
	port   string
	debug  bool
	cmdOut []byte
)

// serveCmd represents the serve command
var serveCmd = &cobra.Command{
	Use:   "serve",
	Short: "Starts the yummy webserver",
	Run: func(cmd *cobra.Command, args []string) {

		repoPath := viper.GetString("yum.repopath")
		http.Handle("/", http.StripPrefix("/", http.FileServer(http.Dir(repoPath))))
		http.HandleFunc("/help", helpHandler)
		http.HandleFunc("/api/upload", apiuploadhandler)

		log.Fatal(http.ListenAndServe(":8080", nil))
	},
}

func helpHandler(w http.ResponseWriter, r *http.Request) {
	// get helpFile path from configuration
	helpFile := viper.GetString("yum.helpFile")

	// ingest the configured helpFile
	help, err := ioutil.ReadFile(helpFile)
	if err != nil {
		http.Error(w, "Could not load the help file", http.StatusInternalServerError)
		return
	}

	// render the Markdown file to HTML using the
	// blackfriday library
	output := blackfriday.Run(help)
	fmt.Fprintf(w, string(output))
}

func apiuploadhandler(w http.ResponseWriter, r *http.Request) {

	repoPath := viper.GetString("yum.repopath")
	workers := viper.GetString("yum.workers")
	createrepoBinary := viper.GetString("yum.createrepoBinary")

	if debug {
		fmt.Println("Method:", r.Method)
		fmt.Println("Header:", r.Header)
		fmt.Println("repoPath:", repoPath)
	}

	// will handle file uploads
	if r.Method == "POST" {
		file, handler, err := r.FormFile("fileupload")
		if err != nil {
			http.Error(w, "FormFile does not match - use fileupload\n", http.StatusBadRequest)
			return
		}
		defer file.Close()

		if filepath.Ext(handler.Filename) != ".rpm" {
			http.Error(w, "File not RPM\n", http.StatusUnsupportedMediaType)
			return
		}

		// create file handler to write uploaded file to
		f, err := os.OpenFile(repoPath+"/"+handler.Filename, os.O_WRONLY|os.O_CREATE, 0644)
		if err != nil {
			http.Error(w, "An error occurred", http.StatusInternalServerError)
			log.Fatal(err)
		}
		defer f.Close()

		// copy the file buffer into the file handle
		_, err = io.Copy(f, file)
		if err != nil {
			http.Error(w, "An error occurred applying the upload to the filesystem", http.StatusInternalServerError)
			log.Println(err)
			return
		}

		// process the uploaded file
		cmdOut, err = exec.Command(createrepoBinary, "-v", "-p", "--update", "--workers", workers, repoPath).Output()
		if err != nil {
			http.Error(w, "Could not update repository", http.StatusInternalServerError)
			log.Println(err)
			return
		}

		if debug {
			log.Println(string(cmdOut))
		}
	}

	// assume curl --upload-file style of upload type
	// this is currently not supported
	if r.Method == "PUT" {
		http.Error(w, "Method not allowed, POST binary to URI\n", http.StatusMethodNotAllowed)
	}
}

func init() {
	RootCmd.AddCommand(serveCmd)

	// Flags for the serve command.
	serveCmd.Flags().StringVarP(&port, "port", "p", "8080", "Port to listen on")
	serveCmd.Flags().BoolVarP(&debug, "debug", "d", false, "Enable debug output")
}
<commit_msg>[fix] Race Condition fix<commit_after>// Copyright © 2017 Marcus Franke <marcus.franke@gmail.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package cmd

import (
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"

	"github.com/spf13/cobra"
	"github.com/spf13/viper"
	"gopkg.in/russross/blackfriday.v2"
	"sync"
)

var (
	port   string
	debug  bool
	cmdOut []byte
	mutex  sync.Mutex
)

// serveCmd represents the serve command
var serveCmd = &cobra.Command{
	Use:   "serve",
	Short: "Starts the yummy webserver",
	Run: func(cmd *cobra.Command, args []string) {

		repoPath := viper.GetString("yum.repopath")
		http.Handle("/", http.StripPrefix("/", http.FileServer(http.Dir(repoPath))))
		http.HandleFunc("/help", helpHandler)
		http.HandleFunc("/api/upload", apiuploadhandler)

		log.Fatal(http.ListenAndServe(":8080", nil))
	},
}

func helpHandler(w http.ResponseWriter, r *http.Request) {
	// get helpFile path from configuration
	helpFile := viper.GetString("yum.helpFile")

	// ingest the configured helpFile
	help, err := ioutil.ReadFile(helpFile)
	if err != nil {
		http.Error(w, "Could not load the help file", http.StatusInternalServerError)
		return
	}

	// render the Markdown file to HTML using the
	// blackfriday library
	output := blackfriday.Run(help)
	fmt.Fprintf(w, string(output))
}

func apiuploadhandler(w http.ResponseWriter, r *http.Request) {

	repoPath := viper.GetString("yum.repopath")
	workers := viper.GetString("yum.workers")
	createrepoBinary := viper.GetString("yum.createrepoBinary")

	if debug {
		fmt.Println("Method:", r.Method)
		fmt.Println("Header:", r.Header)
		fmt.Println("repoPath:", repoPath)
	}

	// will handle file uploads
	if r.Method == "POST" {
		file, handler, err := r.FormFile("fileupload")
		if err != nil {
			http.Error(w, "FormFile does not match - use fileupload\n", http.StatusBadRequest)
			return
		}
		defer file.Close()

		if filepath.Ext(handler.Filename) != ".rpm" {
			http.Error(w, "File not RPM\n", http.StatusUnsupportedMediaType)
			return
		}

		// create file handler to write uploaded file to
		f, err := os.OpenFile(repoPath+"/"+handler.Filename, os.O_WRONLY|os.O_CREATE, 0644)
		if err != nil {
			http.Error(w, "An error occurred", http.StatusInternalServerError)
			log.Fatal(err)
		}
		defer f.Close()

		// copy the file buffer into the file handle
		_, err = io.Copy(f, file)
		if err != nil {
			http.Error(w, "An error occurred applying the upload to the filesystem", http.StatusInternalServerError)
			log.Println(err)
			return
		}

		// process the uploaded file
		mutex.Lock()
		cmdOut, err = exec.Command(createrepoBinary, "-v", "-p", "--update", "--workers", workers, repoPath).Output()
		if err != nil {
			http.Error(w, "Could not update repository", http.StatusInternalServerError)
			log.Println(err)
			mutex.Unlock()
			return
		}
		mutex.Unlock()

		if debug {
			log.Println(string(cmdOut))
		}
	}

	// assume curl --upload-file style of upload type
	// this is currently not supported
	if r.Method == "PUT" {
		http.Error(w, "Method not allowed, POST binary to URI\n", http.StatusMethodNotAllowed)
	}
}

func init() {
	RootCmd.AddCommand(serveCmd)

	// Flags for the serve command.
	serveCmd.Flags().StringVarP(&port, "port", "p", "8080", "Port to listen on")
	serveCmd.Flags().BoolVarP(&debug, "debug", "d", false, "Enable debug output")
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"github.com/gliderlabs/sigil"
	_ "github.com/gliderlabs/sigil/builtin"
)

var Version string

var (
	filename = flag.String("f", "", "use template file instead of STDIN")
	inline   = flag.String("i", "", "use inline template string instead of STDIN")
	posix    = flag.Bool("p", false, "preprocess with POSIX variable expansion")
	version  = flag.Bool("v", false, "prints version")
)

func template() ([]byte, string, error) {
	if *filename != "" {
		data, err := ioutil.ReadFile(*filename)
		if err != nil {
			return []byte{}, "", err
		}
		sigil.PushPath(filepath.Dir(*filename))
		return data, filepath.Base(*filename), nil
	}
	if *inline != "" {
		return []byte(*inline), "<inline>", nil
	}
	data, err := ioutil.ReadAll(os.Stdin)
	if err != nil {
		return []byte{}, "", err
	}
	return data, "<stdin>", nil
}

func main() {
	flag.Parse()
	if *version {
		fmt.Println(Version)
		os.Exit(0)
	}
	if *posix {
		sigil.PosixPreprocess = true
	}
	if os.Getenv("SIGIL_PATH") != "" {
		sigil.TemplatePath = strings.Split(os.Getenv("SIGIL_PATH"), ":")
	}
	vars := make(map[string]string)
	for _, arg := range os.Args {
		if strings.HasPrefix(arg, "-") {
			continue
		}
		parts := strings.SplitN(arg, "=", 2)
		if len(parts) == 2 {
			vars[parts[0]] = parts[1]
		}
	}
	tmpl, name, err := template()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	render, err := sigil.Execute(tmpl, vars, name)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	os.Stdout.Write(render.Bytes())
}
<commit_msg>Fix variable handling<commit_after>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"github.com/gliderlabs/sigil"
	_ "github.com/gliderlabs/sigil/builtin"
)

var Version string

var (
	filename = flag.String("f", "", "use template file instead of STDIN")
	inline   = flag.String("i", "", "use inline template string instead of STDIN")
	posix    = flag.Bool("p", false, "preprocess with POSIX variable expansion")
	version  = flag.Bool("v", false, "prints version")
)

func template() ([]byte, string, error) {
	if *filename != "" {
		data, err := ioutil.ReadFile(*filename)
		if err != nil {
			return []byte{}, "", err
		}
		sigil.PushPath(filepath.Dir(*filename))
		return data, filepath.Base(*filename), nil
	}
	if *inline != "" {
		return []byte(*inline), "<inline>", nil
	}
	data, err := ioutil.ReadAll(os.Stdin)
	if err != nil {
		return []byte{}, "", err
	}
	return data, "<stdin>", nil
}

func main() {
	flag.Parse()
	if *version {
		fmt.Println(Version)
		os.Exit(0)
	}
	if *posix {
		sigil.PosixPreprocess = true
	}
	if os.Getenv("SIGIL_PATH") != "" {
		sigil.TemplatePath = strings.Split(os.Getenv("SIGIL_PATH"), ":")
	}
	vars := make(map[string]string)
	for _, arg := range flag.Args() {
		parts := strings.SplitN(arg, "=", 2)
		if len(parts) == 2 {
			vars[parts[0]] = parts[1]
		}
	}
	tmpl, name, err := template()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	render, err := sigil.Execute(tmpl, vars, name)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	os.Stdout.Write(render.Bytes())
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"runtime"
	"time"

	"github.com/BurntSushi/xgb"
	"github.com/BurntSushi/xgb/randr"
	"github.com/BurntSushi/xgb/xproto"
)

type CmdWatch struct{}

func init() {
	_, err := parser.AddCommand("watch",
		"watch for changes",
		"The watch command listens for changes and configures the outputs accordingly",
		&CmdWatch{})
	if err != nil {
		panic(err)
	}
}

type Event struct {
	Event xgb.Event
	Error error
}

const eventSendTimeout = 500 * time.Millisecond

func subscribeXEvents(ch chan<- Event, done <-chan struct{}) {
	X, err := xgb.NewConn()
	if err != nil {
		ch <- Event{Error: err}
		return
	}

	defer X.Close()
	if err = randr.Init(X); err != nil {
		ch <- Event{Error: err}
		return
	}

	root := xproto.Setup(X).DefaultScreen(X).Root

	eventMask := randr.NotifyMaskScreenChange |
		randr.NotifyMaskCrtcChange |
		randr.NotifyMaskOutputChange |
		randr.NotifyMaskOutputProperty

	err = randr.SelectInputChecked(X, root, uint16(eventMask)).Check()
	if err != nil {
		ch <- Event{Error: err}
		return
	}

	for {
		ev, err := X.WaitForEvent()
		select {
		case ch <- Event{Event: ev, Error: err}:
		case <-time.After(eventSendTimeout):
			continue
		case <-done:
			return
		}
		if err != nil {
			log.Fatal(err)
		}
	}
}

func (cmd CmdWatch) Execute(args []string) error {
	globalOpts.ReadConfigfile()

	done := make(chan struct{})
	defer close(done)

	ch := make(chan Event)
	go subscribeXEvents(ch, done)

	V("grobi %s, compiled with %v on %v\n", version, runtime.Version(), runtime.GOOS)
	V("successfully subscribed to X RANDR change events\n")

	var tickerCh <-chan time.Time
	if globalOpts.PollInterval > 0 {
		tickerCh = time.NewTicker(time.Duration(globalOpts.PollInterval) * time.Second).C
	}

	var backoffCh <-chan time.Time
	var disablePoll bool
	var eventReceived bool

	var lastOutputs Outputs
	for {
		if !disablePoll {
			var newOutputs Outputs
			var err error

			if eventReceived {
				newOutputs, err = DetectOutputs()
				eventReceived = false
				V("current outputs: %v", newOutputs)
				V("last outputs:    %v", lastOutputs)
			} else {
				newOutputs, err = GetOutputs()
			}

			if err != nil {
				return err
			}

			if !lastOutputs.Equals(newOutputs) {
				V("  new output configuration found")
				err = MatchRules(globalOpts.cfg.Rules, newOutputs)
				if err != nil {
					return err
				}

				lastOutputs, err = DetectOutputs()
				if err != nil {
					return err
				}

				if globalOpts.Pause > 0 {
					V("disable polling for %d seconds\n", globalOpts.Pause)
					disablePoll = true
					backoffCh = time.After(time.Duration(globalOpts.Pause) * time.Second)
				}
			}
		}

		select {
		case ev := <-ch:
			V("new RANDR change event received: %v\n", ev)
			if ev.Error != nil {
				return ev.Error
			}

			eventReceived = true
		case <-tickerCh:
		case <-backoffCh:
			V("reenable polling\n")
			backoffCh = nil
			disablePoll = false
		}
	}
}
<commit_msg>watch: Do not re-detect outputs<commit_after>package main

import (
	"log"
	"runtime"
	"time"

	"github.com/BurntSushi/xgb"
	"github.com/BurntSushi/xgb/randr"
	"github.com/BurntSushi/xgb/xproto"
)

type CmdWatch struct{}

func init() {
	_, err := parser.AddCommand("watch",
		"watch for changes",
		"The watch command listens for changes and configures the outputs accordingly",
		&CmdWatch{})
	if err != nil {
		panic(err)
	}
}

type Event struct {
	Event xgb.Event
	Error error
}

const eventSendTimeout = 500 * time.Millisecond

func subscribeXEvents(ch chan<- Event, done <-chan struct{}) {
	X, err := xgb.NewConn()
	if err != nil {
		ch <- Event{Error: err}
		return
	}

	defer X.Close()
	if err = randr.Init(X); err != nil {
		ch <- Event{Error: err}
		return
	}

	root := xproto.Setup(X).DefaultScreen(X).Root

	eventMask := randr.NotifyMaskScreenChange |
		randr.NotifyMaskCrtcChange |
		randr.NotifyMaskOutputChange |
		randr.NotifyMaskOutputProperty

	err = randr.SelectInputChecked(X, root, uint16(eventMask)).Check()
	if err != nil {
		ch <- Event{Error: err}
		return
	}

	for {
		ev, err := X.WaitForEvent()
		select {
		case ch <- Event{Event: ev, Error: err}:
		case <-time.After(eventSendTimeout):
			continue
		case <-done:
			return
		}
		if err != nil {
			log.Fatal(err)
		}
	}
}

func (cmd CmdWatch) Execute(args []string) error {
	globalOpts.ReadConfigfile()

	done := make(chan struct{})
	defer close(done)

	ch := make(chan Event)
	go subscribeXEvents(ch, done)

	V("grobi %s, compiled with %v on %v\n", version, runtime.Version(), runtime.GOOS)
	V("successfully subscribed to X RANDR change events\n")

	var tickerCh <-chan time.Time
	if globalOpts.PollInterval > 0 {
		tickerCh = time.NewTicker(time.Duration(globalOpts.PollInterval) * time.Second).C
	}

	var backoffCh <-chan time.Time
	var disablePoll bool
	var eventReceived bool

	var lastOutputs Outputs
	for {
		if !disablePoll {
			var newOutputs Outputs
			var err error

			if eventReceived {
				newOutputs, err = DetectOutputs()
				eventReceived = false
				V("current outputs: %v", newOutputs)
				V("last outputs:    %v", lastOutputs)
			} else {
				newOutputs, err = GetOutputs()
			}

			if err != nil {
				return err
			}

			if !lastOutputs.Equals(newOutputs) {
				V("  new output configuration found")
				err = MatchRules(globalOpts.cfg.Rules, newOutputs)
				if err != nil {
					return err
				}

				lastOutputs = newOutputs

				if globalOpts.Pause > 0 {
					V("disable polling for %d seconds\n", globalOpts.Pause)
					disablePoll = true
					backoffCh = time.After(time.Duration(globalOpts.Pause) * time.Second)
				}
			}
		}

		select {
		case ev := <-ch:
			V("new RANDR change event received: %v\n", ev)
			if ev.Error != nil {
				return ev.Error
			}

			eventReceived = true
		case <-tickerCh:
		case <-backoffCh:
			V("reenable polling\n")
			backoffCh = nil
			disablePoll = false
		}
	}
}
<|endoftext|>


<commit_before>package cmds

import (
	"bytemark.co.uk/client/cmds/util"
	"bytemark.co.uk/client/util/log"
	"strings"
)

// HelpForHelp shows overall usage information for the BigV client, including a list of available commands.
func (cmds *CommandSet) HelpForHelp() util.ExitCode {
	log.Log("Bytemark command-line client")
	log.Log()
	log.Log("Usage")
	log.Log()
	log.Log("    bytemark [flags] <command> [flags] [args]")
	log.Log()
	log.Log("See `bytemark help <command>` for help specific to a command")
	log.Log()
	log.Log("Commands available")
	log.Log()
	log.Log("    help [command | topic] - output the help for the client or for the given command or topic")
	log.Log()

	// ALL of this should be in a sweet datastructure
	// config
	log.Log("  Config commands:")
	log.Log("    config  output all info about the current config")
	log.Log("    config get <variable>  output the value & source of the given variable")
	log.Log("    config set <variable> <value>  persistently sets a bigv-client variable")
	log.Log("    config unset <variable> - persistently unsets a bigv-client variable")
	log.Log()

	// machines generally
	log.Log("  Common server commands:")
	log.Log("    console [--serial | --vnc] [--connect | --panel] <virtual machine>")
	log.Log("    request ip <virtual machine> <reason>")
	log.Log("    set rdns <ip> <host name>")
	log.Log("    shutdown [--force] <virtual machine> - if force given, immediately stop the VM.")
	log.Log("    start <virtual machine>")
	log.Log()

	// virtual machine
	log.Log("  Virtual machine commands:")
	log.Log("    create disc[s] [--account <account>] [--group <group>] [--size <size>] [--grade <storage grade>] <virtual machine> [<disc specs>]")
	log.Log("    create vm [flags] <name> [<cores> [<memory> [<disc specs>]]] - creates a vm. See `bytemark help create` for detail on the flags")
	log.Log("    delete disc [--force] [---purge] <virtual machine> <disc label>")
	log.Log("    delete vm [--force] [---purge] <virtual machine>")
	log.Log("    list discs <virtual machine> - lists the discs in the given VM, with their size and labels")
	log.Log("    list vms <group> - lists the vms in the given group, one per line")
	log.Log("    lock hwprofile <virtual machine>")
	log.Log("    reimage [--image <image>] <virtual machine> [<image>]")
	log.Log("    resize disc [--size <size>] <virtual machine> [<resize spec>] - resize to size. if ambiguous, berate user.")
	log.Log("    set cores <virtual machine> <num>")
	log.Log("    set hwprofile <virtual machine> <hardware profile>")
	log.Log("    set memory <virtual machine> <size>")
	log.Log("    show vm [--json] [--nics] <virtual machine> - shows an overview of the given VM. Its discs, IPs, and such.")
	log.Log("    undelete vm <virtual machine> - Bring an unpurged machine back from deletion")
	log.Log("    unlock hwprofile <virtual machine>")
	log.Log()

	//log.Log("  Dedicated host commands:")
	//log.Log("    None yet!")

	log.Log("  Account management commands:")
	log.Log("    create group [--account <account>] <name>")
	log.Log("    delete account <account>")
	log.Log("    delete group <group>")
	log.Log("    list accounts - lists the accounts you can see, one per line")
	log.Log("    list groups <account> - lists the groups in the given account, one per line")
	log.Log("    show account [--json] <account> - shows an overview of the given account, a list of groups and vms within them")
	log.Log("    show group [--json] <group> - shows an overview of the given group, a list of VMs in them w/ size information")
	log.Log()

	// users
	log.Log("  User management commands:")
	log.Log("    add key <public key>")
	//log.Log("    grant <user> <privilege> <object>")
	log.Log("    list keys <public key>")
	log.Log("    remove key <public key>")
	//log.Log("    revoke <user> <privilege>")
	log.Log("    show user <name> - shows details about the given user - their authorised keys and any privileges you have granted them")
	log.Log()

	log.Log("  Informative commands:")
	log.Log("    list images - lists the available operating system images that can be passed to create vm and reimage")
	log.Log("    list (grades | storage-grades) - lists the available storage grades, along with a description.")
	log.Log("    list privileges - lists the privileges that can possibly be granted")

	return util.E_USAGE_DISPLAYED
}

// Help implements the help command, which gives usage information specific to each command. Usage: bytemark help [command]
func (cmds *CommandSet) Help(args []string) util.ExitCode {
	if len(args) == 0 {
		return cmds.HelpForHelp()
	}

	// please try and keep these in alphabetical order
	switch strings.ToLower(args[0]) {
	case "config":
		return cmds.HelpForConfig()
	case "create":
		return cmds.HelpForCreate()
	case "debug":
		return cmds.HelpForDebug()
	case "delete":
		return cmds.HelpForDelete()
	case "exit":
		return util.HelpForExitCodes()
	case "exit-codes":
		return util.HelpForExitCodes()
	case "show":
		return cmds.HelpForShow()
	}
	return cmds.HelpForHelp()

}
<commit_msg>s/list (images|grades|privileges)/\1/<commit_after>package cmds

import (
	"bytemark.co.uk/client/cmds/util"
	"bytemark.co.uk/client/util/log"
	"strings"
)

// HelpForHelp shows overall usage information for the BigV client, including a list of available commands.
func (cmds *CommandSet) HelpForHelp() util.ExitCode {
	log.Log("Bytemark command-line client")
	log.Log()
	log.Log("Usage")
	log.Log()
	log.Log("    bytemark [flags] <command> [flags] [args]")
	log.Log()
	log.Log("See `bytemark help <command>` for help specific to a command")
	log.Log()
	log.Log("Commands available")
	log.Log()
	log.Log("    help [command | topic] - output the help for the client or for the given command or topic")
	log.Log()

	// ALL of this should be in a sweet datastructure
	// config
	log.Log("  Config commands:")
	log.Log("    config  output all info about the current config")
	log.Log("    config get <variable>  output the value & source of the given variable")
	log.Log("    config set <variable> <value>  persistently sets a bigv-client variable")
	log.Log("    config unset <variable> - persistently unsets a bigv-client variable")
	log.Log()

	// machines generally
	log.Log("  Common server commands:")
	log.Log("    console [--serial | --vnc] [--connect | --panel] <virtual machine>")
	log.Log("    request ip <virtual machine> <reason>")
	log.Log("    set rdns <ip> <host name>")
	log.Log("    shutdown [--force] <virtual machine> - if force given, immediately stop the VM.")
	log.Log("    start <virtual machine>")
	log.Log()

	// virtual machine
	log.Log("  Virtual machine commands:")
	log.Log("    create disc[s] [--account <account>] [--group <group>] [--size <size>] [--grade <storage grade>] <virtual machine> [<disc specs>]")
	log.Log("    create vm [flags] <name> [<cores> [<memory> [<disc specs>]]] - creates a vm. See `bytemark help create` for detail on the flags")
	log.Log("    delete disc [--force] [---purge] <virtual machine> <disc label>")
	log.Log("    delete vm [--force] [---purge] <virtual machine>")
	log.Log("    list discs <virtual machine> - lists the discs in the given VM, with their size and labels")
	log.Log("    list vms <group> - lists the vms in the given group, one per line")
	log.Log("    lock hwprofile <virtual machine>")
	log.Log("    reimage [--image <image>] <virtual machine> [<image>]")
	log.Log("    resize disc [--size <size>] <virtual machine> [<resize spec>] - resize to size. if ambiguous, berate user.")
	log.Log("    set cores <virtual machine> <num>")
	log.Log("    set hwprofile <virtual machine> <hardware profile>")
	log.Log("    set memory <virtual machine> <size>")
	log.Log("    show vm [--json] [--nics] <virtual machine> - shows an overview of the given VM. Its discs, IPs, and such.")
	log.Log("    undelete vm <virtual machine> - Bring an unpurged machine back from deletion")
	log.Log("    unlock hwprofile <virtual machine>")
	log.Log()

	//log.Log("  Dedicated host commands:")
	//log.Log("    None yet!")

	log.Log("  Account management commands:")
	log.Log("    create group [--account <account>] <name>")
	log.Log("    delete account <account>")
	log.Log("    delete group <group>")
	log.Log("    list accounts - lists the accounts you can see, one per line")
	log.Log("    list groups <account> - lists the groups in the given account, one per line")
	log.Log("    show account [--json] <account> - shows an overview of the given account, a list of groups and vms within them")
	log.Log("    show group [--json] <group> - shows an overview of the given group, a list of VMs in them w/ size information")
	log.Log()

	// users
	log.Log("  User management commands:")
	log.Log("    add key <public key>")
	//log.Log("    grant <user> <privilege> <object>")
	log.Log("    list keys <public key>")
	log.Log("    remove key <public key>")
	//log.Log("    revoke <user> <privilege>")
	log.Log("    show user <name> - shows details about the given user - their authorised keys and any privileges you have granted them")
	log.Log()

	log.Log("  Informative commands:")
	log.Log("    images - lists the available operating system images that can be passed to create vm and reimage")
	log.Log("    storage grades - lists the available storage grades, along with a description.")
	log.Log("    privileges - lists the privileges that can possibly be granted")

	return util.E_USAGE_DISPLAYED
}

// Help implements the help command, which gives usage information specific to each command. Usage: bytemark help [command]
func (cmds *CommandSet) Help(args []string) util.ExitCode {
	if len(args) == 0 {
		return cmds.HelpForHelp()
	}

	// please try and keep these in alphabetical order
	switch strings.ToLower(args[0]) {
	case "config":
		return cmds.HelpForConfig()
	case "create":
		return cmds.HelpForCreate()
	case "debug":
		return cmds.HelpForDebug()
	case "delete":
		return cmds.HelpForDelete()
	case "exit":
		return util.HelpForExitCodes()
	case "exit-codes":
		return util.HelpForExitCodes()
	case "show":
		return cmds.HelpForShow()
	}
	return cmds.HelpForHelp()

}
<|endoftext|>


<commit_before>package fastimage

import (
	"bytes"
	"io"
	"net/http"
	"time"
)

const defaultTimeout = uint(time.Duration(5000) * time.Millisecond)

// DetectImageType is the main function used to detect the type and size
// of a remote image represented by the url.
//
// Only check ImageType and ImageSize if error is not nil.
//
// If you want to set request timeout for uri, use DetectImageTypeWithTimeout() instead.
func DetectImageType(uri string) (ImageType, *ImageSize, error) {
	return DetectImageTypeWithTimeout(uri, defaultTimeout)
}

// DetectImageTypeWithTimeout acts same as DetectImageType(),
// except this function takes additional parameter timeout (millisecond)
// for setting request timeout.
//
// If timeout < 1, default timeout 5000 is used.
func DetectImageTypeWithTimeout(uri string, timeout uint) (ImageType, *ImageSize, error) {
	logger.Printf("Opening HTTP stream")
	client := &http.Client{
		Timeout: time.Duration(timeout) * time.Millisecond,
	}
	resp, err := client.Get(uri)
	if err != nil {
		return Unknown, nil, err
	}
	defer closeHTTPStream(resp)

	return DetectImageTypeFromResponse(resp)
}

// DetectImageTypeFromResponse is a secondary function used to detect the type
// and size of a remote image represented by the resp.
// This way you can create your own request and then pass it here.
// Check examples from http://golang.org/pkg/net/http/
//
// Only check ImageType and ImageSize if error is not nil.
func DetectImageTypeFromResponse(resp *http.Response) (ImageType, *ImageSize, error) {
	logger.Printf("Response content-length: %v bytes", resp.ContentLength)
	return DetectImageTypeFromReader(resp.Body)
}

// DetectImageTypeFromReader detects the type and size from a stream of bytes.
//
// Only check ImageType and ImageSize if error is not nil.
func DetectImageTypeFromReader(r io.Reader) (ImageType, *ImageSize, error) {
	buffer := bytes.Buffer{}

	logger.Printf("Starting operation")
	defer logger.Printf("Ended after reading %v bytes", buffer.Len())

	for {
		err := readToBuffer(r, &buffer)
		if buffer.Len() < 2 {
			continue
		}
		if buffer.Len() > 20000 {
			return Unknown, nil, err
		}

		if err != nil {
			logger.Printf("Bailing out because of err %v", err)
			return Unknown, nil, err
		}

		for _, ImageTypeParser := range imageTypeParsers {
			if ImageTypeParser.Detect(buffer.Bytes()) {
				t := ImageTypeParser.Type()
				size, err := ImageTypeParser.GetSize(buffer.Bytes())

				if err == nil {
					logger.Printf("Found image type %v with size %v", t, size)
					return t, size, nil
				}
				break
			}
		}
	}
}

func readToBuffer(body io.Reader, buffer *bytes.Buffer) error {
	chunk := make([]byte, 8)
	count, err := body.Read(chunk)

	logger.Printf("Read %v bytes", count)
	buffer.Write(chunk[:count])

	return err
}

func closeHTTPStream(http *http.Response) {
	logger.Printf("Closing HTTP Stream")
	http.Body.Close()
}
<commit_msg>Check error before checking buffer length<commit_after>package fastimage

import (
	"bytes"
	"io"
	"net/http"
	"time"
)

const defaultTimeout = uint(time.Duration(5000) * time.Millisecond)

// DetectImageType is the main function used to detect the type and size
// of a remote image represented by the url.
//
// Only check ImageType and ImageSize if error is not nil.
//
// If you want to set request timeout for uri, use DetectImageTypeWithTimeout() instead.
func DetectImageType(uri string) (ImageType, *ImageSize, error) {
	return DetectImageTypeWithTimeout(uri, defaultTimeout)
}

// DetectImageTypeWithTimeout acts same as DetectImageType(),
// except this function takes additional parameter timeout (millisecond)
// for setting request timeout.
//
// If timeout < 1, default timeout 5000 is used.
func DetectImageTypeWithTimeout(uri string, timeout uint) (ImageType, *ImageSize, error) {
	logger.Printf("Opening HTTP stream")
	client := &http.Client{
		Timeout: time.Duration(timeout) * time.Millisecond,
	}
	resp, err := client.Get(uri)
	if err != nil {
		return Unknown, nil, err
	}
	defer closeHTTPStream(resp)

	return DetectImageTypeFromResponse(resp)
}

// DetectImageTypeFromResponse is a secondary function used to detect the type
// and size of a remote image represented by the resp.
// This way you can create your own request and then pass it here.
// Check examples from http://golang.org/pkg/net/http/
//
// Only check ImageType and ImageSize if error is not nil.
func DetectImageTypeFromResponse(resp *http.Response) (ImageType, *ImageSize, error) {
	logger.Printf("Response content-length: %v bytes", resp.ContentLength)
	return DetectImageTypeFromReader(resp.Body)
}

// DetectImageTypeFromReader detects the type and size from a stream of bytes.
//
// Only check ImageType and ImageSize if error is not nil.
func DetectImageTypeFromReader(r io.Reader) (ImageType, *ImageSize, error) {
	buffer := bytes.Buffer{}

	logger.Printf("Starting operation")
	defer logger.Printf("Ended after reading %v bytes", buffer.Len())

	for {
		err := readToBuffer(r, &buffer)
		if err != nil {
			logger.Printf("Bailing out because of err %v", err)
			return Unknown, nil, err
		}

		if buffer.Len() < 2 {
			continue
		}
		if buffer.Len() > 20000 {
			logger.Println("Type not found until buffer read exceeds 20000 bytes")
			return Unknown, nil, err
		}

		for _, ImageTypeParser := range imageTypeParsers {
			if ImageTypeParser.Detect(buffer.Bytes()) {
				t := ImageTypeParser.Type()
				size, err := ImageTypeParser.GetSize(buffer.Bytes())

				if err == nil {
					logger.Printf("Found image type %v with size %v", t, size)
					return t, size, nil
				}
				break
			}
		}
	}
}

func readToBuffer(body io.Reader, buffer *bytes.Buffer) error {
	chunk := make([]byte, 8)
	count, err := body.Read(chunk)

	logger.Printf("Read %v bytes", count)
	buffer.Write(chunk[:count])

	return err
}

func closeHTTPStream(http *http.Response) {
	logger.Printf("Closing HTTP Stream")
	http.Body.Close()
}
<|endoftext|>


<commit_before>package backoff

import (
	"time"
)

type FibonacciBackoff struct {
	Retries    int
	MaxRetries int
	Delay      time.Duration
	Interval   time.Duration // time.Second, time.Millisecond, etc.
	Slots      []time.Duration
}

// Fibonacci creates a new instance of FibonacciBackoff.
func Fibonacci() *FibonacciBackoff {
	return &FibonacciBackoff{
		Retries:    0,
		MaxRetries: 5,
		Delay:      time.Duration(0),
	}
}

/*
Next gets the next backoff delay. This method will increment the retries and check
if the maximum number of retries has been met. If this condition is satisfied, then
the function will return. Otherwise, the next backoff delay will be computed.

The fibonacci backoff delay is computed as follows:
`n = fib(c - 1) + fib(c - 2); f(0) = 0, f(1) = 1; n >= 0.` where
`n` is the backoff delay and `c` is the retry slot.

This method maintains a slice of time.Duration to save on fibonacci computation.

Example, given a 1 second interval:

  Retry #        Backoff delay (in seconds)
    0                   0
    1                   1
    2                   1
    3                   2
    4                   3
    5                   5
    6                   8
    7                   13
*/
func (self *FibonacciBackoff) Next() bool {
	self.Retries++

	if self.Retries >= self.MaxRetries {
		return false
	}

	// Add to Slot capacity, calculate new fib value, add it to the slice.
	if self.Retries == 1 {
		self.Slots = append(self.Slots, time.Duration(0*self.Interval))
		self.Slots = append(self.Slots, time.Duration(1*self.Interval))
		self.Delay = time.Duration(1 * self.Interval)
	} else {
		self.Delay = self.Slots[self.Retries-1] + self.Slots[self.Retries-2]
		self.Slots = append(self.Slots, self.Delay)
	}

	return true
}

/*
Retry will retry a function until the maximum number of retries is met. This method expects
the function `f` to return an error. If the failure condition is met, this method
will surface the error outputted from `f`, otherwise nil will be returned as normal.
*/
func (self *FibonacciBackoff) Retry(f func() error) error {
	err := f()

	if err == nil {
		return nil
	}

	for self.Next() {
		if err := f(); err == nil {
			return nil
		}

		time.Sleep(self.Delay)
	}

	return err
}

// Reset will reset the retry count, the backoff delay, and backoff slots back to its initial state.
func (self *FibonacciBackoff) Reset() {
	self.Retries = 0
	self.Delay = time.Duration(0 * time.Second)
	self.Slots = nil
	self.Slots = append(self.Slots, time.Duration(0*self.Interval))
}
<commit_msg>Remove minor excess comment.<commit_after>package backoff

import (
	"time"
)

type FibonacciBackoff struct {
	Retries    int
	MaxRetries int
	Delay      time.Duration
	Interval   time.Duration // time.Second, time.Millisecond, etc.
	Slots      []time.Duration
}

// Fibonacci creates a new instance of FibonacciBackoff.
func Fibonacci() *FibonacciBackoff {
	return &FibonacciBackoff{
		Retries:    0,
		MaxRetries: 5,
		Delay:      time.Duration(0),
	}
}

/*
Next gets the next backoff delay. This method will increment the retries and check
if the maximum number of retries has been met. If this condition is satisfied, then
the function will return. Otherwise, the next backoff delay will be computed.

The fibonacci backoff delay is computed as follows:
`n = fib(c - 1) + fib(c - 2); f(0) = 0, f(1) = 1; n >= 0.` where
`n` is the backoff delay and `c` is the retry slot.

This method maintains a slice of time.Duration to save on fibonacci computation.

Example, given a 1 second interval:

  Retry #        Backoff delay (in seconds)
    0                   0
    1                   1
    2                   1
    3                   2
    4                   3
    5                   5
    6                   8
    7                   13
*/
func (self *FibonacciBackoff) Next() bool {
	self.Retries++

	if self.Retries >= self.MaxRetries {
		return false
	}

	if self.Retries == 1 {
		self.Slots = append(self.Slots, time.Duration(0*self.Interval))
		self.Slots = append(self.Slots, time.Duration(1*self.Interval))
		self.Delay = time.Duration(1 * self.Interval)
	} else {
		self.Delay = self.Slots[self.Retries-1] + self.Slots[self.Retries-2]
		self.Slots = append(self.Slots, self.Delay)
	}

	return true
}

/*
Retry will retry a function until the maximum number of retries is met. This method expects
the function `f` to return an error. If the failure condition is met, this method
will surface the error outputted from `f`, otherwise nil will be returned as normal.
*/
func (self *FibonacciBackoff) Retry(f func() error) error {
	err := f()

	if err == nil {
		return nil
	}

	for self.Next() {
		if err := f(); err == nil {
			return nil
		}

		time.Sleep(self.Delay)
	}

	return err
}

// Reset will reset the retry count, the backoff delay, and backoff slots back to its initial state.
func (self *FibonacciBackoff) Reset() {
	self.Retries = 0
	self.Delay = time.Duration(0 * time.Second)
	self.Slots = nil
	self.Slots = append(self.Slots, time.Duration(0*self.Interval))
}
<|endoftext|>


<commit_before>package main

import (
	"os"
	"log"
	"fmt"
	"path"
	"flag"
	"runtime"
	"strings"
	"time"

	"dir"
)

const (
	BLACK = iota
	RED
	GREEN
	YELLOW
	BLUE
	PURPLE
	CYAN
	WHITE
)

var hint *int
var debug *bool
var workers *int

func colorize(c int, txt string) string {
	return fmt.Sprintf("\x1b[01;3%dm%s\x1b[00m", c, txt)
}

// don't waste time on cleaning
func pjoin(elem ...string) string {
	return strings.Join(elem, "/")
}

func countdir(dpath string, c chan string) int {
	f, err := os.Open(dpath)
	if err != nil { log.Fatal(err) }
	des, err := dir.Readdir(f, *hint)
	f.Close()
	if err != nil { log.Fatal(err) }

	fc := 0
	for _, de := range des {
		switch de.Type {
		case dir.DT_UNKNOWN:
			log.Fatalf("got no filetype info: %s", pjoin(dpath, de.Name))
		case dir.DT_REG:
			fc += 1
		case dir.DT_DIR:
			c <- pjoin(dpath, de.Name)
		}
	}

	if *debug { fmt.Println(dpath, fc) }

	return fc
}

func counter(cwi, cwo chan string, cc chan int) {
	count := 0
	for {
		dp := <-cwi
		if dp == "" {
			cc <- count
			return
		}
		count += countdir(dp, cwo)
		cwo <- ""
	}
}

func countdirs(dpaths []string) int {
	cwi := make(chan string, 3 * *hint / *workers)
	cwo := make(chan string, 3 * *hint / *workers)
	cc := make(chan int)
	for i := 0; i < *workers; i++ {
		go counter(cwi, cwo, cc)
	}

	for _, dp := range dpaths {
		cwi <- dp
	}

	q := make([]string, 0, 3 * *hint)
	for n := len(dpaths); n != 0; {
		dp := <-cwo
		if *debug { fmt.Println("jobstat", dp, n, q, len(cwi), cap(cwi)) }
		if dp == "" {
			n -= 1
			if len(q) > 0 {
				dp = q[0]
				q = q[1:]
			}
		}
		if dp != "" {
			select {
			case cwi <- dp:
				n += 1
			default:
				q = append(q, dp)
			}
		}
	}

	count := 0
	for i := 0; i < *workers; i++ {
		cwi <- ""
		count += <-cc
	}

	return count
}

func next(n, m , t int) int {
	n = t - (t % n) + n
	m = t - (t % m) + m
	if (n < m) {
		return n
	}
	return m
}

const usage = `Intuit mon.sh clone

%s [options] [targets...]
options:
`

func main() {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, usage, os.Args[0])
		flag.PrintDefaults()
	}
	debug = flag.Bool("debug", false, "debug mode")
	hint = flag.Int("hint", 256, "filenumber hint")
	workers = flag.Int("workers", runtime.NumCPU() + 1, "number of workers")
	scan := flag.Int("scanby", 10, "interval to scan by")
	rec  := flag.Int("recby", 20, "interval to record by")
	turns := flag.Int("turns", 0, "number of iterations")
	//logf := flag.String("logf", "", "log file")
	flag.Parse()

	runtime.GOMAXPROCS(*workers)

	targets := flag.Args()
	if len(targets) == 0 {
		targets = []string { "." }
	}
	for i, _ := range targets {
		targets[i] = path.Clean(targets[i])
	}

	for t := 0;; {
		*turns -= 1
		go func(t, tr int) {
			count := countdirs(targets)
			m := fmt.Sprintf("%3d %6d", t, count)
			if t % *rec == 0 {
				m = colorize(RED, m)
			}
			fmt.Println(m)
			if tr == 0 { os.Exit(0) }
		}(t, *turns)
		to := t
		t = next(*scan, *rec, t)
		time.Sleep(time.Duration(t - to) * time.Second)
	}
}
<commit_msg>implement logging in json format<commit_after>package main

import (
	"os"
	"log"
	"fmt"
	"path"
	"flag"
	"runtime"
	"strings"
	"time"
	"encoding/json"

	"dir"
)

const (
	BLACK = iota
	RED
	GREEN
	YELLOW
	BLUE
	PURPLE
	CYAN
	WHITE
)

var hint *int
var debug *bool
var workers *int

func colorize(c int, txt string) string {
	return fmt.Sprintf("\x1b[01;3%dm%s\x1b[00m", c, txt)
}

// don't waste time on cleaning
func pjoin(elem ...string) string {
	return strings.Join(elem, "/")
}

func countdir(dpath string, c chan string) int {
	f, err := os.Open(dpath)
	if err != nil { log.Fatal(err) }
	des, err := dir.Readdir(f, *hint)
	f.Close()
	if err != nil { log.Fatal(err) }

	fc := 0
	for _, de := range des {
		switch de.Type {
		case dir.DT_UNKNOWN:
			log.Fatalf("got no filetype info: %s", pjoin(dpath, de.Name))
		case dir.DT_REG:
			fc += 1
		case dir.DT_DIR:
			c <- pjoin(dpath, de.Name)
		}
	}

	if *debug { fmt.Println(dpath, fc) }

	return fc
}

func counter(cwi, cwo chan string, cc chan int) {
	count := 0
	for {
		dp := <-cwi
		if dp == "" {
			cc <- count
			return
		}
		count += countdir(dp, cwo)
		cwo <- ""
	}
}

func countdirs(dpaths []string) int {
	cwi := make(chan string, 3 * *hint / *workers)
	cwo := make(chan string, 3 * *hint / *workers)
	cc := make(chan int)
	for i := 0; i < *workers; i++ {
		go counter(cwi, cwo, cc)
	}

	for _, dp := range dpaths {
		cwi <- dp
	}

	q := make([]string, 0, 3 * *hint)
	for n := len(dpaths); n != 0; {
		dp := <-cwo
		if *debug { fmt.Println("jobstat", dp, n, q, len(cwi), cap(cwi)) }
		if dp == "" {
			n -= 1
			if len(q) > 0 {
				dp = q[0]
				q = q[1:]
			}
		}
		if dp != "" {
			select {
			case cwi <- dp:
				n += 1
			default:
				q = append(q, dp)
			}
		}
	}

	count := 0
	for i := 0; i < *workers; i++ {
		cwi <- ""
		count += <-cc
	}

	return count
}

func next(n, m , t int) int {
	n = t - (t % n) + n
	m = t - (t % m) + m
	if (n < m) {
		return n
	}
	return m
}

const usage = `Intuit mon.sh clone

%s [options] [targets...]
options:
`

type scanrec struct {
	Tstart, Tend time.Time
	Trel, Files int
}

func main() {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, usage, os.Args[0])
		flag.PrintDefaults()
	}
	debug = flag.Bool("debug", false, "debug mode")
	hint = flag.Int("hint", 256, "filenumber hint")
	workers = flag.Int("workers", runtime.NumCPU() + 1, "number of workers")
	scan := flag.Int("scanby", 10, "interval to scan by")
	rec  := flag.Int("recby", 20, "interval to record by")
	turns := flag.Int("turns", 0, "number of iterations")
	logp := flag.String("logf", "", "log file")
	flag.Parse()

	runtime.GOMAXPROCS(*workers)

	var logj *json.Encoder
	if *logp != "" {
		logf, err := os.OpenFile(*logp, os.O_WRONLY|os.O_CREATE, 0600)
		if err != nil { log.Fatal(err) }
		logj = json.NewEncoder(logf)
	}

	targets := flag.Args()
	if len(targets) == 0 {
		targets = []string { "." }
	}
	for i, _ := range targets {
		targets[i] = path.Clean(targets[i])
	}

	for t := 0;; {
		*turns -= 1
		go func(t, tr int) {
			tn0 := time.Now()
			count := countdirs(targets)
			tn1 := time.Now()
			m := fmt.Sprintf("%3d %6d", t, count)
			if t % *rec == 0 {
				m = colorize(RED, m)
			}
			fmt.Println(m)
			if logj != nil {
				err := logj.Encode(scanrec{ tn0, tn1, t, count})
				if err != nil { log.Fatal(err) }
			}
			if tr == 0 { os.Exit(0) }
		}(t, *turns)
		to := t
		t = next(*scan, *rec, t)
		time.Sleep(time.Duration(t - to) * time.Second)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/codegangsta/cli"
	"github.com/hatofmonkeys/cloudfocker/focker"
	"log"
	"os"
)

func main() {
	app := cli.NewApp()
	app.Name = "Focker"
	app.Usage = "Fock the Cloud, run apps locally!"
	app.Action = func(c *cli.Context) {

	}

	app.Commands = []cli.Command{
		{
			Name:  "docker",
			Usage: "print the local Docker info",
			Action: func(c *cli.Context) {
				focker := focker.NewFocker()
				focker.DockerVersion(os.Stdout)
			},
		},
		{
			Name:  "this",
			Usage: "download the Cloud Foundry base image",
			Action: func(c *cli.Context) {
				focker := focker.NewFocker()
				focker.ImportRootfsImage(os.Stdout)
			},
		},
		{
			Name:  "up",
			Usage: "start the container for the application",
			Action: func(c *cli.Context) {
				focker := focker.NewFocker()
				pwd, err := os.Getwd()
				if err != nil {
					log.Fatalf(" %s", err)
				} else {
					if err := focker.RunStager(os.Stdout, pwd); err != nil {
						log.Fatalf(" %s", err)
					}
				}
				focker.RunRuntime(os.Stdout)
			},
		},
		{
			Name:  "off",
			Usage: "stop the container and remove it",
			Action: func(c *cli.Context) {
				focker := focker.NewFocker()
				focker.StopRuntime(os.Stdout)
			},
		},
		{
			Name:  "add-buildpack",
			Usage: "add-buildpack [URL] - add a buildpack from a GitHub URL",
			Action: func(c *cli.Context) {
				focker := focker.NewFocker()
				if url := c.Args().First(); url != "" {
					focker.AddBuildpack(os.Stdout, url)
				} else {
					fmt.Println("Please supply a GitHub URL to download")
				}
			},
		},
		{
			Name:  "stage",
			Usage: "stage an application",
			Action: func(c *cli.Context) {
				focker := focker.NewFocker()
				pwd, err := os.Getwd()
				if err != nil {
					log.Fatalf(" %s", err)
				} else {
					if err := focker.RunStager(os.Stdout, pwd); err != nil {
						log.Fatalf(" %s", err)
					}
				}
			},
		},
		{
			Name:  "run",
			Usage: "run the staged container",
			Action: func(c *cli.Context) {
				focker := focker.NewFocker()
				focker.RunRuntime(os.Stdout)
			},
		},
		{
			Name: "stage-internal",
			Action: func(c *cli.Context) {
				focker := focker.NewFocker()
				focker.StageApp(os.Stdout)
			},
			HideHelp: true,
		},
	}

	app.Run(os.Args)
}
<commit_msg>Improve help text<commit_after>package main

import (
	"fmt"
	"github.com/codegangsta/cli"
	"github.com/hatofmonkeys/cloudfocker/focker"
	"log"
	"os"
)

func main() {
	app := cli.NewApp()
	app.Name = "Fock"
	app.Usage = "Cloud Focker - fock the Cloud, run apps locally!"
	app.Action = func(c *cli.Context) {
		cli.ShowAppHelp(c)
	}

	app.Commands = []cli.Command{
		{
			Name:  "docker",
			Usage: "print the local Docker info",
			Action: func(c *cli.Context) {
				focker := focker.NewFocker()
				focker.DockerVersion(os.Stdout)
			},
		},
		{
			Name:  "this",
			Usage: "download the Cloud Foundry base image",
			Action: func(c *cli.Context) {
				focker := focker.NewFocker()
				focker.ImportRootfsImage(os.Stdout)
			},
		},
		{
			Name:  "up",
			Usage: "start the container for the application",
			Action: func(c *cli.Context) {
				focker := focker.NewFocker()
				pwd, err := os.Getwd()
				if err != nil {
					log.Fatalf(" %s", err)
				} else {
					if err := focker.RunStager(os.Stdout, pwd); err != nil {
						log.Fatalf(" %s", err)
					}
				}
				focker.RunRuntime(os.Stdout)
			},
		},
		{
			Name:  "off",
			Usage: "stop the container and remove it",
			Action: func(c *cli.Context) {
				focker := focker.NewFocker()
				focker.StopRuntime(os.Stdout)
			},
		},
		{
			Name:  "add-buildpack",
			Usage: "add-buildpack [URL] - add a buildpack from a GitHub URL",
			Action: func(c *cli.Context) {
				focker := focker.NewFocker()
				if url := c.Args().First(); url != "" {
					focker.AddBuildpack(os.Stdout, url)
				} else {
					fmt.Println("Please supply a GitHub URL to download")
				}
			},
		},
		{
			Name:  "stage",
			Usage: "stage an application",
			Action: func(c *cli.Context) {
				focker := focker.NewFocker()
				pwd, err := os.Getwd()
				if err != nil {
					log.Fatalf(" %s", err)
				} else {
					if err := focker.RunStager(os.Stdout, pwd); err != nil {
						log.Fatalf(" %s", err)
					}
				}
			},
		},
		{
			Name:  "run",
			Usage: "run the staged container",
			Action: func(c *cli.Context) {
				focker := focker.NewFocker()
				focker.RunRuntime(os.Stdout)
			},
		},
		{
			Name: "stage-internal",
			Action: func(c *cli.Context) {
				focker := focker.NewFocker()
				focker.StageApp(os.Stdout)
			},
			HideHelp: true,
		},
	}

	app.Run(os.Args)
}
<|endoftext|>


<commit_before>package flagly

import (
	"fmt"
	"reflect"
	"strconv"
	"strings"
	"time"
)

var (
	types    = map[string]Typer{}
	nilValue reflect.Value
)

func init() {
	RegisterAll(Bool{}, String{}, Duration{}, Int{})
	Register(MapStringString{})
}

func getTypeName(t reflect.Type) string {
	if t.Kind() == reflect.Ptr {
		t = t.Elem()
	}
	return t.String()
}

func RegisterAll(objs ...BaseTyperParser) {
	for _, obj := range objs {
		Register(ValueWrap{obj}, SliceWrap{obj})
	}
}

func Register(objs ...Typer) {
	for _, obj := range objs {
		t := obj.Type()
		if t.Kind() == reflect.Ptr {
			t = t.Elem()
		}
		types[getTypeName(t)] = obj
	}
}

func GetTyper(t reflect.Type) (Typer, error) {
	name := getTypeName(t)
	ret := types[name]
	if ret != nil {
		return ret, nil
	}
	return nil, fmt.Errorf("unknown type: %v", name)
}

type BaseTyper interface {
	Type() reflect.Type
	NumArgs() (min int, max int)
	CanBeValue(arg string) bool
}
type BaseTypeArgNamer interface {
	ArgName() string
}
type BaseTyperParser interface {
	BaseTyper
	ParseArgs(args []string) (reflect.Value, error)
}

type SliceWrap struct {
	BaseTyperParser
}

func (s SliceWrap) Type() reflect.Type {
	return reflect.SliceOf(s.BaseTyperParser.Type())
}
func (s SliceWrap) Set(source reflect.Value, args []string) error {
	if len(args) == 0 {
		return nil
	}
	arg, err := s.BaseTyperParser.ParseArgs(args)
	if err != nil {
		return err
	}
	val := reflect.New(s.BaseTyperParser.Type())
	SetToSource(val, arg)
	source.Set(reflect.Append(source, val.Elem()))
	return nil
}

type Typer interface {
	BaseTyper
	Set(source reflect.Value, args []string) error
}

type ValueWrap struct{ BaseTyperParser }

func SetToSource(source, val reflect.Value) {
	if source.Kind() == reflect.Ptr {
		if source.IsNil() {
			ptr := reflect.New(source.Type().Elem())
			source.Set(ptr)
		}
		if val.Kind() != reflect.Ptr {
			source = source.Elem()
		}
	}
	source.Set(val)
}

func (b ValueWrap) ArgName() string {
	if a, ok := b.BaseTyperParser.(BaseTypeArgNamer); ok {
		return a.ArgName()
	}
	return ""
}

func (b ValueWrap) Set(source reflect.Value, args []string) error {
	val, err := b.ParseArgs(args)
	if err != nil {
		return err
	}
	SetToSource(source, val)
	return nil
}

type Int struct{}

func (Int) Type() reflect.Type     { return reflect.TypeOf(int(0)) }
func (Int) CanBeValue(string) bool { return true }
func (Int) NumArgs() (int, int)    { return 1, 1 }
func (Int) ArgName() string        { return "number" }
func (Int) ParseArgs(args []string) (reflect.Value, error) {
	val, err := strconv.Atoi(args[0])
	if err != nil {
		return nilValue, err
	}
	return reflect.ValueOf(val), nil
}

type String struct{}

func (String) Type() reflect.Type     { return reflect.TypeOf("") }
func (String) CanBeValue(string) bool { return true }
func (String) NumArgs() (int, int)    { return 1, 1 }
func (String) ParseArgs(args []string) (reflect.Value, error) {
	return reflect.ValueOf(args[0]), nil
}

type Bool struct{}

func (Bool) Type() reflect.Type  { return reflect.TypeOf(true) }
func (Bool) NumArgs() (int, int) { return 0, 1 }
func (Bool) CanBeValue(arg string) bool {
	return arg == "true" || arg == "false"
}
func (Bool) ParseArgs(args []string) (reflect.Value, error) {
	val := true
	if len(args) == 1 && args[0] == "false" {
		val = false
	}
	return reflect.ValueOf(val), nil
}

type Duration struct{}

func (Duration) Type() reflect.Type     { return reflect.TypeOf(time.Second) }
func (Duration) NumArgs() (int, int)    { return 1, 1 }
func (Duration) CanBeValue(string) bool { return true }
func (Duration) ParseArgs(args []string) (reflect.Value, error) {
	a, err := time.ParseDuration(args[0])
	if err != nil {
		return nilValue, err
	}
	return reflect.ValueOf(a), nil
}

type MapStringString struct{}

func (MapStringString) Type() reflect.Type {
	return reflect.TypeOf(map[string]string{})
}
func (MapStringString) NumArgs() (int, int)    { return 1, 1 }
func (MapStringString) CanBeValue(string) bool { return true }
func (MapStringString) Set(source reflect.Value, args []string) error {
	idx := strings.Index(args[0], "=")
	if idx < 0 {
		return fmt.Errorf("invalid config: %v", args[0])
	}
	elem := source
	if source.Kind() == reflect.Ptr {
		elem = source.Elem()
	}
	if elem.IsNil() {
		elem.Set(reflect.ValueOf(map[string]string{}))
	}
	key := args[0][:idx]
	value := args[0][idx+1:]
	elem.SetMapIndex(reflect.ValueOf(key), reflect.ValueOf(value))
	return nil
}
func (MapStringString) ArgName() string {
	return "key=value"
}
<commit_msg>refactor interfaces<commit_after>package flagly

import (
	"fmt"
	"reflect"
	"strconv"
	"strings"
	"time"
)

var (
	types    = map[string]Typer{}
	nilValue reflect.Value
)

func init() {
	RegisterAll(Bool{}, String{}, Duration{}, Int{})
	Register(MapStringString{})
}

func getTypeName(t reflect.Type) string {
	if t.Kind() == reflect.Ptr {
		t = t.Elem()
	}
	return t.String()
}

func RegisterAll(objs ...BaseTyperParser) {
	for _, obj := range objs {
		Register(ValueWrap{obj}, SliceWrap{obj})
	}
}

func Register(objs ...Typer) {
	for _, obj := range objs {
		t := obj.Type()
		if t.Kind() == reflect.Ptr {
			t = t.Elem()
		}
		types[getTypeName(t)] = obj
	}
}

func GetTyper(t reflect.Type) (Typer, error) {
	name := getTypeName(t)
	ret := types[name]
	if ret != nil {
		return ret, nil
	}
	return nil, fmt.Errorf("unknown type: %v", name)
}

type BaseTyper interface {
	Type() reflect.Type
}
type BaseTypeArgNamer interface {
	ArgName() string
}
type BaseTypeCanBeValuer interface {
	CanBeValue(arg string) bool
}
type BaseTypeNumArgs interface {
	NumArgs() (int, int)
}
type BaseTyperParser interface {
	BaseTyper
	ParseArgs(args []string) (reflect.Value, error)
}

type SliceWrap struct {
	BaseTyperParser
}

func (s SliceWrap) Type() reflect.Type {
	return reflect.SliceOf(s.BaseTyperParser.Type())
}
func (s SliceWrap) Set(source reflect.Value, args []string) error {
	if len(args) == 0 {
		return nil
	}
	arg, err := s.BaseTyperParser.ParseArgs(args)
	if err != nil {
		return err
	}
	val := reflect.New(s.BaseTyperParser.Type())
	SetToSource(val, arg)
	source.Set(reflect.Append(source, val.Elem()))
	return nil
}

func (s SliceWrap) CanBeValue(arg string) bool {
	if cbv, ok := s.BaseTyperParser.(BaseTypeCanBeValuer); ok {
		return cbv.CanBeValue(arg)
	}
	return true
}

func (s SliceWrap) NumArgs() (int, int) {
	if an, ok := s.BaseTyperParser.(BaseTypeNumArgs); ok {
		return an.NumArgs()
	}
	return 1, 1
}

type Typer interface {
	BaseTyper
	BaseTypeNumArgs
	BaseTypeCanBeValuer
	Set(source reflect.Value, args []string) error
}

type ValueWrap struct{ BaseTyperParser }

func SetToSource(source, val reflect.Value) {
	if source.Kind() == reflect.Ptr {
		if source.IsNil() {
			ptr := reflect.New(source.Type().Elem())
			source.Set(ptr)
		}
		if val.Kind() != reflect.Ptr {
			source = source.Elem()
		}
	}
	source.Set(val)
}

func (b ValueWrap) CanBeValue(arg string) bool {
	if cbv, ok := b.BaseTyperParser.(BaseTypeCanBeValuer); ok {
		return cbv.CanBeValue(arg)
	}
	return true
}

func (b ValueWrap) NumArgs() (int, int) {
	if an, ok := b.BaseTyperParser.(BaseTypeNumArgs); ok {
		return an.NumArgs()
	}
	return 1, 1
}

func (b ValueWrap) ArgName() string {
	if a, ok := b.BaseTyperParser.(BaseTypeArgNamer); ok {
		return a.ArgName()
	}
	return ""
}

func (b ValueWrap) Set(source reflect.Value, args []string) error {
	val, err := b.ParseArgs(args)
	if err != nil {
		return err
	}
	SetToSource(source, val)
	return nil
}

type Int struct{}

func (Int) Type() reflect.Type { return reflect.TypeOf(int(0)) }
func (Int) ArgName() string    { return "number" }
func (Int) ParseArgs(args []string) (reflect.Value, error) {
	val, err := strconv.Atoi(args[0])
	if err != nil {
		return nilValue, err
	}
	return reflect.ValueOf(val), nil
}

type String struct{}

func (String) Type() reflect.Type     { return reflect.TypeOf("") }
func (String) CanBeValue(string) bool { return true }
func (String) NumArgs() (int, int)    { return 1, 1 }
func (String) ParseArgs(args []string) (reflect.Value, error) {
	return reflect.ValueOf(args[0]), nil
}

type Bool struct{}

func (Bool) Type() reflect.Type  { return reflect.TypeOf(true) }
func (Bool) NumArgs() (int, int) { return 0, 1 }
func (Bool) CanBeValue(arg string) bool {
	return arg == "true" || arg == "false"
}
func (Bool) ParseArgs(args []string) (reflect.Value, error) {
	val := true
	if len(args) == 1 && args[0] == "false" {
		val = false
	}
	return reflect.ValueOf(val), nil
}

type Duration struct{}

func (Duration) Type() reflect.Type     { return reflect.TypeOf(time.Second) }
func (Duration) NumArgs() (int, int)    { return 1, 1 }
func (Duration) CanBeValue(string) bool { return true }
func (Duration) ParseArgs(args []string) (reflect.Value, error) {
	a, err := time.ParseDuration(args[0])
	if err != nil {
		return nilValue, err
	}
	return reflect.ValueOf(a), nil
}

type MapStringString struct{}

func (MapStringString) Type() reflect.Type {
	return reflect.TypeOf(map[string]string{})
}
func (MapStringString) NumArgs() (int, int)    { return 1, 1 }
func (MapStringString) CanBeValue(string) bool { return true }
func (MapStringString) Set(source reflect.Value, args []string) error {
	idx := strings.Index(args[0], "=")
	if idx < 0 {
		return fmt.Errorf("invalid config: %v", args[0])
	}
	elem := source
	if source.Kind() == reflect.Ptr {
		elem = source.Elem()
	}
	if elem.IsNil() {
		elem.Set(reflect.ValueOf(map[string]string{}))
	}
	key := args[0][:idx]
	value := args[0][idx+1:]
	elem.SetMapIndex(reflect.ValueOf(key), reflect.ValueOf(value))
	return nil
}
func (MapStringString) ArgName() string {
	return "key=value"
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Kubernetes Authors.

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

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

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

package flagutil

import (
	"errors"
	"flag"
	"fmt"
	"net/url"
	"os"

	"k8s.io/client-go/kubernetes"
	corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
	prow "k8s.io/test-infra/prow/client/clientset/versioned"
	prowv1 "k8s.io/test-infra/prow/client/clientset/versioned/typed/prowjobs/v1"
	"k8s.io/test-infra/prow/kube"
)

// KubernetesOptions holds options for interacting with Kubernetes.
// These options are both useful for clients interacting with ProwJobs
// and other resources on the infrastructure cluster, as well as Pods
// on build clusters.
type KubernetesOptions struct {
	buildCluster string
	kubeconfig   string
	infraContext string

	deckURI string

	// from resolution
	resolved                   bool
	dryRun                     bool
	prowJobClientset           prow.Interface
	kubernetesClientsByContext map[string]kubernetes.Interface
}

// AddFlags injects Kubernetes options into the given FlagSet.
func (o *KubernetesOptions) AddFlags(fs *flag.FlagSet) {
	fs.StringVar(&o.buildCluster, "build-cluster", "", "Path to kube.Cluster YAML file. If empty, uses the local cluster. All clusters are used as build clusters. Cannot be combined with --kubeconfig.")
	fs.StringVar(&o.kubeconfig, "kubeconfig", "", "Path to .kube/config file. If empty, uses the local cluster. All contexts other than the default or whichever is passed to --context are used as build clusters. . Cannot be combined with --build-cluster.")
	fs.StringVar(&o.infraContext, "context", "", "The name of the kubeconfig context to use for the infrastructure client. If empty and --kubeconfig is not set, uses the local cluster.")
	fs.StringVar(&o.deckURI, "deck-url", "", "Deck URI for read-only access to the infrastructure cluster.")
}

// Validate validates Kubernetes options.
func (o *KubernetesOptions) Validate(dryRun bool) error {
	if dryRun && o.deckURI == "" {
		return errors.New("a dry-run was requested but required flag -deck-url was unset")
	}

	if o.deckURI != "" {
		if _, err := url.ParseRequestURI(o.deckURI); err != nil {
			return fmt.Errorf("invalid -deck-url URI: %q", o.deckURI)
		}
	}

	if o.kubeconfig != "" {
		if _, err := os.Stat(o.kubeconfig); err != nil {
			return fmt.Errorf("error accessing --kubeconfig: %v", err)
		}
	}

	if o.infraContext != "" && o.kubeconfig == "" {
		return errors.New("cannot provide --context without --kubeconfig")
	}

	if o.kubeconfig != "" && o.buildCluster != "" {
		return errors.New("must provide only --build-cluster OR --kubeconfig")
	}

	return nil
}

// resolve loads all of the clients we need and caches them for future calls.
func (o *KubernetesOptions) resolve(dryRun bool) (err error) {
	if o.resolved {
		return nil
	}

	o.dryRun = dryRun
	if dryRun {
		return nil
	}

	clusterConfigs, defaultContext, err := kube.LoadClusterConfigs(o.kubeconfig, o.buildCluster)
	clients := map[string]kubernetes.Interface{}
	for context, config := range clusterConfigs {
		client, err := kubernetes.NewForConfig(&config)
		if err != nil {
			return err
		}
		clients[context] = client
	}

	if o.infraContext == "" {
		o.infraContext = defaultContext
	}
	infraConfig, ok := clusterConfigs[o.infraContext]
	if !ok {
		return fmt.Errorf("resolved infrastructure cluster context to %q but did not find it in the kubeconfig", o.infraContext)
	}
	pjClient, err := prow.NewForConfig(&infraConfig)
	if err != nil {
		return err
	}

	o.prowJobClientset = pjClient
	o.kubernetesClientsByContext = clients

	return nil
}

// ProwJobClientset returns a ProwJob clientset for use in informer factories.
func (o *KubernetesOptions) ProwJobClientset(namespace string, dryRun bool) (prowJobClientset prow.Interface, err error) {
	if o.dryRun {
		return nil, errors.New("no dry-run prowjob clientset is supported in dry-run mode")
	}

	if err := o.resolve(dryRun); err != nil {
		return nil, err
	}

	return o.prowJobClientset, nil
}

// ProwJobClient returns a ProwJob client.
func (o *KubernetesOptions) ProwJobClient(namespace string, dryRun bool) (prowJobClient prowv1.ProwJobInterface, err error) {
	if err := o.resolve(dryRun); err != nil {
		return nil, err
	}

	if o.dryRun {
		return kube.NewDryRunProwJobClient(o.deckURI), nil
	}

	return o.prowJobClientset.ProwV1().ProwJobs(namespace), nil
}

// InfrastructureClusterClient returns a Kubernetes client for the infrastructure cluster.
func (o *KubernetesOptions) InfrastructureClusterClient(dryRun bool) (kubernetesClient kubernetes.Interface, err error) {
	if o.dryRun {
		return nil, errors.New("no dry-run kubernetes client is supported in dry-run mode")
	}

	if err := o.resolve(dryRun); err != nil {
		return nil, err
	}

	return o.kubernetesClientsByContext[o.infraContext], nil
}

// BuildClusterClients returns Pod clients for build clusters, mapped by their buildCluster alias, not by context.
func (o *KubernetesOptions) BuildClusterClients(namespace string, dryRun bool) (buildClusterClients map[string]corev1.PodInterface, err error) {
	if o.dryRun {
		return nil, errors.New("no dry-run pod client is supported for build clusters in dry-run mode")
	}

	if err := o.resolve(dryRun); err != nil {
		return nil, err
	}

	buildClients := map[string]corev1.PodInterface{}
	for context, client := range o.kubernetesClientsByContext {
		// we want to map build cluster clients by their alias, not their context
		// the only context that is not the alias is the default context, so
		// we can simply overwrite that one and be content that our build cluster
		// mapping is correct for the controller
		if context == o.infraContext {
			context = kube.DefaultClusterAlias
		}
		buildClients[context] = client.CoreV1().Pods(namespace)
	}
	return buildClients, nil
}
<commit_msg>Resolve Kubernetes clients only once<commit_after>/*
Copyright 2019 The Kubernetes Authors.

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

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

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

package flagutil

import (
	"errors"
	"flag"
	"fmt"
	"net/url"
	"os"

	"k8s.io/client-go/kubernetes"
	corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
	prow "k8s.io/test-infra/prow/client/clientset/versioned"
	prowv1 "k8s.io/test-infra/prow/client/clientset/versioned/typed/prowjobs/v1"
	"k8s.io/test-infra/prow/kube"
)

// KubernetesOptions holds options for interacting with Kubernetes.
// These options are both useful for clients interacting with ProwJobs
// and other resources on the infrastructure cluster, as well as Pods
// on build clusters.
type KubernetesOptions struct {
	buildCluster string
	kubeconfig   string
	infraContext string

	deckURI string

	// from resolution
	resolved                   bool
	dryRun                     bool
	prowJobClientset           prow.Interface
	kubernetesClientsByContext map[string]kubernetes.Interface
}

// AddFlags injects Kubernetes options into the given FlagSet.
func (o *KubernetesOptions) AddFlags(fs *flag.FlagSet) {
	fs.StringVar(&o.buildCluster, "build-cluster", "", "Path to kube.Cluster YAML file. If empty, uses the local cluster. All clusters are used as build clusters. Cannot be combined with --kubeconfig.")
	fs.StringVar(&o.kubeconfig, "kubeconfig", "", "Path to .kube/config file. If empty, uses the local cluster. All contexts other than the default or whichever is passed to --context are used as build clusters. . Cannot be combined with --build-cluster.")
	fs.StringVar(&o.infraContext, "context", "", "The name of the kubeconfig context to use for the infrastructure client. If empty and --kubeconfig is not set, uses the local cluster.")
	fs.StringVar(&o.deckURI, "deck-url", "", "Deck URI for read-only access to the infrastructure cluster.")
}

// Validate validates Kubernetes options.
func (o *KubernetesOptions) Validate(dryRun bool) error {
	if dryRun && o.deckURI == "" {
		return errors.New("a dry-run was requested but required flag -deck-url was unset")
	}

	if o.deckURI != "" {
		if _, err := url.ParseRequestURI(o.deckURI); err != nil {
			return fmt.Errorf("invalid -deck-url URI: %q", o.deckURI)
		}
	}

	if o.kubeconfig != "" {
		if _, err := os.Stat(o.kubeconfig); err != nil {
			return fmt.Errorf("error accessing --kubeconfig: %v", err)
		}
	}

	if o.infraContext != "" && o.kubeconfig == "" {
		return errors.New("cannot provide --context without --kubeconfig")
	}

	if o.kubeconfig != "" && o.buildCluster != "" {
		return errors.New("must provide only --build-cluster OR --kubeconfig")
	}

	return nil
}

// resolve loads all of the clients we need and caches them for future calls.
func (o *KubernetesOptions) resolve(dryRun bool) (err error) {
	if o.resolved {
		return nil
	}

	o.dryRun = dryRun
	if dryRun {
		return nil
	}

	clusterConfigs, defaultContext, err := kube.LoadClusterConfigs(o.kubeconfig, o.buildCluster)
	clients := map[string]kubernetes.Interface{}
	for context, config := range clusterConfigs {
		client, err := kubernetes.NewForConfig(&config)
		if err != nil {
			return err
		}
		clients[context] = client
	}

	if o.infraContext == "" {
		o.infraContext = defaultContext
	}
	infraConfig, ok := clusterConfigs[o.infraContext]
	if !ok {
		return fmt.Errorf("resolved infrastructure cluster context to %q but did not find it in the kubeconfig", o.infraContext)
	}
	pjClient, err := prow.NewForConfig(&infraConfig)
	if err != nil {
		return err
	}

	o.prowJobClientset = pjClient
	o.kubernetesClientsByContext = clients
	o.resolved = true

	return nil
}

// ProwJobClientset returns a ProwJob clientset for use in informer factories.
func (o *KubernetesOptions) ProwJobClientset(namespace string, dryRun bool) (prowJobClientset prow.Interface, err error) {
	if o.dryRun {
		return nil, errors.New("no dry-run prowjob clientset is supported in dry-run mode")
	}

	if err := o.resolve(dryRun); err != nil {
		return nil, err
	}

	return o.prowJobClientset, nil
}

// ProwJobClient returns a ProwJob client.
func (o *KubernetesOptions) ProwJobClient(namespace string, dryRun bool) (prowJobClient prowv1.ProwJobInterface, err error) {
	if err := o.resolve(dryRun); err != nil {
		return nil, err
	}

	if o.dryRun {
		return kube.NewDryRunProwJobClient(o.deckURI), nil
	}

	return o.prowJobClientset.ProwV1().ProwJobs(namespace), nil
}

// InfrastructureClusterClient returns a Kubernetes client for the infrastructure cluster.
func (o *KubernetesOptions) InfrastructureClusterClient(dryRun bool) (kubernetesClient kubernetes.Interface, err error) {
	if o.dryRun {
		return nil, errors.New("no dry-run kubernetes client is supported in dry-run mode")
	}

	if err := o.resolve(dryRun); err != nil {
		return nil, err
	}

	return o.kubernetesClientsByContext[o.infraContext], nil
}

// BuildClusterClients returns Pod clients for build clusters, mapped by their buildCluster alias, not by context.
func (o *KubernetesOptions) BuildClusterClients(namespace string, dryRun bool) (buildClusterClients map[string]corev1.PodInterface, err error) {
	if o.dryRun {
		return nil, errors.New("no dry-run pod client is supported for build clusters in dry-run mode")
	}

	if err := o.resolve(dryRun); err != nil {
		return nil, err
	}

	buildClients := map[string]corev1.PodInterface{}
	for context, client := range o.kubernetesClientsByContext {
		// we want to map build cluster clients by their alias, not their context
		// the only context that is not the alias is the default context, so
		// we can simply overwrite that one and be content that our build cluster
		// mapping is correct for the controller
		if context == o.infraContext {
			context = kube.DefaultClusterAlias
		}
		buildClients[context] = client.CoreV1().Pods(namespace)
	}
	return buildClients, nil
}
<|endoftext|>