<commit_before>package main

import (
	"fmt"
	"github.com/spf13/cobra"
)

type Options struct {
	verbose  bool
	force    bool
	kill     bool
	config   string
	manifest string
	group    string
}

var options = Options{
	false,
	false,
	false,
	"",
	"",
	"",
}
var defaultManifests = []string{"crane.json", "crane.yaml", "crane.yml", "Cranefile"}

func manifestFiles() []string {
	var result = []string(nil)
	if len(options.manifest) > 0 {
		result = []string{options.manifest}
	} else {
		result = defaultManifests
	}
	return result
}

func isVerbose() bool {
	return options.verbose
}

// returns a function to be set as a cobra command run, wrapping a command meant to be run on a set of containers
func containersCommand(wrapped func(containers Containers)) func(cmd *cobra.Command, args []string) {
	return func(cmd *cobra.Command, args []string) {
		if len(args) > 0 {
			cmd.Printf("Error: too many arguments given: %#q", args)
			cmd.Usage()
			return
		}
		containers := getContainers(options)
		wrapped(containers)
	}
}

func handleCmd() {

	var cmdLift = &cobra.Command{
		Use:   "lift",
		Short: "Build or pull images, then run or start the containers",
		Long: `
lift will use specified Dockerfiles to build all the containers, or the specified one(s).
If no Dockerfile is given, it will pull the image(s) from the given registry.`,
		Run: containersCommand(func(containers Containers) {
			containers.lift(options.force, options.kill)
		}),
	}

	var cmdProvision = &cobra.Command{
		Use:   "provision",
		Short: "Build or pull images",
		Long: `
provision will use specified Dockerfiles to build all the containers, or the specified one(s).
If no Dockerfile is given, it will pull the image(s) from the given registry.`,
		Run: containersCommand(func(containers Containers) {
			containers.provision(options.force)
		}),
	}

	var cmdRun = &cobra.Command{
		Use:   "run",
		Short: "Run the containers",
		Long:  `run will call docker run on all containers, or the specified one(s).`,
		Run: containersCommand(func(containers Containers) {
			containers.run(options.force, options.kill)
		}),
	}

	var cmdRm = &cobra.Command{
		Use:   "rm",
		Short: "Remove the containers",
		Long:  `rm will call docker rm on all containers, or the specified one(s).`,
		Run: containersCommand(func(containers Containers) {
			containers.rm(options.force, options.kill)
		}),
	}

	var cmdKill = &cobra.Command{
		Use:   "kill",
		Short: "Kill the containers",
		Long:  `kill will call docker kill on all containers, or the specified one(s).`,
		Run: containersCommand(func(containers Containers) {
			containers.kill()
		}),
	}

	var cmdStart = &cobra.Command{
		Use:   "start",
		Short: "Start the containers",
		Long:  `start will call docker start on all containers, or the specified one(s).`,
		Run: containersCommand(func(containers Containers) {
			containers.start()
		}),
	}

	var cmdStop = &cobra.Command{
		Use:   "stop",
		Short: "Stop the containers",
		Long:  `stop will call docker stop on all containers, or the specified one(s).`,
		Run: containersCommand(func(containers Containers) {
			containers.stop()
		}),
	}

	var cmdStatus = &cobra.Command{
		Use:   "status",
		Short: "Displays status of containers",
		Long:  `Displays the current status of all the containers, or the specified one(s).`,
		Run: containersCommand(func(containers Containers) {
			containers.status()
		}),
	}

	var cmdVersion = &cobra.Command{
		Use:   "version",
		Short: "Display version",
		Long:  `Displays the version of Crane.`,
		Run: func(cmd *cobra.Command, args []string) {
			fmt.Println("v0.7.0")
		},
	}

	var craneCmd = &cobra.Command{
		Use:   "crane",
		Short: "crane - Lift containers with ease",
		Long: `
Crane is a little tool to orchestrate Docker containers.
It works by reading in JSON or YAML (either from crane.json, crane.yaml, the string specified in --config, or a json or yml file specified by --manifest) which describes how to obtain container images and how to run them.
See the corresponding docker commands for more information.`,
	}

	craneCmd.PersistentFlags().BoolVarP(&options.verbose, "verbose", "v", false, "verbose output")
	craneCmd.PersistentFlags().StringVarP(&options.config, "config", "c", "", "config to read from")
	craneCmd.PersistentFlags().StringVarP(&options.manifest, "manifest", "m", "", "config file to read from")
	craneCmd.PersistentFlags().StringVarP(&options.group, "group", "g", "", "group or container to restrict the command to")
	cmdLift.Flags().BoolVarP(&options.force, "force", "f", false, "rebuild all images")
	cmdLift.Flags().BoolVarP(&options.kill, "kill", "k", false, "kill containers")
	cmdProvision.Flags().BoolVarP(&options.force, "force", "f", false, "rebuild all images")
	cmdRun.Flags().BoolVarP(&options.force, "force", "f", false, "stop and remove running containers first")
	cmdRun.Flags().BoolVarP(&options.kill, "kill", "k", false, "when using --force, kill containers instead of stopping them")
	cmdRm.Flags().BoolVarP(&options.force, "force", "f", false, "stop running containers first")
	cmdRm.Flags().BoolVarP(&options.kill, "kill", "k", false, "when using --force, kill containers instead of stopping them")
	craneCmd.AddCommand(cmdLift, cmdProvision, cmdRun, cmdRm, cmdKill, cmdStart, cmdStop, cmdStatus, cmdVersion)
	craneCmd.Execute()
}
<commit_msg>USAGE for invalid CLI input detected by cobra<commit_after>package main

import (
	"fmt"
	"github.com/spf13/cobra"
)

type Options struct {
	verbose  bool
	force    bool
	kill     bool
	config   string
	manifest string
	group    string
}

var options = Options{
	false,
	false,
	false,
	"",
	"",
	"",
}
var defaultManifests = []string{"crane.json", "crane.yaml", "crane.yml", "Cranefile"}

func manifestFiles() []string {
	var result = []string(nil)
	if len(options.manifest) > 0 {
		result = []string{options.manifest}
	} else {
		result = defaultManifests
	}
	return result
}

func isVerbose() bool {
	return options.verbose
}

// returns a function to be set as a cobra command run, wrapping a command meant to be run on a set of containers
func containersCommand(wrapped func(containers Containers)) func(cmd *cobra.Command, args []string) {
	return func(cmd *cobra.Command, args []string) {
		if len(args) > 0 {
			cmd.Printf("Error: too many arguments given: %#q", args)
			cmd.Usage()
			return
		}
		containers := getContainers(options)
		wrapped(containers)
	}
}

func handleCmd() {

	var cmdLift = &cobra.Command{
		Use:   "lift",
		Short: "Build or pull images, then run or start the containers",
		Long: `
lift will use specified Dockerfiles to build all the containers, or the specified one(s).
If no Dockerfile is given, it will pull the image(s) from the given registry.`,
		Run: containersCommand(func(containers Containers) {
			containers.lift(options.force, options.kill)
		}),
	}

	var cmdProvision = &cobra.Command{
		Use:   "provision",
		Short: "Build or pull images",
		Long: `
provision will use specified Dockerfiles to build all the containers, or the specified one(s).
If no Dockerfile is given, it will pull the image(s) from the given registry.`,
		Run: containersCommand(func(containers Containers) {
			containers.provision(options.force)
		}),
	}

	var cmdRun = &cobra.Command{
		Use:   "run",
		Short: "Run the containers",
		Long:  `run will call docker run on all containers, or the specified one(s).`,
		Run: containersCommand(func(containers Containers) {
			containers.run(options.force, options.kill)
		}),
	}

	var cmdRm = &cobra.Command{
		Use:   "rm",
		Short: "Remove the containers",
		Long:  `rm will call docker rm on all containers, or the specified one(s).`,
		Run: containersCommand(func(containers Containers) {
			containers.rm(options.force, options.kill)
		}),
	}

	var cmdKill = &cobra.Command{
		Use:   "kill",
		Short: "Kill the containers",
		Long:  `kill will call docker kill on all containers, or the specified one(s).`,
		Run: containersCommand(func(containers Containers) {
			containers.kill()
		}),
	}

	var cmdStart = &cobra.Command{
		Use:   "start",
		Short: "Start the containers",
		Long:  `start will call docker start on all containers, or the specified one(s).`,
		Run: containersCommand(func(containers Containers) {
			containers.start()
		}),
	}

	var cmdStop = &cobra.Command{
		Use:   "stop",
		Short: "Stop the containers",
		Long:  `stop will call docker stop on all containers, or the specified one(s).`,
		Run: containersCommand(func(containers Containers) {
			containers.stop()
		}),
	}

	var cmdStatus = &cobra.Command{
		Use:   "status",
		Short: "Displays status of containers",
		Long:  `Displays the current status of all the containers, or the specified one(s).`,
		Run: containersCommand(func(containers Containers) {
			containers.status()
		}),
	}

	var cmdVersion = &cobra.Command{
		Use:   "version",
		Short: "Display version",
		Long:  `Displays the version of Crane.`,
		Run: func(cmd *cobra.Command, args []string) {
			fmt.Println("v0.7.0")
		},
	}

	var craneCmd = &cobra.Command{
		Use:   "crane",
		Short: "crane - Lift containers with ease",
		Long: `
Crane is a little tool to orchestrate Docker containers.
It works by reading in JSON or YAML (either from crane.json, crane.yaml, the string specified in --config, or a json or yml file specified by --manifest) which describes how to obtain container images and how to run them.
See the corresponding docker commands for more information.`,
	}

	craneCmd.PersistentFlags().BoolVarP(&options.verbose, "verbose", "v", false, "verbose output")
	craneCmd.PersistentFlags().StringVarP(&options.config, "config", "c", "", "config to read from")
	craneCmd.PersistentFlags().StringVarP(&options.manifest, "manifest", "m", "", "config file to read from")
	craneCmd.PersistentFlags().StringVarP(&options.group, "group", "g", "", "group or container to restrict the command to")
	cmdLift.Flags().BoolVarP(&options.force, "force", "f", false, "rebuild all images")
	cmdLift.Flags().BoolVarP(&options.kill, "kill", "k", false, "kill containers")
	cmdProvision.Flags().BoolVarP(&options.force, "force", "f", false, "rebuild all images")
	cmdRun.Flags().BoolVarP(&options.force, "force", "f", false, "stop and remove running containers first")
	cmdRun.Flags().BoolVarP(&options.kill, "kill", "k", false, "when using --force, kill containers instead of stopping them")
	cmdRm.Flags().BoolVarP(&options.force, "force", "f", false, "stop running containers first")
	cmdRm.Flags().BoolVarP(&options.kill, "kill", "k", false, "when using --force, kill containers instead of stopping them")
	craneCmd.AddCommand(cmdLift, cmdProvision, cmdRun, cmdRm, cmdKill, cmdStart, cmdStop, cmdStatus, cmdVersion)
	err := craneCmd.Execute()
	if err != nil {
		panic(StatusError{status: 64})
	}
}
<|endoftext|>


<commit_before>package gotop

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

var (
	totalCPUFile = "/proc/stat"
)

type CPUInfo struct {
	AverageUtilization float64
	CPUUtilization     []float64
}

func numberOfCpus() int {
	numberOfCpus := 0
	cpuStats, err := readCPUFile()
	if err != nil {
		return 0
	}
	for i := 0; i < len(cpuStats); i++ {
		if strings.Index(cpuStats[i], "cpu") == 0 {
			numberOfCpus = numberOfCpus + 1
		} else {
			break
		}
	}
	// The first line of the file is the average of all CPUs
	return numberOfCpus - 1
}

func getStats(snapshotOne, snapshotTwo string) float64 {
	cpuTimesOne, cpuTimesTwo := strings.Split(snapshotOne[5:], " "), strings.Split(snapshotTwo[5:], " ")
	activeTime, idleTime := 0.0, 0.0
	for i := 0; i < len(cpuTimesOne); i++ {
		time1, _ := strconv.ParseFloat(cpuTimesOne[i], 32)
		time2, _ := strconv.ParseFloat(cpuTimesTwo[i], 32)
		if i != 3 {
			activeTime = activeTime + time2 - time1
		} else {
			// Idle time is the fourth column
			idleTime = +time2 - time1
		}
	}
	activePercentage := 100 * activeTime / (activeTime + idleTime)
	// Return value is truncated to 2 places after decimal
	return float64(int(100*activePercentage)) / 100
}

func readCPUFile() ([]string, error) {
	snapshot, err := readFile(totalCPUFile)
	if err != nil {
		return make([]string, 0), err
	}
	return strings.Split(snapshot, "\n"), nil
}

