<commit_before>package main

import (
	"flag"
	"os"
	"os/signal"
	"syscall"

	"github.com/libopenstorage/stork/drivers/volume"
	_ "github.com/libopenstorage/stork/drivers/volume/portworx"
	"github.com/libopenstorage/stork/pkg/applicationmanager"
	"github.com/libopenstorage/stork/pkg/cluster"
	"github.com/libopenstorage/stork/pkg/clusterdomains"
	"github.com/libopenstorage/stork/pkg/controller"
	"github.com/libopenstorage/stork/pkg/dbg"
	"github.com/libopenstorage/stork/pkg/extender"
	"github.com/libopenstorage/stork/pkg/groupsnapshot"
	"github.com/libopenstorage/stork/pkg/initializer"
	"github.com/libopenstorage/stork/pkg/migration"
	"github.com/libopenstorage/stork/pkg/monitor"
	"github.com/libopenstorage/stork/pkg/pvcwatcher"
	"github.com/libopenstorage/stork/pkg/resourcecollector"
	"github.com/libopenstorage/stork/pkg/rule"
	"github.com/libopenstorage/stork/pkg/schedule"
	"github.com/libopenstorage/stork/pkg/snapshot"
	"github.com/libopenstorage/stork/pkg/version"
	log "github.com/sirupsen/logrus"
	"github.com/urfave/cli"
	api_v1 "k8s.io/api/core/v1"
	clientset "k8s.io/client-go/kubernetes"
	core_v1 "k8s.io/client-go/kubernetes/typed/core/v1"
	_ "k8s.io/client-go/plugin/pkg/client/auth"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/leaderelection"
	"k8s.io/client-go/tools/leaderelection/resourcelock"
	"k8s.io/client-go/tools/record"
	"k8s.io/kubernetes/pkg/api/legacyscheme"
	componentconfig "k8s.io/kubernetes/pkg/apis/componentconfig/v1alpha1"
)

const (
	defaultLockObjectName      = "stork"
	defaultLockObjectNamespace = "kube-system"
	eventComponentName         = "stork"
	debugFilePath              = "/var/cores"
)

var ext *extender.Extender

func main() {
	// Parse empty flags to suppress warnings from the snapshotter which uses
	// glog
	err := flag.CommandLine.Parse([]string{})
	if err != nil {
		log.Warnf("Error parsing flag: %v", err)
	}
	err = flag.Set("logtostderr", "true")
	if err != nil {
		log.Fatalf("Error setting glog flag: %v", err)
	}

	app := cli.NewApp()
	app.Name = "stork"
	app.Usage = "STorage Orchestartor Runtime for Kubernetes (STORK)"
	app.Version = version.Version
	app.Action = run

	app.Flags = []cli.Flag{
		cli.BoolFlag{
			Name:  "verbose",
			Usage: "Enable verbose logging",
		},
		cli.StringFlag{
			Name:  "driver,d",
			Usage: "Storage driver name",
		},
		cli.BoolTFlag{
			Name:  "leader-elect",
			Usage: "Enable leader election (default: true)",
		},
		cli.StringFlag{
			Name:  "lock-object-name",
			Usage: "Name for the lock object (default: stork)",
			Value: defaultLockObjectName,
		},
		cli.StringFlag{
			Name:  "lock-object-namespace",
			Usage: "Namespace for the lock object (default: kube-system)",
			Value: defaultLockObjectNamespace,
		},
		cli.BoolTFlag{
			Name:  "snapshotter",
			Usage: "Enable snapshotter (default: true)",
		},
		cli.BoolTFlag{
			Name:  "extender",
			Usage: "Enable scheduler extender for hyperconvergence (default: true)",
		},
		cli.BoolTFlag{
			Name:  "health-monitor",
			Usage: "Enable health monitoring of the storage driver (default: true)",
		},
		cli.Int64Flag{
			Name:  "health-monitor-interval",
			Usage: "The interval in seconds to monitor the health of the storage driver (default: 120, min: 30)",
		},
		cli.BoolTFlag{
			Name:  "migration-controller",
			Usage: "Start the migration controller (default: true)",
		},
		cli.BoolTFlag{
			Name:  "application-controller",
			Usage: "Start the controllers for managing applications (default: true)",
		},
		cli.BoolFlag{
			Name:  "app-initializer",
			Usage: "EXPERIMENTAL: Enable application initializer to update scheduler name automatically (default: false)",
		},
		cli.StringFlag{
			Name:  "admin-namespace",
			Usage: "Namespace to be used by a cluster admin which can migrate and backup all other namespaces (default: none)",
		},
		cli.BoolFlag{
			Name:  "storage-cluster-controller",
			Usage: "Start the storage cluster controller (default: false)",
		},
		cli.BoolTFlag{
			Name:  "cluster-domain-controllers",
			Usage: "Start the cluster domain controllers (default: true)",
		},
		cli.BoolTFlag{
			Name:  "pvc-watcher",
			Usage: "Start the controller to monitor PVC creation and deletions (default: true)",
		},
	}

	if err := app.Run(os.Args); err != nil {
		log.Fatalf("Error starting stork: %v", err)
	}
}

func run(c *cli.Context) {
	dbg.Init(c.App.Name, debugFilePath)

	log.Infof("Starting stork version %v", version.Version)
	driverName := c.String("driver")
	if len(driverName) == 0 {
		log.Fatalf("driver option is required")
	}

	verbose := c.Bool("verbose")
	if verbose {
		log.SetLevel(log.DebugLevel)
	}

	d, err := volume.Get(driverName)
	if err != nil {
		log.Fatalf("Error getting Stork Driver %v: %v", driverName, err)
	}

	if err = d.Init(nil); err != nil {
		log.Fatalf("Error initializing Stork Driver %v: %v", driverName, err)
	}

	config, err := rest.InClusterConfig()
	if err != nil {
		log.Fatalf("Error getting cluster config: %v", err)
	}

	k8sClient, err := clientset.NewForConfig(config)
	if err != nil {
		log.Fatalf("Error getting client, %v", err)
	}

	eventBroadcaster := record.NewBroadcaster()
	eventBroadcaster.StartRecordingToSink(&core_v1.EventSinkImpl{Interface: core_v1.New(k8sClient.CoreV1().RESTClient()).Events("")})
	recorder := eventBroadcaster.NewRecorder(legacyscheme.Scheme, api_v1.EventSource{Component: eventComponentName})

	if c.Bool("extender") {
		ext = &extender.Extender{
			Driver:   d,
			Recorder: recorder,
		}

		if err = ext.Start(); err != nil {
			log.Fatalf("Error starting scheduler extender: %v", err)
		}
	}

	runFunc := func(_ <-chan struct{}) {
		runStork(d, recorder, c)
	}

	if c.BoolT("leader-elect") {

		lockObjectName := c.String("lock-object-name")
		lockObjectNamespace := c.String("lock-object-namespace")

		id, err := os.Hostname()
		if err != nil {
			log.Fatalf("Error getting hostname: %v", err)
		}

		lockConfig := resourcelock.ResourceLockConfig{
			Identity:      id,
			EventRecorder: recorder,
		}

		resourceLock, err := resourcelock.New(
			resourcelock.ConfigMapsResourceLock,
			lockObjectNamespace,
			lockObjectName,
			k8sClient.CoreV1(),
			lockConfig)
		if err != nil {
			log.Fatalf("Error creating resource lock: %v", err)
		}

		defaultConfig := &componentconfig.LeaderElectionConfiguration{}
		componentconfig.SetDefaults_LeaderElectionConfiguration(defaultConfig)

		leaderElectionConfig := leaderelection.LeaderElectionConfig{
			Lock:          resourceLock,
			LeaseDuration: defaultConfig.LeaseDuration.Duration,
			RenewDeadline: defaultConfig.RenewDeadline.Duration,
			RetryPeriod:   defaultConfig.RetryPeriod.Duration,

			Callbacks: leaderelection.LeaderCallbacks{
				OnStartedLeading: runFunc,
				OnStoppedLeading: func() {
					log.Fatalf("Stork lost master")
				},
			},
		}
		leaderElector, err := leaderelection.NewLeaderElector(leaderElectionConfig)
		if err != nil {
			log.Fatalf("Error creating leader elector: %v", err)
		}

		leaderElector.Run()
	} else {
		runFunc(nil)
	}
}

func runStork(d volume.Driver, recorder record.EventRecorder, c *cli.Context) {
	if err := controller.Init(); err != nil {
		log.Fatalf("Error initializing controller: %v", err)
	}

	if err := rule.Init(); err != nil {
		log.Fatalf("Error initializing rule: %v", err)
	}

	initializer := &initializer.Initializer{
		Driver: d,
	}
	if c.Bool("app-initializer") {
		if err := initializer.Start(); err != nil {
			log.Fatalf("Error starting initializer: %v", err)
		}
	}

	monitor := &monitor.Monitor{
		Driver:      d,
		IntervalSec: c.Int64("health-monitor-interval"),
	}

	if c.Bool("health-monitor") {
		if err := monitor.Start(); err != nil {
			log.Fatalf("Error starting storage monitor: %v", err)
		}
	}

	if err := schedule.Init(); err != nil {
		log.Fatalf("Error initializing schedule: %v", err)
	}

	snapshot := &snapshot.Snapshot{
		Driver:   d,
		Recorder: recorder,
	}
	if c.Bool("snapshotter") {
		if err := snapshot.Start(); err != nil {
			log.Fatalf("Error starting snapshot controller: %v", err)
		}

		groupsnapshotInst := groupsnapshot.GroupSnapshot{
			Driver:   d,
			Recorder: recorder,
		}
		if err := groupsnapshotInst.Init(); err != nil {
			log.Fatalf("Error initializing groupsnapshot controller: %v", err)
		}
	}
	pvcWatcher := pvcwatcher.PVCWatcher{
		Driver:   d,
		Recorder: recorder,
	}
	if c.Bool("pvc-watcher") {
		if err := pvcWatcher.Start(); err != nil {
			log.Fatalf("Error starting pvc watcher: %v", err)
		}
	}

	resourceCollector := resourcecollector.ResourceCollector{
		Driver: d,
	}
	if err := resourceCollector.Init(); err != nil {
		log.Fatalf("Error initializing ResourceCollector: %v", err)
	}

	adminNamespace := c.String("admin-namespace")
	if c.Bool("migration-controller") {
		migration := migration.Migration{
			Driver:            d,
			Recorder:          recorder,
			ResourceCollector: resourceCollector,
		}
		if err := migration.Init(adminNamespace); err != nil {
			log.Fatalf("Error initializing migration: %v", err)
		}
	}

	if c.Bool("application-controller") {
		appManager := applicationmanager.ApplicationManager{
			Driver:            d,
			Recorder:          recorder,
			ResourceCollector: resourceCollector,
		}
		if err := appManager.Init(adminNamespace); err != nil {
			log.Fatalf("Error initializing application manager: %v", err)
		}
	}
	if c.Bool("storage-cluster-controller") {
		clusterController := cluster.Controller{
			Recorder: recorder,
		}
		if err := clusterController.Init(); err != nil {
			log.Fatalf("Error initializing cluster controller: %v", err)
		}
	}

	if c.Bool("cluster-domain-controllers") {
		clusterDomains := clusterdomains.ClusterDomains{
			Driver:   d,
			Recorder: recorder,
		}
		if err := clusterDomains.Init(); err != nil {
			log.Fatalf("Error initializing cluster domain controllers: %v", err)
		}
	}

	// The controller should be started at the end
	err := controller.Run()
	if err != nil {
		log.Fatalf("Error starting controller: %v", err)
	}

	signalChan := make(chan os.Signal, 1)
	signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM)
	for {
		<-signalChan
		log.Printf("Shutdown signal received, exiting...")
		if c.Bool("extender") {
			if err := ext.Stop(); err != nil {
				log.Warnf("Error stopping extender: %v", err)
			}
		}
		if c.Bool("health-monitor") {
			if err := monitor.Stop(); err != nil {
				log.Warnf("Error stopping monitor: %v", err)
			}
		}
		if c.Bool("snapshotter") {
			if err := snapshot.Stop(); err != nil {
				log.Warnf("Error stopping snapshot controllers: %v", err)
			}
		}
		if c.Bool("app-initializer") {
			if err := initializer.Stop(); err != nil {
				log.Warnf("Error stopping app-initializer: %v", err)
			}
		}
		if err := d.Stop(); err != nil {
			log.Warnf("Error stopping driver: %v", err)
		}
		os.Exit(0)
	}
}
<commit_msg>Update stork parameters<commit_after>package main

