<commit_before>/*
Copyright 2017 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 boot

import (
	"log"
	"os"
	"os/exec"
	"path/filepath"

	"github.com/spf13/cobra"
)

var initCmd = &cobra.Command{
	Use:   "init",
	Short: "Initialize a directory",
	Long:  `Initialize a directory`,
	Run:   RunInit,
}

var installDeps bool

func AddInit(cmd *cobra.Command) {
	cmd.AddCommand(initCmd)
	initCmd.Flags().StringVar(&domain, "domain", "", "domain the api groups live under")
	initCmd.Flags().StringVar(&copyright, "copyright", "", "path to copyright file.  defaults to boilerplate.go.txt")
	initCmd.Flags().BoolVar(&installDeps, "install-deps", true, "if true, install the vendored deps")
}

func RunInit(cmd *cobra.Command, args []string) {
	if len(domain) == 0 {
		log.Fatal("apiserver-boot init requires the --domain flag")
	}
	cr := getCopyright()

	createApiserver(cr)
	createControllerManager(cr)
	createAPIs(cr)
	createDocs()

	createPackage(cr, filepath.Join("pkg"))
	createPackage(cr, filepath.Join("pkg", "controller"))
	createPackage(cr, filepath.Join("pkg", "controller", "sharedinformers"))
	createPackage(cr, filepath.Join("pkg", "openapi"))

	exec.Command("mkdir", "-p", filepath.Join("bin")).CombinedOutput()

	if installDeps {
		log.Printf("installing godeps.  To disable this, run with --install-deps=false.")
		copyGlide()
	}
}

type controllerManagerTemplateArguments struct {
	BoilerPlate string
	Repo        string
}

var controllerManagerTemplate = `
{{.BoilerPlate}}

package main

import (
	"flag"
	"log"

	controllerlib "github.com/kubernetes-incubator/apiserver-builder/pkg/controller"

	"{{ .Repo }}/pkg/controller"
)

var kubeconfig = flag.String("kubeconfig", "", "path to kubeconfig")

func main() {
	flag.Parse()
	config, err := controllerlib.GetConfig(*kubeconfig)
	if err != nil {
		log.Fatalf("Could not create Config for talking to the apiserver: %v", err)
	}

	controllers, _ := controller.GetAllControllers(config)
	controllerlib.StartControllerManager(controllers...)

	// Blockforever
	select {}
}
`

func createControllerManager(boilerplate string) {
	dir, err := os.Getwd()
	if err != nil {
		log.Fatal(err)
	}
	path := filepath.Join(dir, "cmd", "controller", "main.go")
	writeIfNotFound(path, "main-template", controllerManagerTemplate, controllerManagerTemplateArguments{boilerplate, Repo})

}

type apiserverTemplateArguments struct {
	BoilerPlate string
	Repo        string
}

var apiserverTemplate = `
{{.BoilerPlate}}

package main

import (
	// Make sure glide gets these dependencies
	_ "k8s.io/apimachinery/pkg/apis/meta/v1"
	_ "github.com/go-openapi/loads"

	"github.com/kubernetes-incubator/apiserver-builder/pkg/cmd/server"
	_ "k8s.io/client-go/plugin/pkg/client/auth" // Enable cloud provider auth

	"{{.Repo}}/pkg/apis"
	"{{.Repo}}/pkg/openapi"
)

func main() {
	server.StartApiServer("/registry/sample.kubernetes.io", apis.GetAllApiBuilders(), openapi.GetOpenAPIDefinitions)
}
`

func createApiserver(boilerplate string) {
	dir, err := os.Getwd()
	if err != nil {
		log.Fatal(err)
	}
	path := filepath.Join(dir, "cmd", "apiserver", "main.go")
	writeIfNotFound(path, "apiserver-template", apiserverTemplate, apiserverTemplateArguments{boilerplate, Repo})

}

func createPackage(boilerplate, path string) {
	pkg := filepath.Base(path)
	dir, err := os.Getwd()
	if err != nil {
		log.Fatal(err)
	}
	path = filepath.Join(dir, path, "doc.go")
	writeIfNotFound(path, "pkg-template", packageDocTemplate, packageDocTemplateArguments{boilerplate, pkg})
}

type packageDocTemplateArguments struct {
	BoilerPlate string
	Package     string
}

var packageDocTemplate = `
{{.BoilerPlate}}

package {{.Package}}

`

func createAPIs(boilerplate string) {
	dir, err := os.Getwd()
	if err != nil {
		log.Fatal(err)
	}
	path := filepath.Join(dir, "pkg", "apis", "doc.go")
	writeIfNotFound(path, "apis-template", apisDocTemplate, apisDocTemplateArguments{boilerplate, domain})
}

type apisDocTemplateArguments struct {
	BoilerPlate string
	Domain      string
}

var apisDocTemplate = `
{{.BoilerPlate}}

//
// +domain={{.Domain}}

package apis

`

func createDocs() {
	exec.Command("mkdir", "-p", filepath.Join("docs", "openapi-spec")).CombinedOutput()
	exec.Command("mkdir", "-p", filepath.Join("docs", "static_includes")).CombinedOutput()
	exec.Command("mkdir", "-p", filepath.Join("docs", "examples")).CombinedOutput()
}
<commit_msg>Fixes #63<commit_after>/*
Copyright 2017 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 boot

import (
	"log"
	"os"
	"os/exec"
	"path/filepath"

	"github.com/spf13/cobra"
)

var initCmd = &cobra.Command{
	Use:   "init",
	Short: "Initialize a directory",
	Long:  `Initialize a directory`,
	Run:   RunInit,
}

var installDeps bool

func AddInit(cmd *cobra.Command) {
	cmd.AddCommand(initCmd)
	initCmd.Flags().StringVar(&domain, "domain", "", "domain the api groups live under")
	initCmd.Flags().StringVar(&copyright, "copyright", "", "path to copyright file.  defaults to boilerplate.go.txt")
	initCmd.Flags().BoolVar(&installDeps, "install-deps", true, "if true, install the vendored deps")
}

func RunInit(cmd *cobra.Command, args []string) {
	if len(domain) == 0 {
		log.Fatal("apiserver-boot init requires the --domain flag")
	}
	cr := getCopyright()

	createApiserver(cr)
	createControllerManager(cr)
	createAPIs(cr)
	createDocs()

	createPackage(cr, filepath.Join("pkg"))
	createPackage(cr, filepath.Join("pkg", "controller"))
	createPackage(cr, filepath.Join("pkg", "controller", "sharedinformers"))
	createPackage(cr, filepath.Join("pkg", "openapi"))

	exec.Command("mkdir", "-p", filepath.Join("bin")).CombinedOutput()

	if installDeps {
		log.Printf("installing godeps.  To disable this, run with --install-deps=false.")
		copyGlide()
	}
}

type controllerManagerTemplateArguments struct {
	BoilerPlate string
	Repo        string
}

var controllerManagerTemplate = `
{{.BoilerPlate}}

package main

import (
	"flag"
	"log"

	controllerlib "github.com/kubernetes-incubator/apiserver-builder/pkg/controller"

	"{{ .Repo }}/pkg/controller"
)

var kubeconfig = flag.String("kubeconfig", "", "path to kubeconfig")

func main() {
	flag.Parse()
	config, err := controllerlib.GetConfig(*kubeconfig)
	if err != nil {
		log.Fatalf("Could not create Config for talking to the apiserver: %v", err)
	}

	controllers, _ := controller.GetAllControllers(config)
	controllerlib.StartControllerManager(controllers...)

	// Blockforever
	select {}
}
`

func createControllerManager(boilerplate string) {
	dir, err := os.Getwd()
	if err != nil {
		log.Fatal(err)
	}
	path := filepath.Join(dir, "cmd", "controller", "main.go")
	writeIfNotFound(path, "main-template", controllerManagerTemplate, controllerManagerTemplateArguments{boilerplate, Repo})

}

type apiserverTemplateArguments struct {
	Domain      string
	BoilerPlate string
	Repo        string
}

var apiserverTemplate = `
{{.BoilerPlate}}

package main

import (
	// Make sure glide gets these dependencies
	_ "k8s.io/apimachinery/pkg/apis/meta/v1"
	_ "github.com/go-openapi/loads"

	"github.com/kubernetes-incubator/apiserver-builder/pkg/cmd/server"
	_ "k8s.io/client-go/plugin/pkg/client/auth" // Enable cloud provider auth

	"{{.Repo}}/pkg/apis"
	"{{.Repo}}/pkg/openapi"
)

func main() {
	server.StartApiServer("/registry/{{ .Domain }}", apis.GetAllApiBuilders(), openapi.GetOpenAPIDefinitions)
}
`

func createApiserver(boilerplate string) {
	dir, err := os.Getwd()
	if err != nil {
		log.Fatal(err)
	}
	path := filepath.Join(dir, "cmd", "apiserver", "main.go")
	writeIfNotFound(path, "apiserver-template", apiserverTemplate, apiserverTemplateArguments{domain, boilerplate, Repo})

}

func createPackage(boilerplate, path string) {
	pkg := filepath.Base(path)
	dir, err := os.Getwd()
	if err != nil {
		log.Fatal(err)
	}
	path = filepath.Join(dir, path, "doc.go")
	writeIfNotFound(path, "pkg-template", packageDocTemplate, packageDocTemplateArguments{boilerplate, pkg})
}

type packageDocTemplateArguments struct {
	BoilerPlate string
	Package     string
}

var packageDocTemplate = `
{{.BoilerPlate}}

package {{.Package}}

`

func createAPIs(boilerplate string) {
	dir, err := os.Getwd()
	if err != nil {
		log.Fatal(err)
	}
	path := filepath.Join(dir, "pkg", "apis", "doc.go")
	writeIfNotFound(path, "apis-template", apisDocTemplate, apisDocTemplateArguments{boilerplate, domain})
}

type apisDocTemplateArguments struct {
	BoilerPlate string
	Domain      string
}

var apisDocTemplate = `
{{.BoilerPlate}}

//
// +domain={{.Domain}}

package apis

`

func createDocs() {
	exec.Command("mkdir", "-p", filepath.Join("docs", "openapi-spec")).CombinedOutput()
	exec.Command("mkdir", "-p", filepath.Join("docs", "static_includes")).CombinedOutput()
	exec.Command("mkdir", "-p", filepath.Join("docs", "examples")).CombinedOutput()
}
<|endoftext|>


<commit_before>package cmd

import (
	"context"
	"fmt"
	"log"
	"os"
	"time"

	"github.com/danesparza/appliance-monitor/data"
	"github.com/gregdel/pushover"
	influxdb "github.com/influxdb/influxdb/client/v2"
	"github.com/montanaflynn/stats"
	"github.com/spf13/viper"

	"github.com/danesparza/embd/sensor/envirophat"
	"github.com/kidoman/embd"
	_ "github.com/kidoman/embd/host/rpi" // This loads the RPi driver
)

func collectionloop(ctx context.Context) {
	//	Connect to the datastores:
	log.Printf("[INFO] Config database: %s\n", viper.GetString("datastore.config"))
	configDB := data.ConfigDB{
		Database: viper.GetString("datastore.config")}
	log.Printf("[INFO] Activities database: %s\n", viper.GetString("datastore.activity"))
	activityDB := data.ActivityDB{
		Database: viper.GetString("datastore.activity")}

	log.Println("[INFO] Initializing GPIO...")
	err := embd.InitGPIO()
	if err != nil {
		log.Fatal("[ERROR] Initializing GPIO:", err)
	}
	defer embd.CloseGPIO()

	pin, err := embd.NewDigitalPin("GPIO_4")
	if err != nil {
		log.Fatal("[ERROR] opening pin:", err)
	}
	defer resetPin(pin)

	if err = pin.SetDirection(embd.Out); err != nil {
		log.Fatal("[ERROR] setting pin direction:", err)
	}

	log.Println("[INFO] Initializing I2C...")
	if err := embd.InitI2C(); err != nil {
		log.Fatal("[ERROR] Initalizing I2C:", err)
	}
	defer embd.CloseI2C()

	bus := embd.NewI2CBus(1)
	sensor := envirophat.New(bus)

	hostname, _ := os.Hostname()
	log.Printf("[INFO] Using hostname %v...", hostname)

	//	influxserver should be a url, like
	//	http://chile.lan:8086
	influxURL, err := configDB.Get("influxserver")
	if err == nil && influxURL.Value != "" {
		log.Printf("[INFO] Using influxserver %v...", influxURL.Value)
	}
	c, _ := influxdb.NewHTTPClient(influxdb.HTTPConfig{Addr: influxURL.Value})

	//	Keep track of the axis values
	var xaxis, yaxis, zaxis []float64
	var xdev, ydev, zdev []float64

	//	Keep track of state of device
	currentlyRunning := false
	timeStart := time.Now()

	//	Loop and respond to channels:
	for {
		select {
		case <-ctx.Done():
			return
		case <-time.After(1 * time.Second):
			//	Perform sensor data gathering
			pin.Write(embd.High)

			//	Get accelerometer values from the sensor
			x, y, z, err := sensor.Accelerometer()
			if err != nil {
				log.Fatal(err)
			}

			//	Track the measurements
			xaxis = append(xaxis, x)
			yaxis = append(yaxis, y)
			zaxis = append(zaxis, z)

			//	Calculate standard deviation
			xdevcurrent, err := stats.StandardDeviation(xaxis)
			if err != nil {
				log.Fatal(err)
			}
			xdev = append(xdev, xdevcurrent)

			ydevcurrent, err := stats.StandardDeviation(yaxis)
			if err != nil {
				log.Fatal(err)
			}
			ydev = append(ydev, ydevcurrent)

			zdevcurrent, err := stats.StandardDeviation(zaxis)
			if err != nil {
				log.Fatal(err)
			}
			zdev = append(zdev, zdevcurrent)

			//	Keep a rolling collection of data...
			//	If we already have maxPoints items
			//	remove the first item:
			if len(xaxis) > maxPoints {
				xaxis = xaxis[1:]
			}

			if len(yaxis) > maxPoints {
				yaxis = yaxis[1:]
			}

			if len(zaxis) > maxPoints {
				zaxis = zaxis[1:]
			}

			if len(zdev) > maxPoints {
				xdev = xdev[1:]
			}

			if len(ydev) > maxPoints {
				ydev = ydev[1:]
			}

			if len(zdev) > maxPoints {
				zdev = zdev[1:]
			}

			if influxURL.Value != "" {
				// Create a new point batch
				bp, err := influxdb.NewBatchPoints(influxdb.BatchPointsConfig{
					Database:  "sensors",
					Precision: "s",
				})
				if err != nil {
					log.Fatal(err)
				}

				// Create a point and add to batch
				tags := map[string]string{"host": hostname}
				fields := map[string]interface{}{
					"x":    x,
					"y":    y,
					"z":    z,
					"xdev": xdevcurrent,
					"ydev": ydevcurrent,
					"zdev": zdevcurrent}

				pt, err := influxdb.NewPoint("envirophat-lsm303d", tags, fields, time.Now())
				if err != nil {
					log.Fatal(err)
				}
				bp.AddPoint(pt)

				// Write the batch
				if err := c.Write(bp); err != nil {
					log.Printf("[WARN] Problem writing to InfluxDB server: %v", err)
				}
			}

			//	Calculate ... are we currently running?
			if ((xdevcurrent * 1000) > applianceRunThreshold) && ((ydevcurrent * 1000) > applianceRunThreshold) && ((zdevcurrent * 1000) > applianceRunThreshold) && currentlyRunning == false {
				log.Println("[DEBUG] Looks like the machine is running")

				currentlyRunning = true
				timeStart = time.Now()

				//	Track the activity:
				activityDB.Add(data.Activity{Type: data.ApplianceRunning})
			}

			if ((xdevcurrent * 1000) < applianceRunThreshold) && ((ydevcurrent * 1000) < applianceRunThreshold) && ((zdevcurrent * 1000) < applianceRunThreshold) && currentlyRunning == true {
				log.Println("[DEBUG] Looks like the machine is stopped")
				currentlyRunning = false

				//	Calculate the run time:
				runningTime := time.Since(timeStart)

				//	Track the activity:
				activityDB.Add(data.Activity{Type: data.ApplianceStopped})

				// Send a Pushover message
				pushAPIkey, err := configDB.Get("pushoverapikey")
				pushTo, err := configDB.Get("pushoverrecipient")
				if err == nil && pushTo.Value != "" {
					pushClient := pushover.New(pushAPIkey.Value)
					recipient := pushover.NewRecipient(pushTo.Value)
					message := pushover.NewMessage(fmt.Sprintf("The dryer has finished running.  It ran for about %v minutes", int(runningTime.Minutes())))
					message.Sound = "bike"
					_, err := pushClient.SendMessage(message, recipient)
					if err != nil {
						log.Printf("[WARN] Problem sending pushover message: %v\n", err)
					}
				}
			}

			pin.Write(embd.Low)

		}
	}
}

func resetPin(pin embd.DigitalPin) {
	if err := pin.SetDirection(embd.In); err != nil {
		log.Fatal("resetting pin:", err)
	}
	pin.Close()
}
<commit_msg>Fixed influxdb import.  Again.  Also fixed locally<commit_after>package cmd

import (
	"context"
	"fmt"
	"log"
	"os"
	"time"

	"github.com/danesparza/appliance-monitor/data"
	"github.com/gregdel/pushover"
	influxdb "github.com/influxdata/influxdb/client/v2"
	"github.com/montanaflynn/stats"
	"github.com/spf13/viper"

	"github.com/danesparza/embd/sensor/envirophat"
	"github.com/kidoman/embd"
	_ "github.com/kidoman/embd/host/rpi" // This loads the RPi driver
)

func collectionloop(ctx context.Context) {
	//	Connect to the datastores:
	log.Printf("[INFO] Config database: %s\n", viper.GetString("datastore.config"))
	configDB := data.ConfigDB{
		Database: viper.GetString("datastore.config")}
	log.Printf("[INFO] Activities database: %s\n", viper.GetString("datastore.activity"))
	activityDB := data.ActivityDB{
		Database: viper.GetString("datastore.activity")}

	log.Println("[INFO] Initializing GPIO...")
	err := embd.InitGPIO()
	if err != nil {
		log.Fatal("[ERROR] Initializing GPIO:", err)
	}
	defer embd.CloseGPIO()

	pin, err := embd.NewDigitalPin("GPIO_4")
	if err != nil {
		log.Fatal("[ERROR] opening pin:", err)
	}
	defer resetPin(pin)

	if err = pin.SetDirection(embd.Out); err != nil {
		log.Fatal("[ERROR] setting pin direction:", err)
	}

	log.Println("[INFO] Initializing I2C...")
	if err := embd.InitI2C(); err != nil {
		log.Fatal("[ERROR] Initalizing I2C:", err)
	}
	defer embd.CloseI2C()

	bus := embd.NewI2CBus(1)
	sensor := envirophat.New(bus)

	hostname, _ := os.Hostname()
	log.Printf("[INFO] Using hostname %v...", hostname)

	//	influxserver should be a url, like
	//	http://chile.lan:8086
	influxURL, err := configDB.Get("influxserver")
	if err == nil && influxURL.Value != "" {
		log.Printf("[INFO] Using influxserver %v...", influxURL.Value)
	}
	c, _ := influxdb.NewHTTPClient(influxdb.HTTPConfig{Addr: influxURL.Value})

	//	Keep track of the axis values
	var xaxis, yaxis, zaxis []float64
	var xdev, ydev, zdev []float64

	//	Keep track of state of device
	currentlyRunning := false
	timeStart := time.Now()

	//	Loop and respond to channels:
	for {
		select {
		case <-ctx.Done():
			return
		case <-time.After(1 * time.Second):
			//	Perform sensor data gathering
			pin.Write(embd.High)

			//	Get accelerometer values from the sensor
			x, y, z, err := sensor.Accelerometer()
			if err != nil {
				log.Fatal(err)
			}

			//	Track the measurements
			xaxis = append(xaxis, x)
			yaxis = append(yaxis, y)
			zaxis = append(zaxis, z)

			//	Calculate standard deviation
			xdevcurrent, err := stats.StandardDeviation(xaxis)
			if err != nil {
				log.Fatal(err)
			}
			xdev = append(xdev, xdevcurrent)

			ydevcurrent, err := stats.StandardDeviation(yaxis)
			if err != nil {
				log.Fatal(err)
			}
			ydev = append(ydev, ydevcurrent)

			zdevcurrent, err := stats.StandardDeviation(zaxis)
			if err != nil {
				log.Fatal(err)
			}
			zdev = append(zdev, zdevcurrent)

			//	Keep a rolling collection of data...
			//	If we already have maxPoints items
			//	remove the first item:
			if len(xaxis) > maxPoints {
				xaxis = xaxis[1:]
			}

			if len(yaxis) > maxPoints {
				yaxis = yaxis[1:]
			}

			if len(zaxis) > maxPoints {
				zaxis = zaxis[1:]
			}

			if len(zdev) > maxPoints {
				xdev = xdev[1:]
			}

			if len(ydev) > maxPoints {
				ydev = ydev[1:]
			}

			if len(zdev) > maxPoints {
				zdev = zdev[1:]
			}

			if influxURL.Value != "" {
				// Create a new point batch
				bp, err := influxdb.NewBatchPoints(influxdb.BatchPointsConfig{
					Database:  "sensors",
					Precision: "s",
				})
				if err != nil {
					log.Fatal(err)
				}

				// Create a point and add to batch
				tags := map[string]string{"host": hostname}
				fields := map[string]interface{}{
					"x":    x,
					"y":    y,
					"z":    z,
					"xdev": xdevcurrent,
					"ydev": ydevcurrent,
					"zdev": zdevcurrent}

				pt, err := influxdb.NewPoint("envirophat-lsm303d", tags, fields, time.Now())
				if err != nil {
					log.Fatal(err)
				}
				bp.AddPoint(pt)

				// Write the batch
				if err := c.Write(bp); err != nil {
					log.Printf("[WARN] Problem writing to InfluxDB server: %v", err)
				}
			}

			//	Calculate ... are we currently running?
			if ((xdevcurrent * 1000) > applianceRunThreshold) && ((ydevcurrent * 1000) > applianceRunThreshold) && ((zdevcurrent * 1000) > applianceRunThreshold) && currentlyRunning == false {
				log.Println("[DEBUG] Looks like the machine is running")

				currentlyRunning = true
				timeStart = time.Now()

				//	Track the activity:
				activityDB.Add(data.Activity{Type: data.ApplianceRunning})
			}

			if ((xdevcurrent * 1000) < applianceRunThreshold) && ((ydevcurrent * 1000) < applianceRunThreshold) && ((zdevcurrent * 1000) < applianceRunThreshold) && currentlyRunning == true {
				log.Println("[DEBUG] Looks like the machine is stopped")
				currentlyRunning = false

				//	Calculate the run time:
				runningTime := time.Since(timeStart)

				//	Track the activity:
				activityDB.Add(data.Activity{Type: data.ApplianceStopped})

				// Send a Pushover message
				pushAPIkey, err := configDB.Get("pushoverapikey")
				pushTo, err := configDB.Get("pushoverrecipient")
				if err == nil && pushTo.Value != "" {
					pushClient := pushover.New(pushAPIkey.Value)
					recipient := pushover.NewRecipient(pushTo.Value)
					message := pushover.NewMessage(fmt.Sprintf("The dryer has finished running.  It ran for about %v minutes", int(runningTime.Minutes())))
					message.Sound = "bike"
					_, err := pushClient.SendMessage(message, recipient)
					if err != nil {
						log.Printf("[WARN] Problem sending pushover message: %v\n", err)
					}
				}
			}

			pin.Write(embd.Low)

		}
	}
}

func resetPin(pin embd.DigitalPin) {
	if err := pin.SetDirection(embd.In); err != nil {
		log.Fatal("resetting pin:", err)
	}
	pin.Close()
}
<|endoftext|>


<commit_before>package version

import (
	"fmt"

	"github.com/spf13/cobra"
)

// Command config
var Command = &cobra.Command{
	Use:   "version",
	Short: "Print version of Eclectica",
	Run:   run,
}

// Version number
const Version = "0.0.8"

// Runner
func run(c *cobra.Command, args []string) {
	fmt.Println(Version)
}
<commit_msg>Bump version to 0.0.9<commit_after>package version

import (
	"fmt"

	"github.com/spf13/cobra"
)

// Command config
var Command = &cobra.Command{
	Use:   "version",
	Short: "Print version of Eclectica",
	Run:   run,
}

// Version number
const Version = "0.0.9"

// Runner
func run(c *cobra.Command, args []string) {
	fmt.Println(Version)
}
<|endoftext|>


<commit_before>package main

import (
	"io/ioutil"
	"os"
	"path"
	"strings"
	"time"

	"github.com/Cloud-Foundations/Dominator/lib/fsutil"
	"github.com/Cloud-Foundations/Dominator/lib/log"
	libprober "github.com/Cloud-Foundations/health-agent/lib/prober"
	"github.com/Cloud-Foundations/health-agent/lib/proberlist"
	dnsprober "github.com/Cloud-Foundations/health-agent/probers/dns"
	ldapprober "github.com/Cloud-Foundations/health-agent/probers/ldap"
	pidprober "github.com/Cloud-Foundations/health-agent/probers/pidfile"
	testprogprober "github.com/Cloud-Foundations/health-agent/probers/testprog"
	urlprober "github.com/Cloud-Foundations/health-agent/probers/url"
	"github.com/Cloud-Foundations/tricorder/go/tricorder"
	"github.com/Cloud-Foundations/tricorder/go/tricorder/units"
	"gopkg.in/yaml.v2"
)

type testConfig struct {
	Testtype  string `yaml:"type"`
	Probefreq uint8  `yaml:"probe-freq"`
	Specs     testSpecs
}

type testSpecs struct {
	Pathname   string
	Urlpath    string `yaml:"url-path"`
	Urlport    uint   `yaml:"url-port"`
	Hostname   string `yaml:"hostname"`
	SssdConfig string `yaml:"sssd-config"`
}

func setupHealthchecks(configDir string, pl *proberlist.ProberList,
	logger log.Logger) error {
	topDir := "/health-checks"
	configdir, err := os.Open(path.Join(configDir, "tests.d"))
	defer configdir.Close()
	if err != nil {
		if os.IsNotExist(err) {
			return nil
		}
		return err
	}
	configfiles, err := configdir.Readdir(0)
	if err != nil {
		return err
	}
	healthCheckers := make(map[string]libprober.HealthChecker)
	for _, configfile := range configfiles {
		if configfile.IsDir() {
			continue
		}
		data, err := ioutil.ReadFile(path.Join(configdir.Name(),
			configfile.Name()))
		if err != nil {
			logger.Printf("Unable to read file %q: %s",
				configfile.Name(), err)
			return err
		}
		c := testConfig{}
		if err := yaml.Unmarshal([]byte(data), &c); err != nil {
			logger.Printf("Error unmarshalling file %s: %q",
				configfile.Name(), err)
			return err
		}
		testname := strings.Split(configfile.Name(), ".")[0]
		if prober := makeProber(testname, &c, logger); prober != nil {
			pl.Add(prober, path.Join(topDir, testname), c.Probefreq)
			if hc, ok := prober.(libprober.HealthChecker); ok {
				healthCheckers[testname] = hc
			}
		}
	}
	var allHealthy bool
	list := tricorder.NewList([]string{}, tricorder.ImmutableSlice)
	group := tricorder.NewGroup()
	err = group.RegisterMetric(path.Join(topDir, "*", "healthy"), &allHealthy,
		units.None, "If true, all health checks are healthy")
	if err != nil {
		return err
	}
	err = tricorder.RegisterMetricInGroup(path.Join(topDir, "*",
		"unhealthy-list"), list, group, units.None,
		"List of failed health checks")
	if err != nil {
		return err
	}
	group.RegisterUpdateFunc(func() time.Time {
		healthy := true
		var unhealthyList []string
		for testname, healthChecker := range healthCheckers {
			if !healthChecker.HealthCheck() {
				healthy = false
				unhealthyList = append(unhealthyList, testname)
			}
		}
		allHealthy = healthy
		list.Change(unhealthyList, tricorder.ImmutableSlice)
		return time.Now()
	})
	return nil
}

func makeProber(testname string, c *testConfig,
	logger log.Logger) proberlist.RegisterProber {
	switch c.Testtype {
	case "dns":
		hostname := c.Specs.Hostname
		return dnsprober.New(testname, hostname)
	case "ldap":
		sssd := c.Specs.SssdConfig
		file, err := fsutil.WaitFile(sssd, time.Minute*5)
		if err != nil {
			logger.Println(err)
			return nil
		}
		return ldapprober.Makeldapprober(testname, file, c.Probefreq)
	case "pid":
		pidpath := c.Specs.Pathname
		if pidpath == "" {
			return nil
		}
		return pidprober.Makepidprober(testname, pidpath)
	case "testprog":
		testprogpath := c.Specs.Pathname
		if testprogpath == "" {
			return nil
		}
		return testprogprober.Maketestprogprober(testname, testprogpath)
	case "url":
		urlpath := c.Specs.Urlpath
		urlport := c.Specs.Urlport
		return urlprober.Makeurlprober(testname, urlpath, urlport)
	default:
		logger.Printf("Test type %s not supported", c.Testtype)
		return nil
	}
}
<commit_msg>health-agent: add dial prober.<commit_after>package main

import (
	"io/ioutil"
	"os"
	"path"
	"strings"
	"time"

	"github.com/Cloud-Foundations/Dominator/lib/fsutil"
	"github.com/Cloud-Foundations/Dominator/lib/log"
	libprober "github.com/Cloud-Foundations/health-agent/lib/prober"
	"github.com/Cloud-Foundations/health-agent/lib/proberlist"
	dialprober "github.com/Cloud-Foundations/health-agent/probers/dial"
	dnsprober "github.com/Cloud-Foundations/health-agent/probers/dns"
	ldapprober "github.com/Cloud-Foundations/health-agent/probers/ldap"
	pidprober "github.com/Cloud-Foundations/health-agent/probers/pidfile"
	testprogprober "github.com/Cloud-Foundations/health-agent/probers/testprog"
	urlprober "github.com/Cloud-Foundations/health-agent/probers/url"
	"github.com/Cloud-Foundations/tricorder/go/tricorder"
	"github.com/Cloud-Foundations/tricorder/go/tricorder/units"
	"gopkg.in/yaml.v2"
)

type testConfig struct {
	Testtype  string `yaml:"type"`
	Probefreq uint8  `yaml:"probe-freq"`
	Specs     testSpecs
}

type testSpecs struct {
	Address    string `yaml:"address"`
	Hostname   string `yaml:"hostname"`
	Network    string `yaml:"network"`
	Pathname   string
	SssdConfig string `yaml:"sssd-config"`
	Urlpath    string `yaml:"url-path"`
	Urlport    uint   `yaml:"url-port"`
}

func setupHealthchecks(configDir string, pl *proberlist.ProberList,
	logger log.Logger) error {
	topDir := "/health-checks"
	configdir, err := os.Open(path.Join(configDir, "tests.d"))
	defer configdir.Close()
	if err != nil {
		if os.IsNotExist(err) {
			return nil
		}
		return err
	}
	configfiles, err := configdir.Readdir(0)
	if err != nil {
		return err
	}
	healthCheckers := make(map[string]libprober.HealthChecker)
	for _, configfile := range configfiles {
		if configfile.IsDir() {
			continue
		}
		data, err := ioutil.ReadFile(path.Join(configdir.Name(),
			configfile.Name()))
		if err != nil {
			logger.Printf("Unable to read file %q: %s",
				configfile.Name(), err)
			return err
		}
		c := testConfig{}
		if err := yaml.Unmarshal([]byte(data), &c); err != nil {
			logger.Printf("Error unmarshalling file %s: %q",
				configfile.Name(), err)
			return err
		}
		testname := strings.Split(configfile.Name(), ".")[0]
		if prober := makeProber(testname, &c, logger); prober != nil {
			pl.Add(prober, path.Join(topDir, testname), c.Probefreq)
			if hc, ok := prober.(libprober.HealthChecker); ok {
				healthCheckers[testname] = hc
			}
		}
	}
	var allHealthy bool
	list := tricorder.NewList([]string{}, tricorder.ImmutableSlice)
	group := tricorder.NewGroup()
	err = group.RegisterMetric(path.Join(topDir, "*", "healthy"), &allHealthy,
		units.None, "If true, all health checks are healthy")
	if err != nil {
		return err
	}
	err = tricorder.RegisterMetricInGroup(path.Join(topDir, "*",
		"unhealthy-list"), list, group, units.None,
		"List of failed health checks")
	if err != nil {
		return err
	}
	group.RegisterUpdateFunc(func() time.Time {
		healthy := true
		var unhealthyList []string
		for testname, healthChecker := range healthCheckers {
			if !healthChecker.HealthCheck() {
				healthy = false
				unhealthyList = append(unhealthyList, testname)
			}
		}
		allHealthy = healthy
		list.Change(unhealthyList, tricorder.ImmutableSlice)
		return time.Now()
	})
	return nil
}

func makeProber(testname string, c *testConfig,
	logger log.Logger) proberlist.RegisterProber {
	switch c.Testtype {
	case "dial":
		return dialprober.MakeDialProber(c.Specs.Network, c.Specs.Address)
	case "dns":
		hostname := c.Specs.Hostname
		return dnsprober.New(testname, hostname)
	case "ldap":
		sssd := c.Specs.SssdConfig
		file, err := fsutil.WaitFile(sssd, time.Minute*5)
		if err != nil {
			logger.Println(err)
			return nil
		}
		return ldapprober.Makeldapprober(testname, file, c.Probefreq)
	case "pid":
		pidpath := c.Specs.Pathname
		if pidpath == "" {
			return nil
		}
		return pidprober.Makepidprober(testname, pidpath)
	case "testprog":
		testprogpath := c.Specs.Pathname
		if testprogpath == "" {
			return nil
		}
		return testprogprober.Maketestprogprober(testname, testprogpath)
	case "url":
		urlpath := c.Specs.Urlpath
		urlport := c.Specs.Urlport
		return urlprober.Makeurlprober(testname, urlpath, urlport)
	default:
		logger.Printf("Test type %s not supported", c.Testtype)
		return nil
	}
}
<|endoftext|>


<commit_before>package core

import (
	discovery "github.com/googleapis/gnostic/discovery"
	metrics "github.com/googleapis/gnostic/metrics"
	openapi_v2 "github.com/googleapis/gnostic/openapiv2"
	openapi_v3 "github.com/googleapis/gnostic/openapiv3"
)

func SummarizeOpenAPIv2Document(document *openapi_v2.Document) *metrics.Complexity {
	summary := &metrics.Complexity{}
	if document.Definitions != nil && document.Definitions.AdditionalProperties != nil {
		for _, pair := range document.Definitions.AdditionalProperties {
			summarizeOpenAPIv2Schema(summary, pair.Value)
		}
	}
	for _, pair := range document.Paths.Path {
		summary.PathCount++
		v := pair.Value
		if v.Get != nil {
			summary.GetCount++
		}
		if v.Post != nil {
			summary.PostCount++
		}
		if v.Put != nil {
			summary.PutCount++
		}
		if v.Delete != nil {
			summary.DeleteCount++
		}
	}
	return summary
}

func summarizeOpenAPIv2Schema(summary *metrics.Complexity, schema *openapi_v2.Schema) {
	summary.SchemaCount++
	if schema.Properties != nil {
		for _, pair := range schema.Properties.AdditionalProperties {
			summary.SchemaPropertyCount++
			summarizeOpenAPIv2Schema(summary, pair.Value)
		}
	}
}

func SummarizeOpenAPIv3Document(document *openapi_v3.Document) *metrics.Complexity {
	summary := &metrics.Complexity{}
	if document.Components != nil && document.Components.Schemas != nil {
		for _, pair := range document.Components.Schemas.AdditionalProperties {
			summarizeOpenAPIv3Schema(summary, pair.Value)
		}
	}
	for _, pair := range document.Paths.Path {
		summary.PathCount++
		v := pair.Value
		if v.Get != nil {
			summary.GetCount++
		}
		if v.Post != nil {
			summary.PostCount++
		}
		if v.Put != nil {
			summary.PutCount++
		}
		if v.Delete != nil {
			summary.DeleteCount++
		}
	}
	return summary
}

func summarizeOpenAPIv3Schema(summary *metrics.Complexity, schemaOrReference *openapi_v3.SchemaOrReference) {
	summary.SchemaCount++
	schema := schemaOrReference.GetSchema()
	if schema != nil && schema.Properties != nil {
		for _, pair := range schema.Properties.AdditionalProperties {
			summary.SchemaPropertyCount++
			summarizeOpenAPIv3Schema(summary, pair.Value)
		}
	}
}

func SummarizeDiscoveryDocument(document *discovery.Document) *metrics.Complexity {
	summary := &metrics.Complexity{}
	if document.Schemas != nil && document.Schemas.AdditionalProperties != nil {
		for _, pair := range document.Schemas.AdditionalProperties {
			summarizeDiscoverySchema(summary, pair.Value)
		}
	}
	if document.Methods != nil {
		for _, pair := range document.Methods.AdditionalProperties {
			summary.PathCount++
			v := pair.Value
			switch v.HttpMethod {
			case "GET":
				summary.GetCount++
			case "POST":
				summary.PostCount++
			case "PUT":
				summary.PutCount++
			case "DELETE":
				summary.DeleteCount++
			}
		}
	}
	return summary
}

func summarizeDiscoverySchema(summary *metrics.Complexity, schema *discovery.Schema) {
	summary.SchemaCount++
	if schema != nil && schema.Properties != nil {
		for _, pair := range schema.Properties.AdditionalProperties {
			summary.SchemaPropertyCount++
			summarizeDiscoverySchema(summary, pair.Value)
		}
	}
}
<commit_msg>fix gap in discovery complexity calculation<commit_after>package core

import (
	discovery "github.com/googleapis/gnostic/discovery"
	metrics "github.com/googleapis/gnostic/metrics"
	openapi_v2 "github.com/googleapis/gnostic/openapiv2"
	openapi_v3 "github.com/googleapis/gnostic/openapiv3"
)

func SummarizeOpenAPIv2Document(document *openapi_v2.Document) *metrics.Complexity {
	summary := &metrics.Complexity{}
	if document.Definitions != nil && document.Definitions.AdditionalProperties != nil {
		for _, pair := range document.Definitions.AdditionalProperties {
			summarizeOpenAPIv2Schema(summary, pair.Value)
		}
	}
	for _, pair := range document.Paths.Path {
		summary.PathCount++
		v := pair.Value
		if v.Get != nil {
			summary.GetCount++
		}
		if v.Post != nil {
			summary.PostCount++
		}
		if v.Put != nil {
			summary.PutCount++
		}
		if v.Delete != nil {
			summary.DeleteCount++
		}
	}
	return summary
}

func summarizeOpenAPIv2Schema(summary *metrics.Complexity, schema *openapi_v2.Schema) {
	summary.SchemaCount++
	if schema.Properties != nil {
		for _, pair := range schema.Properties.AdditionalProperties {
			summary.SchemaPropertyCount++
			summarizeOpenAPIv2Schema(summary, pair.Value)
		}
	}
}

func SummarizeOpenAPIv3Document(document *openapi_v3.Document) *metrics.Complexity {
	summary := &metrics.Complexity{}
	if document.Components != nil && document.Components.Schemas != nil {
		for _, pair := range document.Components.Schemas.AdditionalProperties {
			summarizeOpenAPIv3Schema(summary, pair.Value)
		}
	}
	for _, pair := range document.Paths.Path {
		summary.PathCount++
		v := pair.Value
		if v.Get != nil {
			summary.GetCount++
		}
		if v.Post != nil {
			summary.PostCount++
		}
		if v.Put != nil {
			summary.PutCount++
		}
		if v.Delete != nil {
			summary.DeleteCount++
		}
	}
	return summary
}

func summarizeOpenAPIv3Schema(summary *metrics.Complexity, schemaOrReference *openapi_v3.SchemaOrReference) {
	summary.SchemaCount++
	schema := schemaOrReference.GetSchema()
	if schema != nil && schema.Properties != nil {
		for _, pair := range schema.Properties.AdditionalProperties {
			summary.SchemaPropertyCount++
			summarizeOpenAPIv3Schema(summary, pair.Value)
		}
	}
}

func SummarizeDiscoveryDocument(document *discovery.Document) *metrics.Complexity {
	summary := &metrics.Complexity{}
	if document.Schemas != nil && document.Schemas.AdditionalProperties != nil {
		for _, pair := range document.Schemas.AdditionalProperties {
			summarizeDiscoverySchema(summary, pair.Value)
		}
	}
	if document.Resources != nil {
		for _, pair := range document.Resources.AdditionalProperties {
			summarizeDiscoveryResource(summary, pair.Value)
		}
	}
	if document.Methods != nil {
		for _, pair := range document.Methods.AdditionalProperties {
			summary.PathCount++
			v := pair.Value
			switch v.HttpMethod {
			case "GET":
				summary.GetCount++
			case "POST":
				summary.PostCount++
			case "PUT":
				summary.PutCount++
			case "DELETE":
				summary.DeleteCount++
			}
		}
	}
	return summary
}

func summarizeDiscoverySchema(summary *metrics.Complexity, schema *discovery.Schema) {
	summary.SchemaCount++
	if schema != nil && schema.Properties != nil {
		for _, pair := range schema.Properties.AdditionalProperties {
			summary.SchemaPropertyCount++
			summarizeDiscoverySchema(summary, pair.Value)
		}
	}
}

func summarizeDiscoveryResource(summary *metrics.Complexity, resource *discovery.Resource) {
	if resource.Resources != nil {
		for _, pair := range resource.Resources.AdditionalProperties {
			summarizeDiscoveryResource(summary, pair.Value)
		}
	}
	if resource.Methods != nil {
		for _, pair := range resource.Methods.AdditionalProperties {
			summary.PathCount++
			v := pair.Value
			switch v.HttpMethod {
			case "GET":
				summary.GetCount++
			case "POST":
				summary.PostCount++
			case "PUT":
				summary.PutCount++
			case "DELETE":
				summary.DeleteCount++
			}
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"context"
	_ "embed"
	"log"
	"os"
	"os/exec"
	"os/signal"
	"path/filepath"
	"sort"
	"strings"
	"syscall"
	"text/template"

	"github.com/urfave/cli/v2"
	"golang.org/x/text/cases"
	"golang.org/x/text/language"
	"gopkg.in/yaml.v3"
)

const (
	defaultPackageName = "cli"
)

var (
	//go:embed generated.gotmpl
	TemplateString string

	//go:embed generated_test.gotmpl
	TestTemplateString string

	titler = cases.Title(language.Und, cases.NoLower)
)

func sh(ctx context.Context, exe string, args ...string) (string, error) {
	cmd := exec.CommandContext(ctx, exe, args...)
	cmd.Stderr = os.Stderr
	outBytes, err := cmd.Output()
	return string(outBytes), err
}

func main() {
	ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
	defer stop()

	top := "../../"
	if v, err := sh(ctx, "git", "rev-parse", "--show-toplevel"); err == nil {
		top = strings.TrimSpace(v)
	}

	app := &cli.App{
		Name:  "genflags",
		Usage: "Generate flag types for urfave/cli",
		Flags: []cli.Flag{
			&cli.PathFlag{
				Name:    "flag-spec-yaml",
				Aliases: []string{"f"},
				Value:   filepath.Join(top, "flag-spec.yaml"),
			},
			&cli.PathFlag{
				Name:    "generated-output",
				Aliases: []string{"o"},
				Value:   filepath.Join(top, "zz_generated.flags.go"),
			},
			&cli.PathFlag{
				Name:    "generated-test-output",
				Aliases: []string{"t"},
				Value:   filepath.Join(top, "zz_generated.flags_test.go"),
			},
			&cli.StringFlag{
				Name:    "generated-package-name",
				Aliases: []string{"p"},
				Value:   defaultPackageName,
			},
			&cli.StringFlag{
				Name:    "generated-test-package-name",
				Aliases: []string{"T"},
				Value:   defaultPackageName + "_test",
			},
			&cli.StringFlag{
				Name:    "urfave-cli-namespace",
				Aliases: []string{"n"},
				Value:   "",
			},
			&cli.StringFlag{
				Name:    "urfave-cli-test-namespace",
				Aliases: []string{"N"},
				Value:   "cli.",
			},
		},
		Action: runGenFlags,
	}

	if err := app.RunContext(ctx, os.Args); err != nil {
		log.Fatal(err)
	}
}

func runGenFlags(cCtx *cli.Context) error {
	specBytes, err := os.ReadFile(cCtx.Path("flag-spec-yaml"))
	if err != nil {
		return err
	}

	spec := &Spec{}
	if err := yaml.Unmarshal(specBytes, spec); err != nil {
		return err
	}

	if cCtx.IsSet("generated-package-name") {
		spec.PackageName = strings.TrimSpace(cCtx.String("generated-package-name"))
	}

	if strings.TrimSpace(spec.PackageName) == "" {
		spec.PackageName = defaultPackageName
	}

	if cCtx.IsSet("generated-test-package-name") {
		spec.TestPackageName = strings.TrimSpace(cCtx.String("generated-test-package-name"))
	}

	if strings.TrimSpace(spec.TestPackageName) == "" {
		spec.TestPackageName = defaultPackageName + "_test"
	}

	if cCtx.IsSet("urfave-cli-namespace") {
		spec.UrfaveCLINamespace = strings.TrimSpace(cCtx.String("urfave-cli-namespace"))
	}

	if cCtx.IsSet("urfave-cli-test-namespace") {
		spec.UrfaveCLITestNamespace = strings.TrimSpace(cCtx.String("urfave-cli-test-namespace"))
	} else {
		spec.UrfaveCLITestNamespace = "cli."
	}

	genTmpl, err := template.New("gen").Parse(TemplateString)
	if err != nil {
		return err
	}

	genTestTmpl, err := template.New("gen_test").Parse(TestTemplateString)
	if err != nil {
		return err
	}

	genBuf := &bytes.Buffer{}
	if err := genTmpl.Execute(genBuf, spec); err != nil {
		return err
	}

	genTestBuf := &bytes.Buffer{}
	if err := genTestTmpl.Execute(genTestBuf, spec); err != nil {
		return err
	}

	if err := os.WriteFile(cCtx.Path("generated-output"), genBuf.Bytes(), 0644); err != nil {
		return err
	}

	if err := os.WriteFile(cCtx.Path("generated-test-output"), genTestBuf.Bytes(), 0644); err != nil {
		return err
	}

	if _, err := sh(cCtx.Context, "goimports", "-w", cCtx.Path("generated-output")); err != nil {
		return err
	}

	if _, err := sh(cCtx.Context, "goimports", "-w", cCtx.Path("generated-test-output")); err != nil {
		return err
	}

	return nil
}

func TypeName(goType string, fc *FlagTypeConfig) string {
	if fc != nil && strings.TrimSpace(fc.TypeName) != "" {
		return strings.TrimSpace(fc.TypeName)
	}

	dotSplit := strings.Split(goType, ".")
	goType = dotSplit[len(dotSplit)-1]

	if strings.HasPrefix(goType, "[]") {
		return titler.String(strings.TrimPrefix(goType, "[]")) + "SliceFlag"
	}

	return titler.String(goType) + "Flag"
}

type Spec struct {
	FlagTypes              map[string]*FlagTypeConfig `yaml:"flag_types"`
	PackageName            string                     `yaml:"package_name"`
	TestPackageName        string                     `yaml:"test_package_name"`
	UrfaveCLINamespace     string                     `yaml:"urfave_cli_namespace"`
	UrfaveCLITestNamespace string                     `yaml:"urfave_cli_test_namespace"`
}

func (gfs *Spec) SortedFlagTypes() []*FlagType {
	typeNames := []string{}

	for name := range gfs.FlagTypes {
		if strings.HasPrefix(name, "[]") {
			name = strings.TrimPrefix(name, "[]") + "Slice"
		}

		typeNames = append(typeNames, name)
	}

	sort.Strings(typeNames)

	ret := make([]*FlagType, len(typeNames))

	for i, typeName := range typeNames {
		ret[i] = &FlagType{
			GoType: typeName,
			Config: gfs.FlagTypes[typeName],
		}
	}

	return ret
}

type FlagTypeConfig struct {
	SkipInterfaces []string           `yaml:"skip_interfaces"`
	StructFields   []*FlagStructField `yaml:"struct_fields"`
	TypeName       string             `yaml:"type_name"`
	ValuePointer   bool               `yaml:"value_pointer"`
}

type FlagStructField struct {
	Name string
	Type string
}

type FlagType struct {
	GoType string
	Config *FlagTypeConfig
}

func (ft *FlagType) StructFields() []*FlagStructField {
	if ft.Config == nil || ft.Config.StructFields == nil {
		return []*FlagStructField{}
	}

	return ft.Config.StructFields
}

func (ft *FlagType) ValuePointer() bool {
	if ft.Config == nil {
		return false
	}

	return ft.Config.ValuePointer
}

func (ft *FlagType) TypeName() string {
	return TypeName(ft.GoType, ft.Config)
}

func (ft *FlagType) GenerateFmtStringerInterface() bool {
	return ft.skipInterfaceNamed("fmt.Stringer")
}

func (ft *FlagType) GenerateFlagInterface() bool {
	return ft.skipInterfaceNamed("Flag")
}

func (ft *FlagType) GenerateRequiredFlagInterface() bool {
	return ft.skipInterfaceNamed("RequiredFlag")
}

func (ft *FlagType) GenerateVisibleFlagInterface() bool {
	return ft.skipInterfaceNamed("VisibleFlag")
}

func (ft *FlagType) skipInterfaceNamed(name string) bool {
	if ft.Config == nil {
		return true
	}

	lowName := strings.ToLower(name)

	for _, interfaceName := range ft.Config.SkipInterfaces {
		if strings.ToLower(interfaceName) == lowName {
			return false
		}
	}

	return true
}
<commit_msg>Update for build<commit_after>package main

import (
	"bytes"
	"context"
	_ "embed"
	"log"
	"os"
	"os/exec"
	"os/signal"
	"path/filepath"
	"sort"
	"strings"
	"syscall"
	"text/template"

	"github.com/urfave/cli/v2"
	"golang.org/x/text/cases"
	"golang.org/x/text/language"
	"gopkg.in/yaml.v3"
)

const (
	defaultPackageName = "cli"
)

var (
	//go:embed generated.gotmpl
	TemplateString string

	//go:embed generated_test.gotmpl
	TestTemplateString string

	titler = cases.Title(language.Und, cases.NoLower)
)

func sh(ctx context.Context, exe string, args ...string) (string, error) {
	cmd := exec.CommandContext(ctx, exe, args...)
	cmd.Stderr = os.Stderr
	outBytes, err := cmd.Output()
	return string(outBytes), err
}

func main() {
	ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
	defer stop()

	top := "../../"
	if v, err := sh(ctx, "git", "rev-parse", "--show-toplevel"); err == nil {
		top = strings.TrimSpace(v)
	}

	app := &cli.App{
		Name:  "genflags",
		Usage: "Generate flag types for urfave/cli",
		Flags: []cli.Flag{
			&cli.PathFlag{
				Name:    "flag-spec-yaml",
				Aliases: []string{"f"},
				Value:   filepath.Join(top, "flag-spec.yaml"),
			},
			&cli.PathFlag{
				Name:    "generated-output",
				Aliases: []string{"o"},
				Value:   filepath.Join(top, "zz_generated.flags.go"),
			},
			&cli.PathFlag{
				Name:    "generated-test-output",
				Aliases: []string{"t"},
				Value:   filepath.Join(top, "zz_generated.flags_test.go"),
			},
			&cli.StringFlag{
				Name:    "generated-package-name",
				Aliases: []string{"p"},
				Value:   defaultPackageName,
			},
			&cli.StringFlag{
				Name:    "generated-test-package-name",
				Aliases: []string{"T"},
				Value:   defaultPackageName + "_test",
			},
			&cli.StringFlag{
				Name:    "urfave-cli-namespace",
				Aliases: []string{"n"},
				Value:   "",
			},
			&cli.StringFlag{
				Name:    "urfave-cli-test-namespace",
				Aliases: []string{"N"},
				Value:   "cli.",
			},
		},
		Action: runGenFlags,
	}

	if err := app.RunContext(ctx, os.Args); err != nil {
		log.Fatal(err)
	}
}

func runGenFlags(cCtx *cli.Context) error {
	specBytes, err := os.ReadFile(cCtx.Path("flag-spec-yaml"))
	if err != nil {
		return err
	}

	spec := &Spec{}
	if err := yaml.Unmarshal(specBytes, spec); err != nil {
		return err
	}

	if cCtx.IsSet("generated-package-name") {
		spec.PackageName = strings.TrimSpace(cCtx.String("generated-package-name"))
	}

	if strings.TrimSpace(spec.PackageName) == "" {
		spec.PackageName = defaultPackageName
	}

	if cCtx.IsSet("generated-test-package-name") {
		spec.TestPackageName = strings.TrimSpace(cCtx.String("generated-test-package-name"))
	}

	if strings.TrimSpace(spec.TestPackageName) == "" {
		spec.TestPackageName = defaultPackageName + "_test"
	}

	if cCtx.IsSet("urfave-cli-namespace") {
		spec.UrfaveCLINamespace = strings.TrimSpace(cCtx.String("urfave-cli-namespace"))
	}

	if cCtx.IsSet("urfave-cli-test-namespace") {
		spec.UrfaveCLITestNamespace = strings.TrimSpace(cCtx.String("urfave-cli-test-namespace"))
	} else {
		spec.UrfaveCLITestNamespace = "cli."
	}

	genTmpl, err := template.New("gen").Parse(TemplateString)
	if err != nil {
		return err
	}

	genTestTmpl, err := template.New("gen_test").Parse(TestTemplateString)
	if err != nil {
		return err
	}

	genBuf := &bytes.Buffer{}
	if err := genTmpl.Execute(genBuf, spec); err != nil {
		return err
	}

	genTestBuf := &bytes.Buffer{}
	if err := genTestTmpl.Execute(genTestBuf, spec); err != nil {
		return err
	}

	if err := os.WriteFile(cCtx.Path("generated-output"), genBuf.Bytes(), 0644); err != nil {
		return err
	}

	if err := os.WriteFile(cCtx.Path("generated-test-output"), genTestBuf.Bytes(), 0644); err != nil {
		return err
	}

	if _, err := sh(cCtx.Context, "goimports", "-w", cCtx.Path("generated-output")); err != nil {
		return err
	}

	if _, err := sh(cCtx.Context, "goimports", "-w", cCtx.Path("generated-test-output")); err != nil {
		return err
	}

	return nil
}

func TypeName(goType string, fc *FlagTypeConfig) string {
	if fc != nil && strings.TrimSpace(fc.TypeName) != "" {
		return strings.TrimSpace(fc.TypeName)
	}

	dotSplit := strings.Split(goType, ".")
	goType = dotSplit[len(dotSplit)-1]

	if strings.HasPrefix(goType, "[]") {
		return titler.String(strings.TrimPrefix(goType, "[]")) + "SliceFlag"
	}

	return titler.String(goType) + "Flag"
}

type Spec struct {
	FlagTypes              map[string]*FlagTypeConfig `yaml:"flag_types"`
	PackageName            string                     `yaml:"package_name"`
	TestPackageName        string                     `yaml:"test_package_name"`
	UrfaveCLINamespace     string                     `yaml:"urfave_cli_namespace"`
	UrfaveCLITestNamespace string                     `yaml:"urfave_cli_test_namespace"`
}

func (gfs *Spec) SortedFlagTypes() []*FlagType {
	typeNames := []string{}

	for name := range gfs.FlagTypes {
		if strings.HasPrefix(name, "[]") {
			name = strings.TrimPrefix(name, "[]") + "Slice"
		}

		typeNames = append(typeNames, name)
	}

	sort.Strings(typeNames)

	ret := make([]*FlagType, len(typeNames))

	for i, typeName := range typeNames {
		ret[i] = &FlagType{
			GoType: typeName,
			Config: gfs.FlagTypes[typeName],
		}
	}

	return ret
}

type FlagTypeConfig struct {
	SkipInterfaces []string           `yaml:"skip_interfaces"`
	StructFields   []*FlagStructField `yaml:"struct_fields"`
	TypeName       string             `yaml:"type_name"`
	ValuePointer   bool               `yaml:"value_pointer"`
}

type FlagStructField struct {
	Name    string
	Type    string
	Pointer bool
}

type FlagType struct {
	GoType string
	Config *FlagTypeConfig
}

func (ft *FlagType) StructFields() []*FlagStructField {
	if ft.Config == nil || ft.Config.StructFields == nil {
		return []*FlagStructField{}
	}

	return ft.Config.StructFields
}

func (ft *FlagType) ValuePointer() bool {
	if ft.Config == nil {
		return false
	}

	return ft.Config.ValuePointer
}

func (ft *FlagType) TypeName() string {
	return TypeName(ft.GoType, ft.Config)
}

func (ft *FlagType) GenerateFmtStringerInterface() bool {
	return ft.skipInterfaceNamed("fmt.Stringer")
}

func (ft *FlagType) GenerateFlagInterface() bool {
	return ft.skipInterfaceNamed("Flag")
}

func (ft *FlagType) GenerateRequiredFlagInterface() bool {
	return ft.skipInterfaceNamed("RequiredFlag")
}

func (ft *FlagType) GenerateVisibleFlagInterface() bool {
	return ft.skipInterfaceNamed("VisibleFlag")
}

func (ft *FlagType) skipInterfaceNamed(name string) bool {
	if ft.Config == nil {
		return true
	}

	lowName := strings.ToLower(name)

	for _, interfaceName := range ft.Config.SkipInterfaces {
		if strings.ToLower(interfaceName) == lowName {
			return false
		}
	}

	return true
}
<|endoftext|>


<commit_before>package startCmd

import (
	// Stdlib
	"errors"
	"fmt"
	"os"

	// Internal
	"github.com/salsaflow/salsaflow/action"
	"github.com/salsaflow/salsaflow/app"
	"github.com/salsaflow/salsaflow/errs"
	"github.com/salsaflow/salsaflow/git"
	"github.com/salsaflow/salsaflow/log"
	"github.com/salsaflow/salsaflow/modules"
	"github.com/salsaflow/salsaflow/modules/common"
	"github.com/salsaflow/salsaflow/prompt"

	// Other
	"github.com/extemporalgenome/slug"
	"gopkg.in/tchap/gocli.v2"
)

var Command = &gocli.Command{
	UsageLine: "start [-no_branch] [-no_push]",
	Short:     "start a new story",
	Long: `
Start a new issue tracker story.

The user is shown the list of stories that can be started.
When they choose one, they are assigned to that story and the story
is started in the issue tracker.

Unless -no_branch is specified, the user is asked to insert
the branch name to be used for the branch holding the story commits.
The branch of the given name is created on top of the trunk branch
and checked out. Then it is pushed unless -no_push is specified.
	`,
	Action: run,
}

var (
	flagNoBranch bool
	flagNoPush   bool
)

func init() {
	Command.Flags.BoolVar(&flagNoBranch, "no_branch", flagNoBranch,
		"do not create a new story branch")
	Command.Flags.BoolVar(&flagNoPush, "no_push", flagNoPush,
		"do not push the newly created story branch")
}

func run(cmd *gocli.Command, args []string) {
	if len(args) != 0 {
		cmd.Usage()
		os.Exit(2)
	}

	app.InitOrDie()

	// Exit cleanly when the panic is actually ErrCanceled.
	defer func() {
		if r := recover(); r != nil {
			if r == prompt.ErrCanceled {
				log.Println("\nOperation canceled. You are welcome to come back any time!")
			} else {
				panic(r)
			}
		}
	}()

	if err := runMain(); err != nil {
		errs.Fatal(err)
	}
}

func runMain() error {
	tracker, err := modules.GetIssueTracker()
	if err != nil {
		return err
	}

	// Fetch stories from the issue tracker.
	task := "Fetch stories from the issue tracker"
	log.Run(task)
	stories, err := tracker.StartableStories()
	if err != nil {
		return errs.NewError(task, err)
	}
	if len(stories) == 0 {
		return errs.NewError(task, errors.New("no startable stories found"))
	}

	// Filter out the stories that are not relevant,
	// i.e. not owned by the current user or assigned to someone else.
	task = "Fetch the current user record from the issue tracker"
	user, err := tracker.CurrentUser()
	if err != nil {
		return errs.NewError(task, err)
	}

	var filteredStories []common.Story
StoryLoop:
	for _, story := range stories {
		assignees := story.Assignees()
		// Include the story in case there is no assignee set yet.
		if len(assignees) == 0 {
			filteredStories = append(filteredStories, story)
			continue StoryLoop
		}
		// Include the story in case the current user is assigned.
		for _, assignee := range assignees {
			if assignee.Id() == user.Id() {
				filteredStories = append(filteredStories, story)
				continue StoryLoop
			}
		}
	}
	stories = filteredStories

	// Prompt the user to select a story.
	story, err := prompt.PromptStory(
		"\nYou can start working on one of the following stories:", stories)
	if err != nil {
		switch err {
		case prompt.ErrNoStories:
			return errors.New("no startable stories found")
		case prompt.ErrCanceled:
			prompt.PanicCancel()
		default:
			return err
		}
	}
	fmt.Println()

	// Create the story branch, optionally.
	if flagNoBranch {
		log.Log("Not creating any feature branch")
	} else {
		var act action.Action
		act, err = createBranch()
		if err != nil {
			return err
		}
		// Roll back on error.
		defer action.RollbackTaskOnError(&err, task, act)
	}

	// Add the current user to the list of story assignees.
	task = "Amend the list of story assignees"
	log.Run(task)
	originalAssignees := story.Assignees()
	if err := story.AddAssignee(user); err != nil {
		return errs.NewError(task, err)
	}
	defer action.RollbackTaskOnError(&err, task, action.ActionFunc(func() error {
		task := "Reset the list of story assignees"
		if err := story.SetAssignees(originalAssignees); err != nil {
			return errs.NewError(task, err)
		}
		return nil
	}))

	// Start the selected story. No need to roll back.
	task = "Start the selected story"
	log.Run(task)
	if err := story.Start(); err != nil {
		return errs.NewError(task, err)
	}

	return nil
}

func createBranch() (action.Action, error) {
	// Get the current branch name.
	originalBranch, err := git.CurrentBranch()
	if err != nil {
		return nil, err
	}

	// Fetch the remote repository.
	task := "Fetch the remote repository"
	log.Run(task)

	gitConfig, err := git.LoadConfig()
	if err != nil {
		return nil, errs.NewError(task, err)
	}

	var (
		remoteName  = gitConfig.RemoteName()
		trunkBranch = gitConfig.TrunkBranchName()
	)
	if err := git.UpdateRemotes(remoteName); err != nil {
		return nil, errs.NewError(task, err)
	}

	// Make sure the trunk branch is up to date.
	task = fmt.Sprintf("Make sure branch '%v' is up to date", trunkBranch)
	log.Run(task)
	if err := git.EnsureBranchSynchronized(trunkBranch, remoteName); err != nil {
		return nil, errs.NewError(task, err)
	}

	// Prompt the user for the branch name.
	task = "Prompt the user for the branch name"
	line, err := prompt.Prompt(`
Please insert the branch slug now.
Insert an empty string to skip the branch creation step: `)
	if err != nil {
		return nil, errs.NewError(task, err)
	}

	sluggedLine := slug.Slug(line)
	if sluggedLine == "" {
		fmt.Println()
		log.Log("Not creating any feature branch")
		return nil, nil
	}

	branchName := "story/" + sluggedLine
	ok, err := prompt.Confirm(
		fmt.Sprintf("\nThe branch that is going to be created will be called '%s'.\nIs that alright?", branchName))
	if err != nil {
		return nil, errs.NewError(task, err)
	}
	if !ok {
		panic(prompt.ErrCanceled)
	}
	fmt.Println()

	createTask := fmt.Sprintf(
		"Create branch '%v' on top of branch '%v'", branchName, trunkBranch)
	log.Run(createTask)
	if err := git.Branch(branchName, trunkBranch); err != nil {
		return nil, errs.NewError(createTask, err)
	}

	deleteTask := fmt.Sprintf("Delete branch '%v'", branchName)
	deleteBranch := func() error {
		// Roll back and delete the newly created branch.
		log.Rollback(createTask)
		if err := git.Branch("-D", branchName); err != nil {
			return errs.NewError(deleteTask, err)
		}
		return nil
	}

	// Checkout the newly created branch.
	checkoutTask := fmt.Sprintf("Checkout branch '%v'", branchName)
	log.Run(checkoutTask)
	if err := git.Checkout(branchName); err != nil {
		if err := deleteBranch(); err != nil {
			errs.Log(err)
		}
		return nil, errs.NewError(checkoutTask, err)
	}

	// Push the newly created branch unless -no_push.
	pushTask := fmt.Sprintf("Push branch '%v' to remote '%v'", branchName, remoteName)
	if !flagNoPush {
		log.Run(pushTask)
		if err := git.Push(remoteName, branchName); err != nil {
			if err := deleteBranch(); err != nil {
				errs.Log(err)
			}
			return nil, errs.NewError(pushTask, err)
		}
	}

	return action.ActionFunc(func() error {
		// Checkout the original branch.
		log.Rollback(checkoutTask)
		if err := git.Checkout(originalBranch); err != nil {
			return errs.NewError(
				fmt.Sprintf("Checkout the original branch '%v'", originalBranch), err)
		}

		// Delete the newly created branch.
		deleteErr := deleteBranch()

		// In case we haven't pushed anything, we are done.
		if flagNoPush {
			return deleteErr
		}

		// Delete the branch from the remote repository.
		log.Rollback(pushTask)
		if _, err := git.Run("push", "--delete", remoteName, branchName); err != nil {
			// In case deleteBranch failed, tell the user now
			// since we are not going to return that error.
			if deleteErr != nil {
				errs.Log(deleteErr)
			}

			return errs.NewError(
				fmt.Sprintf("Delete branch '%v' from remote '%v'", branchName, remoteName), err)
		}

		// Return deleteErr to make sure it propagates up.
		return deleteErr
	}), nil
}
<commit_msg>story start: Fix broken prompt<commit_after>package startCmd

import (
	// Stdlib
	"errors"
	"fmt"
	"os"

	// Internal
	"github.com/salsaflow/salsaflow/action"
	"github.com/salsaflow/salsaflow/app"
	"github.com/salsaflow/salsaflow/errs"
	"github.com/salsaflow/salsaflow/git"
	"github.com/salsaflow/salsaflow/log"
	"github.com/salsaflow/salsaflow/modules"
	"github.com/salsaflow/salsaflow/modules/common"
	"github.com/salsaflow/salsaflow/prompt"

	// Other
	"github.com/extemporalgenome/slug"
	"gopkg.in/tchap/gocli.v2"
)

var Command = &gocli.Command{
	UsageLine: "start [-no_branch] [-no_push]",
	Short:     "start a new story",
	Long: `
Start a new issue tracker story.

The user is shown the list of stories that can be started.
When they choose one, they are assigned to that story and the story
is started in the issue tracker.

Unless -no_branch is specified, the user is asked to insert
the branch name to be used for the branch holding the story commits.
The branch of the given name is created on top of the trunk branch
and checked out. Then it is pushed unless -no_push is specified.
	`,
	Action: run,
}

var (
	flagNoBranch bool
	flagNoPush   bool
)

func init() {
	Command.Flags.BoolVar(&flagNoBranch, "no_branch", flagNoBranch,
		"do not create a new story branch")
	Command.Flags.BoolVar(&flagNoPush, "no_push", flagNoPush,
		"do not push the newly created story branch")
}

func run(cmd *gocli.Command, args []string) {
	if len(args) != 0 {
		cmd.Usage()
		os.Exit(2)
	}

	app.InitOrDie()

	// Exit cleanly when the panic is actually ErrCanceled.
	defer func() {
		if r := recover(); r != nil {
			if r == prompt.ErrCanceled {
				log.Println("\nOperation canceled. You are welcome to come back any time!")
			} else {
				panic(r)
			}
		}
	}()

	if err := runMain(); err != nil {
		errs.Fatal(err)
	}
}

func runMain() error {
	tracker, err := modules.GetIssueTracker()
	if err != nil {
		return err
	}

	// Fetch stories from the issue tracker.
	task := "Fetch stories from the issue tracker"
	log.Run(task)
	stories, err := tracker.StartableStories()
	if err != nil {
		return errs.NewError(task, err)
	}
	if len(stories) == 0 {
		return errs.NewError(task, errors.New("no startable stories found"))
	}

	// Filter out the stories that are not relevant,
	// i.e. not owned by the current user or assigned to someone else.
	task = "Fetch the current user record from the issue tracker"
	user, err := tracker.CurrentUser()
	if err != nil {
		return errs.NewError(task, err)
	}

	var filteredStories []common.Story
StoryLoop:
	for _, story := range stories {
		assignees := story.Assignees()
		// Include the story in case there is no assignee set yet.
		if len(assignees) == 0 {
			filteredStories = append(filteredStories, story)
			continue StoryLoop
		}
		// Include the story in case the current user is assigned.
		for _, assignee := range assignees {
			if assignee.Id() == user.Id() {
				filteredStories = append(filteredStories, story)
				continue StoryLoop
			}
		}
	}
	stories = filteredStories

	// Prompt the user to select a story.
	story, err := prompt.PromptStory(
		"\nYou can start working on one of the following stories:", stories)
	if err != nil {
		switch err {
		case prompt.ErrNoStories:
			return errors.New("no startable stories found")
		case prompt.ErrCanceled:
			prompt.PanicCancel()
		default:
			return err
		}
	}
	fmt.Println()

	// Create the story branch, optionally.
	if flagNoBranch {
		log.Log("Not creating any feature branch")
	} else {
		var act action.Action
		act, err = createBranch()
		if err != nil {
			return err
		}
		// Roll back on error.
		defer action.RollbackTaskOnError(&err, task, act)
	}

	// Add the current user to the list of story assignees.
	task = "Amend the list of story assignees"
	log.Run(task)
	originalAssignees := story.Assignees()
	if err := story.AddAssignee(user); err != nil {
		return errs.NewError(task, err)
	}
	defer action.RollbackTaskOnError(&err, task, action.ActionFunc(func() error {
		task := "Reset the list of story assignees"
		if err := story.SetAssignees(originalAssignees); err != nil {
			return errs.NewError(task, err)
		}
		return nil
	}))

	// Start the selected story. No need to roll back.
	task = "Start the selected story"
	log.Run(task)
	if err := story.Start(); err != nil {
		return errs.NewError(task, err)
	}

	return nil
}

func createBranch() (action.Action, error) {
	// Get the current branch name.
	originalBranch, err := git.CurrentBranch()
	if err != nil {
		return nil, err
	}

	// Fetch the remote repository.
	task := "Fetch the remote repository"
	log.Run(task)

	gitConfig, err := git.LoadConfig()
	if err != nil {
		return nil, errs.NewError(task, err)
	}

	var (
		remoteName  = gitConfig.RemoteName()
		trunkBranch = gitConfig.TrunkBranchName()
	)
	if err := git.UpdateRemotes(remoteName); err != nil {
		return nil, errs.NewError(task, err)
	}

	// Make sure the trunk branch is up to date.
	task = fmt.Sprintf("Make sure branch '%v' is up to date", trunkBranch)
	log.Run(task)
	if err := git.EnsureBranchSynchronized(trunkBranch, remoteName); err != nil {
		return nil, errs.NewError(task, err)
	}

	// Prompt the user for the branch name.
	task = "Prompt the user for the branch name"
	line, err := prompt.Prompt(`
Please insert the branch slug now.
Insert an empty string to skip the branch creation step: `)
	if err != nil && err != prompt.ErrCanceled {
		return nil, errs.NewError(task, err)
	}

	sluggedLine := slug.Slug(line)
	if sluggedLine == "" {
		fmt.Println()
		log.Log("Not creating any feature branch")
		return nil, nil
	}

	branchName := "story/" + sluggedLine
	ok, err := prompt.Confirm(
		fmt.Sprintf("\nThe branch that is going to be created will be called '%s'.\nIs that alright?", branchName))
	if err != nil {
		return nil, errs.NewError(task, err)
	}
	if !ok {
		panic(prompt.ErrCanceled)
	}
	fmt.Println()

	createTask := fmt.Sprintf(
		"Create branch '%v' on top of branch '%v'", branchName, trunkBranch)
	log.Run(createTask)
	if err := git.Branch(branchName, trunkBranch); err != nil {
		return nil, errs.NewError(createTask, err)
	}

	deleteTask := fmt.Sprintf("Delete branch '%v'", branchName)
	deleteBranch := func() error {
		// Roll back and delete the newly created branch.
		log.Rollback(createTask)
		if err := git.Branch("-D", branchName); err != nil {
			return errs.NewError(deleteTask, err)
		}
		return nil
	}

	// Checkout the newly created branch.
	checkoutTask := fmt.Sprintf("Checkout branch '%v'", branchName)
	log.Run(checkoutTask)
	if err := git.Checkout(branchName); err != nil {
		if err := deleteBranch(); err != nil {
			errs.Log(err)
		}
		return nil, errs.NewError(checkoutTask, err)
	}

	// Push the newly created branch unless -no_push.
	pushTask := fmt.Sprintf("Push branch '%v' to remote '%v'", branchName, remoteName)
	if !flagNoPush {
		log.Run(pushTask)
		if err := git.Push(remoteName, branchName); err != nil {
			if err := deleteBranch(); err != nil {
				errs.Log(err)
			}
			return nil, errs.NewError(pushTask, err)
		}
	}

	return action.ActionFunc(func() error {
		// Checkout the original branch.
		log.Rollback(checkoutTask)
		if err := git.Checkout(originalBranch); err != nil {
			return errs.NewError(
				fmt.Sprintf("Checkout the original branch '%v'", originalBranch), err)
		}

		// Delete the newly created branch.
		deleteErr := deleteBranch()

		// In case we haven't pushed anything, we are done.
		if flagNoPush {
			return deleteErr
		}

		// Delete the branch from the remote repository.
		log.Rollback(pushTask)
		if _, err := git.Run("push", "--delete", remoteName, branchName); err != nil {
			// In case deleteBranch failed, tell the user now
			// since we are not going to return that error.
			if deleteErr != nil {
				errs.Log(deleteErr)
			}

			return errs.NewError(
				fmt.Sprintf("Delete branch '%v' from remote '%v'", branchName, remoteName), err)
		}

		// Return deleteErr to make sure it propagates up.
		return deleteErr
	}), nil
}
<|endoftext|>


<commit_before>// Copyright 2013 The Go 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 debug

import (
	"runtime"
	"sort"
	"time"
)

// GCStats collect information about recent garbage collections.
type GCStats struct {
	LastGC         time.Time       // time of last collection
	NumGC          int64           // number of garbage collections
	PauseTotal     time.Duration   // total pause for all collections
	Pause          []time.Duration // pause history, most recent first
	PauseQuantiles []time.Duration
}

// Implemented in package runtime.
func readGCStats(*[]time.Duration)
func enableGC(bool) bool
func setGCPercent(int) int
func freeOSMemory()
func setMaxStack(int) int
func setMaxThreads(int) int

// ReadGCStats reads statistics about garbage collection into stats.
// The number of entries in the pause history is system-dependent;
// stats.Pause slice will be reused if large enough, reallocated otherwise.
// ReadGCStats may use the full capacity of the stats.Pause slice.
// If stats.PauseQuantiles is non-empty, ReadGCStats fills it with quantiles
// summarizing the distribution of pause time. For example, if
// len(stats.PauseQuantiles) is 5, it will be filled with the minimum,
// 25%, 50%, 75%, and maximum pause times.
func ReadGCStats(stats *GCStats) {
	// Create a buffer with space for at least two copies of the
	// pause history tracked by the runtime. One will be returned
	// to the caller and the other will be used as a temporary buffer
	// for computing quantiles.
	const maxPause = len(((*runtime.MemStats)(nil)).PauseNs)
	if cap(stats.Pause) < 2*maxPause {
		stats.Pause = make([]time.Duration, 2*maxPause)
	}

	// readGCStats fills in the pause history (up to maxPause entries)
	// and then three more: Unix ns time of last GC, number of GC,
	// and total pause time in nanoseconds. Here we depend on the
	// fact that time.Duration's native unit is nanoseconds, so the
	// pauses and the total pause time do not need any conversion.
	readGCStats(&stats.Pause)
	n := len(stats.Pause) - 3
	stats.LastGC = time.Unix(0, int64(stats.Pause[n]))
	stats.NumGC = int64(stats.Pause[n+1])
	stats.PauseTotal = stats.Pause[n+2]
	stats.Pause = stats.Pause[:n]

	if len(stats.PauseQuantiles) > 0 {
		if n == 0 {
			for i := range stats.PauseQuantiles {
				stats.PauseQuantiles[i] = 0
			}
		} else {
			// There's room for a second copy of the data in stats.Pause.
			// See the allocation at the top of the function.
			sorted := stats.Pause[n : n+n]
			copy(sorted, stats.Pause)
			sort.Sort(byDuration(sorted))
			nq := len(stats.PauseQuantiles) - 1
			for i := 0; i < nq; i++ {
				stats.PauseQuantiles[i] = sorted[len(sorted)*i/nq]
			}
			stats.PauseQuantiles[nq] = sorted[len(sorted)-1]
		}
	}
}

type byDuration []time.Duration

func (x byDuration) Len() int           { return len(x) }
func (x byDuration) Swap(i, j int)      { x[i], x[j] = x[j], x[i] }
func (x byDuration) Less(i, j int) bool { return x[i] < x[j] }

// SetGCPercent sets the garbage collection target percentage:
// a collection is triggered when the ratio of freshly allocated data
// to live data remaining after the previous collection reaches this percentage.
// SetGCPercent returns the previous setting.
// The initial setting is the value of the GOGC environment variable
// at startup, or 100 if the variable is not set.
// A negative percentage disables garbage collection.
func SetGCPercent(percent int) int {
	return setGCPercent(percent)
}

// FreeOSMemory forces a garbage collection followed by an
// attempt to return as much memory to the operating system
// as possible. (Even if this is not called, the runtime gradually
// returns memory to the operating system in a background task.)
func FreeOSMemory() {
	freeOSMemory()
}

// SetMaxStack sets the maximum amount of memory that
// can be used by a single goroutine stack.
// If any goroutine exceeds this limit while growing its stack,
// the program crashes.
// SetMaxStack returns the previous setting.
// The initial setting is 1 GB on 64-bit systems, 250 MB on 32-bit systems.
//
// SetMaxStack is useful mainly for limiting the damage done by
// goroutines that enter an infinite recursion. It only limits future
// stack growth.
func SetMaxStack(bytes int) int {
	return setMaxStack(bytes)
}

// SetMaxThreads sets the maximum number of operating system
// threads that the Go program can use. If it attempts to use more than
// this many, the program crashes.
// SetMaxThreads returns the previous setting.
// The initial setting is 10,000 threads.
//
// The limit controls the number of operating system threads, not the number
// of goroutines. A Go program creates a new thread only when a goroutine
// is ready to run but all the existing threads are blocked in system calls, cgo calls,
// or are locked to other goroutines due to use of runtime.LockOSThread.
//
// SetMaxThreads is useful mainly for limiting the damage done by
// programs that create an unbounded number of threads. The idea is
// to take down the program before it takes down the operating system.
func SetMaxThreads(threads int) int {
	return setMaxThreads(threads)
}
<commit_msg>runtime/debug: force GC after setting of GCPercent to make it effective. See also discussion in CL 51010045.<commit_after>// Copyright 2013 The Go 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 debug

import (
	"runtime"
	"sort"
	"time"
)

// GCStats collect information about recent garbage collections.
type GCStats struct {
	LastGC         time.Time       // time of last collection
	NumGC          int64           // number of garbage collections
	PauseTotal     time.Duration   // total pause for all collections
	Pause          []time.Duration // pause history, most recent first
	PauseQuantiles []time.Duration
}

// Implemented in package runtime.
func readGCStats(*[]time.Duration)
func enableGC(bool) bool
func setGCPercent(int) int
func freeOSMemory()
func setMaxStack(int) int
func setMaxThreads(int) int

// ReadGCStats reads statistics about garbage collection into stats.
// The number of entries in the pause history is system-dependent;
// stats.Pause slice will be reused if large enough, reallocated otherwise.
// ReadGCStats may use the full capacity of the stats.Pause slice.
// If stats.PauseQuantiles is non-empty, ReadGCStats fills it with quantiles
// summarizing the distribution of pause time. For example, if
// len(stats.PauseQuantiles) is 5, it will be filled with the minimum,
// 25%, 50%, 75%, and maximum pause times.
func ReadGCStats(stats *GCStats) {
	// Create a buffer with space for at least two copies of the
	// pause history tracked by the runtime. One will be returned
	// to the caller and the other will be used as a temporary buffer
	// for computing quantiles.
	const maxPause = len(((*runtime.MemStats)(nil)).PauseNs)
	if cap(stats.Pause) < 2*maxPause {
		stats.Pause = make([]time.Duration, 2*maxPause)
	}

	// readGCStats fills in the pause history (up to maxPause entries)
	// and then three more: Unix ns time of last GC, number of GC,
	// and total pause time in nanoseconds. Here we depend on the
	// fact that time.Duration's native unit is nanoseconds, so the
	// pauses and the total pause time do not need any conversion.
	readGCStats(&stats.Pause)
	n := len(stats.Pause) - 3
	stats.LastGC = time.Unix(0, int64(stats.Pause[n]))
	stats.NumGC = int64(stats.Pause[n+1])
	stats.PauseTotal = stats.Pause[n+2]
	stats.Pause = stats.Pause[:n]

	if len(stats.PauseQuantiles) > 0 {
		if n == 0 {
			for i := range stats.PauseQuantiles {
				stats.PauseQuantiles[i] = 0
			}
		} else {
			// There's room for a second copy of the data in stats.Pause.
			// See the allocation at the top of the function.
			sorted := stats.Pause[n : n+n]
			copy(sorted, stats.Pause)
			sort.Sort(byDuration(sorted))
			nq := len(stats.PauseQuantiles) - 1
			for i := 0; i < nq; i++ {
				stats.PauseQuantiles[i] = sorted[len(sorted)*i/nq]
			}
			stats.PauseQuantiles[nq] = sorted[len(sorted)-1]
		}
	}
}

type byDuration []time.Duration

func (x byDuration) Len() int           { return len(x) }
func (x byDuration) Swap(i, j int)      { x[i], x[j] = x[j], x[i] }
func (x byDuration) Less(i, j int) bool { return x[i] < x[j] }

// SetGCPercent sets the garbage collection target percentage:
// a collection is triggered when the ratio of freshly allocated data
// to live data remaining after the previous collection reaches this percentage.
// SetGCPercent returns the previous setting.
// The initial setting is the value of the GOGC environment variable
// at startup, or 100 if the variable is not set.
// A negative percentage disables garbage collection.
func SetGCPercent(percent int) int {
	old := setGCPercent(percent)
	runtime.GC()
	return old
}

// FreeOSMemory forces a garbage collection followed by an
// attempt to return as much memory to the operating system
// as possible. (Even if this is not called, the runtime gradually
// returns memory to the operating system in a background task.)
func FreeOSMemory() {
	freeOSMemory()
}

// SetMaxStack sets the maximum amount of memory that
// can be used by a single goroutine stack.
// If any goroutine exceeds this limit while growing its stack,
// the program crashes.
// SetMaxStack returns the previous setting.
// The initial setting is 1 GB on 64-bit systems, 250 MB on 32-bit systems.
//
// SetMaxStack is useful mainly for limiting the damage done by
// goroutines that enter an infinite recursion. It only limits future
// stack growth.
func SetMaxStack(bytes int) int {
	return setMaxStack(bytes)
}

// SetMaxThreads sets the maximum number of operating system
// threads that the Go program can use. If it attempts to use more than
// this many, the program crashes.
// SetMaxThreads returns the previous setting.
// The initial setting is 10,000 threads.
//
// The limit controls the number of operating system threads, not the number
// of goroutines. A Go program creates a new thread only when a goroutine
// is ready to run but all the existing threads are blocked in system calls, cgo calls,
// or are locked to other goroutines due to use of runtime.LockOSThread.
//
// SetMaxThreads is useful mainly for limiting the damage done by
// programs that create an unbounded number of threads. The idea is
// to take down the program before it takes down the operating system.
func SetMaxThreads(threads int) int {
	return setMaxThreads(threads)
}
<|endoftext|>


<commit_before>package fuse

import (
	"crypto/sha256"
	"io/ioutil"
	"math/rand"
	"os"
	"path/filepath"
	"strings"
	"testing"
	"time"

	"github.com/pachyderm/pachyderm/src/client"
	"github.com/pachyderm/pachyderm/src/client/pfs"
	"github.com/pachyderm/pachyderm/src/client/pkg/require"
	"github.com/pachyderm/pachyderm/src/server/pfs/server"
	"github.com/pachyderm/pachyderm/src/server/pkg/workload"
)

const (
	MB = 1024 * 1024
	GB = 1024 * 1024 * 1024
)

func TestBasic(t *testing.T) {
	c := server.GetPachClient(t)
	require.NoError(t, c.CreateRepo("repo"))
	_, err := c.PutFile("repo", "master", "file", strings.NewReader("foo"))
	require.NoError(t, err)
	mount(t, c, nil, func(mountPoint string) {
		repos, err := ioutil.ReadDir(mountPoint)
		require.NoError(t, err)
		require.Equal(t, 1, len(repos))
		require.Equal(t, "repo", filepath.Base(repos[0].Name()))

		files, err := ioutil.ReadDir(filepath.Join(mountPoint, "repo"))
		require.NoError(t, err)
		require.Equal(t, 1, len(files))
		require.Equal(t, "file", filepath.Base(files[0].Name()))

		data, err := ioutil.ReadFile(filepath.Join(mountPoint, "repo", "file"))
		require.NoError(t, err)
		require.Equal(t, "foo", string(data))
	})
}

func TestChunkSize(t *testing.T) {
	c := server.GetPachClient(t)
	require.NoError(t, c.CreateRepo("repo"))
	_, err := c.PutFile("repo", "master", "file", strings.NewReader(strings.Repeat("p", int(pfs.ChunkSize))))
	require.NoError(t, err)
	mount(t, c, nil, func(mountPoint string) {
		data, err := ioutil.ReadFile(filepath.Join(mountPoint, "repo", "file"))
		require.NoError(t, err)
		require.Equal(t, int(pfs.ChunkSize), len(data))
	})
}

func TestLargeFile(t *testing.T) {
	c := server.GetPachClient(t)
	require.NoError(t, c.CreateRepo("repo"))
	src := workload.RandString(rand.New(rand.NewSource(123)), GB+17)
	_, err := c.PutFile("repo", "master", "file", strings.NewReader(src))
	require.NoError(t, err)
	mount(t, c, nil, func(mountPoint string) {
		data, err := ioutil.ReadFile(filepath.Join(mountPoint, "repo", "file"))
		require.NoError(t, err)
		require.Equal(t, sha256.Sum256([]byte(src)), sha256.Sum256(data))
	})
}

func BenchmarkLargeFile(b *testing.B) {
	c := server.GetPachClient(b)
	require.NoError(b, c.CreateRepo("repo"))
	src := workload.RandString(rand.New(rand.NewSource(123)), GB)
	_, err := c.PutFile("repo", "master", "file", strings.NewReader(src))
	require.NoError(b, err)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		mount(b, c, nil, func(mountPoint string) {
			data, err := ioutil.ReadFile(filepath.Join(mountPoint, "repo", "file"))
			require.NoError(b, err)
			require.Equal(b, GB, len(data))
			b.SetBytes(GB)
		})
	}
}

func TestSeek(t *testing.T) {
	c := server.GetPachClient(t)
	require.NoError(t, c.CreateRepo("repo"))
	data := strings.Repeat("foo", MB)
	_, err := c.PutFile("repo", "master", "file", strings.NewReader(data))
	require.NoError(t, err)
	mount(t, c, nil, func(mountPoint string) {
		f, err := os.Open(filepath.Join(mountPoint, "repo", "file"))
		require.NoError(t, err)
		defer func() {
			require.NoError(t, f.Close())
		}()

		testSeek := func(offset int64) {
			_, err = f.Seek(offset, 0)
			require.NoError(t, err)
			d, err := ioutil.ReadAll(f)
			require.NoError(t, err)
			require.Equal(t, data[offset:], string(d))
		}

		testSeek(0)
		testSeek(MB)
		testSeek(2 * MB)
		testSeek(3 * MB)
	})
}

func TestHeadlessBranch(t *testing.T) {
	c := server.GetPachClient(t)
	require.NoError(t, c.CreateRepo("repo"))
	require.NoError(t, c.CreateBranch("repo", "master", "", nil))
	mount(t, c, nil, func(mountPoint string) {
		fis, err := ioutil.ReadDir(filepath.Join(mountPoint, "repo"))
		require.NoError(t, err)
		// Headless branches display with 0 files.
		require.Equal(t, 0, len(fis))
	})
}

func mount(tb testing.TB, c *client.APIClient, commits map[string]string, f func(mountPoint string)) {
	dir, err := ioutil.TempDir("", "pfs")
	require.NoError(tb, err)
	defer os.RemoveAll(dir)
	opts := &Options{
		Commits: commits,
		Unmount: make(chan struct{}),
	}
	defer func() {
		close(opts.Unmount)
	}()
	go func() {
		Mount(c, dir, opts)
	}()
	// Gotta give the fuse mount time to come up.
	time.Sleep(2 * time.Second)
	f(dir)
}
<commit_msg>Test to check that nested filesystems work with fuse<commit_after>package fuse

import (
	"crypto/sha256"
	"io/ioutil"
	"math/rand"
	"os"
	"path/filepath"
	"strings"
	"testing"
	"time"

	"github.com/pachyderm/pachyderm/src/client"
	"github.com/pachyderm/pachyderm/src/client/pfs"
	"github.com/pachyderm/pachyderm/src/client/pkg/require"
	"github.com/pachyderm/pachyderm/src/server/pfs/server"
	"github.com/pachyderm/pachyderm/src/server/pkg/workload"
)

const (
	MB = 1024 * 1024
	GB = 1024 * 1024 * 1024
)

func TestBasic(t *testing.T) {
	c := server.GetPachClient(t)
	require.NoError(t, c.CreateRepo("repo"))
	_, err := c.PutFile("repo", "master", "dir/file1", strings.NewReader("foo"))
	_, err = c.PutFile("repo", "master", "dir/file2", strings.NewReader("foo"))
	require.NoError(t, err)
	mount(t, c, nil, func(mountPoint string) {
		repos, err := ioutil.ReadDir(mountPoint)
		require.NoError(t, err)
		require.Equal(t, 1, len(repos))
		require.Equal(t, "repo", filepath.Base(repos[0].Name()))

		files, err := ioutil.ReadDir(filepath.Join(mountPoint, "repo"))
		require.NoError(t, err)
		require.Equal(t, 1, len(files))
		require.Equal(t, "dir", filepath.Base(files[0].Name()))

		files, err = ioutil.ReadDir(filepath.Join(mountPoint, "repo", "dir"))
		require.NoError(t, err)
		require.Equal(t, 2, len(files))
		require.Equal(t, "file1", filepath.Base(files[0].Name()))
		require.Equal(t, "file2", filepath.Base(files[1].Name()))

		data, err := ioutil.ReadFile(filepath.Join(mountPoint, "repo", "dir", "file1"))
		require.NoError(t, err)
		require.Equal(t, "foo", string(data))
	})
}

func TestChunkSize(t *testing.T) {
	c := server.GetPachClient(t)
	require.NoError(t, c.CreateRepo("repo"))
	_, err := c.PutFile("repo", "master", "file", strings.NewReader(strings.Repeat("p", int(pfs.ChunkSize))))
	require.NoError(t, err)
	mount(t, c, nil, func(mountPoint string) {
		data, err := ioutil.ReadFile(filepath.Join(mountPoint, "repo", "file"))
		require.NoError(t, err)
		require.Equal(t, int(pfs.ChunkSize), len(data))
	})
}

func TestLargeFile(t *testing.T) {
	c := server.GetPachClient(t)
	require.NoError(t, c.CreateRepo("repo"))
	src := workload.RandString(rand.New(rand.NewSource(123)), GB+17)
	_, err := c.PutFile("repo", "master", "file", strings.NewReader(src))
	require.NoError(t, err)
	mount(t, c, nil, func(mountPoint string) {
		data, err := ioutil.ReadFile(filepath.Join(mountPoint, "repo", "file"))
		require.NoError(t, err)
		require.Equal(t, sha256.Sum256([]byte(src)), sha256.Sum256(data))
	})
}

func BenchmarkLargeFile(b *testing.B) {
	c := server.GetPachClient(b)
	require.NoError(b, c.CreateRepo("repo"))
	src := workload.RandString(rand.New(rand.NewSource(123)), GB)
	_, err := c.PutFile("repo", "master", "file", strings.NewReader(src))
	require.NoError(b, err)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		mount(b, c, nil, func(mountPoint string) {
			data, err := ioutil.ReadFile(filepath.Join(mountPoint, "repo", "file"))
			require.NoError(b, err)
			require.Equal(b, GB, len(data))
			b.SetBytes(GB)
		})
	}
}

func TestSeek(t *testing.T) {
	c := server.GetPachClient(t)
	require.NoError(t, c.CreateRepo("repo"))
	data := strings.Repeat("foo", MB)
	_, err := c.PutFile("repo", "master", "file", strings.NewReader(data))
	require.NoError(t, err)
	mount(t, c, nil, func(mountPoint string) {
		f, err := os.Open(filepath.Join(mountPoint, "repo", "file"))
		require.NoError(t, err)
		defer func() {
			require.NoError(t, f.Close())
		}()

		testSeek := func(offset int64) {
			_, err = f.Seek(offset, 0)
			require.NoError(t, err)
			d, err := ioutil.ReadAll(f)
			require.NoError(t, err)
			require.Equal(t, data[offset:], string(d))
		}

		testSeek(0)
		testSeek(MB)
		testSeek(2 * MB)
		testSeek(3 * MB)
	})
}

func TestHeadlessBranch(t *testing.T) {
	c := server.GetPachClient(t)
	require.NoError(t, c.CreateRepo("repo"))
	require.NoError(t, c.CreateBranch("repo", "master", "", nil))
	mount(t, c, nil, func(mountPoint string) {
		fis, err := ioutil.ReadDir(filepath.Join(mountPoint, "repo"))
		require.NoError(t, err)
		// Headless branches display with 0 files.
		require.Equal(t, 0, len(fis))
	})
}

func mount(tb testing.TB, c *client.APIClient, commits map[string]string, f func(mountPoint string)) {
	dir, err := ioutil.TempDir("", "pfs")
	require.NoError(tb, err)
	defer os.RemoveAll(dir)
	opts := &Options{
		Commits: commits,
		Unmount: make(chan struct{}),
	}
	defer func() {
		close(opts.Unmount)
	}()
	go func() {
		Mount(c, dir, opts)
	}()
	// Gotta give the fuse mount time to come up.
	time.Sleep(2 * time.Second)
	f(dir)
}
<|endoftext|>