func TotalCPU(done <-chan struct{}, interval time.Duration) (<-chan CPUInfo, <-chan error) {
	result := make(chan CPUInfo)
	errc := make(chan error, 1)
	var err error
	cleanup := func() {
		errc <- err
		close(errc)
		close(result)
	}
	go func() {
		defer cleanup()
		numberOfCpus := numberOfCpus()
		cpuInfo := CPUInfo{AverageUtilization: 0.0, CPUUtilization: make([]float64, numberOfCpus)}
		var currentFile, previousFile []string
		currentFile, err = readCPUFile()
		if err != nil {
			return
		}
		for {
			previousFile = currentFile
			time.Sleep(interval)
			currentFile, err = readCPUFile()
			if err != nil {
				return
			}
			cpuInfo.AverageUtilization = getStats(currentFile[0], previousFile[0])
			for i := 1; i <= numberOfCpus; i++ {
				cpuInfo.CPUUtilization[i-1] = getStats(currentFile[i], previousFile[i])
			}
			select {
			case result <- cpuInfo:
			case <-done:
				return
			}
		}
	}()
	return result, errc
}<commit_msg>Move public function upwards<commit_after>package gotop

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

var (
	totalCPUFile = "/proc/stat"
)

type CPUInfo struct {
	AverageUtilization float64
	CPUUtilization     []float64
}

func TotalCPU(done <-chan struct{}, interval time.Duration) (<-chan CPUInfo, <-chan error) {
	result := make(chan CPUInfo)
	errc := make(chan error, 1)
	var err error
	cleanup := func() {
		errc <- err
		close(errc)
		close(result)
	}
	go func() {
		defer cleanup()
		numberOfCpus := numberOfCpus()
		cpuInfo := CPUInfo{AverageUtilization: 0.0, CPUUtilization: make([]float64, numberOfCpus)}
		var currentFile, previousFile []string
		currentFile, err = readCPUFile()
		if err != nil {
			return
		}
		for {
			previousFile = currentFile
			time.Sleep(interval)
			currentFile, err = readCPUFile()
			if err != nil {
				return
			}
			cpuInfo.AverageUtilization = getStats(currentFile[0], previousFile[0])
			for i := 1; i <= numberOfCpus; i++ {
				cpuInfo.CPUUtilization[i-1] = getStats(currentFile[i], previousFile[i])
			}
			select {
			case result <- cpuInfo:
			case <-done:
				return
			}
		}
	}()
	return result, errc
}

func numberOfCpus() int {
	numberOfCpus := 0
	cpuStats, err := readCPUFile()
	if err != nil {
		return 0
	}
	for i := 0; i < len(cpuStats); i++ {
		if strings.Index(cpuStats[i], "cpu") == 0 {
			numberOfCpus = numberOfCpus + 1
		} else {
			break
		}
	}
	// The first line of the file is the average of all CPUs
	return numberOfCpus - 1
}

func getStats(snapshotOne, snapshotTwo string) float64 {
	cpuTimesOne, cpuTimesTwo := strings.Split(snapshotOne[5:], " "), strings.Split(snapshotTwo[5:], " ")
	activeTime, idleTime := 0.0, 0.0
	for i := 0; i < len(cpuTimesOne); i++ {
		time1, _ := strconv.ParseFloat(cpuTimesOne[i], 32)
		time2, _ := strconv.ParseFloat(cpuTimesTwo[i], 32)
		if i != 3 {
			activeTime = activeTime + time2 - time1
		} else {
			// Idle time is the fourth column
			idleTime = +time2 - time1
		}
	}
	activePercentage := 100 * activeTime / (activeTime + idleTime)
	// Return value is truncated to 2 places after decimal
	return float64(int(100*activePercentage)) / 100
}

func readCPUFile() ([]string, error) {
	snapshot, err := readFile(totalCPUFile)
	if err != nil {
		return make([]string, 0), err
	}
	return strings.Split(snapshot, "\n"), nil
}<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"time"

	"gobot.io/x/gobot"
	"gobot.io/x/gobot/api"
	"gobot.io/x/gobot/drivers/gpio"
	"gobot.io/x/gobot/platforms/raspi"

	"flag"

	local "./drivers/gpio"
)

func main() {
	debug := flag.Bool("debug", false, "Debug API")
	doorOpenTemp := flag.Int("temp-open-door", 30, "Temperature for trigger open door")
	flapOpenTemp := flag.Int("temp-open-flap", 40, "Temperature for trigger open flap")
	allCloseTemp := flag.Int("temp-close-all", 24, "Temperature for trigget class all")
	dhtInterval := flag.String("interval-poll-sensor", "5m", "Interval polling DHT")

	flag.Parse()

	fmt.Println(
		time.Microsecond,
		time.Millisecond,
		time.Nanosecond,
	)

	master := gobot.NewMaster()

	a := api.NewAPI(master)

	if *debug {
		a.Debug()
	}

	a.Start()

	r := raspi.NewAdaptor()

	m1 := gpio.NewMotorDriver(r, "")
	m1.SetName("Door Motor")
	m1.ForwardPin = "7"   // 4
	m1.BackwardPin = "11" // 17

	m2 := gpio.NewMotorDriver(r, "")
	m2.SetName("Flap Motor")
	m2.ForwardPin = "13"  // 27
	m2.BackwardPin = "15" // 22

	s := local.NewDHTSensorDriver(r, "16") //23
	s.SetName("Sensor")
	if d, err := time.ParseDuration(*dhtInterval); err != nil {
		s.PollInterval(d)
	}

	f := NewFire(m1, m2, s)

	work := func() {
		s.On(gpio.Data, func(data interface{}) {
			t := data.(float32)
			log.Printf("Current temperature %.1f", t)

			if t >= float32(*flapOpenTemp) {
				f.OpenFlap()
			}

			if t >= float32(*doorOpenTemp) {
				f.OpenDoor()
			}

			if t > 0.0 && t <= float32(*allCloseTemp) {
				f.CloseDoor()
				f.CloseFlap()
			}
		})

		s.On(gpio.Error, func(data interface{}) {
			log.Print("Error DHT")
			log.Println(data)
		})
	}

	master.AddRobot(gobot.NewRobot("fire",
		[]gobot.Connection{r},
		[]gobot.Device{m1, m2, s},
		work,
	))

	master.Start()
}
<commit_msg>Remove API<commit_after>package main

import (
	"fmt"
	"log"
	"time"

	"gobot.io/x/gobot"
	"gobot.io/x/gobot/drivers/gpio"
	"gobot.io/x/gobot/platforms/raspi"

	"flag"

	local "./drivers/gpio"
)

func main() {
	doorOpenTemp := flag.Int("temp-open-door", 30, "Temperature for trigger open door")
	flapOpenTemp := flag.Int("temp-open-flap", 40, "Temperature for trigger open flap")
	allCloseTemp := flag.Int("temp-close-all", 24, "Temperature for trigget class all")
	dhtInterval := flag.String("interval-poll-sensor", "5m", "Interval polling DHT")

	flag.Parse()

	fmt.Println(
		time.Microsecond,
		time.Millisecond,
		time.Nanosecond,
	)

	master := gobot.NewMaster()

	r := raspi.NewAdaptor()

	m1 := gpio.NewMotorDriver(r, "")
	m1.SetName("Door Motor")
	m1.ForwardPin = "7"   // 4
	m1.BackwardPin = "11" // 17

	m2 := gpio.NewMotorDriver(r, "")
	m2.SetName("Flap Motor")
	m2.ForwardPin = "13"  // 27
	m2.BackwardPin = "15" // 22

	s := local.NewDHTSensorDriver(r, "16") //23
	s.SetName("Sensor")
	if d, err := time.ParseDuration(*dhtInterval); err != nil {
		s.PollInterval(d)
	}

	f := NewFire(m1, m2, s)

	work := func() {
		s.On(gpio.Data, func(data interface{}) {
			t := data.(float32)
			log.Printf("Current temperature %.1f", t)

			if t >= float32(*flapOpenTemp) {
				f.OpenFlap()
			}

			if t >= float32(*doorOpenTemp) {
				f.OpenDoor()
			}

			if t > 0.0 && t <= float32(*allCloseTemp) {
				f.CloseDoor()
				f.CloseFlap()
			}
		})

		s.On(gpio.Error, func(data interface{}) {
			log.Print("Error DHT")
			log.Println(data)
		})
	}

	master.AddRobot(gobot.NewRobot("fire",
		[]gobot.Connection{r},
		[]gobot.Device{m1, m2, s},
		work,
	))

	master.Start()
}
<|endoftext|>


<commit_before>package ctx

import (
	"context"
	"sync"
	"time"
)

// Binder is the interface that wraps the basic Bind method.
// Bind executes logic until the Doner completes.  Implementations of Bind must
// not return until the Doner has completed.
type Binder interface {
	Bind(Doner)
}

// BindFunc is an adapter to allow the use of ordinary functions as Binders.
type BindFunc func(Doner)

// Bind executes logic until the Doner completes.  It satisfies the Binder
// interface.
func (f BindFunc) Bind(d Doner) { f(d) }

// Doner can block until something is done
type Doner interface {
	Done() <-chan struct{}
}

// C is a basic implementation of Doner
type C <-chan struct{}

// Background is the ctx analog to context.Background().  It never fires.
func Background() C {
	return nil
}

// Done returns a channel that receives when an action is complete
func (dc C) Done() <-chan struct{} { return dc }

type ctx struct {
	Doner
}

// Deadline returns the time when work done on behalf of this context
// should be canceled. Deadline returns ok==false when no deadline is
// set. Successive calls to Deadline return the same results.
func (ctx) Deadline() (deadline time.Time, ok bool) {
	return
}

func (c ctx) Err() error {
	select {
	case <-c.Done():
		return context.Canceled
	default:
		return nil
	}
}

func (c ctx) Value(interface{}) (v interface{}) {
	return
}

// AsContext creates a context that fires when the Doner fires
func AsContext(d Doner) context.Context {
	return ctx{d}
}

// After time time has elapsed, the Doner fires
func After(d time.Duration) C {
	ch := make(chan struct{})
	go func() {
		<-time.After(d)
		close(ch)
	}()
	return ch
}

// WithCancel returns a new Doner that can be cancelled via the associated
// function
func WithCancel(d Doner) (C, func()) {
	var closer sync.Once
	cq := make(chan struct{})
	cancel := func() { closer.Do(func() { close(cq) }) }

	go func() {
		select {
		case <-cq:
		case <-d.Done():
			cancel()
		}
	}()

	return cq, cancel
}

// Tick returns a <-chan whose range ends when the underlying context cancels
func Tick(d Doner) <-chan struct{} {
	c := make(chan struct{})
	cq := d.Done()
	go func() {
		for {
			select {
			case <-cq:
				close(c)
				return
			default:
				select {
				case c <- struct{}{}:
				case <-cq:
				}
			}
		}
	}()
	return c
}

// Defer guarantees that a function will be called after a context has cancelled
func Defer(d Doner, cb func()) {
	go func() {
		<-d.Done()
		cb()
	}()
}

// Link returns a channel that fires if ANY of the constituent Doners have fired
func Link(doners ...Doner) C {
	c := make(chan struct{})
	cancel := func() { close(c) }

	var once sync.Once
	for _, d := range doners {
		Defer(d, func() { once.Do(cancel) })
	}

	return c
}

// Join returns a channel that receives when all constituent Doners have fired
func Join(doners ...Doner) C {
	var wg sync.WaitGroup
	wg.Add(len(doners))
	for _, d := range doners {
		Defer(d, wg.Done)
	}

	cq := make(chan struct{})
	go func() {
		wg.Wait()
		close(cq)
	}()
	return cq
}

// FTick calls a function in a loop until the Doner has fired
func FTick(d Doner, f func()) {
	for _ = range Tick(d) {
		f()
	}
}

// FTickInterval calls a function repeatedly at a given internval, until the Doner
// has fired.  Note that FTickInterval ignores the time spent executing a function,
// and instead guarantees an interval of `t` between of return of the previous
// function call and the invocation of the next function call.
func FTickInterval(d Doner, t time.Duration, f func()) {
	for {
		select {
		case <-d.Done():
			return
		case <-time.After(t):
			f()
		}
	}
}

// FDone returns a doner that fires when the function returns or panics
func FDone(f func()) C {
	ch := make(chan struct{})
	go func() {
		defer close(ch)
		f()
	}()
	return ch
}
<commit_msg>Omit ignored variable from range loop<commit_after>package ctx

import (
	"context"
	"sync"
	"time"
)

// Binder is the interface that wraps the basic Bind method.
// Bind executes logic until the Doner completes.  Implementations of Bind must
// not return until the Doner has completed.
type Binder interface {
	Bind(Doner)
}

// BindFunc is an adapter to allow the use of ordinary functions as Binders.
type BindFunc func(Doner)