import (
	"flag"
	"os"
	"os/signal"
	"syscall"

	"github.com/libopenstorage/stork/drivers/volume"
	_ "github.com/libopenstorage/stork/drivers/volume/portworx"
	"github.com/libopenstorage/stork/pkg/applicationmanager"
	"github.com/libopenstorage/stork/pkg/cluster"
	"github.com/libopenstorage/stork/pkg/clusterdomains"
	"github.com/libopenstorage/stork/pkg/controller"
	"github.com/libopenstorage/stork/pkg/dbg"
	"github.com/libopenstorage/stork/pkg/extender"
	"github.com/libopenstorage/stork/pkg/groupsnapshot"
	"github.com/libopenstorage/stork/pkg/initializer"
	"github.com/libopenstorage/stork/pkg/migration"
	"github.com/libopenstorage/stork/pkg/monitor"
	"github.com/libopenstorage/stork/pkg/pvcwatcher"
	"github.com/libopenstorage/stork/pkg/resourcecollector"
	"github.com/libopenstorage/stork/pkg/rule"
	"github.com/libopenstorage/stork/pkg/schedule"
	"github.com/libopenstorage/stork/pkg/snapshot"
	"github.com/libopenstorage/stork/pkg/version"
	log "github.com/sirupsen/logrus"
	"github.com/urfave/cli"
	api_v1 "k8s.io/api/core/v1"
	clientset "k8s.io/client-go/kubernetes"
	core_v1 "k8s.io/client-go/kubernetes/typed/core/v1"
	_ "k8s.io/client-go/plugin/pkg/client/auth"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/leaderelection"
	"k8s.io/client-go/tools/leaderelection/resourcelock"
	"k8s.io/client-go/tools/record"
	"k8s.io/kubernetes/pkg/api/legacyscheme"
	componentconfig "k8s.io/kubernetes/pkg/apis/componentconfig/v1alpha1"
)

const (
	defaultLockObjectName      = "stork"
	defaultLockObjectNamespace = "kube-system"
	defaultAdminNamespace      = "kube-system"
	eventComponentName         = "stork"
	debugFilePath              = "/var/cores"
)

var ext *extender.Extender

func main() {
	// Parse empty flags to suppress warnings from the snapshotter which uses
	// glog
	err := flag.CommandLine.Parse([]string{})
	if err != nil {
		log.Warnf("Error parsing flag: %v", err)
	}
	err = flag.Set("logtostderr", "true")
	if err != nil {
		log.Fatalf("Error setting glog flag: %v", err)
	}

	app := cli.NewApp()
	app.Name = "stork"
	app.Usage = "STorage Orchestartor Runtime for Kubernetes (STORK)"
	app.Version = version.Version
	app.Action = run

	app.Flags = []cli.Flag{
		cli.BoolFlag{
			Name:  "verbose",
			Usage: "Enable verbose logging",
		},
		cli.StringFlag{
			Name:  "driver,d",
			Usage: "Storage driver name",
		},
		cli.BoolTFlag{
			Name:  "leader-elect",
			Usage: "Enable leader election (default: true)",
		},
		cli.StringFlag{
			Name:  "lock-object-name",
			Usage: "Name for the lock object",
			Value: defaultLockObjectName,
		},
		cli.StringFlag{
			Name:  "lock-object-namespace",
			Usage: "Namespace for the lock object",
			Value: defaultLockObjectNamespace,
		},
		cli.BoolTFlag{
			Name:  "snapshotter",
			Usage: "Enable snapshotter (default: true)",
		},
		cli.BoolTFlag{
			Name:  "extender",
			Usage: "Enable scheduler extender for hyperconvergence (default: true)",
		},
		cli.BoolTFlag{
			Name:  "health-monitor",
			Usage: "Enable health monitoring of the storage driver (default: true)",
		},
		cli.Int64Flag{
			Name:  "health-monitor-interval",
			Value: 120,
			Usage: "The interval in seconds to monitor the health of the storage driver (min: 30)",
		},
		cli.BoolTFlag{
			Name:  "migration-controller",
			Usage: "Start the migration controller (default: true)",
		},
		cli.BoolTFlag{
			Name:  "application-controller",
			Usage: "Start the controllers for managing applications (default: true)",
		},
		cli.BoolFlag{
			Name:  "app-initializer",
			Usage: "EXPERIMENTAL: Enable application initializer to update scheduler name automatically (default: false)",
		},
		cli.StringFlag{
			Name:  "admin-namespace",
			Value: defaultAdminNamespace,
			Usage: "Namespace to be used by a cluster admin which can migrate and backup all other namespaces",
		},
		cli.BoolFlag{
			Name:  "storage-cluster-controller",
			Usage: "Start the storage cluster controller (default: false)",
		},
		cli.BoolTFlag{
			Name:  "cluster-domain-controllers",
			Usage: "Start the cluster domain controllers (default: true)",
		},
		cli.BoolTFlag{
			Name:  "pvc-watcher",
			Usage: "Start the controller to monitor PVC creation and deletions (default: true)",
		},
	}

	if err := app.Run(os.Args); err != nil {
		log.Fatalf("Error starting stork: %v", err)
	}
}

func run(c *cli.Context) {
	dbg.Init(c.App.Name, debugFilePath)

	log.Infof("Starting stork version %v", version.Version)
	driverName := c.String("driver")
	if len(driverName) == 0 {
		log.Fatalf("driver option is required")
	}

	verbose := c.Bool("verbose")
	if verbose {
		log.SetLevel(log.DebugLevel)
	}

	d, err := volume.Get(driverName)
	if err != nil {
		log.Fatalf("Error getting Stork Driver %v: %v", driverName, err)
	}

	if err = d.Init(nil); err != nil {
		log.Fatalf("Error initializing Stork Driver %v: %v", driverName, err)
	}

	config, err := rest.InClusterConfig()
	if err != nil {
		log.Fatalf("Error getting cluster config: %v", err)
	}

	k8sClient, err := clientset.NewForConfig(config)
	if err != nil {
		log.Fatalf("Error getting client, %v", err)
	}

	eventBroadcaster := record.NewBroadcaster()
	eventBroadcaster.StartRecordingToSink(&core_v1.EventSinkImpl{Interface: core_v1.New(k8sClient.CoreV1().RESTClient()).Events("")})
	recorder := eventBroadcaster.NewRecorder(legacyscheme.Scheme, api_v1.EventSource{Component: eventComponentName})

	if c.Bool("extender") {
		ext = &extender.Extender{
			Driver:   d,
			Recorder: recorder,
		}

		if err = ext.Start(); err != nil {
			log.Fatalf("Error starting scheduler extender: %v", err)
		}
	}

	runFunc := func(_ <-chan struct{}) {
		runStork(d, recorder, c)
	}

	if c.BoolT("leader-elect") {

		lockObjectName := c.String("lock-object-name")
		lockObjectNamespace := c.String("lock-object-namespace")

		id, err := os.Hostname()
		if err != nil {
			log.Fatalf("Error getting hostname: %v", err)
		}

		lockConfig := resourcelock.ResourceLockConfig{
			Identity:      id,
			EventRecorder: recorder,
		}

		resourceLock, err := resourcelock.New(
			resourcelock.ConfigMapsResourceLock,
			lockObjectNamespace,
			lockObjectName,
			k8sClient.CoreV1(),
			lockConfig)
		if err != nil {
			log.Fatalf("Error creating resource lock: %v", err)
		}

		defaultConfig := &componentconfig.LeaderElectionConfiguration{}
		componentconfig.SetDefaults_LeaderElectionConfiguration(defaultConfig)

		leaderElectionConfig := leaderelection.LeaderElectionConfig{
			Lock:          resourceLock,
			LeaseDuration: defaultConfig.LeaseDuration.Duration,
			RenewDeadline: defaultConfig.RenewDeadline.Duration,
			RetryPeriod:   defaultConfig.RetryPeriod.Duration,

			Callbacks: leaderelection.LeaderCallbacks{
				OnStartedLeading: runFunc,
				OnStoppedLeading: func() {
					log.Fatalf("Stork lost master")
				},
			},
		}
		leaderElector, err := leaderelection.NewLeaderElector(leaderElectionConfig)
		if err != nil {
			log.Fatalf("Error creating leader elector: %v", err)
		}

		leaderElector.Run()
	} else {
		runFunc(nil)
	}
}

func runStork(d volume.Driver, recorder record.EventRecorder, c *cli.Context) {
	if err := controller.Init(); err != nil {
		log.Fatalf("Error initializing controller: %v", err)
	}

	if err := rule.Init(); err != nil {
		log.Fatalf("Error initializing rule: %v", err)
	}

	initializer := &initializer.Initializer{
		Driver: d,
	}
	if c.Bool("app-initializer") {
		if err := initializer.Start(); err != nil {
			log.Fatalf("Error starting initializer: %v", err)
		}
	}

	monitor := &monitor.Monitor{
		Driver:      d,
		IntervalSec: c.Int64("health-monitor-interval"),
	}

	if c.Bool("health-monitor") {
		if err := monitor.Start(); err != nil {
			log.Fatalf("Error starting storage monitor: %v", err)
		}
	}

	if err := schedule.Init(); err != nil {
		log.Fatalf("Error initializing schedule: %v", err)
	}

	snapshot := &snapshot.Snapshot{
		Driver:   d,
		Recorder: recorder,
	}
	if c.Bool("snapshotter") {
		if err := snapshot.Start(); err != nil {
			log.Fatalf("Error starting snapshot controller: %v", err)
		}

		groupsnapshotInst := groupsnapshot.GroupSnapshot{
			Driver:   d,
			Recorder: recorder,
		}
		if err := groupsnapshotInst.Init(); err != nil {
			log.Fatalf("Error initializing groupsnapshot controller: %v", err)
		}
	}
	pvcWatcher := pvcwatcher.PVCWatcher{
		Driver:   d,
		Recorder: recorder,
	}
	if c.Bool("pvc-watcher") {
		if err := pvcWatcher.Start(); err != nil {
			log.Fatalf("Error starting pvc watcher: %v", err)
		}
	}

	resourceCollector := resourcecollector.ResourceCollector{
		Driver: d,
	}
	if err := resourceCollector.Init(); err != nil {
		log.Fatalf("Error initializing ResourceCollector: %v", err)
	}

	adminNamespace := c.String("admin-namespace")
	if c.Bool("migration-controller") {
		migration := migration.Migration{
			Driver:            d,
			Recorder:          recorder,
			ResourceCollector: resourceCollector,
		}
		if err := migration.Init(adminNamespace); err != nil {
			log.Fatalf("Error initializing migration: %v", err)
		}
	}

	if c.Bool("application-controller") {
		appManager := applicationmanager.ApplicationManager{
			Driver:            d,
			Recorder:          recorder,
			ResourceCollector: resourceCollector,
		}
		if err := appManager.Init(adminNamespace); err != nil {
			log.Fatalf("Error initializing application manager: %v", err)
		}
	}
	if c.Bool("storage-cluster-controller") {
		clusterController := cluster.Controller{
			Recorder: recorder,
		}
		if err := clusterController.Init(); err != nil {
			log.Fatalf("Error initializing cluster controller: %v", err)
		}
	}

	if c.Bool("cluster-domain-controllers") {
		clusterDomains := clusterdomains.ClusterDomains{
			Driver:   d,
			Recorder: recorder,
		}
		if err := clusterDomains.Init(); err != nil {
			log.Fatalf("Error initializing cluster domain controllers: %v", err)
		}
	}

	// The controller should be started at the end
	err := controller.Run()
	if err != nil {
		log.Fatalf("Error starting controller: %v", err)
	}

	signalChan := make(chan os.Signal, 1)
	signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM)
	for {
		<-signalChan
		log.Printf("Shutdown signal received, exiting...")
		if c.Bool("extender") {
			if err := ext.Stop(); err != nil {
				log.Warnf("Error stopping extender: %v", err)
			}
		}
		if c.Bool("health-monitor") {
			if err := monitor.Stop(); err != nil {
				log.Warnf("Error stopping monitor: %v", err)
			}
		}
		if c.Bool("snapshotter") {
			if err := snapshot.Stop(); err != nil {
				log.Warnf("Error stopping snapshot controllers: %v", err)
			}
		}
		if c.Bool("app-initializer") {
			if err := initializer.Stop(); err != nil {
				log.Warnf("Error stopping app-initializer: %v", err)
			}
		}
		if err := d.Stop(); err != nil {
			log.Warnf("Error stopping driver: %v", err)
		}
		os.Exit(0)
	}
}
<|endoftext|>


<commit_before>package task

import (
	"fmt"
	"github.com/golang/protobuf/proto"
	"github.com/ohsu-comp-bio/funnel/client"
	"github.com/ohsu-comp-bio/funnel/proto/tes"
	"golang.org/x/net/context"
	"io"
	"io/ioutil"
)

// Create runs the "task create" CLI command, connecting to the server,
// calling CreateTask, and writing output to the given writer.
// Tasks are loaded from the "files" arg. "files" are file paths to JSON objects.
func Create(server string, files []string, writer io.Writer) error {
	cli := client.NewClient(server)
	res := []string{}

	for _, taskFile := range files {
		var err error
		var taskMessage []byte
		var task tes.Task

		taskMessage, err = ioutil.ReadFile(taskFile)
		if err != nil {
			return err
		}

		err = proto.Unmarshal(taskMessage, &task)
		if err != nil {
			return err
		}

		r, err := cli.CreateTask(context.Background(), &task)
		if err != nil {
			return err
		}
		res = append(res, r.Id)
	}

	for _, x := range res {
		fmt.Fprintln(writer, x)
	}

	return nil
}
<commit_msg>cmd/task/create: bug fix, unmarshal json, not protobuf<commit_after>package task

import (
	"fmt"
	"github.com/golang/protobuf/jsonpb"
	"github.com/ohsu-comp-bio/funnel/client"
	"github.com/ohsu-comp-bio/funnel/proto/tes"
	"golang.org/x/net/context"
	"io"
	"os"
)

// Create runs the "task create" CLI command, connecting to the server,
// calling CreateTask, and writing output to the given writer.
// Tasks are loaded from the "files" arg. "files" are file paths to JSON objects.
func Create(server string, files []string, writer io.Writer) error {
	cli := client.NewClient(server)
	res := []string{}

	for _, taskFile := range files {
		var err error
		var task tes.Task

		f, err := os.Open(taskFile)
		defer f.Close()
		if err != nil {
			return err
		}

		err = jsonpb.Unmarshal(f, &task)
		if err != nil {
			return fmt.Errorf("can't load task: %s", err)
		}

		r, err := cli.CreateTask(context.Background(), &task)
		if err != nil {
			return err
		}
		res = append(res, r.Id)
	}

	for _, x := range res {
		fmt.Fprintln(writer, x)
	}

	return nil
}
<|endoftext|>


<commit_before>// Copyright 2016 The Upspin 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 main

import (
	"bytes"
	"flag"
	"fmt"

	yaml "gopkg.in/yaml.v2"

	"upspin.io/factotum"
	"upspin.io/key/usercache"
	"upspin.io/upspin"
	"upspin.io/user"
)

func (s *State) user(args ...string) {
	const help = `
User prints in YAML format the user record stored in the key server
for the specified user, by default the current user.

With the -put flag, user writes or replaces the information stored
for the current user, such as to update keys or server information.
The information is read from standard input or from the file provided
with the -in flag. The input must provide the complete record for
the user, and must be in the same YAML format printed by the command
without the -put flag.

When using -put, the command does takes no arguments. The name of
the user whose record is to be updated must be provided in the input
record and must either be the current user or the name of another
user whose domain is administered by the current user.

A handy way to use the command is to edit the config file and run
	upspin user | upspin user -put

To install new users see the signup command.
`
	fs := flag.NewFlagSet("user", flag.ExitOnError)
	put := fs.Bool("put", false, "write new user record")
	inFile := fs.String("in", "", "input file (default standard input)")
	force := fs.Bool("force", false, "force writing user record even if key is empty")
	// TODO: the username is not accepted with -put. We may need two lines to fix this (like 'man printf').
	s.parseFlags(fs, args, help, "user [-put [-in=inputfile] [-force]] [username...]")
	keyServer := s.KeyServer()
	if *put {
		if fs.NArg() != 0 {
			fs.Usage()
		}
		s.putUser(keyServer, s.globOneLocal(*inFile), *force)
		return
	}
	if *inFile != "" {
		s.exitf("-in only available with -put")
	}
	if *force {
		s.exitf("-force only available with -put")
	}
	var userNames []upspin.UserName
	if fs.NArg() == 0 {
		userNames = append(userNames, s.config.UserName())
	} else {
		for i := 0; i < fs.NArg(); i++ {
			userName, err := user.Clean(upspin.UserName(fs.Arg(i)))
			if err != nil {
				s.exit(err)
			}
			userNames = append(userNames, userName)
		}
	}
	for _, name := range userNames {
		u, err := keyServer.Lookup(name)
		if err != nil {
			s.exit(err)
		}
		blob, err := yaml.Marshal(u)
		if err != nil {
			// TODO(adg): better error message?
			s.exit(err)
		}
		fmt.Printf("%s\n", blob)
		if name != s.config.UserName() {
			continue
		}
		// When it's the user asking about herself, the result comes
		// from the configuration and may disagree with the value in the
		// key store. This is a common source of error so we want to
		// diagnose it. To do that, we wipe the key cache and go again.
		// This will wipe the memory of our remembered configuration and
		// reload it from the key server.
		usercache.ResetGlobal()
		keyU, err := keyServer.Lookup(name)
		if err != nil {
			s.exit(err)
		}
		var buf bytes.Buffer
		if keyU.Name != u.Name {
			fmt.Fprintf(&buf, "user name in configuration: %s\n", u.Name)
			fmt.Fprintf(&buf, "user name in key server: %s\n", keyU.Name)
		}
		if keyU.PublicKey != u.PublicKey {
			fmt.Fprintf(&buf, "public key in configuration does not match key server\n")
		}
		// There must be dir servers defined in both and we expect agreement.
		if !equalEndpoints(keyU.Dirs, u.Dirs) {
			fmt.Fprintf(&buf, "dirs in configuration: %s\n", u.Dirs)
			fmt.Fprintf(&buf, "dirs in key server: %s\n", keyU.Dirs)
		}
		// Remote stores need not be defined (yet).
		if len(keyU.Stores) > 0 && !equalEndpoints(keyU.Stores, u.Stores) {
			fmt.Fprintf(&buf, "stores in configuration: %s\n", u.Stores)
			fmt.Fprintf(&buf, "stores in key server: %s\n", keyU.Stores)
		}
		if buf.Len() > 0 {
			s.exitf("local configuration differs from public record in key server:\n%s", &buf)
		}
	}
}

func equalEndpoints(a, b []upspin.Endpoint) bool {
	if len(a) != len(b) {
		return false
	}
	for i, e := range a {
		if e != b[i] {
			return false
		}
	}
	return true
}

func (s *State) putUser(keyServer upspin.KeyServer, inFile string, force bool) {
	data := s.readAll(inFile)
	userStruct := new(upspin.User)
	err := yaml.Unmarshal(data, userStruct)
	if err != nil {
		// TODO(adg): better error message?
		s.exit(err)
	}
	// Validate public key.
	if userStruct.PublicKey == "" && !force {
		s.exitf("An empty public key will prevent user from accessing services. To override use -force.")
	}
	_, _, err = factotum.ParsePublicKey(userStruct.PublicKey)
	if err != nil && !force {
		s.exitf("invalid public key, to override use -force: %s", err.Error())
	}
	// Clean the username.
	userStruct.Name, err = user.Clean(userStruct.Name)
	if err != nil {
		s.exit(err)
	}
	err = keyServer.Put(userStruct)
	if err != nil {
		s.exit(err)
	}
}
<commit_msg>cmd/upspin: fix documentation typo in user sub-command<commit_after>// Copyright 2016 The Upspin 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 main

import (
	"bytes"
	"flag"
	"fmt"

	yaml "gopkg.in/yaml.v2"

	"upspin.io/factotum"
	"upspin.io/key/usercache"
	"upspin.io/upspin"
	"upspin.io/user"
)

func (s *State) user(args ...string) {
	const help = `
User prints in YAML format the user record stored in the key server
for the specified user, by default the current user.

With the -put flag, user writes or replaces the information stored
for the current user, such as to update keys or server information.
The information is read from standard input or from the file provided
with the -in flag. The input must provide the complete record for
the user, and must be in the same YAML format printed by the command
without the -put flag.

When using -put, the command takes no arguments. The name of the
user whose record is to be updated must be provided in the input
record and must either be the current user or the name of another
user whose domain is administered by the current user.

A handy way to use the command is to edit the config file and run
	upspin user | upspin user -put

To install new users see the signup command.
`
	fs := flag.NewFlagSet("user", flag.ExitOnError)
	put := fs.Bool("put", false, "write new user record")
	inFile := fs.String("in", "", "input file (default standard input)")
	force := fs.Bool("force", false, "force writing user record even if key is empty")
	// TODO: the username is not accepted with -put. We may need two lines to fix this (like 'man printf').
	s.parseFlags(fs, args, help, "user [-put [-in=inputfile] [-force]] [username...]")
	keyServer := s.KeyServer()
	if *put {
		if fs.NArg() != 0 {
			fs.Usage()
		}
		s.putUser(keyServer, s.globOneLocal(*inFile), *force)
		return
	}
	if *inFile != "" {
		s.exitf("-in only available with -put")
	}
	if *force {
		s.exitf("-force only available with -put")
	}
	var userNames []upspin.UserName
	if fs.NArg() == 0 {
		userNames = append(userNames, s.config.UserName())
	} else {
		for i := 0; i < fs.NArg(); i++ {
			userName, err := user.Clean(upspin.UserName(fs.Arg(i)))
			if err != nil {
				s.exit(err)
			}
			userNames = append(userNames, userName)
		}
	}
	for _, name := range userNames {
		u, err := keyServer.Lookup(name)
		if err != nil {
			s.exit(err)
		}
		blob, err := yaml.Marshal(u)
		if err != nil {
			// TODO(adg): better error message?
			s.exit(err)
		}
		fmt.Printf("%s\n", blob)
		if name != s.config.UserName() {
			continue
		}
		// When it's the user asking about herself, the result comes
		// from the configuration and may disagree with the value in the
		// key store. This is a common source of error so we want to
		// diagnose it. To do that, we wipe the key cache and go again.
		// This will wipe the memory of our remembered configuration and
		// reload it from the key server.
		usercache.ResetGlobal()
		keyU, err := keyServer.Lookup(name)
		if err != nil {
			s.exit(err)
		}
		var buf bytes.Buffer
		if keyU.Name != u.Name {
			fmt.Fprintf(&buf, "user name in configuration: %s\n", u.Name)
			fmt.Fprintf(&buf, "user name in key server: %s\n", keyU.Name)
		}
		if keyU.PublicKey != u.PublicKey {
			fmt.Fprintf(&buf, "public key in configuration does not match key server\n")
		}
		// There must be dir servers defined in both and we expect agreement.
		if !equalEndpoints(keyU.Dirs, u.Dirs) {
			fmt.Fprintf(&buf, "dirs in configuration: %s\n", u.Dirs)
			fmt.Fprintf(&buf, "dirs in key server: %s\n", keyU.Dirs)
		}
		// Remote stores need not be defined (yet).
		if len(keyU.Stores) > 0 && !equalEndpoints(keyU.Stores, u.Stores) {
			fmt.Fprintf(&buf, "stores in configuration: %s\n", u.Stores)
			fmt.Fprintf(&buf, "stores in key server: %s\n", keyU.Stores)
		}
		if buf.Len() > 0 {
			s.exitf("local configuration differs from public record in key server:\n%s", &buf)
		}
	}
}