// Bind executes logic until the Doner completes.  It satisfies the Binder
// interface.
func (f BindFunc) Bind(d Doner) { f(d) }

// Doner can block until something is done
type Doner interface {
	Done() <-chan struct{}
}

// C is a basic implementation of Doner
type C <-chan struct{}

// Background is the ctx analog to context.Background().  It never fires.
func Background() C {
	return nil
}

// Done returns a channel that receives when an action is complete
func (dc C) Done() <-chan struct{} { return dc }

type ctx struct {
	Doner
}

// Deadline returns the time when work done on behalf of this context
// should be canceled. Deadline returns ok==false when no deadline is
// set. Successive calls to Deadline return the same results.
func (ctx) Deadline() (deadline time.Time, ok bool) {
	return
}

func (c ctx) Err() error {
	select {
	case <-c.Done():
		return context.Canceled
	default:
		return nil
	}
}

func (c ctx) Value(interface{}) (v interface{}) {
	return
}

// AsContext creates a context that fires when the Doner fires
func AsContext(d Doner) context.Context {
	return ctx{d}
}

// After time time has elapsed, the Doner fires
func After(d time.Duration) C {
	ch := make(chan struct{})
	go func() {
		<-time.After(d)
		close(ch)
	}()
	return ch
}

// WithCancel returns a new Doner that can be cancelled via the associated
// function
func WithCancel(d Doner) (C, func()) {
	var closer sync.Once
	cq := make(chan struct{})
	cancel := func() { closer.Do(func() { close(cq) }) }

	go func() {
		select {
		case <-cq:
		case <-d.Done():
			cancel()
		}
	}()

	return cq, cancel
}

// Tick returns a <-chan whose range ends when the underlying context cancels
func Tick(d Doner) <-chan struct{} {
	c := make(chan struct{})
	cq := d.Done()
	go func() {
		for {
			select {
			case <-cq:
				close(c)
				return
			default:
				select {
				case c <- struct{}{}:
				case <-cq:
				}
			}
		}
	}()
	return c
}

// Defer guarantees that a function will be called after a context has cancelled
func Defer(d Doner, cb func()) {
	go func() {
		<-d.Done()
		cb()
	}()
}

// Link returns a channel that fires if ANY of the constituent Doners have fired
func Link(doners ...Doner) C {
	c := make(chan struct{})
	cancel := func() { close(c) }

	var once sync.Once
	for _, d := range doners {
		Defer(d, func() { once.Do(cancel) })
	}

	return c
}

// Join returns a channel that receives when all constituent Doners have fired
func Join(doners ...Doner) C {
	var wg sync.WaitGroup
	wg.Add(len(doners))
	for _, d := range doners {
		Defer(d, wg.Done)
	}

	cq := make(chan struct{})
	go func() {
		wg.Wait()
		close(cq)
	}()
	return cq
}

// FTick calls a function in a loop until the Doner has fired
func FTick(d Doner, f func()) {
	for range Tick(d) {
		f()
	}
}

// FTickInterval calls a function repeatedly at a given internval, until the Doner
// has fired.  Note that FTickInterval ignores the time spent executing a function,
// and instead guarantees an interval of `t` between of return of the previous
// function call and the invocation of the next function call.
func FTickInterval(d Doner, t time.Duration, f func()) {
	for {
		select {
		case <-d.Done():
			return
		case <-time.After(t):
			f()
		}
	}
}

// FDone returns a doner that fires when the function returns or panics
func FDone(f func()) C {
	ch := make(chan struct{})
	go func() {
		defer close(ch)
		f()
	}()
	return ch
}
<|endoftext|>


<commit_before>package neo

import (
	"github.com/ivpusic/neo/ebus"
	"net/http"
)

///////////////////////////////////////////////////////////////////
// Context data representation and operations
///////////////////////////////////////////////////////////////////

// Map which will hold your Request contextual data.
type CtxData map[string]interface{}

// Get context data on key
func (r CtxData) Get(key string) interface{} {
	return r[key]
}

// Add new contextual data on key with provided value
func (r CtxData) Set(key string, value interface{}) {
	r[key] = value
}

// Delete contextual data on key
func (r CtxData) Del(key string) {
	delete(r, key)
}

///////////////////////////////////////////////////////////////////
// Context
///////////////////////////////////////////////////////////////////

// Representing context for this request.
type Ctx struct {
	ebus.EBus

	// Wrapped http.Request object.
	Req *Request

	// Object with utility methods for writing responses to http.ResponseWriter
	Res *Response

	// Context data map
	Data CtxData
}

// Will make default contextual data based on provided request and ResponseWriter
func makeCtx(req *http.Request, w http.ResponseWriter) *Ctx {
	request := makeRequest(req)
	response := makeResponse(req, w)
	// 404 - OK by default
	// this can be overided by middlewares and handler fns
	response.Status = 404

	ctx := &Ctx{
		Req:  request,
		Res:  response,
		Data: CtxData{},
	}
	ctx.InitEBus()

	return ctx
}
<commit_msg>added gorm transaction to ctx<commit_after>package neo

import (
	"github.com/ivpusic/neo/ebus"
	"github.com/jinzhu/gorm"
	"net/http"
)

///////////////////////////////////////////////////////////////////
// Context data representation and operations
///////////////////////////////////////////////////////////////////

// Map which will hold your Request contextual data.
type CtxData map[string]interface{}

// Get context data on key
func (r CtxData) Get(key string) interface{} {
	return r[key]
}

// Add new contextual data on key with provided value
func (r CtxData) Set(key string, value interface{}) {
	r[key] = value
}

// Delete contextual data on key
func (r CtxData) Del(key string) {
	delete(r, key)
}

///////////////////////////////////////////////////////////////////
// Context
///////////////////////////////////////////////////////////////////

// Representing context for this request.
type Ctx struct {
	ebus.EBus

	// Wrapped http.Request object.
	Req *Request

	// Object with utility methods for writing responses to http.ResponseWriter
	Res *Response

	// Context data map
	Data CtxData

	// gorm transaction (if you need it in your application).
	// If you use this Tx instance, you should start it somewhere before.
	// When exception happens, neo will automatically rollback transaction, if started.
	Tx *gorm.DB
}

// Will make default contextual data based on provided request and ResponseWriter
func makeCtx(req *http.Request, w http.ResponseWriter) *Ctx {
	request := makeRequest(req)
	response := makeResponse(req, w)
	// 404 - OK by default
	// this can be overided by middlewares and handler fns
	response.Status = 404

	ctx := &Ctx{
		Req:  request,
		Res:  response,
		Data: CtxData{},
	}
	ctx.InitEBus()

	return ctx
}
<|endoftext|>


<commit_before>package ctx

import (
	"context"
	"sync"
	"time"
)

// Binder is the interface that wraps the basic Bind method.
// Bind executes logic until the Doner completes.  Implementations of Bind must
// not return until the Doner has completed.
type Binder interface {
	Bind(Doner)
}

// BindFunc is an adapter to allow the use of ordinary functions as Binders.
type BindFunc func(Doner)

// Bind executes logic until the Doner completes.  It satisfies the Binder
// interface.
func (f BindFunc) Bind(d Doner) { f(d) }

// Doner can block until something is done
type Doner interface {
	Done() <-chan struct{}
}

// C is a basic implementation of Doner
type C <-chan struct{}

// Done returns a channel that receives when an action is complete
func (dc C) Done() <-chan struct{} { return dc }

// AsContext creates a context that fires when the Doner fires
func AsContext(d Doner) context.Context {
	c, cancel := context.WithCancel(context.Background())
	Defer(d, cancel)
	return c
}

// After time time has elapsed, the Doner fires
func After(d time.Duration) C {
	ch := make(chan struct{})
	go func() {
		<-time.After(d)
		close(ch)
	}()
	return ch
}

// WithCancel returns a new Doner that can be cancelled via the associated
// function
func WithCancel(d Doner) (C, func()) {
	var closer sync.Once
	cq := make(chan struct{})
	cancel := func() { closer.Do(func() { close(cq) }) }
	return Link(d, C(cq)), cancel
}

// Tick returns a <-chan whose range ends when the underlying context cancels
func Tick(d Doner) <-chan struct{} {
	cq := make(chan struct{})
	go func() {
		for {
			select {
			case <-d.Done():
				close(cq)
				return
			case cq <- struct{}{}:
			}
		}
	}()
	return cq
}

// Defer guarantees that a function will be called after a context has cancelled
func Defer(d Doner, cb func()) {
	go func() {
		<-d.Done()
		cb()
	}()
}

// Link returns a channel that fires if ANY of the constituent Doners have fired
func Link(doners ...Doner) C {
	c := make(chan struct{})
	cancel := func() { close(c) }

	var once sync.Once
	for _, d := range doners {
		Defer(d, func() { once.Do(cancel) })
	}

	return c
}

// Join returns a channel that receives when all constituent Doners have fired
func Join(doners ...Doner) C {
	var wg sync.WaitGroup
	wg.Add(len(doners))
	for _, d := range doners {
		Defer(d, wg.Done)
	}

	cq := make(chan struct{})
	go func() {
		wg.Wait()
		close(cq)
	}()
	return cq
}

// FTick calls a function in a loop until the Doner has fired
func FTick(d Doner, f func()) {
	for _ = range Tick(d) {
		f()
	}
}

// FTickInterval calls a function repeatedly at a given internval, until the Doner
// has fired.
func FTickInterval(d Doner, t time.Duration, f func()) {
	timer := time.AfterFunc(t, f)
	<-d.Done()
	timer.Stop()
}

// FDone returns a doner that fires when the function returns or panics
func FDone(f func()) C {
	ch := make(chan struct{})
	go func() {
		defer close(ch)
		f()
	}()
	return ch
}
<commit_msg>Remove FTickInterval<commit_after>package ctx

import (
	"context"
	"sync"
	"time"
)

// Binder is the interface that wraps the basic Bind method.
// Bind executes logic until the Doner completes.  Implementations of Bind must
// not return until the Doner has completed.
type Binder interface {
	Bind(Doner)
}

// BindFunc is an adapter to allow the use of ordinary functions as Binders.
type BindFunc func(Doner)

// Bind executes logic until the Doner completes.  It satisfies the Binder
// interface.
func (f BindFunc) Bind(d Doner) { f(d) }

// Doner can block until something is done
type Doner interface {
	Done() <-chan struct{}
}

// C is a basic implementation of Doner
type C <-chan struct{}

// Done returns a channel that receives when an action is complete
func (dc C) Done() <-chan struct{} { return dc }

// AsContext creates a context that fires when the Doner fires
func AsContext(d Doner) context.Context {
	c, cancel := context.WithCancel(context.Background())
	Defer(d, cancel)
	return c
}

// After time time has elapsed, the Doner fires
func After(d time.Duration) C {
	ch := make(chan struct{})
	go func() {
		<-time.After(d)
		close(ch)
	}()
	return ch
}

// WithCancel returns a new Doner that can be cancelled via the associated
// function
func WithCancel(d Doner) (C, func()) {
	var closer sync.Once
	cq := make(chan struct{})
	cancel := func() { closer.Do(func() { close(cq) }) }
	return Link(d, C(cq)), cancel
}

// Tick returns a <-chan whose range ends when the underlying context cancels
func Tick(d Doner) <-chan struct{} {
	cq := make(chan struct{})
	go func() {
		for {
			select {
			case <-d.Done():
				close(cq)
				return
			case cq <- struct{}{}:
			}
		}
	}()
	return cq
}

// Defer guarantees that a function will be called after a context has cancelled
func Defer(d Doner, cb func()) {
	go func() {
		<-d.Done()
		cb()
	}()
}

// Link returns a channel that fires if ANY of the constituent Doners have fired
func Link(doners ...Doner) C {
	c := make(chan struct{})
	cancel := func() { close(c) }

	var once sync.Once
	for _, d := range doners {
		Defer(d, func() { once.Do(cancel) })
	}

	return c
}

// Join returns a channel that receives when all constituent Doners have fired
func Join(doners ...Doner) C {
	var wg sync.WaitGroup
	wg.Add(len(doners))
	for _, d := range doners {
		Defer(d, wg.Done)
	}

	cq := make(chan struct{})
	go func() {
		wg.Wait()
		close(cq)
	}()
	return cq
}

// FTick calls a function in a loop until the Doner has fired
func FTick(d Doner, f func()) {
	for _ = range Tick(d) {
		f()
	}
}

// FTickInterval calls a function repeatedly at a given internval, until the Doner
// has fired.  Note that FTickInterval ignores the time spent executing a function,
// and instead guarantees an interval of `t` between of return of the previous
// function call and the invocation of the next function call.
func FTickInterval(d Doner, t time.Duration, f func()) {
	for {
		select {
		case <-d.Done():
			return
		case <-time.After(t):
			panic("NOT IMPLEMENTED"s)
		}
	}
}