func equalEndpoints(a, b []upspin.Endpoint) bool {
	if len(a) != len(b) {
		return false
	}
	for i, e := range a {
		if e != b[i] {
			return false
		}
	}
	return true
}

func (s *State) putUser(keyServer upspin.KeyServer, inFile string, force bool) {
	data := s.readAll(inFile)
	userStruct := new(upspin.User)
	err := yaml.Unmarshal(data, userStruct)
	if err != nil {
		// TODO(adg): better error message?
		s.exit(err)
	}
	// Validate public key.
	if userStruct.PublicKey == "" && !force {
		s.exitf("An empty public key will prevent user from accessing services. To override use -force.")
	}
	_, _, err = factotum.ParsePublicKey(userStruct.PublicKey)
	if err != nil && !force {
		s.exitf("invalid public key, to override use -force: %s", err.Error())
	}
	// Clean the username.
	userStruct.Name, err = user.Clean(userStruct.Name)
	if err != nil {
		s.exit(err)
	}
	err = keyServer.Put(userStruct)
	if err != nil {
		s.exit(err)
	}
}
<|endoftext|>


<commit_before>package utils

import (
	"crypto/ecdsa"
	"fmt"
	"net"
	"net/http"
	"os"
	"path"
	"runtime"

	"github.com/codegangsta/cli"
	"github.com/ethereum/go-ethereum/accounts"
	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/core"
	"github.com/ethereum/go-ethereum/crypto"
	"github.com/ethereum/go-ethereum/eth"
	"github.com/ethereum/go-ethereum/ethdb"
	"github.com/ethereum/go-ethereum/event"
	"github.com/ethereum/go-ethereum/logger"
	"github.com/ethereum/go-ethereum/p2p/nat"
	"github.com/ethereum/go-ethereum/rpc"
	"github.com/ethereum/go-ethereum/xeth"
)

func init() {
	cli.AppHelpTemplate = `{{.Name}} {{if .Flags}}[global options] {{end}}command{{if .Flags}} [command options]{{end}} [arguments...]

VERSION:
   {{.Version}}

COMMANDS:
   {{range .Commands}}{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}}
   {{end}}{{if .Flags}}
GLOBAL OPTIONS:
   {{range .Flags}}{{.}}
   {{end}}{{end}}
`

	cli.CommandHelpTemplate = `{{.Name}}{{if .Subcommands}} command{{end}}{{if .Flags}} [command options]{{end}} [arguments...]
{{if .Description}}{{.Description}}
{{end}}{{if .Subcommands}}
SUBCOMMANDS:
	{{range .Subcommands}}{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}}
	{{end}}{{end}}{{if .Flags}}
OPTIONS:
	{{range .Flags}}{{.}}
	{{end}}{{end}}
`
}

// NewApp creates an app with sane defaults.
func NewApp(version, usage string) *cli.App {
	app := cli.NewApp()
	app.Name = path.Base(os.Args[0])
	app.Author = ""
	//app.Authors = nil
	app.Email = ""
	app.Version = version
	app.Usage = usage
	return app
}

// These are all the command line flags we support.
// If you add to this list, please remember to include the
// flag in the appropriate command definition.
//
// The flags are defined here so their names and help texts
// are the same for all commands.

var (
	// General settings
	DataDirFlag = cli.StringFlag{
		Name:  "datadir",
		Usage: "Data directory to be used",
		Value: common.DefaultDataDir(),
	}
	ProtocolVersionFlag = cli.IntFlag{
		Name:  "protocolversion",
		Usage: "ETH protocol version",
		Value: eth.ProtocolVersion,
	}
	NetworkIdFlag = cli.IntFlag{
		Name:  "networkid",
		Usage: "Network Id",
		Value: eth.NetworkId,
	}

	// miner settings
	MinerThreadsFlag = cli.IntFlag{
		Name:  "minerthreads",
		Usage: "Number of miner threads",
		Value: runtime.NumCPU(),
	}
	MiningEnabledFlag = cli.BoolFlag{
		Name:  "mine",
		Usage: "Enable mining",
	}
	EtherbaseFlag = cli.StringFlag{
		Name:  "Etherbase",
		Usage: "public address for block mining rewards. By default the address of your primary account is used",
		Value: "primary",
	}

	UnlockedAccountFlag = cli.StringFlag{
		Name:  "unlock",
		Usage: "unlock the account given until this program exits (prompts for password). '--unlock primary' unlocks the primary account",
		Value: "",
	}
	PasswordFileFlag = cli.StringFlag{
		Name:  "password",
		Usage: "Path to password file for (un)locking an existing account.",
		Value: "",
	}

	// logging and debug settings
	LogFileFlag = cli.StringFlag{
		Name:  "logfile",
		Usage: "Send log output to a file",
	}
	LogLevelFlag = cli.IntFlag{
		Name:  "loglevel",
		Usage: "0-5 (silent, error, warn, info, debug, debug detail)",
		Value: int(logger.InfoLevel),
	}
	LogJSONFlag = cli.StringFlag{
		Name:  "logjson",
		Usage: "Send json structured log output to a file or '-' for standard output (default: no json output)",
		Value: "",
	}
	VMDebugFlag = cli.BoolFlag{
		Name:  "vmdebug",
		Usage: "Virtual Machine debug output",
	}

	// RPC settings
	RPCEnabledFlag = cli.BoolFlag{
		Name:  "rpc",
		Usage: "Whether RPC server is enabled",
	}
	RPCListenAddrFlag = cli.StringFlag{
		Name:  "rpcaddr",
		Usage: "Listening address for the JSON-RPC server",
		Value: "127.0.0.1",
	}
	RPCPortFlag = cli.IntFlag{
		Name:  "rpcport",
		Usage: "Port on which the JSON-RPC server should listen",
		Value: 8545,
	}

	// Network Settings
	MaxPeersFlag = cli.IntFlag{
		Name:  "maxpeers",
		Usage: "Maximum number of network peers",
		Value: 16,
	}
	ListenPortFlag = cli.IntFlag{
		Name:  "port",
		Usage: "Network listening port",
		Value: 30303,
	}
	BootnodesFlag = cli.StringFlag{
		Name:  "bootnodes",
		Usage: "Space-separated enode URLs for discovery bootstrap",
		Value: "",
	}
	NodeKeyFileFlag = cli.StringFlag{
		Name:  "nodekey",
		Usage: "P2P node key file",
	}
	NodeKeyHexFlag = cli.StringFlag{
		Name:  "nodekeyhex",
		Usage: "P2P node key as hex (for testing)",
	}
	NATFlag = cli.StringFlag{
		Name:  "nat",
		Usage: "Port mapping mechanism (any|none|upnp|pmp|extip:<IP>)",
		Value: "any",
	}
	JSpathFlag = cli.StringFlag{
		Name:  "jspath",
		Usage: "JS library path to be used with console and js subcommands",
		Value: ".",
	}
)

func GetNAT(ctx *cli.Context) nat.Interface {
	natif, err := nat.Parse(ctx.GlobalString(NATFlag.Name))
	if err != nil {
		Fatalf("Option %s: %v", NATFlag.Name, err)
	}
	return natif
}

func GetNodeKey(ctx *cli.Context) (key *ecdsa.PrivateKey) {
	hex, file := ctx.GlobalString(NodeKeyHexFlag.Name), ctx.GlobalString(NodeKeyFileFlag.Name)
	var err error
	switch {
	case file != "" && hex != "":
		Fatalf("Options %q and %q are mutually exclusive", NodeKeyFileFlag.Name, NodeKeyHexFlag.Name)
	case file != "":
		if key, err = crypto.LoadECDSA(file); err != nil {
			Fatalf("Option %q: %v", NodeKeyFileFlag.Name, err)
		}
	case hex != "":
		if key, err = crypto.HexToECDSA(hex); err != nil {
			Fatalf("Option %q: %v", NodeKeyHexFlag.Name, err)
		}
	}
	return key
}

func MakeEthConfig(clientID, version string, ctx *cli.Context) *eth.Config {
	return &eth.Config{
		Name:            common.MakeName(clientID, version),
		DataDir:         ctx.GlobalString(DataDirFlag.Name),
		ProtocolVersion: ctx.GlobalInt(ProtocolVersionFlag.Name),
		NetworkId:       ctx.GlobalInt(NetworkIdFlag.Name),
		LogFile:         ctx.GlobalString(LogFileFlag.Name),
		LogLevel:        ctx.GlobalInt(LogLevelFlag.Name),
		LogJSON:         ctx.GlobalString(LogJSONFlag.Name),
		Etherbase:       ctx.GlobalString(EtherbaseFlag.Name),
		MinerThreads:    ctx.GlobalInt(MinerThreadsFlag.Name),
		AccountManager:  GetAccountManager(ctx),
		VmDebug:         ctx.GlobalBool(VMDebugFlag.Name),
		MaxPeers:        ctx.GlobalInt(MaxPeersFlag.Name),
		Port:            ctx.GlobalString(ListenPortFlag.Name),
		NAT:             GetNAT(ctx),
		NodeKey:         GetNodeKey(ctx),
		Shh:             true,
		Dial:            true,
		BootNodes:       ctx.GlobalString(BootnodesFlag.Name),
	}
}

func GetChain(ctx *cli.Context) (*core.ChainManager, common.Database, common.Database) {
	dataDir := ctx.GlobalString(DataDirFlag.Name)
	blockDb, err := ethdb.NewLDBDatabase(path.Join(dataDir, "blockchain"))
	if err != nil {
		Fatalf("Could not open database: %v", err)
	}

	stateDb, err := ethdb.NewLDBDatabase(path.Join(dataDir, "state"))
	if err != nil {
		Fatalf("Could not open database: %v", err)
	}
	return core.NewChainManager(blockDb, stateDb, new(event.TypeMux)), blockDb, stateDb
}

func GetAccountManager(ctx *cli.Context) *accounts.Manager {
	dataDir := ctx.GlobalString(DataDirFlag.Name)
	ks := crypto.NewKeyStorePassphrase(path.Join(dataDir, "keys"))
	return accounts.NewManager(ks)
}

func StartRPC(eth *eth.Ethereum, ctx *cli.Context) {
	addr := ctx.GlobalString(RPCListenAddrFlag.Name)
	port := ctx.GlobalInt(RPCPortFlag.Name)
	fmt.Println("Starting RPC on port: ", port)
	l, err := net.Listen("tcp", fmt.Sprintf("%s:%d", addr, port))
	if err != nil {
		Fatalf("Can't listen on %s:%d: %v", addr, port, err)
	}
	go http.Serve(l, rpc.JSONRPC(xeth.New(eth, nil)))
}
<commit_msg>Etherbase => etherbase<commit_after>package utils

import (
	"crypto/ecdsa"
	"fmt"
	"net"
	"net/http"
	"os"
	"path"
	"runtime"

	"github.com/codegangsta/cli"
	"github.com/ethereum/go-ethereum/accounts"
	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/core"
	"github.com/ethereum/go-ethereum/crypto"
	"github.com/ethereum/go-ethereum/eth"
	"github.com/ethereum/go-ethereum/ethdb"
	"github.com/ethereum/go-ethereum/event"
	"github.com/ethereum/go-ethereum/logger"
	"github.com/ethereum/go-ethereum/p2p/nat"
	"github.com/ethereum/go-ethereum/rpc"
	"github.com/ethereum/go-ethereum/xeth"
)

func init() {
	cli.AppHelpTemplate = `{{.Name}} {{if .Flags}}[global options] {{end}}command{{if .Flags}} [command options]{{end}} [arguments...]

VERSION:
   {{.Version}}

COMMANDS:
   {{range .Commands}}{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}}
   {{end}}{{if .Flags}}
GLOBAL OPTIONS:
   {{range .Flags}}{{.}}
   {{end}}{{end}}
`

	cli.CommandHelpTemplate = `{{.Name}}{{if .Subcommands}} command{{end}}{{if .Flags}} [command options]{{end}} [arguments...]
{{if .Description}}{{.Description}}
{{end}}{{if .Subcommands}}
SUBCOMMANDS:
	{{range .Subcommands}}{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}}
	{{end}}{{end}}{{if .Flags}}
OPTIONS:
	{{range .Flags}}{{.}}
	{{end}}{{end}}
`
}

// NewApp creates an app with sane defaults.
func NewApp(version, usage string) *cli.App {
	app := cli.NewApp()
	app.Name = path.Base(os.Args[0])
	app.Author = ""
	//app.Authors = nil
	app.Email = ""
	app.Version = version
	app.Usage = usage
	return app
}

// These are all the command line flags we support.
// If you add to this list, please remember to include the
// flag in the appropriate command definition.
//
// The flags are defined here so their names and help texts
// are the same for all commands.

var (
	// General settings
	DataDirFlag = cli.StringFlag{
		Name:  "datadir",
		Usage: "Data directory to be used",
		Value: common.DefaultDataDir(),
	}
	ProtocolVersionFlag = cli.IntFlag{
		Name:  "protocolversion",
		Usage: "ETH protocol version",
		Value: eth.ProtocolVersion,
	}
	NetworkIdFlag = cli.IntFlag{
		Name:  "networkid",
		Usage: "Network Id",
		Value: eth.NetworkId,
	}

	// miner settings
	MinerThreadsFlag = cli.IntFlag{
		Name:  "minerthreads",
		Usage: "Number of miner threads",
		Value: runtime.NumCPU(),
	}
	MiningEnabledFlag = cli.BoolFlag{
		Name:  "mine",
		Usage: "Enable mining",
	}
	EtherbaseFlag = cli.StringFlag{
		Name:  "etherbase",
		Usage: "public address for block mining rewards. By default the address of your primary account is used",
		Value: "primary",
	}

	UnlockedAccountFlag = cli.StringFlag{
		Name:  "unlock",
		Usage: "unlock the account given until this program exits (prompts for password). '--unlock primary' unlocks the primary account",
		Value: "",
	}
	PasswordFileFlag = cli.StringFlag{
		Name:  "password",
		Usage: "Path to password file for (un)locking an existing account.",
		Value: "",
	}

	// logging and debug settings
	LogFileFlag = cli.StringFlag{
		Name:  "logfile",
		Usage: "Send log output to a file",
	}
	LogLevelFlag = cli.IntFlag{
		Name:  "loglevel",
		Usage: "0-5 (silent, error, warn, info, debug, debug detail)",
		Value: int(logger.InfoLevel),
	}
	LogJSONFlag = cli.StringFlag{
		Name:  "logjson",
		Usage: "Send json structured log output to a file or '-' for standard output (default: no json output)",
		Value: "",
	}
	VMDebugFlag = cli.BoolFlag{
		Name:  "vmdebug",
		Usage: "Virtual Machine debug output",
	}

	// RPC settings
	RPCEnabledFlag = cli.BoolFlag{
		Name:  "rpc",
		Usage: "Whether RPC server is enabled",
	}
	RPCListenAddrFlag = cli.StringFlag{
		Name:  "rpcaddr",
		Usage: "Listening address for the JSON-RPC server",
		Value: "127.0.0.1",
	}
	RPCPortFlag = cli.IntFlag{
		Name:  "rpcport",
		Usage: "Port on which the JSON-RPC server should listen",
		Value: 8545,
	}

	// Network Settings
	MaxPeersFlag = cli.IntFlag{
		Name:  "maxpeers",
		Usage: "Maximum number of network peers",
		Value: 16,
	}
	ListenPortFlag = cli.IntFlag{
		Name:  "port",
		Usage: "Network listening port",
		Value: 30303,
	}
	BootnodesFlag = cli.StringFlag{
		Name:  "bootnodes",
		Usage: "Space-separated enode URLs for discovery bootstrap",
		Value: "",
	}
	NodeKeyFileFlag = cli.StringFlag{
		Name:  "nodekey",
		Usage: "P2P node key file",
	}
	NodeKeyHexFlag = cli.StringFlag{
		Name:  "nodekeyhex",
		Usage: "P2P node key as hex (for testing)",
	}
	NATFlag = cli.StringFlag{
		Name:  "nat",
		Usage: "Port mapping mechanism (any|none|upnp|pmp|extip:<IP>)",
		Value: "any",
	}
	JSpathFlag = cli.StringFlag{
		Name:  "jspath",
		Usage: "JS library path to be used with console and js subcommands",
		Value: ".",
	}
)

func GetNAT(ctx *cli.Context) nat.Interface {
	natif, err := nat.Parse(ctx.GlobalString(NATFlag.Name))
	if err != nil {
		Fatalf("Option %s: %v", NATFlag.Name, err)
	}
	return natif
}

func GetNodeKey(ctx *cli.Context) (key *ecdsa.PrivateKey) {
	hex, file := ctx.GlobalString(NodeKeyHexFlag.Name), ctx.GlobalString(NodeKeyFileFlag.Name)
	var err error
	switch {
	case file != "" && hex != "":
		Fatalf("Options %q and %q are mutually exclusive", NodeKeyFileFlag.Name, NodeKeyHexFlag.Name)
	case file != "":
		if key, err = crypto.LoadECDSA(file); err != nil {
			Fatalf("Option %q: %v", NodeKeyFileFlag.Name, err)
		}
	case hex != "":
		if key, err = crypto.HexToECDSA(hex); err != nil {
			Fatalf("Option %q: %v", NodeKeyHexFlag.Name, err)
		}
	}
	return key
}

func MakeEthConfig(clientID, version string, ctx *cli.Context) *eth.Config {
	return &eth.Config{
		Name:            common.MakeName(clientID, version),
		DataDir:         ctx.GlobalString(DataDirFlag.Name),
		ProtocolVersion: ctx.GlobalInt(ProtocolVersionFlag.Name),
		NetworkId:       ctx.GlobalInt(NetworkIdFlag.Name),
		LogFile:         ctx.GlobalString(LogFileFlag.Name),
		LogLevel:        ctx.GlobalInt(LogLevelFlag.Name),
		LogJSON:         ctx.GlobalString(LogJSONFlag.Name),
		Etherbase:       ctx.GlobalString(EtherbaseFlag.Name),
		MinerThreads:    ctx.GlobalInt(MinerThreadsFlag.Name),
		AccountManager:  GetAccountManager(ctx),
		VmDebug:         ctx.GlobalBool(VMDebugFlag.Name),
		MaxPeers:        ctx.GlobalInt(MaxPeersFlag.Name),
		Port:            ctx.GlobalString(ListenPortFlag.Name),
		NAT:             GetNAT(ctx),
		NodeKey:         GetNodeKey(ctx),
		Shh:             true,
		Dial:            true,
		BootNodes:       ctx.GlobalString(BootnodesFlag.Name),
	}
}

func GetChain(ctx *cli.Context) (*core.ChainManager, common.Database, common.Database) {
	dataDir := ctx.GlobalString(DataDirFlag.Name)
	blockDb, err := ethdb.NewLDBDatabase(path.Join(dataDir, "blockchain"))
	if err != nil {
		Fatalf("Could not open database: %v", err)
	}

	stateDb, err := ethdb.NewLDBDatabase(path.Join(dataDir, "state"))
	if err != nil {
		Fatalf("Could not open database: %v", err)
	}
	return core.NewChainManager(blockDb, stateDb, new(event.TypeMux)), blockDb, stateDb
}

func GetAccountManager(ctx *cli.Context) *accounts.Manager {
	dataDir := ctx.GlobalString(DataDirFlag.Name)
	ks := crypto.NewKeyStorePassphrase(path.Join(dataDir, "keys"))
	return accounts.NewManager(ks)
}

func StartRPC(eth *eth.Ethereum, ctx *cli.Context) {
	addr := ctx.GlobalString(RPCListenAddrFlag.Name)
	port := ctx.GlobalInt(RPCPortFlag.Name)
	fmt.Println("Starting RPC on port: ", port)
	l, err := net.Listen("tcp", fmt.Sprintf("%s:%d", addr, port))
	if err != nil {
		Fatalf("Can't listen on %s:%d: %v", addr, port, err)
	}
	go http.Serve(l, rpc.JSONRPC(xeth.New(eth, nil)))
}
<|endoftext|>


<commit_before>package httpmux

import (
	"regexp"
	"strings"
)

type section struct {
	sName        string // section name without leading char if not raw type
	sType        sectionType
	regexp       *regexp.Regexp
	hasNonRawSub bool // only one non-raw sub section is allowed
	subs         map[string]*section
	ts           bool // trailing slash, useful if this is last section
	h            Handle
}

type flags uint32

type sectionType int

const (
	RedirectTrailingSlash = 1 << iota
)

const (
	SectionTypeRaw sectionType = iota
	SectionTypeWildCard
	SectionTypeMatch
	SectionTypeRegexp
)

func (s sectionType) String() string {
	n := "SectionTypeUnknown"
	switch s {
	case SectionTypeRaw:
		n = "SectionTypeRaw"
	case SectionTypeWildCard:
		n = "SectionTypeWildCard"
	case SectionTypeMatch:
		n = "SectionTypeMatch"
	case SectionTypeRegexp:
		n = "SectionTypeRexexp"
	}
	return n
}

func newSection(sParent *section, name string) (*section, string) {
	s := &section{}
	switch name[0] {
	case ':':
		s.sType = SectionTypeMatch
		s.sName = name[1:]
	case '*':
		s.sType = SectionTypeWildCard
		s.sName = name[1:]
	case '#':
		s.sType = SectionTypeRegexp
		var re string
		if len(name) == 1 {
			return nil, "regexp empty"
		}
		if name[1] == '{' {
			if i := strings.Index(name, "}"); i == -1 {
				return nil, "regexp format error"
			} else {
				s.sName = name[2:i]
				re = name[i+1:]
			}
		} else {
			re = name[1:]
		}
		if len(re) == 0 {
			return nil, "regexp empty"
		}
		var err error
		s.regexp, err = regexp.Compile(re)
		if err != nil {
			return nil, "regexp compile error"
		}
	default:
		s.sType = SectionTypeRaw
		s.sName = name
	}

	/*
		if sParent != nil {
			fmt.Printf("pname=%s ptype=%s", sParent.sName, sParent.sType)
		}
		fmt.Printf(" name=%s sName=%s sType=%s regexp=%s\n", name, s.sName, s.sType, s.regexp)
	*/

	if sParent != nil {
		if sParent.sType == SectionTypeWildCard {
			return nil, "wildcard not the last section"
		}
		if s.sType != SectionTypeRaw {
			if sParent.hasNonRawSub {
				return nil, "multiple non raw section"
			}
			sParent.hasNonRawSub = true
		}
	}

	return s, ""
}