// FDone returns a doner that fires when the function returns or panics
func FDone(f func()) C {
	ch := make(chan struct{})
	go func() {
		defer close(ch)
		f()
	}()
	return ch
}
<|endoftext|>


<commit_before>package appcast

import (
	"testing"

	"github.com/hashicorp/go-version"
	"github.com/stretchr/testify/assert"
)

func TestNewRelease(t *testing.T) {
	// test (successful)
	r, err := NewRelease("1.0.0", "1000")
	assert.Nil(t, err)
	assert.IsType(t, Release{}, *r)
	assert.Equal(t, "1.0.0", r.Version.String())
	assert.Equal(t, "1000", r.Build)

	// test (error)
	r, err = NewRelease("invalid", "1000")
	assert.Error(t, err)
	assert.Nil(t, r)
}

func TestSetVersion(t *testing.T) {
	// test (successful)
	r := new(Release)
	assert.Nil(t, r.Version)
	err := r.SetVersion("1.0.0")
	assert.Nil(t, err)
	assert.IsType(t, version.Version{}, *r.Version)
	assert.Equal(t, "1.0.0", r.Version.String())

	// test (error)
	r = new(Release)
	assert.Nil(t, r.Version)
	err = r.SetVersion("invalid")
	assert.Error(t, err)
	assert.Nil(t, r.Version)
}

func TestParsePublishedDateTime(t *testing.T) {
	testCases := map[string]string{
		"Sun, 14 May 2017 05:04:01 -0700": "2017-05-14 12:04:01 +0000 UTC", // RFC1123Z
		"Thu, 25 May 2017 19:26:48 UTC":   "2017-05-25 19:26:48 +0000 UTC", // RFC1123
		"2016-05-13T12:00:00+02:00":       "2016-05-13 10:00:00 +0000 UTC", // RFC3339

		// custom
		"Thu, 25 May 2017 19:26:48 UT":              "2017-05-25 19:26:48 +0000 UTC",
		"Monday, January 12th, 2010 23:30:00 GMT-5": "2010-01-12 23:30:00 +0000 UTC",
	}

	for dateTime, expected := range testCases {
		r := new(Release)
		r.ParsePublishedDateTime(dateTime)
		assert.Equal(t, expected, r.PublishedDateTime.String())
	}
}
<commit_msg>Add missing tests for convenience functions in release<commit_after>package appcast

import (
	"testing"

	"github.com/hashicorp/go-version"
	"github.com/stretchr/testify/assert"
)

func TestNewRelease(t *testing.T) {
	// test (successful)
	r, err := NewRelease("1.0.0", "1000")
	assert.Nil(t, err)
	assert.IsType(t, Release{}, *r)
	assert.Equal(t, "1.0.0", r.Version.String())
	assert.Equal(t, "1000", r.Build)

	// test (error)
	r, err = NewRelease("invalid", "1000")
	assert.Error(t, err)
	assert.Nil(t, r)
}

func TestSetVersion(t *testing.T) {
	// test (successful)
	r := new(Release)
	assert.Nil(t, r.Version)
	err := r.SetVersion("1.0.0")
	assert.Nil(t, err)
	assert.IsType(t, version.Version{}, *r.Version)
	assert.Equal(t, "1.0.0", r.Version.String())

	// test (error)
	r = new(Release)
	assert.Nil(t, r.Version)
	err = r.SetVersion("invalid")
	assert.Error(t, err)
	assert.Nil(t, r.Version)
}

func TestParsePublishedDateTime(t *testing.T) {
	testCases := map[string]string{
		"Sun, 14 May 2017 05:04:01 -0700": "2017-05-14 12:04:01 +0000 UTC", // RFC1123Z
		"Thu, 25 May 2017 19:26:48 UTC":   "2017-05-25 19:26:48 +0000 UTC", // RFC1123
		"2016-05-13T12:00:00+02:00":       "2016-05-13 10:00:00 +0000 UTC", // RFC3339

		// custom
		"Thu, 25 May 2017 19:26:48 UT":              "2017-05-25 19:26:48 +0000 UTC",
		"Monday, January 12th, 2010 23:30:00 GMT-5": "2010-01-12 23:30:00 +0000 UTC",
	}

	for dateTime, expected := range testCases {
		r := new(Release)
		r.ParsePublishedDateTime(dateTime)
		assert.Equal(t, expected, r.PublishedDateTime.String())
	}
}

func TestGetVersionString(t *testing.T) {
	// preparations
	r := new(Release)
	r.SetVersion("1.0.0")

	// test
	assert.Equal(t, "1.0.0", r.GetVersionString())
}

func TestGetBuildString(t *testing.T) {
	// preparations
	r := new(Release)
	r.Build = "1000"

	// test
	assert.Equal(t, "1000", r.GetBuildString())
}

func TestGetVersionOrBuildString(t *testing.T) {
	// preparations
	r := new(Release)
	r.Build = "1000"

	// test (only build is set)
	assert.Equal(t, "1000", r.GetVersionOrBuildString())

	// test (version is set also)
	r.SetVersion("1.0.0")
	assert.Equal(t, "1.0.0", r.GetVersionOrBuildString())
}

func TestGetDownloads(t *testing.T) {
	// preparations
	r := new(Release)

	d := NewDownload("https://example.com/one.dmg", "application/octet-stream", 100000)
	r.AddDownload(*d)

	d = NewDownload("https://example.com/two.dmg", "application/octet-stream", 100000)
	r.AddDownload(*d)

	// test
	assert.Equal(t, 2, len(r.GetDownloads()))
}
<|endoftext|>


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

package server

import (
	"io"

	"github.com/juju/errors"
	"github.com/juju/loggo"
	"github.com/juju/names"
	charmresource "gopkg.in/juju/charm.v6-unstable/resource"

	"github.com/juju/juju/apiserver/common"
	"github.com/juju/juju/apiserver/params"
	"github.com/juju/juju/resource"
	"github.com/juju/juju/resource/api"
)

var logger = loggo.GetLogger("juju.resource.api.server")

const (
	// Version is the version number of the current Facade.
	Version = 1
)

// DataStore is the functionality of Juju's state needed for the resources API.
type DataStore interface {
	resourceInfoStore
	UploadDataStore
}

// Facade is the public API facade for resources.
type Facade struct {
	// store is the data source for the facade.
	store resourceInfoStore
}

// NewFacade returns a new resoures facade for the given Juju state.
func NewFacade(store DataStore) *Facade {
	return &Facade{
		store: store,
	}
}

// resourceInfoStore is the portion of Juju's "state" needed
// for the resources facade.
type resourceInfoStore interface {
	// ListResources returns the resources for the given service.
	ListResources(service string) (resource.ServiceResources, error)

	// AddPendingResource adds the resource to the data store in a
	// "pending" state. It will stay pending (and unavailable) until
	// it is resolved. The returned ID is used to identify the pending
	// resources when resolving it.
	AddPendingResource(serviceID, userID string, chRes charmresource.Resource, r io.Reader) (string, error)

	// Units returns the tags for all units in the given service.
	Units(serviceID string) (units []names.UnitTag, err error)
}

// ListResources returns the list of resources for the given service.
func (f Facade) ListResources(args api.ListResourcesArgs) (api.ResourcesResults, error) {
	var r api.ResourcesResults
	r.Results = make([]api.ResourcesResult, len(args.Entities))

	for i, e := range args.Entities {
		logger.Tracef("Listing resources for %q", e.Tag)
		tag, apierr := parseServiceTag(e.Tag)
		if apierr != nil {
			r.Results[i] = api.ResourcesResult{
				ErrorResult: params.ErrorResult{
					Error: apierr,
				},
			}
			continue
		}

		svcRes, err := f.store.ListResources(tag.Id())
		if err != nil {
			r.Results[i] = errorResult(err)
			continue
		}

		units, err := f.store.Units(tag.Id())
		if err != nil {
			r.Results[i] = errorResult(err)
			continue
		}

		r.Results[i] = ServiceResources2APIResult(tag, svcRes, units)
	}
	return r, nil
}

// AddPendingResources adds the provided resources (info) to the Juju
// model in a pending state, meaning they are not available until
// resolved.
func (f Facade) AddPendingResources(args api.AddPendingResourcesArgs) (api.AddPendingResourcesResult, error) {
	var result api.AddPendingResourcesResult

	tag, apiErr := parseServiceTag(args.Tag)
	if apiErr != nil {
		result.Error = apiErr
		return result, nil
	}
	serviceID := tag.Id()

	var ids []string
	for _, apiRes := range args.Resources {
		pendingID, err := f.addPendingResource(serviceID, apiRes)
		if err != nil {
			result.Error = common.ServerError(err)
			// We don't bother aggregating errors since a partial
			// completion is disruptive and a retry of this endpoint
			// is not expensive.
			return result, nil
		}
		ids = append(ids, pendingID)
	}
	result.PendingIDs = ids
	return result, nil
}

func (f Facade) addPendingResource(serviceID string, apiRes api.CharmResource) (pendingID string, err error) {
	chRes, err := api.API2CharmResource(apiRes)
	if err != nil {
		return "", errors.Annotatef(err, "bad resource info for %q", chRes.Name)
	}

	userID := ""
	var reader io.Reader
	pendingID, err = f.store.AddPendingResource(serviceID, userID, chRes, reader)
	if err != nil {
		return "", errors.Annotatef(err, "while adding pending resource info for %q", chRes.Name)
	}
	return pendingID, nil
}

func parseServiceTag(tagStr string) (names.ServiceTag, *params.Error) { // note the concrete error type
	serviceTag, err := names.ParseServiceTag(tagStr)
	if err != nil {
		return serviceTag, &params.Error{
			Message: err.Error(),
			Code:    params.CodeBadRequest,
		}
	}
	return serviceTag, nil
}

func errorResult(err error) api.ResourcesResult {
	return api.ResourcesResult{
		ErrorResult: params.ErrorResult{
			Error: common.ServerError(err),
		},
	}
}
<commit_msg>compile fix<commit_after>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package server

import (
	"io"

	"github.com/juju/errors"
	"github.com/juju/loggo"
	"github.com/juju/names"
	charmresource "gopkg.in/juju/charm.v6-unstable/resource"

	"github.com/juju/juju/apiserver/common"
	"github.com/juju/juju/apiserver/params"
	"github.com/juju/juju/resource"
	"github.com/juju/juju/resource/api"
)

var logger = loggo.GetLogger("juju.resource.api.server")

const (
	// Version is the version number of the current Facade.
	Version = 1
)

// DataStore is the functionality of Juju's state needed for the resources API.
type DataStore interface {
	resourceInfoStore
	UploadDataStore
}

// Facade is the public API facade for resources.
type Facade struct {
	// store is the data source for the facade.
	store resourceInfoStore
}

// NewFacade returns a new resoures facade for the given Juju state.
func NewFacade(store DataStore) *Facade {
	return &Facade{
		store: store,
	}
}

// resourceInfoStore is the portion of Juju's "state" needed
// for the resources facade.
type resourceInfoStore interface {
	// ListResources returns the resources for the given service.
	ListResources(service string) (resource.ServiceResources, error)

	// AddPendingResource adds the resource to the data store in a
	// "pending" state. It will stay pending (and unavailable) until
	// it is resolved. The returned ID is used to identify the pending
	// resources when resolving it.
	AddPendingResource(serviceID, userID string, chRes charmresource.Resource, r io.Reader) (string, error)

	// Units returns the tags for all units in the given service.
	Units(serviceID string) (units []names.UnitTag, err error)
}

// ListResources returns the list of resources for the given service.
func (f Facade) ListResources(args api.ListResourcesArgs) (api.ResourcesResults, error) {
	var r api.ResourcesResults
	r.Results = make([]api.ResourcesResult, len(args.Entities))

	for i, e := range args.Entities {
		logger.Tracef("Listing resources for %q", e.Tag)
		tag, apierr := parseServiceTag(e.Tag)
		if apierr != nil {
			r.Results[i] = api.ResourcesResult{
				ErrorResult: params.ErrorResult{
					Error: apierr,
				},
			}
			continue
		}

		svcRes, err := f.store.ListResources(tag.Id())
		if err != nil {
			r.Results[i] = errorResult(err)
			continue
		}

		units, err := f.store.Units(tag.Id())
		if err != nil {
			r.Results[i] = errorResult(err)
			continue
		}

		r.Results[i] = api.ServiceResources2APIResult(svcRes, units)
	}
	return r, nil
}