func (rs *section) addRoute(path string, h Handle) {
	if h == nil {
		panic("handle not defined for path " + path)
	}

	s := rs
	ps := strings.Split(path, "/")
	for _, p := range ps {
		if len(p) == 0 {
			continue
		}
		if s.subs == nil {
			s.subs = make(map[string]*section)
		}

		p = strings.ToLower(p)
		ss, ok := s.subs[p]
		if !ok {
			errmsg := ""
			if ss, errmsg = newSection(s, p); errmsg != "" {
				panic("error: addRoute: " + path + " " + errmsg)
			}
			s.subs[p] = ss

		}
		s = ss
	}

	if s.h != nil {
		panic("handle for '" + path + "' redefined")
	}
	s.h = h

	if s != rs {
		s.ts = strings.HasSuffix(path, "/")
	}
}

func (s *section) match(ps []string, ctx *Context) (m bool, h Handle, stop bool) {
	switch s.sType {
	case SectionTypeWildCard:
		ctx.setParam(s.sName, strings.Join(ps, "/"))
		m, h, stop = true, s.h, true
	case SectionTypeMatch:
		ctx.setParam(s.sName, ps[0])
		m, h = true, s.h
	case SectionTypeRegexp:
		if s.regexp.Match([]byte(ps[0])) {
			ctx.setParam(s.sName, ps[0])
			m, h = true, s.h
		}
	case SectionTypeRaw:
		fallthrough
	default:
	}
	return
}

func (rs *section) findRoute(path string, ctx *Context) Handle {
	var h Handle
	s := rs
	ps := strings.Split(path, "/")
loop:
	for i, p := range ps {
		if len(p) == 0 {
			continue
		}
		if s.subs == nil {
			return nil
		}
		if ss, ok := s.subs[p]; ok { // matches raw
			h = ss.h
			s = ss
			continue
		}

		match, stop := false, false
		for _, ss := range s.subs {
			match, h, stop = ss.match(ps[i:], ctx)
			if match {
				if stop {
					break loop
				} else {
					s = ss
					continue loop
				}
			}
		}
	}
	return h
}
<commit_msg>fix: root handler search<commit_after>package httpmux

import (
	"regexp"
	"strings"
)

type section struct {
	sName        string // section name without leading char if not raw type
	sType        sectionType
	regexp       *regexp.Regexp
	hasNonRawSub bool // only one non-raw sub section is allowed
	subs         map[string]*section
	ts           bool // trailing slash, useful if this is last section
	h            Handle
}

type flags uint32

type sectionType int

const (
	RedirectTrailingSlash = 1 << iota
)

const (
	SectionTypeRaw sectionType = iota
	SectionTypeWildCard
	SectionTypeMatch
	SectionTypeRegexp
)

func (s sectionType) String() string {
	n := "SectionTypeUnknown"
	switch s {
	case SectionTypeRaw:
		n = "SectionTypeRaw"
	case SectionTypeWildCard:
		n = "SectionTypeWildCard"
	case SectionTypeMatch:
		n = "SectionTypeMatch"
	case SectionTypeRegexp:
		n = "SectionTypeRexexp"
	}
	return n
}

func newSection(sParent *section, name string) (*section, string) {
	s := &section{}
	switch name[0] {
	case ':':
		s.sType = SectionTypeMatch
		s.sName = name[1:]
	case '*':
		s.sType = SectionTypeWildCard
		s.sName = name[1:]
	case '#':
		s.sType = SectionTypeRegexp
		var re string
		if len(name) == 1 {
			return nil, "regexp empty"
		}
		if name[1] == '{' {
			if i := strings.Index(name, "}"); i == -1 {
				return nil, "regexp format error"
			} else {
				s.sName = name[2:i]
				re = name[i+1:]
			}
		} else {
			re = name[1:]
		}
		if len(re) == 0 {
			return nil, "regexp empty"
		}
		var err error
		s.regexp, err = regexp.Compile(re)
		if err != nil {
			return nil, "regexp compile error"
		}
	default:
		s.sType = SectionTypeRaw
		s.sName = name
	}

	/*
		if sParent != nil {
			fmt.Printf("pname=%s ptype=%s", sParent.sName, sParent.sType)
		}
		fmt.Printf(" name=%s sName=%s sType=%s regexp=%s\n", name, s.sName, s.sType, s.regexp)
	*/

	if sParent != nil {
		if sParent.sType == SectionTypeWildCard {
			return nil, "wildcard not the last section"
		}
		if s.sType != SectionTypeRaw {
			if sParent.hasNonRawSub {
				return nil, "multiple non raw section"
			}
			sParent.hasNonRawSub = true
		}
	}

	return s, ""
}

func (rs *section) addRoute(path string, h Handle) {
	if h == nil {
		panic("handle not defined for path " + path)
	}
	if len(path) == 0 || path[0] != '/' {
		panic("path must begin with '/'")
	}

	s := rs
	ps := strings.Split(path, "/")
	for _, p := range ps {
		if len(p) == 0 {
			continue
		}
		if s.subs == nil {
			s.subs = make(map[string]*section)
		}

		p = strings.ToLower(p)
		ss, ok := s.subs[p]
		if !ok {
			errmsg := ""
			if ss, errmsg = newSection(s, p); errmsg != "" {
				panic("error: addRoute: " + path + " " + errmsg)
			}
			s.subs[p] = ss

		}
		s = ss
	}

	if s.h != nil {
		panic("handle for '" + path + "' redefined")
	}
	s.h = h

	if s != rs {
		s.ts = strings.HasSuffix(path, "/")
	}
}

func (s *section) match(ps []string, ctx *Context) (m bool, h Handle, stop bool) {
	switch s.sType {
	case SectionTypeWildCard:
		ctx.setParam(s.sName, strings.Join(ps, "/"))
		m, h, stop = true, s.h, true
	case SectionTypeMatch:
		ctx.setParam(s.sName, ps[0])
		m, h = true, s.h
	case SectionTypeRegexp:
		if s.regexp.Match([]byte(ps[0])) {
			ctx.setParam(s.sName, ps[0])
			m, h = true, s.h
		}
	case SectionTypeRaw:
		fallthrough
	default:
	}
	return
}

func (rs *section) findRoute(path string, ctx *Context) Handle {
	var h Handle
	isRoot := true
	s := rs
	ps := strings.Split(path, "/")
loop:
	for i, p := range ps {
		if len(p) == 0 {
			continue
		}
		isRoot = false
		if s.subs == nil {
			return nil
		}
		if ss, ok := s.subs[p]; ok { // matches raw
			h = ss.h
			s = ss
			continue
		}

		match, stop := false, false
		for _, ss := range s.subs {
			match, h, stop = ss.match(ps[i:], ctx)
			if match {
				if stop {
					break loop
				} else {
					s = ss
					continue loop
				}
			}
		}

		return nil
	}

	if isRoot {
		return s.h
	}
	return h
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"net/http"
	"regexp"
	//"url"
)

type HandlerFunc http.HandlerFunc

type SelectHandler interface {
	http.Handler
	Select([]string)
	//Store(url.Values)
	//Get()
	//Post()
	//Put()
	//Delete()
}

type route struct {
	re      *regexp.Regexp
	handler SelectHandler
}

type ReHandler struct {
	routes []*route
}

func (f HandlerFunc) Select(_ []string)                                {}
func (f HandlerFunc) ServeHTTP(w http.ResponseWriter, r *http.Request) { f(w, r) }

func (h *ReHandler) Handle(re string, handler SelectHandler) {
	log.Println("SelectHandler", re)
	r := &route{
		re:      regexp.MustCompile(re),
		handler: handler,
	}
	h.routes = append(h.routes, r)
}

func (h *ReHandler) HandleFunc(re string, handler HandlerFunc) {
	log.Println("HandlerFunc", re)
	r := &route{
		re:      regexp.MustCompile(re),
		handler: handler,
	}
	h.routes = append(h.routes, r)
}

func (h *ReHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	for _, route := range h.routes {
		matches := route.re.FindStringSubmatch(r.URL.Path)
		if matches != nil {
			log.Println("Match", matches, r.URL)
			route.handler.Select(matches[1:])
			log.Println(r.Method, matches) // debug output
			if r.Method == "POST" {
				r.ParseForm()
				//route.handler.Store(r.PostForm)
				log.Println("Redirect:", r.URL, r.PostForm)
				http.Redirect(w, r, r.URL.Path, http.StatusFound)
			}
			route.handler.ServeHTTP(w, r)
			return
		}
	}
	http.NotFound(w, r)
}
<commit_msg>change debug output<commit_after>package main

import (
	"log"
	"net/http"
	"regexp"
	//"url"
)

type HandlerFunc http.HandlerFunc

type SelectHandler interface {
	http.Handler
	Select([]string)
	//Store(url.Values)
	//Get()
	//Post()
	//Put()
	//Delete()
}

type route struct {
	re      *regexp.Regexp
	handler SelectHandler
}

type ReHandler struct {
	routes []*route
}

func (f HandlerFunc) Select(_ []string)                                {}
func (f HandlerFunc) ServeHTTP(w http.ResponseWriter, r *http.Request) { f(w, r) }

func (h *ReHandler) Handle(re string, handler SelectHandler) {
	log.Println("SelectHandler", re)
	r := &route{
		re:      regexp.MustCompile(re),
		handler: handler,
	}
	h.routes = append(h.routes, r)
}

func (h *ReHandler) HandleFunc(re string, handler HandlerFunc) {
	log.Println("HandlerFunc", re)
	r := &route{
		re:      regexp.MustCompile(re),
		handler: handler,
	}
	h.routes = append(h.routes, r)
}

func (h *ReHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	log.Println(r.Method, r.URL)
	for _, route := range h.routes {
		matches := route.re.FindStringSubmatch(r.URL.Path)
		if matches != nil {
			log.Println("Match", matches, r.URL)
			route.handler.Select(matches[1:])
			if r.Method == "POST" {
				r.ParseForm()
				//route.handler.Store(r.PostForm)
				log.Println("Redirect:", r.URL, r.PostForm)
				http.Redirect(w, r, r.URL.Path, http.StatusFound)
			}
			route.handler.ServeHTTP(w, r)
			return
		}
	}
	http.NotFound(w, r)
}
<|endoftext|>


<commit_before>package powermux

import (
	"net/http"
	"net/url"
	"sort"
	"strings"
)

const (
	methodAny = "ANY"
	notFound  = "NOT_FOUND"
)

type childList []*Route

func (l childList) Len() int {
	return len(l)
}

func (l childList) Less(i, j int) bool {
	return l[i].pattern < l[j].pattern
}

func (l childList) Swap(i, j int) {
	l[i], l[j] = l[j], l[i]
}

func (l childList) Search(pattern string) *Route {
	index := sort.Search(l.Len(), func(i int) bool {
		return l[i].pattern >= pattern
	})

	if index < l.Len() && l[index].pattern == pattern {
		return l[index]
	}

	return nil
}

// routeExecution is the complete instructions for running serve on a route
type routeExecution struct {
	pattern    string
	params     map[string]string
	notFound   http.Handler
	middleware []Middleware
	handler    http.Handler
}

// A Route represents a specific path for a request.
// Routes can be absolute paths, rooted subtrees, or path parameters that accept any stringRoutes.
type Route struct {
	// the pattern our node matches
	pattern string
	// the full path to this node
	fullPath string
	// if we are a named path param node '/:name'
	isParam bool
	// the name of our path parameter
	paramName string
	// if we are a rooted sub tree '/dir/*'
	isWildcard bool
	// the array of middleware this node invokes
	middleware []Middleware
	// child nodes
	children childList
	// child node for path parameters
	paramChild *Route
	// set if there's a wildcard handler child (lowest priority)
	wildcardChild *Route
	// the map of handlers for different methods
	handlers map[string]http.Handler
}

// newRoute allocates all the structures required for a route node.
// Default pattern is "" which matches only the top level node.
func newRoute() *Route {
	return &Route{
		handlers:   make(map[string]http.Handler),
		middleware: make([]Middleware, 0),
		children:   make([]*Route, 0),
	}
}

// execute sets up the tree traversal required to get the execution instructions for
// a route.
func (r *Route) execute(method, pattern string) *routeExecution {

	pathParts := strings.Split(pattern, "/")

	if pattern == "/" {
		pathParts = pathParts[1:]
	}

	// Create a new routeExecution
	ex := &routeExecution{
		middleware: make([]Middleware, 0),
		params:     make(map[string]string),
	}

	// Fill the execution
	r.getExecution(method, pathParts, ex)

	// return the result
	return ex
}

// getExecution is a recursive step in the tree traversal. It checks to see if this node matches,
// fills out any instructions in the execution, and returns. The return value indicates only if
// this node matched, not if anything was added to the execution.
func (r *Route) getExecution(method string, pathParts []string, ex *routeExecution) {

	var curRoute *Route = r

	for {

		// save all the middleware
		ex.middleware = append(ex.middleware, curRoute.middleware...)

		// save not found handler
		if h, ok := curRoute.handlers[notFound]; ok {
			ex.notFound = h
		}

		// save options handler
		if method == http.MethodOptions {
			if h, ok := curRoute.handlers[http.MethodOptions]; ok {
				ex.handler = h
			}
		}

		// save path parameters
		if curRoute.isParam {
			value, err := url.PathUnescape(pathParts[0])
			if err != nil {
				// TODO: maybe handle errors more gracefully
				panic(err)
			}
			ex.params[curRoute.paramName] = value
		}

		// check if this is the bottom of the path
		if len(pathParts) == 1 || curRoute.isWildcard {

			// hit the bottom of the tree, see if we have a handler to offer
			curRoute.getHandler(method, ex)

			if curRoute.fullPath == "" {
				ex.pattern = "/"
			} else {
				ex.pattern = curRoute.fullPath
			}
			return

		}

		// iterate over our children looking for deeper to go

		// binary search over regular children
		if child := curRoute.children.Search(pathParts[1]); child != nil {
			pathParts = pathParts[1:]
			curRoute = child
			continue
		}

		// try for params and wildcard children
		if curRoute.paramChild != nil {
			pathParts = pathParts[1:]
			curRoute = curRoute.paramChild
			continue
		}
		if curRoute.wildcardChild != nil {
			pathParts = pathParts[1:]
			curRoute = curRoute.wildcardChild
			continue
		}

		return
	}
}

// getHandler is a convenience function for choosing a handler from the route's map of options
// Order of precedence:
// 1. An exact method match
// 2. HEAD requests can use GET handlers
// 3. The ANY handler
// 4. A generated Options handler if this is an options request and no previous handler is set
// 5. A generated Method Not Allowed response
func (r *Route) getHandler(method string, ex *routeExecution) {
	// check specific method match
	if h, ok := r.handlers[method]; ok {
		ex.handler = h
		return
	}

	// if this is a HEAD we can fall back on GET
	if method == http.MethodHead {
		if h, ok := r.handlers[http.MethodGet]; ok {
			ex.handler = h
			return
		}
	}

	// check the ANY handler
	if h, ok := r.handlers[methodAny]; ok {
		ex.handler = h
		return
	}

	// generate an options handler if none is already set
	if method == http.MethodOptions && ex.handler == nil {
		ex.handler = r.defaultOptions()
		return
	}

	// last ditch effort is to generate our own method not allowed handler
	// this is regenerated each time in case routes are added during runtime
	// not generated if a previous handler is already set
	if ex.handler == nil {
		ex.handler = r.methodNotAllowed()
	}
	return
}

// Route walks down the route tree following pattern and returns either a new or previously
// existing node that represents that specific path.
func (r *Route) Route(path string) *Route {

	// prepend a leading slash if not present
	if path[0] != '/' {
		path = "/" + path
	}

	// remove the tailing slash if it is present
	if path != "/" {
		path = strings.TrimRight(path, "/")
	}

	// append our node name to the search if we're not root
	if r.pattern != "" {
		path = r.pattern + path
	}

	// chop the path into pieces
	pathParts := strings.Split(path, "/")

	// handle the case where we're the root node
	if path == "/" {
		// strings.Split will have returned ["", ""]
		// drop one of them
		pathParts = pathParts[1:]
	}

	// find/create the new path
	return r.create(pathParts, r.fullPath)
}

// Create descends the tree following path, creating nodes as needed and returns the target node
func (r *Route) create(path []string, parentPath string) *Route {

	// ensure this path matches us
	if r.pattern != path[0] {
		// not us
		return nil
	}

	// if this is us, return, no creation necessary
	if len(path) == 1 {
		return r
	}

	// iterate over all children looking for a place to put this
	for _, child := range r.getChildren() {
		if r := child.create(path[1:], r.fullPath); r != nil {
			return r
		}
	}

	// child can't create it, so we will
	newRoute := newRoute()

	// set the pattern name
	newRoute.pattern = path[1]
	newRoute.fullPath = r.fullPath + "/" + path[1]

	// check if it's a path param
	if strings.HasPrefix(path[1], ":") {
		newRoute.isParam = true
		newRoute.paramName = strings.TrimLeft(path[1], ":")

		// save it in the correct place
		r.paramChild = newRoute

	} else if path[1] == "*" {
		// check if this is a rooted subtree
		newRoute.isWildcard = true

		// save to wildcard child
		r.wildcardChild = newRoute

		// go no deeper
		return newRoute
	} else {
		// Just a regular child
		r.children = append(r.children, newRoute)

		// sort children alphabetically for efficient run time searching
		sort.Sort(r.children)
	}

	// the cycle continues
	return newRoute.create(path[1:], r.fullPath)
}

// stringRoutes returns the stringRoutes representation of this route and all below it.
func (r *Route) stringRoutes(routes *[]string) {

	var thisRoute string

	// handle root node
	if r.fullPath == "" {
		thisRoute = "/"
	} else {
		thisRoute = r.fullPath
	}

	if len(r.handlers) > 0 {
		thisRoute = thisRoute + "\t["
		methods := make([]string, 0, 8)
		for method := range r.handlers {
			methods = append(methods, method)
		}
		thisRoute = thisRoute + strings.Join(methods, ", ") + "]"
		*routes = append(*routes, thisRoute)
	}

	// recursion
	for _, child := range r.getChildren() {
		child.stringRoutes(routes)
	}
}

// getChildren returns the all the route handler with the correct order of precedence
func (r *Route) getChildren() []*Route {

	// allocate once
	allRoutes := make([]*Route, 0, len(r.children)+2)

	// start with the normal routes
	allRoutes = append(allRoutes, r.children...)

	// then add the param child
	if r.paramChild != nil {
		allRoutes = append(allRoutes, r.paramChild)
	}

	// then add the wildcard child
	if r.wildcardChild != nil {
		allRoutes = append(allRoutes, r.wildcardChild)
	}

	return allRoutes
}

// Middleware adds a middleware to this Route.
//
// Middlewares are executed if the path to the target route crosses this route.
func (r *Route) Middleware(m Middleware) *Route {
	r.middleware = append(r.middleware, m)
	return r
}

// MiddlewareFunc registers a plain function as a middleware.
func (r *Route) MiddlewareFunc(m MiddlewareFunc) *Route {
	return r.Middleware(MiddlewareFunc(m))
}

// Any registers a catch-all handler for any method sent to this route.
// This takes lower precedence than a specific method match.
func (r *Route) Any(handler http.Handler) *Route {
	r.handlers[methodAny] = handler
	return r
}

// Post adds a handler for PUT methods to this route.
func (r *Route) Put(handler http.Handler) *Route {
	r.handlers[http.MethodPut] = handler
	return r
}

// Post adds a handler for POST methods to this route.
func (r *Route) Post(handler http.Handler) *Route {
	r.handlers[http.MethodPost] = handler
	return r
}

// Patch adds a handler for PATCH methods to this route.
func (r *Route) Patch(handler http.Handler) *Route {
	r.handlers[http.MethodPatch] = handler
	return r
}

// Get adds a handler for GET methods to this route.
// Get handlers will also be called for HEAD requests if no specific
// HEAD handler is registered.
func (r *Route) Get(handler http.Handler) *Route {
	r.handlers[http.MethodGet] = handler
	return r
}

// Delete adds a handler for DELETE methods to this route.
func (r *Route) Delete(handler http.Handler) *Route {
	r.handlers[http.MethodDelete] = handler
	return r
}

// Head adds a handler for HEAD methods to this route.
func (r *Route) Head(handler http.Handler) *Route {
	r.handlers[http.MethodHead] = handler
	return r
}

// Connect adds a handler for CONNECT methods to this route.
func (r *Route) Connect(handler http.Handler) *Route {
	r.handlers[http.MethodConnect] = handler
	return r
}

// Options adds a handler for OPTIONS methods to this route.
// This handler will also be called for any routes further down the path from
// this point if no other OPTIONS handlers are registered below.
func (r *Route) Options(handler http.Handler) *Route {
	r.handlers[http.MethodOptions] = handler
	return r
}

// NotFound adds a handler for requests that do not correspond to a route.
// This handler will also be called for any routes further down the path from
// this point if no other not found handlers are registered below.
func (r *Route) NotFound(handler http.Handler) *Route {
	r.handlers[notFound] = handler
	return r
}
<commit_msg>add route helpers which accept handler functions<commit_after>package powermux

import (
	"net/http"
	"net/url"
	"sort"
	"strings"
)

const (
	methodAny = "ANY"
	notFound  = "NOT_FOUND"
)

type childList []*Route

func (l childList) Len() int {
	return len(l)
}

func (l childList) Less(i, j int) bool {
	return l[i].pattern < l[j].pattern
}

func (l childList) Swap(i, j int) {
	l[i], l[j] = l[j], l[i]
}

func (l childList) Search(pattern string) *Route {
	index := sort.Search(l.Len(), func(i int) bool {
		return l[i].pattern >= pattern
	})

	if index < l.Len() && l[index].pattern == pattern {
		return l[index]
	}

	return nil
}

// routeExecution is the complete instructions for running serve on a route
type routeExecution struct {
	pattern    string
	params     map[string]string
	notFound   http.Handler
	middleware []Middleware
	handler    http.Handler
}

// A Route represents a specific path for a request.
// Routes can be absolute paths, rooted subtrees, or path parameters that accept any stringRoutes.
type Route struct {
	// the pattern our node matches
	pattern string
	// the full path to this node
	fullPath string
	// if we are a named path param node '/:name'
	isParam bool
	// the name of our path parameter
	paramName string
	// if we are a rooted sub tree '/dir/*'
	isWildcard bool
	// the array of middleware this node invokes
	middleware []Middleware
	// child nodes
	children childList
	// child node for path parameters
	paramChild *Route
	// set if there's a wildcard handler child (lowest priority)
	wildcardChild *Route
	// the map of handlers for different methods
	handlers map[string]http.Handler
}