// AddPendingResources adds the provided resources (info) to the Juju
// model in a pending state, meaning they are not available until
// resolved.
func (f Facade) AddPendingResources(args api.AddPendingResourcesArgs) (api.AddPendingResourcesResult, error) {
	var result api.AddPendingResourcesResult

	tag, apiErr := parseServiceTag(args.Tag)
	if apiErr != nil {
		result.Error = apiErr
		return result, nil
	}
	serviceID := tag.Id()

	var ids []string
	for _, apiRes := range args.Resources {
		pendingID, err := f.addPendingResource(serviceID, apiRes)
		if err != nil {
			result.Error = common.ServerError(err)
			// We don't bother aggregating errors since a partial
			// completion is disruptive and a retry of this endpoint
			// is not expensive.
			return result, nil
		}
		ids = append(ids, pendingID)
	}
	result.PendingIDs = ids
	return result, nil
}

func (f Facade) addPendingResource(serviceID string, apiRes api.CharmResource) (pendingID string, err error) {
	chRes, err := api.API2CharmResource(apiRes)
	if err != nil {
		return "", errors.Annotatef(err, "bad resource info for %q", chRes.Name)
	}

	userID := ""
	var reader io.Reader
	pendingID, err = f.store.AddPendingResource(serviceID, userID, chRes, reader)
	if err != nil {
		return "", errors.Annotatef(err, "while adding pending resource info for %q", chRes.Name)
	}
	return pendingID, nil
}

func parseServiceTag(tagStr string) (names.ServiceTag, *params.Error) { // note the concrete error type
	serviceTag, err := names.ParseServiceTag(tagStr)
	if err != nil {
		return serviceTag, &params.Error{
			Message: err.Error(),
			Code:    params.CodeBadRequest,
		}
	}
	return serviceTag, nil
}

func errorResult(err error) api.ResourcesResult {
	return api.ResourcesResult{
		ErrorResult: params.ErrorResult{
			Error: common.ServerError(err),
		},
	}
}
<|endoftext|>


<commit_before>// Copyright 2014-2015 The DevMine authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package git defines a Git repository type that implements the repo.Repo
// interface.
package git

import (
	"net/http"
	"net/url"
	"os"
	"os/exec"
	"path/filepath"
	"strings"

	"github.com/golang/glog"
)

// GitRepo implements the Repo interface.
type GitRepo struct {
	absPath string
	gitBin  string
	url     string
}

// New creates a new GitRepo.
func New(absPath string, url string) (*GitRepo, error) {
	path, err := exec.LookPath("git")
	if err != nil {
		glog.Error(err)
		return nil, err
	}

	return &GitRepo{absPath: absPath, gitBin: path, url: url}, nil
}

// AbsPath implements the AbsPath() method of the Repo interface.
func (gr GitRepo) AbsPath() string {
	return gr.absPath
}

// URL implements the URL() method of the Repo interface.
func (gr GitRepo) URL() string {
	return gr.url
}

// Clone implements the Clone() method of the Repo interface.
func (gr GitRepo) Clone() error {
	if ok, err := gr.isAvailable(); err != nil {
		return err
	} else if !ok {
		glog.Info(gr.url, " not available")
		return nil
	}

	out, err := exec.Command(gr.gitBin, "clone", "--quiet", gr.url, gr.absPath).CombinedOutput()
	if err != nil {
		glog.Errorf("%v\n%s", err, string(out))
		return err
	}

	return nil
}

// Update implements the Update() method of the Repo interface.
func (gr GitRepo) Update() error {
	if ok, err := gr.isAvailable(); err != nil {
		return err
	} else if !ok {
		glog.Info(gr.url, " not available")
		return nil
	}

	err := os.Chdir(gr.absPath)
	if err != nil {
		glog.Error(err)
		return err
	}

	out, err := exec.Command(gr.gitBin, "pull", "--quiet").CombinedOutput()
	if err != nil {
		glog.Errorf("%v\n%s", err, string(out))
		return err
	}

	return nil
}

// isAvailable queries the git repository in order to determine whether a the
// repository exists and is publicly available.
func (gr GitRepo) isAvailable() (bool, error) {
	u, err := url.Parse(gr.url)
	if err != nil {
		return false, err
	}

	switch u.Scheme {
	case "http", "https":
		queryURL := cleanURL(gr.url)
		if !hasGitExt(u.Path) {
			queryURL += ".git"
		}

		resp, err := http.Get(queryURL + "/git-upload-pack")
		if err != nil {
			glog.Warning(err)
			return false, nil
		}
		defer resp.Body.Close()

		if resp.StatusCode != http.StatusOK {
			glog.Warningf("invalid HTTP status: expected %d, received %d",
				http.StatusOK, resp.StatusCode)
			return false, nil
		}
	default:
		glog.Warningf("protocol %s not supported", u.Scheme)
		return false, nil
	}

	return true, nil
}

// hasGitExt returns true if the path ends with a ".git" extension,
// false otherwise.
func hasGitExt(path string) bool {
	return filepath.Ext(path) == ".git"
}