// newRoute allocates all the structures required for a route node.
// Default pattern is "" which matches only the top level node.
func newRoute() *Route {
	return &Route{
		handlers:   make(map[string]http.Handler),
		middleware: make([]Middleware, 0),
		children:   make([]*Route, 0),
	}
}

// execute sets up the tree traversal required to get the execution instructions for
// a route.
func (r *Route) execute(method, pattern string) *routeExecution {

	pathParts := strings.Split(pattern, "/")

	if pattern == "/" {
		pathParts = pathParts[1:]
	}

	// Create a new routeExecution
	ex := &routeExecution{
		middleware: make([]Middleware, 0),
		params:     make(map[string]string),
	}

	// Fill the execution
	r.getExecution(method, pathParts, ex)

	// return the result
	return ex
}

// getExecution is a recursive step in the tree traversal. It checks to see if this node matches,
// fills out any instructions in the execution, and returns. The return value indicates only if
// this node matched, not if anything was added to the execution.
func (r *Route) getExecution(method string, pathParts []string, ex *routeExecution) {

	var curRoute *Route = r

	for {

		// save all the middleware
		ex.middleware = append(ex.middleware, curRoute.middleware...)

		// save not found handler
		if h, ok := curRoute.handlers[notFound]; ok {
			ex.notFound = h
		}

		// save options handler
		if method == http.MethodOptions {
			if h, ok := curRoute.handlers[http.MethodOptions]; ok {
				ex.handler = h
			}
		}

		// save path parameters
		if curRoute.isParam {
			value, err := url.PathUnescape(pathParts[0])
			if err != nil {
				// TODO: maybe handle errors more gracefully
				panic(err)
			}
			ex.params[curRoute.paramName] = value
		}

		// check if this is the bottom of the path
		if len(pathParts) == 1 || curRoute.isWildcard {

			// hit the bottom of the tree, see if we have a handler to offer
			curRoute.getHandler(method, ex)

			if curRoute.fullPath == "" {
				ex.pattern = "/"
			} else {
				ex.pattern = curRoute.fullPath
			}
			return

		}

		// iterate over our children looking for deeper to go

		// binary search over regular children
		if child := curRoute.children.Search(pathParts[1]); child != nil {
			pathParts = pathParts[1:]
			curRoute = child
			continue
		}

		// try for params and wildcard children
		if curRoute.paramChild != nil {
			pathParts = pathParts[1:]
			curRoute = curRoute.paramChild
			continue
		}
		if curRoute.wildcardChild != nil {
			pathParts = pathParts[1:]
			curRoute = curRoute.wildcardChild
			continue
		}

		return
	}
}

// getHandler is a convenience function for choosing a handler from the route's map of options
// Order of precedence:
// 1. An exact method match
// 2. HEAD requests can use GET handlers
// 3. The ANY handler
// 4. A generated Options handler if this is an options request and no previous handler is set
// 5. A generated Method Not Allowed response
func (r *Route) getHandler(method string, ex *routeExecution) {
	// check specific method match
	if h, ok := r.handlers[method]; ok {
		ex.handler = h
		return
	}

	// if this is a HEAD we can fall back on GET
	if method == http.MethodHead {
		if h, ok := r.handlers[http.MethodGet]; ok {
			ex.handler = h
			return
		}
	}

	// check the ANY handler
	if h, ok := r.handlers[methodAny]; ok {
		ex.handler = h
		return
	}

	// generate an options handler if none is already set
	if method == http.MethodOptions && ex.handler == nil {
		ex.handler = r.defaultOptions()
		return
	}

	// last ditch effort is to generate our own method not allowed handler
	// this is regenerated each time in case routes are added during runtime
	// not generated if a previous handler is already set
	if ex.handler == nil {
		ex.handler = r.methodNotAllowed()
	}
	return
}

// Route walks down the route tree following pattern and returns either a new or previously
// existing node that represents that specific path.
func (r *Route) Route(path string) *Route {

	// prepend a leading slash if not present
	if path[0] != '/' {
		path = "/" + path
	}

	// remove the tailing slash if it is present
	if path != "/" {
		path = strings.TrimRight(path, "/")
	}

	// append our node name to the search if we're not root
	if r.pattern != "" {
		path = r.pattern + path
	}

	// chop the path into pieces
	pathParts := strings.Split(path, "/")

	// handle the case where we're the root node
	if path == "/" {
		// strings.Split will have returned ["", ""]
		// drop one of them
		pathParts = pathParts[1:]
	}

	// find/create the new path
	return r.create(pathParts, r.fullPath)
}

// Create descends the tree following path, creating nodes as needed and returns the target node
func (r *Route) create(path []string, parentPath string) *Route {

	// ensure this path matches us
	if r.pattern != path[0] {
		// not us
		return nil
	}

	// if this is us, return, no creation necessary
	if len(path) == 1 {
		return r
	}

	// iterate over all children looking for a place to put this
	for _, child := range r.getChildren() {
		if r := child.create(path[1:], r.fullPath); r != nil {
			return r
		}
	}

	// child can't create it, so we will
	newRoute := newRoute()

	// set the pattern name
	newRoute.pattern = path[1]
	newRoute.fullPath = r.fullPath + "/" + path[1]

	// check if it's a path param
	if strings.HasPrefix(path[1], ":") {
		newRoute.isParam = true
		newRoute.paramName = strings.TrimLeft(path[1], ":")

		// save it in the correct place
		r.paramChild = newRoute

	} else if path[1] == "*" {
		// check if this is a rooted subtree
		newRoute.isWildcard = true

		// save to wildcard child
		r.wildcardChild = newRoute

		// go no deeper
		return newRoute
	} else {
		// Just a regular child
		r.children = append(r.children, newRoute)

		// sort children alphabetically for efficient run time searching
		sort.Sort(r.children)
	}

	// the cycle continues
	return newRoute.create(path[1:], r.fullPath)
}

// stringRoutes returns the stringRoutes representation of this route and all below it.
func (r *Route) stringRoutes(routes *[]string) {

	var thisRoute string

	// handle root node
	if r.fullPath == "" {
		thisRoute = "/"
	} else {
		thisRoute = r.fullPath
	}

	if len(r.handlers) > 0 {
		thisRoute = thisRoute + "\t["
		methods := make([]string, 0, 8)
		for method := range r.handlers {
			methods = append(methods, method)
		}
		thisRoute = thisRoute + strings.Join(methods, ", ") + "]"
		*routes = append(*routes, thisRoute)
	}

	// recursion
	for _, child := range r.getChildren() {
		child.stringRoutes(routes)
	}
}

// getChildren returns all the routes with the correct order of precedence
func (r *Route) getChildren() []*Route {

	// allocate once
	allRoutes := make([]*Route, 0, len(r.children)+2)

	// start with the normal routes
	allRoutes = append(allRoutes, r.children...)

	// then add the param child
	if r.paramChild != nil {
		allRoutes = append(allRoutes, r.paramChild)
	}

	// then add the wildcard child
	if r.wildcardChild != nil {
		allRoutes = append(allRoutes, r.wildcardChild)
	}

	return allRoutes
}

// Middleware adds a middleware to this Route.
//
// Middlewares are executed if the path to the target route crosses this route.
func (r *Route) Middleware(m Middleware) *Route {
	r.middleware = append(r.middleware, m)
	return r
}

// MiddlewareFunc registers a plain function as a middleware.
func (r *Route) MiddlewareFunc(m MiddlewareFunc) *Route {
	return r.Middleware(MiddlewareFunc(m))
}

// Any registers a catch-all handler for any method sent to this route.
// This takes lower precedence than a specific method match.
func (r *Route) Any(handler http.Handler) *Route {
	r.handlers[methodAny] = handler
	return r
}

// AnyFunc registers a plain function as a catch-all handler
// for any method sent to this route.
// This takes lower precedence than a specific method match.
func (r *Route) AnyFunc(f http.HandlerFunc) *Route {
	return r.Any(http.HandlerFunc(f))
}

// Post adds a handler for POST methods to this route.
func (r *Route) Post(handler http.Handler) *Route {
	r.handlers[http.MethodPost] = handler
	return r
}

// PostFunc adds a plain function as a handler
// for POST methods to this route.
func (r *Route) PostFunc(f http.HandlerFunc) *Route {
	return r.Post(http.HandlerFunc(f))
}

// Put adds a handler for PUT methods to this route.
func (r *Route) Put(handler http.Handler) *Route {
	r.handlers[http.MethodPut] = handler
	return r
}

// PutFunc adds a plain function as a handler
// for PUT methods to this route.
func (r *Route) PutFunc(f http.HandlerFunc) *Route {
	return r.Put(http.HandlerFunc(f))
}

// Patch adds a handler for PATCH methods to this route.
func (r *Route) Patch(handler http.Handler) *Route {
	r.handlers[http.MethodPatch] = handler
	return r
}

// PatchFunc adds a plain function as a handler
// for PATCH methods to this route.
func (r *Route) PatchFunc(f http.HandlerFunc) *Route {
	return r.Patch(http.HandlerFunc(f))
}

// Get adds a handler for GET methods to this route.
// GET handlers will also be called for HEAD requests
// if no specific HEAD handler is registered.
func (r *Route) Get(handler http.Handler) *Route {
	r.handlers[http.MethodGet] = handler
	return r
}

// GetFunc adds a plain function as a handler
// for GET methods to this route.
// GET handlers will also be called for HEAD requests
// if no specific HEAD handler is registered.
func (r *Route) GetFunc(f http.HandlerFunc) *Route {
	return r.Get(http.HandlerFunc(f))
}

// Delete adds a handler for DELETE methods to this route.
func (r *Route) Delete(handler http.Handler) *Route {
	r.handlers[http.MethodDelete] = handler
	return r
}

// DeleteFunc adds a plain function as a handler
// for DELETE methods to this route.
func (r *Route) DeleteFunc(f http.HandlerFunc) *Route {
	return r.Delete(http.HandlerFunc(f))
}

// Head adds a handler for HEAD methods to this route.
func (r *Route) Head(handler http.Handler) *Route {
	r.handlers[http.MethodHead] = handler
	return r
}

// HeadFunc adds a plain function as a handler
// for HEAD methods to this route.
func (r *Route) HeadFunc(f http.HandlerFunc) *Route {
	return r.Head(http.HandlerFunc(f))
}

// Connect adds a handler for CONNECT methods to this route.
func (r *Route) Connect(handler http.Handler) *Route {
	r.handlers[http.MethodConnect] = handler
	return r
}

// ConnectFunc adds a plain function as a handler
// for CONNECT methods to this route.
func (r *Route) ConnectFunc(f http.HandlerFunc) *Route {
	return r.Connect(http.HandlerFunc(f))
}

// Options adds a handler for OPTIONS methods to this route.
// This handler will also be called for any routes further down the path
// from this point if no other OPTIONS handlers are registered below.
func (r *Route) Options(handler http.Handler) *Route {
	r.handlers[http.MethodOptions] = handler
	return r
}

// OptionsFunc adds a plain function as a handler
// for OPTIONS methods to this route.
// This handler will also be called for any routes further down the path
// from this point if no other OPTIONS handlers are registered below.
func (r *Route) OptionsFunc(f http.HandlerFunc) *Route {
	return r.Options(http.HandlerFunc(f))
}

// NotFound adds a handler for requests that do not correspond to a route.
// This handler will also be called for any routes further down the path
// from this point if no other not found handlers are registered below.
func (r *Route) NotFound(handler http.Handler) *Route {
	r.handlers[notFound] = handler
	return r
}

// NotFoundFunc adds a plain function as a handler for requests
// that do not correspond to a route.
// This handler will also be called for any routes further down the path
// from this point if no other not found handlers are registered below.
func (r *Route) NotFoundFunc(f http.HandlerFunc) *Route {
	return r.NotFound(http.HandlerFunc(f))
}
<|endoftext|>