// cleanURL removes the trailing slashes of an URL, if any.
func cleanURL(url string) string {
	return strings.TrimSuffix(url, "/")
}
<commit_msg>repo/git: use libgit2 to handle git repositories (close #2)<commit_after>// Copyright 2014-2015 The DevMine authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package git defines a Git repository type that implements the repo.Repo
// interface.
package git

import (
	"net/http"
	"net/url"
	"path/filepath"
	"strings"

	"github.com/golang/glog"
	g2g "github.com/libgit2/git2go"
)

// GitRepo implements the Repo interface.
type GitRepo struct {
	absPath string
	r       *g2g.Repository
	url     string
}

// New creates a new GitRepo.
func New(absPath string, url string) (*GitRepo, error) {
	// attempt opening the repository as it may already exist
	// ignore if it fails since it will be created at first call to Clone()
	r, _ := g2g.OpenRepository(absPath)

	return &GitRepo{absPath: absPath, url: url, r: r}, nil
}

// AbsPath implements the AbsPath() method of the Repo interface.
func (gr GitRepo) AbsPath() string {
	return gr.absPath
}

// URL implements the URL() method of the Repo interface.
func (gr GitRepo) URL() string {
	return gr.url
}

// Clone implements the Clone() method of the Repo interface.
func (gr GitRepo) Clone() error {
	if ok, err := gr.isAvailable(); err != nil {
		return err
	} else if !ok {
		glog.Info(gr.url, " not available")
		return nil
	}

	var err error
	gr.r, err = g2g.Clone(gr.url, gr.absPath, &g2g.CloneOptions{})
	if err != nil {
		return err
	}

	return nil
}

// Update implements the Update() method of the Repo interface.
// It fetches changes from remote and performs a fast-forward on the local
// branch so as to match the remote branch.
func (gr GitRepo) Update() error {
	if ok, err := gr.isAvailable(); err != nil {
		return err
	} else if !ok {
		glog.Info(gr.url, " not available")
		return nil
	}

	origin, err := gr.r.LookupRemote("origin")
	if err != nil {
		return err
	}

	err = origin.Fetch([]string{}, nil, "")
	if err != nil {
		return err
	}

	ref, err := gr.r.Head()
	if err != nil {
		return err
	}

	if !ref.IsBranch() {
		// TODO delete and reclone
	}

	remoteRef, err := ref.Branch().Upstream()
	if err != nil {
		return err
	}
	_, err = ref.SetTarget(remoteRef.Target(), nil, "pull: Fast-forward")
	if err != nil {
		return err
	}

	var checkoutOpts g2g.CheckoutOpts
	checkoutOpts.Strategy = g2g.CheckoutForce

	if err = gr.r.CheckoutHead(&checkoutOpts); err != nil {
		// TODO delete and reclone
	}

	return nil
}

// isAvailable queries the git repository in order to determine whether a the
// repository exists and is publicly available.
func (gr GitRepo) isAvailable() (bool, error) {
	u, err := url.Parse(gr.url)
	if err != nil {
		return false, err
	}

	switch u.Scheme {
	case "http", "https":
		queryURL := cleanURL(gr.url)
		if !hasGitExt(u.Path) {
			queryURL += ".git"
		}

		resp, err := http.Get(queryURL + "/git-upload-pack")
		if err != nil {
			glog.Warning(err)
			return false, nil
		}
		defer resp.Body.Close()

		if resp.StatusCode != http.StatusOK {
			glog.Warningf("invalid HTTP status: expected %d, received %d",
				http.StatusOK, resp.StatusCode)
			return false, nil
		}
	default:
		glog.Warningf("protocol %s not supported", u.Scheme)
		return false, nil
	}

	return true, nil
}

// hasGitExt returns true if the path ends with a ".git" extension,
// false otherwise.
func hasGitExt(path string) bool {
	return filepath.Ext(path) == ".git"
}

// cleanURL removes the trailing slashes of an URL, if any.
func cleanURL(url string) string {
	return strings.TrimSuffix(url, "/")
}
<|endoftext|>


<commit_before>package report

import (
	"fmt"
	"sort"
	"strings"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/weaveworks/common/mtime"
)

const (
	TableEntryKeySeparator = "___"
	TruncationCountPrefix  = "table_truncation_count_"
	MulticolumnTableType   = "multicolumn-table"
	PropertyListType       = "property-list"
)

// AddPrefixMulticolumnTable appends arbitrary rows to the Node, returning a new node.
func (node Node) AddPrefixMulticolumnTable(prefix string, rows []Row) Node {
	now := mtime.Now()
	for _, row := range rows {
		// Add all the row values as separate entries
		for columnID, value := range row.Entries {
			key := strings.Join([]string{row.ID, columnID}, TableEntryKeySeparator)
			node = node.WithLatest(prefix+key, now, value)
		}
	}
	return node
}

// AddPrefixPropertyList appends arbitrary key-value pairs to the Node, returning a new node.
func (node Node) AddPrefixPropertyList(prefix string, propertyList map[string]string) Node {
	now := mtime.Now()
	for label, value := range propertyList {
		node = node.WithLatest(prefix+label, now, value)
	}
	return node
}

// WithoutPrefix returns the string with trimmed prefix and a
// boolean information of whether that prefix was really there.
// NOTE: Consider moving this function to utilities.
func WithoutPrefix(s string, prefix string) (string, bool) {
	return strings.TrimPrefix(s, prefix), len(prefix) > 0 && strings.HasPrefix(s, prefix)
}

// ExtractMulticolumnTable returns the rows to build a multicolumn table from this node
func (node Node) ExtractMulticolumnTable(template TableTemplate) (rows []Row) {
	rowsMapByID := map[string]Row{}

	// Itearate through the whole of our map to extract all the values with the key
	// with the given prefix. Since multicolumn tables don't support fixed rows (yet),
	// all the table values will be stored under the table prefix.
	// NOTE: It would be nice to optimize this part by only iterating through the keys
	// with the given prefix. If it is possible to traverse the keys in the Latest map
	// in a sorted order, then having LowerBoundEntry(key) and UpperBoundEntry(key)
	// methods should be enough to implement ForEachWithPrefix(prefix) straightforwardly.
	node.Latest.ForEach(func(key string, _ time.Time, value string) {
		if keyWithoutPrefix, ok := WithoutPrefix(key, template.Prefix); ok {
			ids := strings.Split(keyWithoutPrefix, TableEntryKeySeparator)
			rowID, columnID := ids[0], ids[1]
			// If the row with the given ID doesn't yet exist, we create an empty one.
			if _, ok := rowsMapByID[rowID]; !ok {
				rowsMapByID[rowID] = Row{
					ID:      rowID,
					Entries: map[string]string{},
				}
			}
			// At this point, the row with that ID always exists, so we just update the value.
			rowsMapByID[rowID].Entries[columnID] = value
		}
	})

	// Gather a list of rows.
	rows = make([]Row, 0, len(rowsMapByID))
	for _, row := range rowsMapByID {
		rows = append(rows, row)
	}

	// Return the rows sorted by ID.
	sort.Sort(rowsByID(rows))
	return rows
}

// ExtractPropertyList returns the rows to build a property list from this node
func (node Node) ExtractPropertyList(template TableTemplate) (rows []Row) {
	valuesMapByLabel := map[string]string{}

	// Itearate through the whole of our map to extract all the values with the key
	// with the given prefix as well as the keys corresponding to the fixed table rows.
	node.Latest.ForEach(func(key string, _ time.Time, value string) {
		if label, ok := template.FixedRows[key]; ok {
			valuesMapByLabel[label] = value
		} else if label, ok := WithoutPrefix(key, template.Prefix); ok {
			valuesMapByLabel[label] = value
		}
	})

	// Gather a label-value formatted list of rows.
	rows = make([]Row, 0, len(valuesMapByLabel))
	for label, value := range valuesMapByLabel {
		rows = append(rows, Row{
			ID: "label_" + label,
			Entries: map[string]string{
				"label": label,
				"value": value,
			},
		})
	}

	// Return the rows sorted by ID.
	sort.Sort(rowsByID(rows))
	return rows
}

// ExtractTable returns the rows to build either a property list or a
// generic table from this node. It also returns the number of rows,
// if any, that were truncated. The probes used to limit the number of
// labels, env vars and Weave Net connections they report, but this
// logic has since been removed. So the code here dealing with
// truncation is only retained in order to handle legacy reports.
func (node Node) ExtractTable(template TableTemplate) (rows []Row, truncationCount int) {
	switch template.Type {
	case MulticolumnTableType:
		rows = node.ExtractMulticolumnTable(template)
	default: // By default assume it's a property list (for backward compatibility).
		rows = node.ExtractPropertyList(template)
	}

	truncationCount = 0
	if str, ok := node.Latest.Lookup(TruncationCountPrefix + template.Prefix); ok {
		if n, err := fmt.Sscanf(str, "%d", &truncationCount); n != 1 || err != nil {
			log.Warn("Unexpected truncation count format %q", str)
		}
	}

	return rows, truncationCount
}

// Column is the type for multi-column tables in the UI.
type Column struct {
	ID       string `json:"id"`
	Label    string `json:"label"`
	DataType string `json:"dataType"`
}

// Row is the type that holds the table data for the UI. Entries map from column ID to cell value.
type Row struct {
	ID      string            `json:"id"`
	Entries map[string]string `json:"entries"`
}

type rowsByID []Row

func (t rowsByID) Len() int           { return len(t) }
func (t rowsByID) Swap(i, j int)      { t[i], t[j] = t[j], t[i] }
func (t rowsByID) Less(i, j int) bool { return t[i].ID < t[j].ID }

// Table is the type for a table in the UI.
type Table struct {
	ID              string   `json:"id"`
	Label           string   `json:"label"`
	Type            string   `json:"type"`
	Columns         []Column `json:"columns"`
	Rows            []Row    `json:"rows"`
	TruncationCount int      `json:"truncationCount,omitempty"`
}

type tablesByID []Table

func (t tablesByID) Len() int           { return len(t) }
func (t tablesByID) Swap(i, j int)      { t[i], t[j] = t[j], t[i] }
func (t tablesByID) Less(i, j int) bool { return t[i].ID < t[j].ID }

// TableTemplate describes how to render a table for the UI.
type TableTemplate struct {
	ID      string   `json:"id"`
	Label   string   `json:"label"`
	Prefix  string   `json:"prefix"`
	Type    string   `json:"type"`
	Columns []Column `json:"columns"`
	// FixedRows indicates what predetermined rows to render each entry is
	// indexed by the key to extract the row value is mapped to the row
	// label
	FixedRows map[string]string `json:"fixedRows"`
}

// Copy returns a value-copy of the TableTemplate
func (t TableTemplate) Copy() TableTemplate {
	fixedRowsCopy := make(map[string]string, len(t.FixedRows))
	for key, value := range t.FixedRows {
		fixedRowsCopy[key] = value
	}
	t.FixedRows = fixedRowsCopy
	return t
}

// Merge other into t, returning a fresh copy.  Does fieldwise max -
// whilst this isn't particularly meaningful, at least it idempotent,
// commutativite and associative.
func (t TableTemplate) Merge(other TableTemplate) TableTemplate {
	max := func(s1, s2 string) string {
		if s1 > s2 {
			return s1
		}
		return s2
	}

	// NOTE: Consider actually merging the columns and fixed rows.
	fixedRows := t.FixedRows
	if len(other.FixedRows) > len(fixedRows) {
		fixedRows = other.FixedRows
	}
	columns := t.Columns
	if len(other.Columns) > len(columns) {
		columns = other.Columns
	}

	// TODO: Refactor the merging logic, as mixing the types now might result in
	// invalid tables. Maybe we should return an error if the types are different?
	return TableTemplate{
		ID:        max(t.ID, other.ID),
		Label:     max(t.Label, other.Label),
		Prefix:    max(t.Prefix, other.Prefix),
		Type:      max(t.Type, other.Type),
		Columns:   columns,
		FixedRows: fixedRows,
	}
}

// TableTemplates is a mergeable set of TableTemplate
type TableTemplates map[string]TableTemplate

// Tables renders the TableTemplates for a given node.
func (t TableTemplates) Tables(node Node) []Table {
	if len(t) == 0 {
		return nil
	}
	result := make([]Table, 0, len(t))
	for _, template := range t {
		rows, truncationCount := node.ExtractTable(template)
		// Extract the type from the template; default to
		// property list for backwards-compatibility.
		tableType := template.Type
		if tableType == "" {
			tableType = PropertyListType
		}
		result = append(result, Table{
			ID:              template.ID,
			Label:           template.Label,
			Columns:         template.Columns,
			Type:            tableType,
			Rows:            rows,
			TruncationCount: truncationCount,
		})
	}
	sort.Sort(tablesByID(result))
	return result
}

// Copy returns a value copy of the TableTemplates
func (t TableTemplates) Copy() TableTemplates {
	if t == nil {
		return nil
	}
	result := TableTemplates{}
	for k, v := range t {
		result[k] = v.Copy()
	}
	return result
}

// Merge merges two sets of TableTemplates
func (t TableTemplates) Merge(other TableTemplates) TableTemplates {
	if t == nil && other == nil {
		return nil
	}
	if len(other) > len(t) {
		t, other = other, t
	}
	result := t.Copy()
	for k, v := range other {
		if existing, ok := result[k]; ok {
			result[k] = v.Merge(existing)
		} else {
			result[k] = v
		}
	}
	return result
}
<commit_msg>export less & comment more<commit_after>package report

import (
	"fmt"
	"sort"
	"strings"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/weaveworks/common/mtime"
)

// Table types
const (
	MulticolumnTableType = "multicolumn-table"
	PropertyListType     = "property-list"
)

const (
	tableEntryKeySeparator = "___"
	truncationCountPrefix  = "table_truncation_count_"
)

// AddPrefixMulticolumnTable appends arbitrary rows to the Node, returning a new node.
func (node Node) AddPrefixMulticolumnTable(prefix string, rows []Row) Node {
	now := mtime.Now()
	for _, row := range rows {
		// Add all the row values as separate entries
		for columnID, value := range row.Entries {
			key := strings.Join([]string{row.ID, columnID}, tableEntryKeySeparator)
			node = node.WithLatest(prefix+key, now, value)
		}
	}
	return node
}

// AddPrefixPropertyList appends arbitrary key-value pairs to the Node, returning a new node.
func (node Node) AddPrefixPropertyList(prefix string, propertyList map[string]string) Node {
	now := mtime.Now()
	for label, value := range propertyList {
		node = node.WithLatest(prefix+label, now, value)
	}
	return node
}

// WithoutPrefix returns the string with trimmed prefix and a
// boolean information of whether that prefix was really there.
// NOTE: Consider moving this function to utilities.
func WithoutPrefix(s string, prefix string) (string, bool) {
	return strings.TrimPrefix(s, prefix), len(prefix) > 0 && strings.HasPrefix(s, prefix)
}

// ExtractMulticolumnTable returns the rows to build a multicolumn table from this node
func (node Node) ExtractMulticolumnTable(template TableTemplate) (rows []Row) {
	rowsMapByID := map[string]Row{}

	// Itearate through the whole of our map to extract all the values with the key
	// with the given prefix. Since multicolumn tables don't support fixed rows (yet),
	// all the table values will be stored under the table prefix.
	// NOTE: It would be nice to optimize this part by only iterating through the keys
	// with the given prefix. If it is possible to traverse the keys in the Latest map
	// in a sorted order, then having LowerBoundEntry(key) and UpperBoundEntry(key)
	// methods should be enough to implement ForEachWithPrefix(prefix) straightforwardly.
	node.Latest.ForEach(func(key string, _ time.Time, value string) {
		if keyWithoutPrefix, ok := WithoutPrefix(key, template.Prefix); ok {
			ids := strings.Split(keyWithoutPrefix, tableEntryKeySeparator)
			rowID, columnID := ids[0], ids[1]
			// If the row with the given ID doesn't yet exist, we create an empty one.
			if _, ok := rowsMapByID[rowID]; !ok {
				rowsMapByID[rowID] = Row{
					ID:      rowID,
					Entries: map[string]string{},
				}
			}
			// At this point, the row with that ID always exists, so we just update the value.
			rowsMapByID[rowID].Entries[columnID] = value
		}
	})

	// Gather a list of rows.
	rows = make([]Row, 0, len(rowsMapByID))
	for _, row := range rowsMapByID {
		rows = append(rows, row)
	}

	// Return the rows sorted by ID.
	sort.Sort(rowsByID(rows))
	return rows
}

// ExtractPropertyList returns the rows to build a property list from this node
func (node Node) ExtractPropertyList(template TableTemplate) (rows []Row) {
	valuesMapByLabel := map[string]string{}

	// Itearate through the whole of our map to extract all the values with the key
	// with the given prefix as well as the keys corresponding to the fixed table rows.
	node.Latest.ForEach(func(key string, _ time.Time, value string) {
		if label, ok := template.FixedRows[key]; ok {
			valuesMapByLabel[label] = value
		} else if label, ok := WithoutPrefix(key, template.Prefix); ok {
			valuesMapByLabel[label] = value
		}
	})

	// Gather a label-value formatted list of rows.
	rows = make([]Row, 0, len(valuesMapByLabel))
	for label, value := range valuesMapByLabel {
		rows = append(rows, Row{
			ID: "label_" + label,
			Entries: map[string]string{
				"label": label,
				"value": value,
			},
		})
	}

	// Return the rows sorted by ID.
	sort.Sort(rowsByID(rows))
	return rows
}

// ExtractTable returns the rows to build either a property list or a
// generic table from this node. It also returns the number of rows,
// if any, that were truncated. The probes used to limit the number of
// labels, env vars and Weave Net connections they report, but this
// logic has since been removed. So the code here dealing with
// truncation is only retained in order to handle legacy reports.
func (node Node) ExtractTable(template TableTemplate) (rows []Row, truncationCount int) {
	switch template.Type {
	case MulticolumnTableType:
		rows = node.ExtractMulticolumnTable(template)
	default: // By default assume it's a property list (for backward compatibility).
		rows = node.ExtractPropertyList(template)
	}

	truncationCount = 0
	if str, ok := node.Latest.Lookup(truncationCountPrefix + template.Prefix); ok {
		if n, err := fmt.Sscanf(str, "%d", &truncationCount); n != 1 || err != nil {
			log.Warn("Unexpected truncation count format %q", str)
		}
	}

	return rows, truncationCount
}

// Column is the type for multi-column tables in the UI.
type Column struct {
	ID       string `json:"id"`
	Label    string `json:"label"`
	DataType string `json:"dataType"`
}

// Row is the type that holds the table data for the UI. Entries map from column ID to cell value.
type Row struct {
	ID      string            `json:"id"`
	Entries map[string]string `json:"entries"`
}

type rowsByID []Row

func (t rowsByID) Len() int           { return len(t) }
func (t rowsByID) Swap(i, j int)      { t[i], t[j] = t[j], t[i] }
func (t rowsByID) Less(i, j int) bool { return t[i].ID < t[j].ID }

// Table is the type for a table in the UI.
type Table struct {
	ID              string   `json:"id"`
	Label           string   `json:"label"`
	Type            string   `json:"type"`
	Columns         []Column `json:"columns"`
	Rows            []Row    `json:"rows"`
	TruncationCount int      `json:"truncationCount,omitempty"`
}

type tablesByID []Table

func (t tablesByID) Len() int           { return len(t) }
func (t tablesByID) Swap(i, j int)      { t[i], t[j] = t[j], t[i] }
func (t tablesByID) Less(i, j int) bool { return t[i].ID < t[j].ID }

// TableTemplate describes how to render a table for the UI.
type TableTemplate struct {
	ID      string   `json:"id"`
	Label   string   `json:"label"`
	Prefix  string   `json:"prefix"`
	Type    string   `json:"type"`
	Columns []Column `json:"columns"`
	// FixedRows indicates what predetermined rows to render each entry is
	// indexed by the key to extract the row value is mapped to the row
	// label
	FixedRows map[string]string `json:"fixedRows"`
}

// Copy returns a value-copy of the TableTemplate
func (t TableTemplate) Copy() TableTemplate {
	fixedRowsCopy := make(map[string]string, len(t.FixedRows))
	for key, value := range t.FixedRows {
		fixedRowsCopy[key] = value
	}
	t.FixedRows = fixedRowsCopy
	return t
}

// Merge other into t, returning a fresh copy.  Does fieldwise max -
// whilst this isn't particularly meaningful, at least it idempotent,
// commutativite and associative.
func (t TableTemplate) Merge(other TableTemplate) TableTemplate {
	max := func(s1, s2 string) string {
		if s1 > s2 {
			return s1
		}
		return s2
	}

	// NOTE: Consider actually merging the columns and fixed rows.
	fixedRows := t.FixedRows
	if len(other.FixedRows) > len(fixedRows) {
		fixedRows = other.FixedRows
	}
	columns := t.Columns
	if len(other.Columns) > len(columns) {
		columns = other.Columns
	}

	// TODO: Refactor the merging logic, as mixing the types now might result in
	// invalid tables. Maybe we should return an error if the types are different?
	return TableTemplate{
		ID:        max(t.ID, other.ID),
		Label:     max(t.Label, other.Label),
		Prefix:    max(t.Prefix, other.Prefix),
		Type:      max(t.Type, other.Type),
		Columns:   columns,
		FixedRows: fixedRows,
	}
}

// TableTemplates is a mergeable set of TableTemplate
type TableTemplates map[string]TableTemplate

// Tables renders the TableTemplates for a given node.
func (t TableTemplates) Tables(node Node) []Table {
	if len(t) == 0 {
		return nil
	}
	result := make([]Table, 0, len(t))
	for _, template := range t {
		rows, truncationCount := node.ExtractTable(template)
		// Extract the type from the template; default to
		// property list for backwards-compatibility.
		tableType := template.Type
		if tableType == "" {
			tableType = PropertyListType
		}
		result = append(result, Table{
			ID:              template.ID,
			Label:           template.Label,
			Columns:         template.Columns,
			Type:            tableType,
			Rows:            rows,
			TruncationCount: truncationCount,
		})
	}
	sort.Sort(tablesByID(result))
	return result
}

// Copy returns a value copy of the TableTemplates
func (t TableTemplates) Copy() TableTemplates {
	if t == nil {
		return nil
	}
	result := TableTemplates{}
	for k, v := range t {
		result[k] = v.Copy()
	}
	return result
}

// Merge merges two sets of TableTemplates
func (t TableTemplates) Merge(other TableTemplates) TableTemplates {
	if t == nil && other == nil {
		return nil
	}
	if len(other) > len(t) {
		t, other = other, t
	}
	result := t.Copy()
	for k, v := range other {
		if existing, ok := result[k]; ok {
			result[k] = v.Merge(existing)
		} else {
			result[k] = v
		}
	}
	return result
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"sync/atomic"
)

type Message struct {
	Tag       string
	Timestamp int64
	Data      map[string]interface{}
}

type PipelinePack struct {
	MsgBytes    []byte
	Msg         Message
	RecycleChan chan *PipelinePack
	RefCount    int32
}

func NewPipelinePack(recycleChan chan *PipelinePack) (pack *PipelinePack) {
	msgBytes := make([]byte, 100)
	data := make(map[string]interface{})
	msg := Message{Data: data}
	return &PipelinePack{
		MsgBytes:    msgBytes,
		Msg:         msg,
		RecycleChan: recycleChan,
		RefCount:    1,
	}
}

func (this *PipelinePack) Zero() {
	this.MsgBytes = this.MsgBytes[:cap(this.MsgBytes)]
	this.RefCount = 1
}

func (this *PipelinePack) Recycle() {
	cnt := atomic.AddInt32(&this.RefCount, -1)
	if cnt == 0 {
		this.Zero()
		this.RecycleChan <- this
	}
}

type PipelineConfig struct {
	Gc            *GlobalConfig
	InputRunners  []interface{}
	OutputRunners []interface{}
	router        Router
}

func NewPipeLineConfig(gc *GlobalConfig) *PipelineConfig {
	config := new(PipelineConfig)
	config.router.Init()
	config.Gc = gc

	return config
}

func (this *PipelineConfig) LoadConfig(path string) error {
	configure, _ := ParseConfig(nil, path)
	for _, v := range configure.Root.Elems {
		if v.Name == "source" {
			this.InputRunners = append(this.InputRunners, v.Attrs)
		} else if v.Name == "match" {
			v.Attrs["tag"] = v.Args
			this.OutputRunners = append(this.OutputRunners, v.Attrs)
		}
	}

	return nil
}

func Run(config *PipelineConfig) {
	log.Println("Starting gofluent...")

	rChan := make(chan *PipelinePack, config.Gc.PoolSize)
	config.router.AddInChan(rChan)

	for _, input_config := range config.InputRunners {
		cf := input_config.(map[string]string)

		InputRecycleChan := make(chan *PipelinePack, config.Gc.PoolSize)
		for i := 0; i < config.Gc.PoolSize; i++ {
			iPack := NewPipelinePack(InputRecycleChan)
			InputRecycleChan <- iPack
		}
		iRunner := NewInputRunner(InputRecycleChan, rChan)

		go iRunner.Start(cf)
	}

	for _, output_config := range config.OutputRunners {
		cf := output_config.(map[string]string)

		inChan := make(chan *PipelinePack, config.Gc.PoolSize)
		oRunner := NewOutputRunner(inChan)
		config.router.AddOutChan(cf["tag"], oRunner.InChan())

		go oRunner.Start(cf)
	}

	config.router.Loop()
}
<commit_msg>PipelinePack Zero map bug<commit_after>package main

import (
	"log"
	"sync/atomic"
)

type Message struct {
	Tag       string
	Timestamp int64
	Data      map[string]interface{}
}

type PipelinePack struct {
	MsgBytes    []byte
	Msg         Message
	RecycleChan chan *PipelinePack
	RefCount    int32
}

func NewPipelinePack(recycleChan chan *PipelinePack) (pack *PipelinePack) {
	msgBytes := make([]byte, 100)
	data := make(map[string]interface{})
	msg := Message{Data: data}
	return &PipelinePack{
		MsgBytes:    msgBytes,
		Msg:         msg,
		RecycleChan: recycleChan,
		RefCount:    1,
	}
}

func (this *PipelinePack) Zero() {
	this.MsgBytes = this.MsgBytes[:cap(this.MsgBytes)]
	this.Msg.Data = make(map[string]interface{})
	this.RefCount = 1
}

func (this *PipelinePack) Recycle() {
	cnt := atomic.AddInt32(&this.RefCount, -1)
	if cnt == 0 {
		this.Zero()
		this.RecycleChan <- this
	}
}

type PipelineConfig struct {
	Gc            *GlobalConfig
	InputRunners  []interface{}
	OutputRunners []interface{}
	router        Router
}

func NewPipeLineConfig(gc *GlobalConfig) *PipelineConfig {
	config := new(PipelineConfig)
	config.router.Init()
	config.Gc = gc

	return config
}

func (this *PipelineConfig) LoadConfig(path string) error {
	configure, _ := ParseConfig(nil, path)
	for _, v := range configure.Root.Elems {
		if v.Name == "source" {
			this.InputRunners = append(this.InputRunners, v.Attrs)
		} else if v.Name == "match" {
			v.Attrs["tag"] = v.Args
			this.OutputRunners = append(this.OutputRunners, v.Attrs)
		}
	}

	return nil
}

func Run(config *PipelineConfig) {
	log.Println("Starting gofluent...")

	rChan := make(chan *PipelinePack, config.Gc.PoolSize)
	config.router.AddInChan(rChan)

	for _, input_config := range config.InputRunners {
		cf := input_config.(map[string]string)

		InputRecycleChan := make(chan *PipelinePack, config.Gc.PoolSize)
		for i := 0; i < config.Gc.PoolSize; i++ {
			iPack := NewPipelinePack(InputRecycleChan)
			InputRecycleChan <- iPack
		}
		iRunner := NewInputRunner(InputRecycleChan, rChan)

		go iRunner.Start(cf)
	}

	for _, output_config := range config.OutputRunners {
		cf := output_config.(map[string]string)

		inChan := make(chan *PipelinePack, config.Gc.PoolSize)
		oRunner := NewOutputRunner(inChan)
		config.router.AddOutChan(cf["tag"], oRunner.InChan())

		go oRunner.Start(cf)
	}

	config.router.Loop()
}
<|endoftext|>


<commit_before>// Copyright 2017 Kohei YOSHIDA <https://yosida95.com/>.
//
// 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 chame

import (
	"context"
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rsa"
	"fmt"
	"sync"
	"time"

	"github.com/golang-jwt/jwt/v4"
)

type Token = jwt.RegisteredClaims

func JWTEpoch(unix int64) *jwt.NumericDate {
	if unix == 0 {
		return nil
	}
	return jwt.NewNumericDate(time.Unix(unix, 0))
}

func fromNumericDate(t *jwt.NumericDate) time.Time {
	if t == nil {
		return time.Time{}
	}
	return t.Time
}

func toNumericDate(t time.Time) *jwt.NumericDate {
	if t.IsZero() {
		return nil
	}
	return jwt.NewNumericDate(t)
}

func EncodeToken(ctx context.Context, store Store, token *Token, kid string) (string, error) {
	key, err := store.GetSigningKey(token.Issuer, kid)
	if err != nil {
		return "", fmt.Errorf("chame: failed to retrieve a signing key: %w", err)
	}

	var mech jwt.SigningMethod
	switch key := key.(type) {
	default:
		return "", fmt.Errorf("chame: unsupported key algorithm")
	case []byte:
		mech = jwt.SigningMethodHS256
	case *rsa.PrivateKey:
		mech = jwt.SigningMethodRS256
	case *ecdsa.PrivateKey:
		switch key.Curve {
		case elliptic.P256():
			mech = jwt.SigningMethodES256
		case elliptic.P384():
			mech = jwt.SigningMethodES384
		case elliptic.P521():
			mech = jwt.SigningMethodES512
		default:
			return "", fmt.Errorf("chame: unsupported elliptic curve")
		}
	}

	jwtobj := jwt.NewWithClaims(mech, token)
	if kid != "" {
		jwtobj.Header["kid"] = kid
	}
	signed, err := jwtobj.SignedString(key)
	if err != nil {
		return "", fmt.Errorf("chame: failed to sign a token: %w", err)
	}
	return signed, nil
}

var parserPool = sync.Pool{
	New: func() interface{} {
		return jwt.NewParser(
			jwt.WithValidMethods([]string{
				jwt.SigningMethodHS256.Name,
				jwt.SigningMethodHS384.Name,
				jwt.SigningMethodHS512.Name,
				jwt.SigningMethodRS256.Name,
				jwt.SigningMethodES384.Name,
				jwt.SigningMethodRS512.Name,
				jwt.SigningMethodES256.Name,
				jwt.SigningMethodES384.Name,
				jwt.SigningMethodES512.Name,
			}),
			jwt.WithLeeway(1*time.Minute))
	},
}

func DecodeToken(ctx context.Context, store Store, tokenString string) (string, error) {
	parser := parserPool.Get().(*jwt.Parser)
	defer parserPool.Put(parser)
	token, err := parser.ParseWithClaims(tokenString, &Token{}, func(token *jwt.Token) (interface{}, error) {
		claim := token.Claims.(*Token)
		kid, _ := token.Header["kid"].(string)
		return store.GetVerifyingKey(claim.Issuer, kid)
	})
	if err != nil {
		if _, ok := err.(interface {
			Cause() error
		}); ok {
			return "", err
		}
		return "", fmt.Errorf("chame: failed to decode signed token: %w", err)
	}
	claims := token.Claims.(*Token)
	return claims.Subject, nil
}
<commit_msg>revert changes for github.com/golang-jwt/jwt/v4@v4.4.0<commit_after>// Copyright 2017 Kohei YOSHIDA <https://yosida95.com/>.
//
// 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 chame

import (
	"context"
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rsa"
	"fmt"
	"sync"
	"time"

	"github.com/golang-jwt/jwt/v4"
)

type Token = jwt.RegisteredClaims

func JWTEpoch(unix int64) *jwt.NumericDate {
	if unix == 0 {
		return nil
	}
	return jwt.NewNumericDate(time.Unix(unix, 0))
}

func fromNumericDate(t *jwt.NumericDate) time.Time {
	if t == nil {
		return time.Time{}
	}
	return t.Time
}

func toNumericDate(t time.Time) *jwt.NumericDate {
	if t.IsZero() {
		return nil
	}
	return jwt.NewNumericDate(t)
}

func EncodeToken(ctx context.Context, store Store, token *Token, kid string) (string, error) {
	key, err := store.GetSigningKey(token.Issuer, kid)
	if err != nil {
		return "", fmt.Errorf("chame: failed to retrieve a signing key: %w", err)
	}

	var mech jwt.SigningMethod
	switch key := key.(type) {
	default:
		return "", fmt.Errorf("chame: unsupported key algorithm")
	case []byte:
		mech = jwt.SigningMethodHS256
	case *rsa.PrivateKey:
		mech = jwt.SigningMethodRS256
	case *ecdsa.PrivateKey:
		switch key.Curve {
		case elliptic.P256():
			mech = jwt.SigningMethodES256
		case elliptic.P384():
			mech = jwt.SigningMethodES384
		case elliptic.P521():
			mech = jwt.SigningMethodES512
		default:
			return "", fmt.Errorf("chame: unsupported elliptic curve")
		}
	}

	jwtobj := jwt.NewWithClaims(mech, token)
	if kid != "" {
		jwtobj.Header["kid"] = kid
	}
	signed, err := jwtobj.SignedString(key)
	if err != nil {
		return "", fmt.Errorf("chame: failed to sign a token: %w", err)
	}
	return signed, nil
}

var parserPool = sync.Pool{
	New: func() interface{} {
		return jwt.NewParser(
			jwt.WithValidMethods([]string{
				jwt.SigningMethodHS256.Name,
				jwt.SigningMethodHS384.Name,
				jwt.SigningMethodHS512.Name,
				jwt.SigningMethodRS256.Name,
				jwt.SigningMethodES384.Name,
				jwt.SigningMethodRS512.Name,
				jwt.SigningMethodES256.Name,
				jwt.SigningMethodES384.Name,
				jwt.SigningMethodES512.Name,
			}),
			jwt.WithoutClaimsValidation())
	},
}

func DecodeToken(ctx context.Context, store Store, tokenString string) (string, error) {
	const (
		leeway = 1 * time.Minute
	)
	parser := parserPool.Get().(*jwt.Parser)
	defer parserPool.Put(parser)
	claims := Token{}
	_, err := parser.ParseWithClaims(tokenString, &claims, func(token *jwt.Token) (interface{}, error) {
		kid, _ := token.Header["kid"].(string)
		return store.GetVerifyingKey(claims.Issuer, kid)
	})
	if err != nil {
		if _, ok := err.(interface {
			Cause() error
		}); ok {
			return "", err
		}
		return "", fmt.Errorf("chame: failed to decode signed token: %w", err)
	}

	now := time.Now()
	nowMin, nowMax := now.Add(-leeway), now.Add(leeway)
	if !claims.VerifyNotBefore(nowMax, false) ||
		!claims.VerifyIssuedAt(nowMax, false) ||
		!claims.VerifyExpiresAt(nowMin, false) {
		return "", fmt.Errorf("chame: failed to decode signed token: %w", err)
	}

	return claims.Subject, nil
}
<|endoftext|>


<commit_before>/*
Copyright 2016 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 top

import (
	"github.com/spf13/cobra"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/cli-runtime/pkg/genericclioptions"
	cmdutil "k8s.io/kubectl/pkg/cmd/util"
	"k8s.io/kubectl/pkg/util/i18n"
	"k8s.io/kubectl/pkg/util/templates"
	metricsapi "k8s.io/metrics/pkg/apis/metrics"
)

const (
	sortByCPU    = "cpu"
	sortByMemory = "memory"
)

var (
	supportedMetricsAPIVersions = []string{
		"v1beta1",
	}
	topLong = templates.LongDesc(i18n.T(`
		Display Resource (CPU/Memory/Storage) usage.

		The top command allows you to see the resource consumption for nodes or pods.

		This command requires Heapster to be correctly configured and working on the server. `))
)

func NewCmdTop(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command {
	cmd := &cobra.Command{
		Use:   "top",
		Short: i18n.T("Display Resource (CPU/Memory/Storage) usage."),
		Long:  topLong,
		Run:   cmdutil.DefaultSubCommandRun(streams.ErrOut),
	}

	// create subcommands
	cmd.AddCommand(NewCmdTopNode(f, nil, streams))
	cmd.AddCommand(NewCmdTopPod(f, nil, streams))

	return cmd
}

func SupportedMetricsAPIVersionAvailable(discoveredAPIGroups *metav1.APIGroupList) bool {
	for _, discoveredAPIGroup := range discoveredAPIGroups.Groups {
		if discoveredAPIGroup.Name != metricsapi.GroupName {
			continue
		}
		for _, version := range discoveredAPIGroup.Versions {
			for _, supportedVersion := range supportedMetricsAPIVersions {
				if version.Version == supportedVersion {
					return true
				}
			}
		}
	}
	return false
}
<commit_msg>Fix help text in kubectl top -h<commit_after>/*
Copyright 2016 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 top

import (
	"github.com/spf13/cobra"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/cli-runtime/pkg/genericclioptions"
	cmdutil "k8s.io/kubectl/pkg/cmd/util"
	"k8s.io/kubectl/pkg/util/i18n"
	"k8s.io/kubectl/pkg/util/templates"
	metricsapi "k8s.io/metrics/pkg/apis/metrics"
)

const (
	sortByCPU    = "cpu"
	sortByMemory = "memory"
)

var (
	supportedMetricsAPIVersions = []string{
		"v1beta1",
	}
	topLong = templates.LongDesc(i18n.T(`
		Display Resource (CPU/Memory/Storage) usage.

		The top command allows you to see the resource consumption for nodes or pods.

		This command requires Metrics Server to be correctly configured and working on the server. `))
)

func NewCmdTop(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command {
	cmd := &cobra.Command{
		Use:   "top",
		Short: i18n.T("Display Resource (CPU/Memory/Storage) usage."),
		Long:  topLong,
		Run:   cmdutil.DefaultSubCommandRun(streams.ErrOut),
	}

	// create subcommands
	cmd.AddCommand(NewCmdTopNode(f, nil, streams))
	cmd.AddCommand(NewCmdTopPod(f, nil, streams))

	return cmd
}

func SupportedMetricsAPIVersionAvailable(discoveredAPIGroups *metav1.APIGroupList) bool {
	for _, discoveredAPIGroup := range discoveredAPIGroups.Groups {
		if discoveredAPIGroup.Name != metricsapi.GroupName {
			continue
		}
		for _, version := range discoveredAPIGroup.Versions {
			for _, supportedVersion := range supportedMetricsAPIVersions {
				if version.Version == supportedVersion {
					return true
				}
			}
		}
	}
	return false
}
<|endoftext|>


<commit_before>// Copyright 2017 Sorint.lab
//
// 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 store

import (
	"context"

	"github.com/docker/leadership"
	libkvstore "github.com/docker/libkv/store"
	"github.com/docker/libkv/store/consul"
	"github.com/docker/libkv/store/etcd"
)

func init() {
	etcd.Register()
	consul.Register()
}

func fromLibKVStoreErr(err error) error {
	switch err {
	case libkvstore.ErrKeyNotFound:
		return ErrKeyNotFound
	}
	return err
}

type libKVStore struct {
	store libkvstore.Store
}

func (s *libKVStore) Put(ctx context.Context, key string, value []byte, options *WriteOptions) error {
	var libkvOptions *libkvstore.WriteOptions
	if options != nil {
		libkvOptions = &libkvstore.WriteOptions{TTL: options.TTL}
	}
	err := s.store.Put(key, value, libkvOptions)
	return fromLibKVStoreErr(err)
}

func (s *libKVStore) Get(ctx context.Context, key string) (*KVPair, error) {
	pair, err := s.store.Get(key)
	if err != nil {
		return nil, fromLibKVStoreErr(err)
	}
	return &KVPair{Key: pair.Key, Value: pair.Value, LastIndex: pair.LastIndex}, nil
}

func (s *libKVStore) List(ctx context.Context, directory string) ([]*KVPair, error) {
	pairs, err := s.store.List(directory)
	if err != nil {
		return nil, fromLibKVStoreErr(err)
	}
	kvPairs := make([]*KVPair, len(pairs))
	for i, p := range pairs {
		kvPairs[i] = &KVPair{Key: p.Key, Value: p.Value, LastIndex: p.LastIndex}
	}
	return kvPairs, nil
}

func (s *libKVStore) AtomicPut(ctx context.Context, key string, value []byte, previous *KVPair, options *WriteOptions) (*KVPair, error) {
	var libkvPrevious *libkvstore.KVPair
	if previous != nil {
		libkvPrevious = &libkvstore.KVPair{Key: previous.Key, LastIndex: previous.LastIndex}
	}
	var libkvOptions *libkvstore.WriteOptions
	if options != nil {
		libkvOptions = &libkvstore.WriteOptions{TTL: options.TTL}
	}
	_, pair, err := s.store.AtomicPut(key, value, libkvPrevious, libkvOptions)
	if err != nil {
		return nil, fromLibKVStoreErr(err)
	}
	return &KVPair{Key: pair.Key, Value: pair.Value, LastIndex: pair.LastIndex}, nil
}

func (s *libKVStore) Delete(ctx context.Context, key string) error {
	return fromLibKVStoreErr(s.store.Delete(key))
}

func (s *libKVStore) Close() error {
	s.store.Close()
	return nil
}

type libkvElection struct {
	store     *libKVStore
	path      string
	candidate *leadership.Candidate
}

func (e *libkvElection) RunForElection() (<-chan bool, <-chan error) {
	return e.candidate.RunForElection()
}

func (e *libkvElection) Stop() {
	e.candidate.Stop()
}

func (e *libkvElection) Leader() (string, error) {
	pair, err := e.store.Get(context.TODO(), e.path)
	if err != nil {
		if err != ErrKeyNotFound {
			return "", err
		}
		return "", nil
	}
	return string(pair.Value), nil
}
<commit_msg>store: handle libkv ErrKeyModified<commit_after>// Copyright 2017 Sorint.lab
//
// 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 store

import (
	"context"

	"github.com/docker/leadership"
	libkvstore "github.com/docker/libkv/store"
	"github.com/docker/libkv/store/consul"
	"github.com/docker/libkv/store/etcd"
)

func init() {
	etcd.Register()
	consul.Register()
}

func fromLibKVStoreErr(err error) error {
	switch err {
	case libkvstore.ErrKeyNotFound:
		return ErrKeyNotFound
	case libkvstore.ErrKeyModified:
		return ErrKeyModified
	}
	return err
}

type libKVStore struct {
	store libkvstore.Store
}

func (s *libKVStore) Put(ctx context.Context, key string, value []byte, options *WriteOptions) error {
	var libkvOptions *libkvstore.WriteOptions
	if options != nil {
		libkvOptions = &libkvstore.WriteOptions{TTL: options.TTL}
	}
	err := s.store.Put(key, value, libkvOptions)
	return fromLibKVStoreErr(err)
}

func (s *libKVStore) Get(ctx context.Context, key string) (*KVPair, error) {
	pair, err := s.store.Get(key)
	if err != nil {
		return nil, fromLibKVStoreErr(err)
	}
	return &KVPair{Key: pair.Key, Value: pair.Value, LastIndex: pair.LastIndex}, nil
}

func (s *libKVStore) List(ctx context.Context, directory string) ([]*KVPair, error) {
	pairs, err := s.store.List(directory)
	if err != nil {
		return nil, fromLibKVStoreErr(err)
	}
	kvPairs := make([]*KVPair, len(pairs))
	for i, p := range pairs {
		kvPairs[i] = &KVPair{Key: p.Key, Value: p.Value, LastIndex: p.LastIndex}
	}
	return kvPairs, nil
}

func (s *libKVStore) AtomicPut(ctx context.Context, key string, value []byte, previous *KVPair, options *WriteOptions) (*KVPair, error) {
	var libkvPrevious *libkvstore.KVPair
	if previous != nil {
		libkvPrevious = &libkvstore.KVPair{Key: previous.Key, LastIndex: previous.LastIndex}
	}
	var libkvOptions *libkvstore.WriteOptions
	if options != nil {
		libkvOptions = &libkvstore.WriteOptions{TTL: options.TTL}
	}
	_, pair, err := s.store.AtomicPut(key, value, libkvPrevious, libkvOptions)
	if err != nil {
		return nil, fromLibKVStoreErr(err)
	}
	return &KVPair{Key: pair.Key, Value: pair.Value, LastIndex: pair.LastIndex}, nil
}

func (s *libKVStore) Delete(ctx context.Context, key string) error {
	return fromLibKVStoreErr(s.store.Delete(key))
}

func (s *libKVStore) Close() error {
	s.store.Close()
	return nil
}

type libkvElection struct {
	store     *libKVStore
	path      string
	candidate *leadership.Candidate
}

func (e *libkvElection) RunForElection() (<-chan bool, <-chan error) {
	return e.candidate.RunForElection()
}

func (e *libkvElection) Stop() {
	e.candidate.Stop()
}

func (e *libkvElection) Leader() (string, error) {
	pair, err := e.store.Get(context.TODO(), e.path)
	if err != nil {
		if err != ErrKeyNotFound {
			return "", err
		}
		return "", nil
	}
	return string(pair.Value), nil
}
<|endoftext|>