<commit_before>package main

import (
	"fmt"
	"net/http"
	"net/url"
	"os"
	"os/signal"
	"syscall"
	"time"

	"github.com/go-kit/kit/log"
	"github.com/go-kit/kit/metrics"
	"github.com/go-kit/kit/metrics/prometheus"
	stdprometheus "github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"github.com/spf13/pflag"

	"github.com/weaveworks/flux/automator"
	"github.com/weaveworks/flux/db"
	"github.com/weaveworks/flux/history"
	historysql "github.com/weaveworks/flux/history/sql"
	transport "github.com/weaveworks/flux/http"
	"github.com/weaveworks/flux/instance"
	instancedb "github.com/weaveworks/flux/instance/sql"
	"github.com/weaveworks/flux/jobs"
	fluxmetrics "github.com/weaveworks/flux/metrics"
	"github.com/weaveworks/flux/platform"
	"github.com/weaveworks/flux/platform/rpc/nats"
	"github.com/weaveworks/flux/registry"
	"github.com/weaveworks/flux/release"
	"github.com/weaveworks/flux/server"
)

const shutdownTimeout = 30 * time.Second

var version string

func main() {
	// Flag domain.
	fs := pflag.NewFlagSet("default", pflag.ExitOnError)
	fs.Usage = func() {
		fmt.Fprintf(os.Stderr, "DESCRIPTION\n")
		fmt.Fprintf(os.Stderr, "  fluxsvc is a deployment service.\n")
		fmt.Fprintf(os.Stderr, "\n")
		fmt.Fprintf(os.Stderr, "FLAGS\n")
		fs.PrintDefaults()
	}

	var (
		listenAddr            = fs.StringP("listen", "l", ":3030", "Listen address for Flux API clients")
		databaseSource        = fs.String("database-source", "file://fluxy.db", `Database source name; includes the DB driver as the scheme. The default is a temporary, file-based DB`)
		databaseMigrationsDir = fs.String("database-migrations", "./db/migrations", "Path to database migration scripts, which are in subdirectories named for each driver")
		natsURL               = fs.String("nats-url", "", `URL on which to connect to NATS, or empty to use the standalone message bus (e.g., "nats://user:pass@nats:4222")`)
		memcachedHostname     = fs.String("memcached-hostname", "", "Hostname for memcached service to use when caching chunks. If empty, no memcached will be used.")
		memcachedTimeout      = fs.Duration("memcached-timeout", 100*time.Millisecond, "Maximum time to wait before giving up on memcached requests.")
		memcachedService      = fs.String("memcached-service", "memcached", "SRV service used to discover memcache servers.")
		registryCacheExpiry   = fs.Duration("registry-cache-expiry", 20*time.Minute, "Duration to keep cached registry tag info. Must be < 1 month.")
		versionFlag           = fs.Bool("version", false, "Get version number")
	)
	fs.Parse(os.Args)

	if version == "" {
		version = "unversioned"
	}
	if *versionFlag {
		fmt.Println(version)
		os.Exit(0)
	}

	// Logger component.
	var logger log.Logger
	{
		logger = log.NewLogfmtLogger(os.Stderr)
		logger = log.NewContext(logger).With("ts", log.DefaultTimestampUTC)
		logger = log.NewContext(logger).With("caller", log.DefaultCaller)
	}

	// Initialise database; we must fail if we can't do this, because
	// most things depend on it.
	var dbDriver string
	{
		var version uint64
		u, err := url.Parse(*databaseSource)
		if err == nil {
			version, err = db.Migrate(*databaseSource, *databaseMigrationsDir)
		}

		if err != nil {
			logger.Log("stage", "db init", "err", err)
			os.Exit(1)
		}
		dbDriver = db.DriverForScheme(u.Scheme)
		logger.Log("migrations", "success", "driver", dbDriver, "db-version", fmt.Sprintf("%d", version))
	}

	// Instrumentation
	var (
		busMetrics       platform.BusMetrics
		helperDuration   metrics.Histogram
		historyMetrics   history.Metrics
		httpDuration     *stdprometheus.HistogramVec
		instanceMetrics  instance.Metrics
		jobWorkerMetrics jobs.WorkerMetrics
		registryMetrics  registry.Metrics
		releaseMetrics   release.Metrics
		serverMetrics    server.Metrics
	)
	{
		httpDuration = stdprometheus.NewHistogramVec(stdprometheus.HistogramOpts{
			Namespace: "flux",
			Name:      "request_duration_seconds",
			Help:      "Time (in seconds) spent serving HTTP requests.",
			Buckets:   stdprometheus.DefBuckets,
		}, []string{fluxmetrics.LabelMethod, fluxmetrics.LabelRoute, "status_code", "ws"})
		serverMetrics.StatusDuration = prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{
			Namespace: "flux",
			Subsystem: "fluxsvc",
			Name:      "status_duration_seconds",
			Help:      "Status method duration in seconds.",
			Buckets:   stdprometheus.DefBuckets,
		}, []string{fluxmetrics.LabelSuccess})
		serverMetrics.ListServicesDuration = prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{
			Namespace: "flux",
			Subsystem: "fluxsvc",
			Name:      "list_services_duration_seconds",
			Help:      "ListServices method duration in seconds.",
			Buckets:   stdprometheus.DefBuckets,
		}, []string{fluxmetrics.LabelSuccess})
		serverMetrics.ListImagesDuration = prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{
			Namespace: "flux",
			Subsystem: "fluxsvc",
			Name:      "list_images_duration_seconds",
			Help:      "ListImages method duration in seconds.",
			Buckets:   stdprometheus.DefBuckets,
		}, []string{"service_spec_all", fluxmetrics.LabelSuccess})
		serverMetrics.HistoryDuration = prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{
			Namespace: "flux",
			Subsystem: "fluxsvc",
			Name:      "history_duration_seconds",
			Help:      "History method duration in seconds.",
			Buckets:   stdprometheus.DefBuckets,
		}, []string{"service_spec_all", fluxmetrics.LabelSuccess})
		serverMetrics.RegisterDaemonDuration = prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{
			Namespace: "flux",
			Subsystem: "fluxsvc",
			Name:      "register_daemon_duration_seconds",
			Help:      "RegisterDaemon method duration in seconds.",
			Buckets:   stdprometheus.DefBuckets,
		}, []string{fluxmetrics.LabelSuccess})
		serverMetrics.ConnectedDaemons = prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
			Namespace: "flux",
			Subsystem: "fluxsvc",
			Name:      "connected_daemons_count",
			Help:      "Gauge of the current number of connected daemons",
		}, []string{})
		serverMetrics.PlatformMetrics = platform.NewMetrics()
		releaseMetrics.ReleaseDuration = prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{
			Namespace: "flux",
			Subsystem: "fluxsvc",
			Name:      "release_duration_seconds",
			Help:      "Release method duration in seconds.",
			Buckets:   stdprometheus.DefBuckets,
		}, []string{fluxmetrics.LabelReleaseType, fluxmetrics.LabelReleaseKind, fluxmetrics.LabelSuccess})
		releaseMetrics.ActionDuration = prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{
			Namespace: "flux",
			Subsystem: "fluxsvc",
			Name:      "release_action_duration_seconds",
			Help:      "Duration in seconds of each sub-action invoked as part of a non-dry-run release.",
			Buckets:   stdprometheus.DefBuckets,
		}, []string{fluxmetrics.LabelAction, fluxmetrics.LabelSuccess})
		releaseMetrics.StageDuration = prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{
			Namespace: "flux",
			Subsystem: "fluxsvc",
			Name:      "release_stage_duration_seconds",
			Help:      "Duration in seconds of each stage of a release, including dry-runs.",
			Buckets:   stdprometheus.DefBuckets,
		}, []string{fluxmetrics.LabelMethod, fluxmetrics.LabelStage})
		helperDuration = prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{
			Namespace: "flux",
			Subsystem: "fluxsvc",
			Name:      "release_helper_duration_seconds",
			Help:      "Duration in seconds of a variety of release helper methods.",
			Buckets:   stdprometheus.DefBuckets,
		}, []string{fluxmetrics.LabelMethod, fluxmetrics.LabelSuccess})
		registryMetrics = registry.NewMetrics()
		busMetrics = platform.NewBusMetrics()
		historyMetrics = history.NewMetrics()
		instanceMetrics = instance.NewMetrics()
		jobWorkerMetrics = jobs.NewWorkerMetrics()
	}
	stdprometheus.MustRegister(httpDuration)

	var messageBus platform.MessageBus
	{
		if *natsURL != "" {
			bus, err := nats.NewMessageBus(*natsURL, busMetrics)
			if err != nil {
				logger.Log("component", "message bus", "err", err)
				os.Exit(1)
			}
			logger.Log("component", "message bus", "type", "NATS")
			messageBus = bus
		} else {
			messageBus = platform.NewStandaloneMessageBus(busMetrics)
			logger.Log("component", "message bus", "type", "standalone")
		}
	}

	var historyDB history.DB
	{
		db, err := historysql.NewSQL(dbDriver, *databaseSource)
		if err != nil {
			logger.Log("component", "history", "err", err)
			os.Exit(1)
		}
		historyDB = history.InstrumentedDB(db, historyMetrics)
	}

	// Configuration, i.e., whether services are automated or not.
	var instanceDB instance.DB
	{
		db, err := instancedb.New(dbDriver, *databaseSource)
		if err != nil {
			logger.Log("component", "config", "err", err)
			os.Exit(1)
		}
		instanceDB = instance.InstrumentedDB(db, instanceMetrics)
	}

	var memcacheClient registry.MemcacheClient
	if *memcachedHostname != "" {
		memcacheClient = registry.NewMemcacheClient(registry.MemcacheConfig{
			Host:           *memcachedHostname,
			Service:        *memcachedService,
			Timeout:        *memcachedTimeout,
			UpdateInterval: 1 * time.Minute,
			Logger:         log.NewContext(logger).With("component", "memcached"),
		})
		memcacheClient = registry.InstrumentMemcacheClient(memcacheClient)
		defer memcacheClient.Stop()
	}

	var instancer instance.Instancer
	{
		// Instancer, for the instancing of operations
		instancer = &instance.MultitenantInstancer{
			DB:                  instanceDB,
			Connecter:           messageBus,
			Logger:              logger,
			Histogram:           helperDuration,
			History:             historyDB,
			RegistryMetrics:     registryMetrics,
			MemcacheClient:      memcacheClient,
			RegistryCacheExpiry: *registryCacheExpiry,
		}
	}

	// Job store.
	var jobStore jobs.JobStore
	{
		s, err := jobs.NewDatabaseStore(dbDriver, *databaseSource, time.Hour)
		if err != nil {
			logger.Log("component", "release job store", "err", err)
			os.Exit(1)
		}
		jobStore = jobs.InstrumentedJobStore(s)
	}

	// Automator component.
	var auto *automator.Automator
	{
		var err error
		auto, err = automator.New(automator.Config{
			Jobs:       jobStore,
			InstanceDB: instanceDB,
			Instancer:  instancer,
			Logger:     log.NewContext(logger).With("component", "automator"),
		})
		if err == nil {
			logger.Log("automator", "enabled")
		} else {
			// Service can handle a nil automator pointer.
			logger.Log("automator", "disabled", "reason", err)
		}
	}

	go auto.Start(log.NewContext(logger).With("component", "automator"))

	// Job workers.
	//
	// Doing one worker (and one queue) for each job type for now. This way slow
	// release jobs can't interfere with slow automated service jobs, or vice
	// versa. This is probably not optimal. Really all jobs should be quick and
	// recoverable.
	for _, queue := range []string{
		jobs.DefaultQueue,
		jobs.ReleaseJob,
		jobs.AutomatedInstanceJob,
	} {
		logger := log.NewContext(logger).With("component", "worker", "queues", fmt.Sprint([]string{queue}))
		worker := jobs.NewWorker(jobStore, logger, jobWorkerMetrics, []string{queue})
		worker.Register(jobs.AutomatedInstanceJob, auto)
		worker.Register(jobs.ReleaseJob, release.NewReleaser(instancer, releaseMetrics))

		defer func() {
			if err := worker.Stop(shutdownTimeout); err != nil {
				logger.Log("err", err)
			}
		}()
		go worker.Work()

	}

	// Job GC cleaner
	{
		cleaner := jobs.NewCleaner(jobStore, logger)
		cleanTicker := time.NewTicker(15 * time.Second)
		defer cleanTicker.Stop()
		go cleaner.Clean(cleanTicker.C)
	}

	// The server.
	server := server.New(instancer, instanceDB, messageBus, jobStore, logger, serverMetrics)

	// Mechanical components.
	errc := make(chan error)
	go func() {
		c := make(chan os.Signal)
		signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
		errc <- fmt.Errorf("%s", <-c)
	}()

	// HTTP transport component.
	go func() {
		logger.Log("addr", *listenAddr)
		mux := http.NewServeMux()
		mux.Handle("/metrics", promhttp.Handler())
		handler := transport.NewHandler(server, transport.NewRouter(), logger, httpDuration)
		mux.Handle("/", handler)
		mux.Handle("/api/flux/", http.StripPrefix("/api/flux", handler))
		errc <- http.ListenAndServe(*listenAddr, mux)
	}()

	logger.Log("exiting", <-errc)
}
<commit_msg>Add a log message when terminating workers<commit_after>package main

import (
	"fmt"
	"net/http"
	"net/url"
	"os"
	"os/signal"
	"syscall"
	"time"

	"github.com/go-kit/kit/log"
	"github.com/go-kit/kit/metrics"
	"github.com/go-kit/kit/metrics/prometheus"
	stdprometheus "github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"github.com/spf13/pflag"

	"github.com/weaveworks/flux/automator"
	"github.com/weaveworks/flux/db"
	"github.com/weaveworks/flux/history"
	historysql "github.com/weaveworks/flux/history/sql"
	transport "github.com/weaveworks/flux/http"
	"github.com/weaveworks/flux/instance"
	instancedb "github.com/weaveworks/flux/instance/sql"
	"github.com/weaveworks/flux/jobs"
	fluxmetrics "github.com/weaveworks/flux/metrics"
	"github.com/weaveworks/flux/platform"
	"github.com/weaveworks/flux/platform/rpc/nats"
	"github.com/weaveworks/flux/registry"
	"github.com/weaveworks/flux/release"
	"github.com/weaveworks/flux/server"
)

const shutdownTimeout = 30 * time.Second

var version string

func main() {
	// Flag domain.
	fs := pflag.NewFlagSet("default", pflag.ExitOnError)
	fs.Usage = func() {
		fmt.Fprintf(os.Stderr, "DESCRIPTION\n")
		fmt.Fprintf(os.Stderr, "  fluxsvc is a deployment service.\n")
		fmt.Fprintf(os.Stderr, "\n")
		fmt.Fprintf(os.Stderr, "FLAGS\n")
		fs.PrintDefaults()
	}

	var (
		listenAddr            = fs.StringP("listen", "l", ":3030", "Listen address for Flux API clients")
		databaseSource        = fs.String("database-source", "file://fluxy.db", `Database source name; includes the DB driver as the scheme. The default is a temporary, file-based DB`)
		databaseMigrationsDir = fs.String("database-migrations", "./db/migrations", "Path to database migration scripts, which are in subdirectories named for each driver")
		natsURL               = fs.String("nats-url", "", `URL on which to connect to NATS, or empty to use the standalone message bus (e.g., "nats://user:pass@nats:4222")`)
		memcachedHostname     = fs.String("memcached-hostname", "", "Hostname for memcached service to use when caching chunks. If empty, no memcached will be used.")
		memcachedTimeout      = fs.Duration("memcached-timeout", 100*time.Millisecond, "Maximum time to wait before giving up on memcached requests.")
		memcachedService      = fs.String("memcached-service", "memcached", "SRV service used to discover memcache servers.")
		registryCacheExpiry   = fs.Duration("registry-cache-expiry", 20*time.Minute, "Duration to keep cached registry tag info. Must be < 1 month.")
		versionFlag           = fs.Bool("version", false, "Get version number")
	)
	fs.Parse(os.Args)

	if version == "" {
		version = "unversioned"
	}
	if *versionFlag {
		fmt.Println(version)
		os.Exit(0)
	}

	// Logger component.
	var logger log.Logger
	{
		logger = log.NewLogfmtLogger(os.Stderr)
		logger = log.NewContext(logger).With("ts", log.DefaultTimestampUTC)
		logger = log.NewContext(logger).With("caller", log.DefaultCaller)
	}

	// Initialise database; we must fail if we can't do this, because
	// most things depend on it.
	var dbDriver string
	{
		var version uint64
		u, err := url.Parse(*databaseSource)
		if err == nil {
			version, err = db.Migrate(*databaseSource, *databaseMigrationsDir)
		}

		if err != nil {
			logger.Log("stage", "db init", "err", err)
			os.Exit(1)
		}
		dbDriver = db.DriverForScheme(u.Scheme)
		logger.Log("migrations", "success", "driver", dbDriver, "db-version", fmt.Sprintf("%d", version))
	}

	// Instrumentation
	var (
		busMetrics       platform.BusMetrics
		helperDuration   metrics.Histogram
		historyMetrics   history.Metrics
		httpDuration     *stdprometheus.HistogramVec
		instanceMetrics  instance.Metrics
		jobWorkerMetrics jobs.WorkerMetrics
		registryMetrics  registry.Metrics
		releaseMetrics   release.Metrics
		serverMetrics    server.Metrics
	)
	{
		httpDuration = stdprometheus.NewHistogramVec(stdprometheus.HistogramOpts{
			Namespace: "flux",
			Name:      "request_duration_seconds",
			Help:      "Time (in seconds) spent serving HTTP requests.",
			Buckets:   stdprometheus.DefBuckets,
		}, []string{fluxmetrics.LabelMethod, fluxmetrics.LabelRoute, "status_code", "ws"})
		serverMetrics.StatusDuration = prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{
			Namespace: "flux",
			Subsystem: "fluxsvc",
			Name:      "status_duration_seconds",
			Help:      "Status method duration in seconds.",
			Buckets:   stdprometheus.DefBuckets,
		}, []string{fluxmetrics.LabelSuccess})
		serverMetrics.ListServicesDuration = prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{
			Namespace: "flux",
			Subsystem: "fluxsvc",
			Name:      "list_services_duration_seconds",
			Help:      "ListServices method duration in seconds.",
			Buckets:   stdprometheus.DefBuckets,
		}, []string{fluxmetrics.LabelSuccess})
		serverMetrics.ListImagesDuration = prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{
			Namespace: "flux",
			Subsystem: "fluxsvc",
			Name:      "list_images_duration_seconds",
			Help:      "ListImages method duration in seconds.",
			Buckets:   stdprometheus.DefBuckets,
		}, []string{"service_spec_all", fluxmetrics.LabelSuccess})
		serverMetrics.HistoryDuration = prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{
			Namespace: "flux",
			Subsystem: "fluxsvc",
			Name:      "history_duration_seconds",
			Help:      "History method duration in seconds.",
			Buckets:   stdprometheus.DefBuckets,
		}, []string{"service_spec_all", fluxmetrics.LabelSuccess})
		serverMetrics.RegisterDaemonDuration = prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{
			Namespace: "flux",
			Subsystem: "fluxsvc",
			Name:      "register_daemon_duration_seconds",
			Help:      "RegisterDaemon method duration in seconds.",
			Buckets:   stdprometheus.DefBuckets,
		}, []string{fluxmetrics.LabelSuccess})
		serverMetrics.ConnectedDaemons = prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
			Namespace: "flux",
			Subsystem: "fluxsvc",
			Name:      "connected_daemons_count",
			Help:      "Gauge of the current number of connected daemons",
		}, []string{})
		serverMetrics.PlatformMetrics = platform.NewMetrics()
		releaseMetrics.ReleaseDuration = prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{
			Namespace: "flux",
			Subsystem: "fluxsvc",
			Name:      "release_duration_seconds",
			Help:      "Release method duration in seconds.",
			Buckets:   stdprometheus.DefBuckets,
		}, []string{fluxmetrics.LabelReleaseType, fluxmetrics.LabelReleaseKind, fluxmetrics.LabelSuccess})
		releaseMetrics.ActionDuration = prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{
			Namespace: "flux",
			Subsystem: "fluxsvc",
			Name:      "release_action_duration_seconds",
			Help:      "Duration in seconds of each sub-action invoked as part of a non-dry-run release.",
			Buckets:   stdprometheus.DefBuckets,
		}, []string{fluxmetrics.LabelAction, fluxmetrics.LabelSuccess})
		releaseMetrics.StageDuration = prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{
			Namespace: "flux",
			Subsystem: "fluxsvc",
			Name:      "release_stage_duration_seconds",
			Help:      "Duration in seconds of each stage of a release, including dry-runs.",
			Buckets:   stdprometheus.DefBuckets,
		}, []string{fluxmetrics.LabelMethod, fluxmetrics.LabelStage})
		helperDuration = prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{
			Namespace: "flux",
			Subsystem: "fluxsvc",
			Name:      "release_helper_duration_seconds",
			Help:      "Duration in seconds of a variety of release helper methods.",
			Buckets:   stdprometheus.DefBuckets,
		}, []string{fluxmetrics.LabelMethod, fluxmetrics.LabelSuccess})
		registryMetrics = registry.NewMetrics()
		busMetrics = platform.NewBusMetrics()
		historyMetrics = history.NewMetrics()
		instanceMetrics = instance.NewMetrics()
		jobWorkerMetrics = jobs.NewWorkerMetrics()
	}
	stdprometheus.MustRegister(httpDuration)

	var messageBus platform.MessageBus
	{
		if *natsURL != "" {
			bus, err := nats.NewMessageBus(*natsURL, busMetrics)
			if err != nil {
				logger.Log("component", "message bus", "err", err)
				os.Exit(1)
			}
			logger.Log("component", "message bus", "type", "NATS")
			messageBus = bus
		} else {
			messageBus = platform.NewStandaloneMessageBus(busMetrics)
			logger.Log("component", "message bus", "type", "standalone")
		}
	}

	var historyDB history.DB
	{
		db, err := historysql.NewSQL(dbDriver, *databaseSource)
		if err != nil {
			logger.Log("component", "history", "err", err)
			os.Exit(1)
		}
		historyDB = history.InstrumentedDB(db, historyMetrics)
	}

	// Configuration, i.e., whether services are automated or not.
	var instanceDB instance.DB
	{
		db, err := instancedb.New(dbDriver, *databaseSource)
		if err != nil {
			logger.Log("component", "config", "err", err)
			os.Exit(1)
		}
		instanceDB = instance.InstrumentedDB(db, instanceMetrics)
	}

	var memcacheClient registry.MemcacheClient
	if *memcachedHostname != "" {
		memcacheClient = registry.NewMemcacheClient(registry.MemcacheConfig{
			Host:           *memcachedHostname,
			Service:        *memcachedService,
			Timeout:        *memcachedTimeout,
			UpdateInterval: 1 * time.Minute,
			Logger:         log.NewContext(logger).With("component", "memcached"),
		})
		memcacheClient = registry.InstrumentMemcacheClient(memcacheClient)
		defer memcacheClient.Stop()
	}

	var instancer instance.Instancer
	{
		// Instancer, for the instancing of operations
		instancer = &instance.MultitenantInstancer{
			DB:                  instanceDB,
			Connecter:           messageBus,
			Logger:              logger,
			Histogram:           helperDuration,
			History:             historyDB,
			RegistryMetrics:     registryMetrics,
			MemcacheClient:      memcacheClient,
			RegistryCacheExpiry: *registryCacheExpiry,
		}
	}

	// Job store.
	var jobStore jobs.JobStore
	{
		s, err := jobs.NewDatabaseStore(dbDriver, *databaseSource, time.Hour)
		if err != nil {
			logger.Log("component", "release job store", "err", err)
			os.Exit(1)
		}
		jobStore = jobs.InstrumentedJobStore(s)
	}

	// Automator component.
	var auto *automator.Automator
	{
		var err error
		auto, err = automator.New(automator.Config{
			Jobs:       jobStore,
			InstanceDB: instanceDB,
			Instancer:  instancer,
			Logger:     log.NewContext(logger).With("component", "automator"),
		})
		if err == nil {
			logger.Log("automator", "enabled")
		} else {
			// Service can handle a nil automator pointer.
			logger.Log("automator", "disabled", "reason", err)
		}
	}

	go auto.Start(log.NewContext(logger).With("component", "automator"))

	// Job workers.
	//
	// Doing one worker (and one queue) for each job type for now. This way slow
	// release jobs can't interfere with slow automated service jobs, or vice
	// versa. This is probably not optimal. Really all jobs should be quick and
	// recoverable.
	for _, queue := range []string{
		jobs.DefaultQueue,
		jobs.ReleaseJob,
		jobs.AutomatedInstanceJob,
	} {
		logger := log.NewContext(logger).With("component", "worker", "queues", fmt.Sprint([]string{queue}))
		worker := jobs.NewWorker(jobStore, logger, jobWorkerMetrics, []string{queue})
		worker.Register(jobs.AutomatedInstanceJob, auto)
		worker.Register(jobs.ReleaseJob, release.NewReleaser(instancer, releaseMetrics))

		defer func() {
			logger.Log("stopping", "true")
			if err := worker.Stop(shutdownTimeout); err != nil {
				logger.Log("err", err)
			}
		}()
		go worker.Work()

	}

	// Job GC cleaner
	{
		cleaner := jobs.NewCleaner(jobStore, logger)
		cleanTicker := time.NewTicker(15 * time.Second)
		defer cleanTicker.Stop()
		go cleaner.Clean(cleanTicker.C)
	}

	// The server.
	server := server.New(instancer, instanceDB, messageBus, jobStore, logger, serverMetrics)

	// Mechanical components.
	errc := make(chan error)
	go func() {
		c := make(chan os.Signal)
		signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
		errc <- fmt.Errorf("%s", <-c)
	}()

	// HTTP transport component.
	go func() {
		logger.Log("addr", *listenAddr)
		mux := http.NewServeMux()
		mux.Handle("/metrics", promhttp.Handler())
		handler := transport.NewHandler(server, transport.NewRouter(), logger, httpDuration)
		mux.Handle("/", handler)
		mux.Handle("/api/flux/", http.StripPrefix("/api/flux", handler))
		errc <- http.ListenAndServe(*listenAddr, mux)
	}()

	logger.Log("exiting", <-errc)
}
<|endoftext|>


<commit_before>package main

import (
	"path/filepath"

	"gnd.la/log"

	"gopkgs.com/command.v1"
)

var (
	commands = []*command.Cmd{
		{
			Name: "dev",
			Help: "Start the Gondola development server",
			Func: devCommand,
			Options: &devOptions{
				Dir:  ".",
				Port: 8888,
			},
		},
		{
			Name:    "new",
			Help:    "Create a new Gondola project",
			Usage:   "<dir>",
			Func:    newCommand,
			Options: &newOptions{Template: "hello"},
		},
		{
			Name: "clean",
			Help: "Cleans any Gondola packages which use conditional compilation - DO THIS BEFORE BUILDING A BINARY FOR DEPLOYMENT - see golang.org/issue/3172",
			Func: cleanCommand,
		},
		{
			Name:    "profile",
			Help:    "Show profiling information for a remote server running a Gondola app",
			Usage:   "<url>",
			Func:    profileCommand,
			Options: &profileOptions{Method: "GET"},
		},
		{
			Name:    "gen-app",
			Help:    "Generate boilerplate code for a Gondola app from the appfile.yaml file",
			Func:    genAppCommand,
			Options: &genAppOptions{},
		},
		{
			Name:    "bake",
			Help:    "Converts all assets in <dir> into Go code and generates a VFS named with <name>",
			Usage:   "-dir=<dir> -name=<name> ... additional flags",
			Options: &bakeOptions{},
			Func:    bakeCommand,
		},
		{
			Name:    "random-string",
			Help:    "Generates a random string suitable for use as the app secret",
			Func:    randomStringCommand,
			Options: &randomStringOptions{Length: defaultRandomLength},
		},
		{
			Name:  "rm-gen",
			Help:  "Remove Gondola generated files (identified by *.gen.*)",
			Usage: "[dir]",
			Func:  rmGenCommand,
		},
		{
			Name:    "make-messages",
			Help:    "Generate strings files from the current package (including its non-package subdirectories, like templates)",
			Func:    makeMessagesCommand,
			Options: &makeMessagesOptions{Out: filepath.Join("_messages", "messages.pot")},
		},
		{
			Name:    "compile-messages",
			Help:    "Compiles all po files from the current directory and its subdirectories",
			Func:    compileMessagesCommand,
			Options: &compileMessagesOptions{Out: "messages.go"},
		},
		{
			Name:    "gen",
			Help:    "Perform code generation in the current directory according the rules in the config file",
			Options: &genOptions{Genfile: "genfile.yaml"},
		},
		{
			Name: "gae-dev",
			Help: "Start the Gondola App Engine development server",
			Func: gaeDevCommand,
		},
		{
			Name:    "gae-test",
			Help:    "Start serving your app on localhost and run gnd.la/app/tester tests against it",
			Func:    gaeTestCommand,
			Options: &gaeTestOptions{},
		},
		{
			Name: "gae-deploy",
			Help: "Deploy your application to App Engine",
			Func: gaeDeployCommand,
		},
	}
)

type commonOptions struct {
	Quiet bool `name:"q" help:"Disable verbose output"`
}

func main() {
	opts := &command.Options{
		Options: &commonOptions{},
		Func: func(opts *commonOptions) {
			if opts.Quiet {
				log.SetLevel(log.LError)
			} else {
				log.SetLevel(log.LDebug)
			}
		},
	}
	command.RunOpts(nil, opts, commands)
}
<commit_msg>Use command.Exit() from main<commit_after>package main

import (
	"path/filepath"

	"gnd.la/log"

	"gopkgs.com/command.v1"
)

var (
	commands = []*command.Cmd{
		{
			Name: "dev",
			Help: "Start the Gondola development server",
			Func: devCommand,
			Options: &devOptions{
				Dir:  ".",
				Port: 8888,
			},
		},
		{
			Name:    "new",
			Help:    "Create a new Gondola project",
			Usage:   "<dir>",
			Func:    newCommand,
			Options: &newOptions{Template: "hello"},
		},
		{
			Name: "clean",
			Help: "Cleans any Gondola packages which use conditional compilation - DO THIS BEFORE BUILDING A BINARY FOR DEPLOYMENT - see golang.org/issue/3172",
			Func: cleanCommand,
		},
		{
			Name:    "profile",
			Help:    "Show profiling information for a remote server running a Gondola app",
			Usage:   "<url>",
			Func:    profileCommand,
			Options: &profileOptions{Method: "GET"},
		},
		{
			Name:    "gen-app",
			Help:    "Generate boilerplate code for a Gondola app from the appfile.yaml file",
			Func:    genAppCommand,
			Options: &genAppOptions{},
		},
		{
			Name:    "bake",
			Help:    "Converts all assets in <dir> into Go code and generates a VFS named with <name>",
			Usage:   "-dir=<dir> -name=<name> ... additional flags",
			Options: &bakeOptions{},
			Func:    bakeCommand,
		},
		{
			Name:    "random-string",
			Help:    "Generates a random string suitable for use as the app secret",
			Func:    randomStringCommand,
			Options: &randomStringOptions{Length: defaultRandomLength},
		},
		{
			Name:  "rm-gen",
			Help:  "Remove Gondola generated files (identified by *.gen.*)",
			Usage: "[dir]",
			Func:  rmGenCommand,
		},
		{
			Name:    "make-messages",
			Help:    "Generate strings files from the current package (including its non-package subdirectories, like templates)",
			Func:    makeMessagesCommand,
			Options: &makeMessagesOptions{Out: filepath.Join("_messages", "messages.pot")},
		},
		{
			Name:    "compile-messages",
			Help:    "Compiles all po files from the current directory and its subdirectories",
			Func:    compileMessagesCommand,
			Options: &compileMessagesOptions{Out: "messages.go"},
		},
		{
			Name:    "gen",
			Help:    "Perform code generation in the current directory according the rules in the config file",
			Options: &genOptions{Genfile: "genfile.yaml"},
		},
		{
			Name: "gae-dev",
			Help: "Start the Gondola App Engine development server",
			Func: gaeDevCommand,
		},
		{
			Name:    "gae-test",
			Help:    "Start serving your app on localhost and run gnd.la/app/tester tests against it",
			Func:    gaeTestCommand,
			Options: &gaeTestOptions{},
		},
		{
			Name: "gae-deploy",
			Help: "Deploy your application to App Engine",
			Func: gaeDeployCommand,
		},
	}
)

type commonOptions struct {
	Quiet bool `name:"q" help:"Disable verbose output"`
}

func main() {
	opts := &command.Options{
		Options: &commonOptions{},
		Func: func(opts *commonOptions) {
			if opts.Quiet {
				log.SetLevel(log.LError)
			} else {
				log.SetLevel(log.LDebug)
			}
		},
	}
	command.Exit(command.RunOpts(nil, opts, commands))
}
<|endoftext|>


<commit_before>/*
Copyright 2018 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 main

import (
	"os"

	"github.com/kubernetes-sigs/application/pkg/apis"
	"github.com/kubernetes-sigs/application/pkg/controller"
	_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
	"sigs.k8s.io/controller-runtime/pkg/client/config"
	"sigs.k8s.io/controller-runtime/pkg/manager"
	logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
	"sigs.k8s.io/controller-runtime/pkg/runtime/signals"
)

func main() {
	logf.SetLogger(logf.ZapLogger(false))
	log := logf.Log.WithName("entrypoint")

	// Get a config to talk to the apiserver
	log.Info("setting up client for manager")
	cfg, err := config.GetConfig()
	if err != nil {
		log.Error(err, "unable to set up client config")
		os.Exit(1)
	}

	// Create a new Cmd to provide shared dependencies and start components
	log.Info("setting up manager")
	mgr, err := manager.New(cfg, manager.Options{})
	if err != nil {
		log.Error(err, "unable to set up overall controller manager")
		os.Exit(1)
	}

	log.Info("Registering Components.")

	// Setup Scheme for all resources
	log.Info("setting up scheme")
	if err := apis.AddToScheme(mgr.GetScheme()); err != nil {
		log.Error(err, "unable add APIs to scheme")
		os.Exit(1)
	}

	// Setup all Controllers
	log.Info("Setting up controller")
	if err := controller.AddToManager(mgr); err != nil {
		log.Error(err, "unable to register controllers to the manager")
		os.Exit(1)
	}

	// Start the Cmd
	log.Info("Starting the Cmd.")
	if err := mgr.Start(signals.SetupSignalHandler()); err != nil {
		log.Error(err, "unable to run the manager")
		os.Exit(1)
	}
}
<commit_msg>reconcile every 2 mins<commit_after>/*
Copyright 2018 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 main

import (
	"os"

	"github.com/kubernetes-sigs/application/pkg/apis"
	"github.com/kubernetes-sigs/application/pkg/controller"
	_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
	"sigs.k8s.io/controller-runtime/pkg/client/config"
	"sigs.k8s.io/controller-runtime/pkg/manager"
	logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
	"sigs.k8s.io/controller-runtime/pkg/runtime/signals"
	"time"
)

func main() {
	logf.SetLogger(logf.ZapLogger(false))
	log := logf.Log.WithName("entrypoint")

	// Get a config to talk to the apiserver
	log.Info("setting up client for manager")
	cfg, err := config.GetConfig()
	if err != nil {
		log.Error(err, "unable to set up client config")
		os.Exit(1)
	}

	// Create a new Cmd to provide shared dependencies and start components
	syncperiod := time.Minute * 2
	log.Info("setting up manager")
	mgr, err := manager.New(cfg, manager.Options{SyncPeriod: &syncperiod})
	if err != nil {
		log.Error(err, "unable to set up overall controller manager")
		os.Exit(1)
	}

	log.Info("Registering Components.")

	// Setup Scheme for all resources
	log.Info("setting up scheme")
	if err := apis.AddToScheme(mgr.GetScheme()); err != nil {
		log.Error(err, "unable add APIs to scheme")
		os.Exit(1)
	}

	// Setup all Controllers
	log.Info("Setting up controller")
	if err := controller.AddToManager(mgr); err != nil {
		log.Error(err, "unable to register controllers to the manager")
		os.Exit(1)
	}

	// Start the Cmd
	log.Info("Starting the Cmd.")
	if err := mgr.Start(signals.SetupSignalHandler()); err != nil {
		log.Error(err, "unable to run the manager")
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"errors"
	"flag"
	"fmt"
	stdlog "log"
	"os"
	"os/signal"
	"path/filepath"
	"runtime"
	"strings"
	"time"

	"github.com/ethereum/go-ethereum/log"
	gethmetrics "github.com/ethereum/go-ethereum/metrics"
	"github.com/status-im/status-go/api"
	"github.com/status-im/status-go/cmd/statusd/debug"
	"github.com/status-im/status-go/logutils"
	nodemetrics "github.com/status-im/status-go/metrics/node"
	"github.com/status-im/status-go/node"
	"github.com/status-im/status-go/params"
	"github.com/status-im/status-go/profiling"
	"golang.org/x/crypto/ssh/terminal"
)

const (
	serverClientName = "Statusd"
)

var (
	buildStamp = "N/A" // rely on linker: -ldflags -X main.buildStamp"
)

var (
	configFiles      configFlags
	logLevel         = flag.String("log", "", `Log level, one of: "ERROR", "WARN", "INFO", "DEBUG", and "TRACE"`)
	logWithoutColors = flag.Bool("log-without-color", false, "Disables log colors")
	cliEnabled       = flag.Bool("cli", false, "Enable debugging CLI server")
	cliPort          = flag.String("cli-port", debug.CLIPort, "CLI server's listening port")
	pprofEnabled     = flag.Bool("pprof", false, "Enable runtime profiling via pprof")
	pprofPort        = flag.Int("pprof-port", 52525, "Port for runtime profiling via pprof")
	version          = flag.Bool("version", false, "Print version and dump configuration")

	dataDir    = flag.String("dir", getDefaultDataDir(), "Directory used by node to store data")
	register   = flag.Bool("register", false, "Register and make the node discoverable by other nodes")
	mailserver = flag.Bool("mailserver", false, "Enable Mail Server with default configuration")

	// don't change the name of this flag, https://github.com/ethereum/go-ethereum/blob/master/metrics/metrics.go#L41
	metrics = flag.Bool("metrics", false, "Expose ethereum metrics with debug_metrics jsonrpc call")

	syncAndExit = flag.Int("sync-and-exit", -1, "Timeout in minutes for blockchain sync and exit, zero means no timeout unless sync is finished")
)

// All general log messages in this package should be routed through this logger.
var logger = log.New("package", "status-go/cmd/statusd")

func init() {
	flag.Var(&configFiles, "c", "JSON configuration file(s). Multiple configuration files can be specified, and will be merged in occurrence order")

	colors := terminal.IsTerminal(int(os.Stdin.Fd()))
	if err := logutils.OverrideRootLog(true, "ERROR", "", colors); err != nil {
		stdlog.Fatalf("Error initializing logger: %v", err)
	}

	flag.Usage = printUsage
	flag.Parse()
	if flag.NArg() > 0 {
		printUsage()
		logger.Error("Extra args in command line: %v", flag.Args())
		os.Exit(1)
	}
}

// nolint:gocyclo
func main() {
	opts := []params.Option{params.WithFleet(params.FleetBeta)}
	if *mailserver {
		opts = append(opts, params.WithMailserver())
	}

	config, err := params.NewNodeConfigWithDefaultsAndFiles(
		*dataDir,
		uint64(params.RopstenNetworkID),
		opts,
		configFiles,
	)
	if err != nil {
		printUsage()
		if err != nil {
			logger.Error(err.Error())
		}
		os.Exit(1)
	}

	if *register && *mailserver {
		config.RegisterTopics = append(config.RegisterTopics, params.MailServerDiscv5Topic)
	} else if *register {
		config.RegisterTopics = append(config.RegisterTopics, params.WhisperDiscv5Topic)
	}

	// set up logging options
	setupLogging(config)

	// We want statusd to be distinct from StatusIM client.
	config.Name = serverClientName

	if *version {
		printVersion(config, buildStamp)
		return
	}

	backend := api.NewStatusBackend()
	err = backend.StartNode(config)
	if err != nil {
		logger.Error("Node start failed", "error", err)
		return
	}

	// handle interrupt signals
	interruptCh := haltOnInterruptSignal(backend.StatusNode())

	// Check if debugging CLI connection shall be enabled.
	if *cliEnabled {
		err := startDebug(backend)
		if err != nil {
			logger.Error("Starting debugging CLI server failed", "error", err)
			return
		}
	}

	// Check if profiling shall be enabled.
	if *pprofEnabled {
		profiling.NewProfiler(*pprofPort).Go()
	}

	// Run stats server.
	if *metrics {
		go startCollectingNodeMetrics(interruptCh, backend.StatusNode())
		go gethmetrics.CollectProcessMetrics(3 * time.Second)
	}

	// Sync blockchain and stop.
	if *syncAndExit >= 0 {
		exitCode := syncAndStopNode(interruptCh, backend.StatusNode(), *syncAndExit)
		// Call was interrupted. Wait for graceful shutdown.
		if exitCode == -1 {
			if gethNode := backend.StatusNode().GethNode(); gethNode != nil {
				gethNode.Wait()
			}
			return
		}
		// Otherwise, exit immediately with a returned exit code.
		os.Exit(exitCode)
	}

	gethNode := backend.StatusNode().GethNode()
	if gethNode != nil {
		// wait till node has been stopped
		gethNode.Wait()
	}
}

func getDefaultDataDir() string {
	if home := os.Getenv("HOME"); home != "" {
		return filepath.Join(home, ".statusd")
	}
	return "./statusd-data"
}

func setupLogging(config *params.NodeConfig) {
	if *logLevel != "" {
		config.LogLevel = *logLevel
	}

	colors := !(*logWithoutColors) && terminal.IsTerminal(int(os.Stdin.Fd()))
	if err := logutils.OverrideRootLog(
		logEnabled(config),
		config.LogLevel,
		config.LogFile,
		colors,
	); err != nil {
		stdlog.Fatalf("Error initializing logger: %v", err)
	}
}

// startDebug starts the debugging API server.
func startDebug(backend *api.StatusBackend) error {
	_, err := debug.New(backend, *cliPort)
	return err
}

// startCollectingStats collects various stats about the node and other protocols like Whisper.
func startCollectingNodeMetrics(interruptCh <-chan struct{}, statusNode *node.StatusNode) {
	logger.Info("Starting collecting node metrics")

	gethNode := statusNode.GethNode()
	if gethNode == nil {
		logger.Error("Failed to run metrics because it could not get the node")
		return
	}

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	go func() {
		if err := nodemetrics.SubscribeServerEvents(ctx, gethNode); err != nil {
			logger.Error("Failed to subscribe server events", "error", err)
		}
	}()

	<-interruptCh
}

func logEnabled(config *params.NodeConfig) bool {
	return config.LogLevel != "" || config.LogFile != ""
}

var (
	errStatusServiceRequiresIPC  = errors.New("to enable the StatusService on IPC, -ipc flag must be set")
	errStatusServiceRequiresHTTP = errors.New("to enable the StatusService on HTTP, -http flag must be set")
	errStatusServiceInvalidFlag  = errors.New("-status flag valid values are: ipc, http")
)

func configureStatusService(flagValue string, nodeConfig *params.NodeConfig) (*params.NodeConfig, error) {
	switch flagValue {
	case "ipc":
		if !nodeConfig.IPCEnabled {
			return nil, errStatusServiceRequiresIPC
		}
		nodeConfig.StatusServiceEnabled = true
	case "http":
		if !nodeConfig.HTTPEnabled {
			return nil, errStatusServiceRequiresHTTP
		}
		nodeConfig.StatusServiceEnabled = true
		nodeConfig.AddAPIModule("status")
	case "":
		nodeConfig.StatusServiceEnabled = false
	default:
		return nil, errStatusServiceInvalidFlag
	}

	return nodeConfig, nil
}

// printVersion prints verbose output about version and config.
func printVersion(config *params.NodeConfig, buildStamp string) {
	fmt.Println(strings.Title(config.Name))
	fmt.Println("Version:", config.Version)

	if buildStamp != "" {
		fmt.Println("Build Stamp:", buildStamp)
	}

	fmt.Println("Network Id:", config.NetworkID)
	fmt.Println("Go Version:", runtime.Version())
	fmt.Println("OS:", runtime.GOOS)
	fmt.Printf("GOPATH=%s\n", os.Getenv("GOPATH"))
	fmt.Printf("GOROOT=%s\n", runtime.GOROOT())

	fmt.Println("Loaded Config: ", config)
}

func printUsage() {
	usage := `
Usage: statusd [options]
Examples:
  statusd                                        # run regular Whisper node that joins Status network
  statusd -c ./default.json                      # run node with configuration specified in ./default.json file
  statusd -c ./default.json -c ./standalone.json # run node with configuration specified in ./default.json file, after merging ./standalone.json file
  statusd -c ./default.json -metrics             # run node with configuration specified in ./default.json file, and expose ethereum metrics with debug_metrics jsonrpc call
  statusd -c ./default.json -cli                 # run node with configuration specified in ./default.json file, and enable connection by statusd-cli on default port

Options:
`
	fmt.Fprintf(os.Stderr, usage)
	flag.PrintDefaults()
}

// haltOnInterruptSignal catches interrupt signal (SIGINT) and
// stops the node. It times out after 5 seconds
// if the node can not be stopped.
func haltOnInterruptSignal(statusNode *node.StatusNode) <-chan struct{} {
	interruptCh := make(chan struct{})
	go func() {
		signalCh := make(chan os.Signal, 1)
		signal.Notify(signalCh, os.Interrupt)
		defer signal.Stop(signalCh)
		<-signalCh
		close(interruptCh)
		logger.Info("Got interrupt, shutting down...")
		if err := statusNode.Stop(); err != nil {
			logger.Error("Failed to stop node", "error", err)
			os.Exit(1)
		}
	}()
	return interruptCh
}
<commit_msg>add networkid as switch between ropsten/mainnet is often (#1209)<commit_after>package main

import (
	"context"
	"errors"
	"flag"
	"fmt"
	stdlog "log"
	"os"
	"os/signal"
	"path/filepath"
	"runtime"
	"strings"
	"time"

	"github.com/ethereum/go-ethereum/log"
	gethmetrics "github.com/ethereum/go-ethereum/metrics"
	"github.com/status-im/status-go/api"
	"github.com/status-im/status-go/cmd/statusd/debug"
	"github.com/status-im/status-go/logutils"
	nodemetrics "github.com/status-im/status-go/metrics/node"
	"github.com/status-im/status-go/node"
	"github.com/status-im/status-go/params"
	"github.com/status-im/status-go/profiling"
	"golang.org/x/crypto/ssh/terminal"
)

const (
	serverClientName = "Statusd"
)

var (
	buildStamp = "N/A" // rely on linker: -ldflags -X main.buildStamp"
)

var (
	configFiles      configFlags
	logLevel         = flag.String("log", "", `Log level, one of: "ERROR", "WARN", "INFO", "DEBUG", and "TRACE"`)
	logWithoutColors = flag.Bool("log-without-color", false, "Disables log colors")
	cliEnabled       = flag.Bool("cli", false, "Enable debugging CLI server")
	cliPort          = flag.String("cli-port", debug.CLIPort, "CLI server's listening port")
	pprofEnabled     = flag.Bool("pprof", false, "Enable runtime profiling via pprof")
	pprofPort        = flag.Int("pprof-port", 52525, "Port for runtime profiling via pprof")
	version          = flag.Bool("version", false, "Print version and dump configuration")

	dataDir    = flag.String("dir", getDefaultDataDir(), "Directory used by node to store data")
	register   = flag.Bool("register", false, "Register and make the node discoverable by other nodes")
	mailserver = flag.Bool("mailserver", false, "Enable Mail Server with default configuration")
	networkID  = flag.Int(
		"network-id",
		params.RopstenNetworkID,
		fmt.Sprintf(
			"A network ID: %d (Mainnet), %d (Ropsten), %d (Rinkeby)",
			params.MainNetworkID, params.RopstenNetworkID, params.RinkebyNetworkID,
		),
	)

	// don't change the name of this flag, https://github.com/ethereum/go-ethereum/blob/master/metrics/metrics.go#L41
	metrics = flag.Bool("metrics", false, "Expose ethereum metrics with debug_metrics jsonrpc call")

	syncAndExit = flag.Int("sync-and-exit", -1, "Timeout in minutes for blockchain sync and exit, zero means no timeout unless sync is finished")
)

// All general log messages in this package should be routed through this logger.
var logger = log.New("package", "status-go/cmd/statusd")

func init() {
	flag.Var(&configFiles, "c", "JSON configuration file(s). Multiple configuration files can be specified, and will be merged in occurrence order")

	colors := terminal.IsTerminal(int(os.Stdin.Fd()))
	if err := logutils.OverrideRootLog(true, "ERROR", "", colors); err != nil {
		stdlog.Fatalf("Error initializing logger: %v", err)
	}

	flag.Usage = printUsage
	flag.Parse()
	if flag.NArg() > 0 {
		printUsage()
		logger.Error("Extra args in command line: %v", flag.Args())
		os.Exit(1)
	}
}

// nolint:gocyclo
func main() {
	opts := []params.Option{params.WithFleet(params.FleetBeta)}
	if *mailserver {
		opts = append(opts, params.WithMailserver())
	}

	config, err := params.NewNodeConfigWithDefaultsAndFiles(
		*dataDir,
		uint64(*networkID),
		opts,
		configFiles,
	)
	if err != nil {
		printUsage()
		if err != nil {
			logger.Error(err.Error())
		}
		os.Exit(1)
	}

	if *register && *mailserver {
		config.RegisterTopics = append(config.RegisterTopics, params.MailServerDiscv5Topic)
	} else if *register {
		config.RegisterTopics = append(config.RegisterTopics, params.WhisperDiscv5Topic)
	}

	// set up logging options
	setupLogging(config)

	// We want statusd to be distinct from StatusIM client.
	config.Name = serverClientName

	if *version {
		printVersion(config, buildStamp)
		return
	}

	backend := api.NewStatusBackend()
	err = backend.StartNode(config)
	if err != nil {
		logger.Error("Node start failed", "error", err)
		return
	}

	// handle interrupt signals
	interruptCh := haltOnInterruptSignal(backend.StatusNode())

	// Check if debugging CLI connection shall be enabled.
	if *cliEnabled {
		err := startDebug(backend)
		if err != nil {
			logger.Error("Starting debugging CLI server failed", "error", err)
			return
		}
	}

	// Check if profiling shall be enabled.
	if *pprofEnabled {
		profiling.NewProfiler(*pprofPort).Go()
	}

	// Run stats server.
	if *metrics {
		go startCollectingNodeMetrics(interruptCh, backend.StatusNode())
		go gethmetrics.CollectProcessMetrics(3 * time.Second)
	}

	// Sync blockchain and stop.
	if *syncAndExit >= 0 {
		exitCode := syncAndStopNode(interruptCh, backend.StatusNode(), *syncAndExit)
		// Call was interrupted. Wait for graceful shutdown.
		if exitCode == -1 {
			if gethNode := backend.StatusNode().GethNode(); gethNode != nil {
				gethNode.Wait()
			}
			return
		}
		// Otherwise, exit immediately with a returned exit code.
		os.Exit(exitCode)
	}

	gethNode := backend.StatusNode().GethNode()
	if gethNode != nil {
		// wait till node has been stopped
		gethNode.Wait()
	}
}

func getDefaultDataDir() string {
	if home := os.Getenv("HOME"); home != "" {
		return filepath.Join(home, ".statusd")
	}
	return "./statusd-data"
}

func setupLogging(config *params.NodeConfig) {
	if *logLevel != "" {
		config.LogLevel = *logLevel
	}

	colors := !(*logWithoutColors) && terminal.IsTerminal(int(os.Stdin.Fd()))
	if err := logutils.OverrideRootLog(
		logEnabled(config),
		config.LogLevel,
		config.LogFile,
		colors,
	); err != nil {
		stdlog.Fatalf("Error initializing logger: %v", err)
	}
}

// startDebug starts the debugging API server.
func startDebug(backend *api.StatusBackend) error {
	_, err := debug.New(backend, *cliPort)
	return err
}

// startCollectingStats collects various stats about the node and other protocols like Whisper.
func startCollectingNodeMetrics(interruptCh <-chan struct{}, statusNode *node.StatusNode) {
	logger.Info("Starting collecting node metrics")

	gethNode := statusNode.GethNode()
	if gethNode == nil {
		logger.Error("Failed to run metrics because it could not get the node")
		return
	}

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	go func() {
		if err := nodemetrics.SubscribeServerEvents(ctx, gethNode); err != nil {
			logger.Error("Failed to subscribe server events", "error", err)
		}
	}()

	<-interruptCh
}

func logEnabled(config *params.NodeConfig) bool {
	return config.LogLevel != "" || config.LogFile != ""
}

var (
	errStatusServiceRequiresIPC  = errors.New("to enable the StatusService on IPC, -ipc flag must be set")
	errStatusServiceRequiresHTTP = errors.New("to enable the StatusService on HTTP, -http flag must be set")
	errStatusServiceInvalidFlag  = errors.New("-status flag valid values are: ipc, http")
)

func configureStatusService(flagValue string, nodeConfig *params.NodeConfig) (*params.NodeConfig, error) {
	switch flagValue {
	case "ipc":
		if !nodeConfig.IPCEnabled {
			return nil, errStatusServiceRequiresIPC
		}
		nodeConfig.StatusServiceEnabled = true
	case "http":
		if !nodeConfig.HTTPEnabled {
			return nil, errStatusServiceRequiresHTTP
		}
		nodeConfig.StatusServiceEnabled = true
		nodeConfig.AddAPIModule("status")
	case "":
		nodeConfig.StatusServiceEnabled = false
	default:
		return nil, errStatusServiceInvalidFlag
	}

	return nodeConfig, nil
}

// printVersion prints verbose output about version and config.
func printVersion(config *params.NodeConfig, buildStamp string) {
	fmt.Println(strings.Title(config.Name))
	fmt.Println("Version:", config.Version)

	if buildStamp != "" {
		fmt.Println("Build Stamp:", buildStamp)
	}

	fmt.Println("Network Id:", config.NetworkID)
	fmt.Println("Go Version:", runtime.Version())
	fmt.Println("OS:", runtime.GOOS)
	fmt.Printf("GOPATH=%s\n", os.Getenv("GOPATH"))
	fmt.Printf("GOROOT=%s\n", runtime.GOROOT())

	fmt.Println("Loaded Config: ", config)
}

func printUsage() {
	usage := `
Usage: statusd [options]
Examples:
  statusd                                        # run regular Whisper node that joins Status network
  statusd -c ./default.json                      # run node with configuration specified in ./default.json file
  statusd -c ./default.json -c ./standalone.json # run node with configuration specified in ./default.json file, after merging ./standalone.json file
  statusd -c ./default.json -metrics             # run node with configuration specified in ./default.json file, and expose ethereum metrics with debug_metrics jsonrpc call
  statusd -c ./default.json -cli                 # run node with configuration specified in ./default.json file, and enable connection by statusd-cli on default port

Options:
`
	fmt.Fprintf(os.Stderr, usage)
	flag.PrintDefaults()
}

// haltOnInterruptSignal catches interrupt signal (SIGINT) and
// stops the node. It times out after 5 seconds
// if the node can not be stopped.
func haltOnInterruptSignal(statusNode *node.StatusNode) <-chan struct{} {
	interruptCh := make(chan struct{})
	go func() {
		signalCh := make(chan os.Signal, 1)
		signal.Notify(signalCh, os.Interrupt)
		defer signal.Stop(signalCh)
		<-signalCh
		close(interruptCh)
		logger.Info("Got interrupt, shutting down...")
		if err := statusNode.Stop(); err != nil {
			logger.Error("Failed to stop node", "error", err)
			os.Exit(1)
		}
	}()
	return interruptCh
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"sort"
)

const (
	listURL    = "http://melpon.org/wandbox/api/list.json"
	compileURL = "http://melpon.org/wandbox/api/compile.json"
)

// WandboxInput mean request structure to the compile.
type WandboxInput struct {
	Code     string `json:"code"`
	Compiler string `json:"compiler"`
}

// WandboxOutputCompile mean response structure from the compile.
type WandboxOutputCompile struct {
	ProgramError   string `json:"program_error"`
	ProgramMessage string `json:"program_message"`
	Status         string `json:"status"`
}

// WandboxOutputList mean response structure from the list.
type WandboxOutputList struct {
	Name                  string `json:"name"`
	Language              string `json:"language"`
	DisplayCompileCommand string `json:"display-compile-command"`
}

func detectLatestCompiler(source string) (string, error) {
	ext := filepath.Ext(source)
	name := ""
	switch ext {
	case ".c":
		name = "C"
	case ".C", ".cc", ".cpp", ".cxx":
		name = "C++"
	case ".cs":
		name = "C#"
	case ".coffee":
		name = "CoffeeScript"
	case ".d":
		name = "D"
	case ".erl":
		name = "Erlang"
	case ".ex":
		name = "Elixir"
	case ".go":
		name = "Go"
	case ".groovy":
		name = "Groovy"
	case ".hs":
		name = "Haskell"
	case ".java":
		name = "Java"
	case ".js":
		name = "JavaScript"
	case ".lazyk":
		name = "Lazy K"
	case ".l", ".lsp", ".lisp":
		name = "Lisp"
	case ".lua":
		name = "Lua"
	case ".ml":
		name = "OCaml"
	case ".pas":
		name = "Pascal"
	case ".php":
		name = "PHP"
	case ".pl":
		name = "Perl"
	case ".py":
		name = "Python"
	case ".rb":
		name = "Ruby"
	case ".rs":
		name = "Rust"
	case ".scala":
		name = "Scala"
	case ".sh", ".bash":
		name = "Bash script"
	case ".sqlite3", ".sqlite", ".sql":
		name = "SQL"
	case ".swift":
		name = "Swift"
	case ".vim":
		name = "Vim script"
	}
	m, err := getList()
	if err != nil {
		return "", err
	}
	if value, ok := m[name]; ok {
		return value[0].Name, nil
	} else {
		return "", errors.New(ext + " not supported")
	}
}

func executeCompile(data, compiler string) error {
	in := &WandboxInput{
		data,
		compiler,
	}
	var buf bytes.Buffer
	err := json.NewEncoder(&buf).Encode(in)
	if err != nil {
		return err
	}
	req, err := http.NewRequest("POST", compileURL, &buf)
	if err != nil {
		return err
	}
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	var out WandboxOutputCompile
	err = json.NewDecoder(resp.Body).Decode(&out)
	if err != nil {
		return err
	}
	fmt.Println(out.ProgramMessage)
	return nil
}

func getList() (map[string][]WandboxOutputList, error) {
	req, err := http.NewRequest("GET", listURL, nil)
	if err != nil {
		return nil, err
	}
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	var out []WandboxOutputList
	// bs, _ := ioutil.ReadAll(resp.Body)
	// fmt.Println(string(bs))
	err = json.NewDecoder(resp.Body).Decode(&out)
	if err != nil {
		return nil, err
	}
	m := map[string][]WandboxOutputList{}
	for _, x := range out {
		m[x.Language] = append(m[x.Language], x)
	}
	return m, nil
}

func executeList() error {
	m, _ := getList()
	var keys []string
	for key := range m {
		keys = append(keys, key)
	}
	sort.Strings(keys)
	for _, key := range keys {
		compilers := m[key]
		for i, x := range compilers {
			if i == 0 {
				fmt.Println("[" + key + "]")
			}
			fmt.Println("  " + x.Name)
		}
	}
	return nil
}

func run() int {
	var source, code, compiler string
	var list bool

	flag.StringVar(&source, "source", "", "source file")
	flag.StringVar(&code, "code", "", "code")
	flag.StringVar(&compiler, "compiler", "", "compiler")
	flag.BoolVar(&list, "list", false, "compiler list")
	flag.Parse()

	if list {
		err := executeList()
		if err != nil {
			fmt.Fprintf(os.Stderr, "%s: list error: %v\n", os.Args[0], err)
			return 1
		}
	} else {
		data := ""
		if code != "" {
			data = code
		} else if source != "" {
			xs, err := ioutil.ReadFile(source)
			if err != nil {
				fmt.Fprintf(os.Stderr, "%s: read file: %v\n", os.Args[0], err)
				return 1
			}
			data = string(xs)
			if compiler == "" {
				compiler, err = detectLatestCompiler(source)
				if err != nil {
					fmt.Fprintf(os.Stderr, "%s: detect latest compiler: %v\n", os.Args[0], err)
					return 1
				}
			}
		} else {
			flag.Usage()
			return 0
		}
		err := executeCompile(data, compiler)
		if err != nil {
			fmt.Fprintf(os.Stderr, "%s: compile error: %v\n", os.Args[0], err)
			return 1
		}
	}
	return 0
}

func main() {
	os.Exit(run())
}
<commit_msg>use map in detectLatestCompiler<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"sort"
)

const (
	listURL    = "http://melpon.org/wandbox/api/list.json"
	compileURL = "http://melpon.org/wandbox/api/compile.json"
)

// WandboxInput mean request structure to the compile.
type WandboxInput struct {
	Code     string `json:"code"`
	Compiler string `json:"compiler"`
}

// WandboxOutputCompile mean response structure from the compile.
type WandboxOutputCompile struct {
	ProgramError   string `json:"program_error"`
	ProgramMessage string `json:"program_message"`
	Status         string `json:"status"`
}

// WandboxOutputList mean response structure from the list.
type WandboxOutputList struct {
	Name                  string `json:"name"`
	Language              string `json:"language"`
	DisplayCompileCommand string `json:"display-compile-command"`
}

func detectLatestCompiler(source string) (string, error) {
	var ext2name = map[string]string{
		".c":       "C",
		".C":       "C++",
		".cc":      "C++",
		".cpp":     "C++",
		".cxx":     "C++",
		".cs":      "C#",
		".coffee":  "CoffeeScript",
		".d":       "D",
		".erl":     "Erlang",
		".ex":      "Elixir",
		".go":      "Go",
		".groovy":  "Groovy",
		".hs":      "Haskell",
		".java":    "Java",
		".js":      "JavaScript",
		".lazyk":   "Lazy K",
		".l":       "Lisp",
		".lsp":     "Lisp",
		".lisp":    "Lisp",
		".lua":     "Lua",
		".ml":      "OCaml",
		".pas":     "Pascal",
		".php":     "PHP",
		".pl":      "Perl",
		".py":      "Python",
		".rb":      "Ruby",
		".rs":      "Rust",
		".scala":   "Scala",
		".sh":      "Bash script",
		".bash":    "Bash script",
		".sqlite3": "SQL",
		".sqlite":  "SQL",
		".sql":     "SQL",
		".swift":   "Swift",
		".vim":     "Vim script",
	}
	ext := filepath.Ext(source)
	if name, ok := ext2name[ext]; ok {
		m, err := getList()
		if err != nil {
			return "", err
		}
		if compiler, ok := m[name]; ok {
			return compiler[0].Name, nil
		}
	}
	return "", errors.New(ext + " not supported")
}

func executeCompile(data, compiler string) error {
	in := &WandboxInput{
		data,
		compiler,
	}
	var buf bytes.Buffer
	err := json.NewEncoder(&buf).Encode(in)
	if err != nil {
		return err
	}
	req, err := http.NewRequest("POST", compileURL, &buf)
	if err != nil {
		return err
	}
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	var out WandboxOutputCompile
	err = json.NewDecoder(resp.Body).Decode(&out)
	if err != nil {
		return err
	}
	fmt.Println(out.ProgramMessage)
	return nil
}

func getList() (map[string][]WandboxOutputList, error) {
	req, err := http.NewRequest("GET", listURL, nil)
	if err != nil {
		return nil, err
	}
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	var out []WandboxOutputList
	// bs, _ := ioutil.ReadAll(resp.Body)
	// fmt.Println(string(bs))
	err = json.NewDecoder(resp.Body).Decode(&out)
	if err != nil {
		return nil, err
	}
	m := map[string][]WandboxOutputList{}
	for _, x := range out {
		m[x.Language] = append(m[x.Language], x)
	}
	return m, nil
}

func executeList() error {
	m, err := getList()
	if err != nil {
		return err
	}
	var keys []string
	for key := range m {
		keys = append(keys, key)
	}
	sort.Strings(keys)
	for _, key := range keys {
		compilers := m[key]
		for i, x := range compilers {
			if i == 0 {
				fmt.Println("[" + key + "]")
			}
			fmt.Println("  " + x.Name)
		}
	}
	return nil
}

func run() int {
	var source, code, compiler string
	var list bool

	flag.StringVar(&source, "source", "", "source file")
	flag.StringVar(&code, "code", "", "code")
	flag.StringVar(&compiler, "compiler", "", "compiler")
	flag.BoolVar(&list, "list", false, "compiler list")
	flag.Parse()

	if list {
		err := executeList()
		if err != nil {
			fmt.Fprintf(os.Stderr, "%s: list error: %v\n", os.Args[0], err)
			return 1
		}
	} else {
		data := ""
		if code != "" {
			data = code
		} else if source != "" {
			xs, err := ioutil.ReadFile(source)
			if err != nil {
				fmt.Fprintf(os.Stderr, "%s: read file: %v\n", os.Args[0], err)
				return 1
			}
			data = string(xs)
			if compiler == "" {
				compiler, err = detectLatestCompiler(source)
				if err != nil {
					fmt.Fprintf(os.Stderr, "%s: detect latest compiler: %v\n", os.Args[0], err)
					return 1
				}
			}
		} else {
			flag.Usage()
			return 0
		}
		err := executeCompile(data, compiler)
		if err != nil {
			fmt.Fprintf(os.Stderr, "%s: compile error: %v\n", os.Args[0], err)
			return 1
		}
	}
	return 0
}

func main() {
	os.Exit(run())
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Knative 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 main

import (
	"context"
	"flag"
	"fmt"
	"log"

	"go.uber.org/zap"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/tools/clientcmd"
	"knative.dev/pkg/configmap"
	"knative.dev/pkg/logging"
	"knative.dev/pkg/signals"
	"knative.dev/pkg/system"
	"knative.dev/pkg/version"
	"knative.dev/pkg/webhook"

	"github.com/GoogleCloudPlatform/cloud-run-events/pkg/apis/pubsub/v1alpha1"
)

const (
	component = "webhook"
)

var (
	masterURL  = flag.String("master", "", "The address of the Kubernetes API server. Overrides any value in kubeconfig. Only required if out-of-cluster.")
	kubeconfig = flag.String("kubeconfig", "", "Path to a kubeconfig. Only required if out-of-cluster.")
)

type Store interface {
	WatchConfigs(configmap.Watcher)
	ToContext(context.Context) context.Context
}

type StoreFactory func(*zap.SugaredLogger) Store

func SharedMain(handlers map[schema.GroupVersionKind]webhook.GenericCRD, factories ...StoreFactory) {
	flag.Parse()
	cm, err := configmap.Load("/etc/config-logging")
	if err != nil {
		log.Fatal("Error loading logging configuration:", err)
	}
	config, err := logging.NewConfigFromMap(cm)
	if err != nil {
		log.Fatal("Error parsing logging configuration:", err)
	}
	logger, atomicLevel := logging.NewLoggerFromConfig(config, component)
	defer logger.Sync()
	logger = logger.With(zap.String("cloud.run/events", component))

	logger.Info("Starting the Cloud Run Events Webhook")

	// Set up signals so we handle the first shutdown signal gracefully.
	ctx := signals.NewContext()

	clusterConfig, err := clientcmd.BuildConfigFromFlags(*masterURL, *kubeconfig)
	if err != nil {
		logger.Fatalw("Failed to get cluster config", zap.Error(err))
	}

	kubeClient, err := kubernetes.NewForConfig(clusterConfig)
	if err != nil {
		logger.Fatalw("Failed to get the client set", zap.Error(err))
	}

	if err := version.CheckMinimumVersion(kubeClient.Discovery()); err != nil {
		logger.Fatalw("Version check failed", err)
	}

	// Watch the logging config map and dynamically update logging levels.
	configMapWatcher := configmap.NewInformedWatcher(kubeClient, system.Namespace())
	configMapWatcher.Watch(logging.ConfigMapName(), logging.UpdateLevelFromConfigMap(logger, atomicLevel, component))

	// If you want to control Defaulting or Validation, you can attach config state
	// to the context by watching the configmap here, and then uncommenting the logic
	// below.
	stores := make([]Store, 0, len(factories))
	for _, sf := range factories {
		store := sf(logger)
		store.WatchConfigs(configMapWatcher)
		stores = append(stores, store)
	}

	if err = configMapWatcher.Start(ctx.Done()); err != nil {
		logger.Fatalw("Failed to start the ConfigMap watcher", zap.Error(err))
	}

	options := webhook.ControllerOptions{
		ServiceName:    "webhook",
		DeploymentName: "webhook",
		Namespace:      system.Namespace(),
		Port:           8443,
		SecretName:     "webhook-certs",
		WebhookName:    fmt.Sprintf("webhook.%s.events.cloud.run", system.Namespace()),
	}
	controller := webhook.AdmissionController{
		Client:                kubeClient,
		Options:               options,
		Handlers:              handlers,
		Logger:                logger,
		DisallowUnknownFields: true,

		WithContext: func(ctx context.Context) context.Context {
			for _, store := range stores {
				ctx = store.ToContext(ctx)
			}
			return ctx
		},
	}
	if err = controller.Run(ctx.Done()); err != nil {
		logger.Fatalw("Failed to start the admission controller", zap.Error(err))
	}
}

func main() {
	handlers := map[schema.GroupVersionKind]webhook.GenericCRD{
		v1alpha1.SchemeGroupVersion.WithKind("Channel"):          &v1alpha1.Channel{},
		v1alpha1.SchemeGroupVersion.WithKind("PullSubscription"): &v1alpha1.PullSubscription{},
		v1alpha1.SchemeGroupVersion.WithKind("Topic"):            &v1alpha1.Topic{},
	}
	SharedMain(handlers)

	// To setup a config "Store" to track a set of configurations and persist itself to
	// the context passed to webhook invocations, you would pass something like this to
	// SharedMain as well:
	// func(logger *zap.SugaredLogger) Store {
	// 	return apiconfig.NewStore(logger.Named("config-store"))
	// }
}
<commit_msg>upstream broke webhook.<commit_after>/*
Copyright 2019 The Knative 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 main

import (
	"context"
	"flag"
	"fmt"
	"log"

	"go.uber.org/zap"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/tools/clientcmd"
	"knative.dev/pkg/configmap"
	"knative.dev/pkg/logging"
	"knative.dev/pkg/signals"
	"knative.dev/pkg/system"
	"knative.dev/pkg/version"
	"knative.dev/pkg/webhook"

	"github.com/GoogleCloudPlatform/cloud-run-events/pkg/apis/pubsub/v1alpha1"
)

const (
	component = "webhook"
)

var (
	masterURL  = flag.String("master", "", "The address of the Kubernetes API server. Overrides any value in kubeconfig. Only required if out-of-cluster.")
	kubeconfig = flag.String("kubeconfig", "", "Path to a kubeconfig. Only required if out-of-cluster.")
)

type Store interface {
	WatchConfigs(configmap.Watcher)
	ToContext(context.Context) context.Context
}

type StoreFactory func(*zap.SugaredLogger) Store

func SharedMain(handlers map[schema.GroupVersionKind]webhook.GenericCRD, factories ...StoreFactory) {
	flag.Parse()
	cm, err := configmap.Load("/etc/config-logging")
	if err != nil {
		log.Fatal("Error loading logging configuration:", err)
	}
	config, err := logging.NewConfigFromMap(cm)
	if err != nil {
		log.Fatal("Error parsing logging configuration:", err)
	}
	logger, atomicLevel := logging.NewLoggerFromConfig(config, component)
	defer logger.Sync()
	logger = logger.With(zap.String("cloud.run/events", component))

	logger.Info("Starting the Cloud Run Events Webhook")

	// Set up signals so we handle the first shutdown signal gracefully.
	ctx := signals.NewContext()

	clusterConfig, err := clientcmd.BuildConfigFromFlags(*masterURL, *kubeconfig)
	if err != nil {
		logger.Fatalw("Failed to get cluster config", zap.Error(err))
	}

	kubeClient, err := kubernetes.NewForConfig(clusterConfig)
	if err != nil {
		logger.Fatalw("Failed to get the client set", zap.Error(err))
	}

	if err := version.CheckMinimumVersion(kubeClient.Discovery()); err != nil {
		logger.Fatalw("Version check failed", err)
	}

	// Watch the logging config map and dynamically update logging levels.
	configMapWatcher := configmap.NewInformedWatcher(kubeClient, system.Namespace())
	configMapWatcher.Watch(logging.ConfigMapName(), logging.UpdateLevelFromConfigMap(logger, atomicLevel, component))

	// If you want to control Defaulting or Validation, you can attach config state
	// to the context by watching the configmap here, and then uncommenting the logic
	// below.
	stores := make([]Store, 0, len(factories))
	for _, sf := range factories {
		store := sf(logger)
		store.WatchConfigs(configMapWatcher)
		stores = append(stores, store)
	}

	if err = configMapWatcher.Start(ctx.Done()); err != nil {
		logger.Fatalw("Failed to start the ConfigMap watcher", zap.Error(err))
	}

	options := webhook.ControllerOptions{
		ServiceName:    "webhook",
		DeploymentName: "webhook",
		Namespace:      system.Namespace(),
		Port:           8443,
		SecretName:     "webhook-certs",
		WebhookName:    fmt.Sprintf("webhook.%s.events.cloud.run", system.Namespace()),
	}

	stats, err := webhook.NewStatsReporter()
	if err != nil {
		logger.Fatalw("Failed to initialize the stats reporter", zap.Error(err))
	}

	controller := webhook.AdmissionController{
		Client:                kubeClient,
		Options:               options,
		Handlers:              handlers,
		Logger:                logger,
		DisallowUnknownFields: true,
		StatsReporter:         stats,

		WithContext: func(ctx context.Context) context.Context {
			for _, store := range stores {
				ctx = store.ToContext(ctx)
			}
			return ctx
		},
	}
	if err = controller.Run(ctx.Done()); err != nil {
		logger.Fatalw("Failed to start the admission controller", zap.Error(err))
	}
}

func main() {
	handlers := map[schema.GroupVersionKind]webhook.GenericCRD{
		v1alpha1.SchemeGroupVersion.WithKind("Channel"):          &v1alpha1.Channel{},
		v1alpha1.SchemeGroupVersion.WithKind("PullSubscription"): &v1alpha1.PullSubscription{},
		v1alpha1.SchemeGroupVersion.WithKind("Topic"):            &v1alpha1.Topic{},
	}
	SharedMain(handlers)

	// To setup a config "Store" to track a set of configurations and persist itself to
	// the context passed to webhook invocations, you would pass something like this to
	// SharedMain as well:
	// func(logger *zap.SugaredLogger) Store {
	// 	return apiconfig.NewStore(logger.Named("config-store"))
	// }
}
<|endoftext|>


<commit_before>// Copyright 2015-2017 the u-root Authors. All rights reserved
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Print build information about the kernel and machine.
//
// Synopsis:
//     uname [-asnrvmd]
//
// Options:
//     -a: print everything
//     -s: print the kernel name
//     -n: print the network node name
//     -r: print the kernel release
//     -v: print the kernel version
//     -m: print the machine hardware name
//     -d: print your domain name
package main

import (
	"bytes"
	"flag"
	"fmt"
	"log"
	"strings"

	"golang.org/x/sys/unix"
)

var (
	all     = flag.Bool("a", false, "print everything")
	kernel  = flag.Bool("s", false, "print the kernel name")
	node    = flag.Bool("n", false, "print the network node name")
	release = flag.Bool("r", false, "print the kernel release")
	version = flag.Bool("v", false, "print the kernel version")
	machine = flag.Bool("m", false, "print the machine hardware name")
	domain  = flag.Bool("d", false, "print your domain name")
)

func toString(d []byte) string {
	return string(d[:bytes.IndexByte(d[:], 0)])
}

func handleFlags(u *unix.Utsname) string {
	Sysname, Nodename := toString(u.Sysname[:]), toString(u.Nodename[:])
	Release, Version := toString(u.Release[:]), toString(u.Version[:])
	Machine, Domainname := toString(u.Machine[:]), toString(u.Domainname[:])
	info := make([]string, 0, 6)

	if *all || flag.NFlag() == 0 {
		info = append(info, Sysname, Nodename, Release, Version, Machine, Domainname)
		goto end
	}
	if *kernel {
		info = append(info, Sysname)
	}
	if *node {
		info = append(info, Nodename)
	}
	if *release {
		info = append(info, Release)
	}
	if *version {
		info = append(info, Version)
	}
	if *machine {
		info = append(info, Machine)
	}
	if *domain {
		info = append(info, Domainname)
	}

end:
	return strings.Join(info, " ")
}

func main() {
	flag.Parse()

	var u unix.Utsname
	if err := unix.Uname(&u); err != nil {
		log.Fatalf("%v", err)
	}

	info := handleFlags(&u)
	fmt.Println(info)
}
<commit_msg>uname: add -p switch<commit_after>// Copyright 2015-2017 the u-root Authors. All rights reserved
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Print build information about the kernel and machine.
//
// Synopsis:
//     uname [-asnrvmd]
//
// Options:
//     -a: print everything
//     -s: print the kernel name
//     -n: print the network node name
//     -r: print the kernel release
//     -v: print the kernel version
//     -m: print the machine hardware name
//     -d: print your domain name
package main

import (
	"bytes"
	"flag"
	"fmt"
	"log"
	"strings"

	"golang.org/x/sys/unix"
)

var (
	all       = flag.Bool("a", false, "print everything")
	kernel    = flag.Bool("s", false, "print the kernel name")
	node      = flag.Bool("n", false, "print the network node name")
	release   = flag.Bool("r", false, "print the kernel release")
	version   = flag.Bool("v", false, "print the kernel version")
	machine   = flag.Bool("m", false, "print the machine hardware name")
	processor = flag.Bool("p", false, "print the machine hardware name")
	domain    = flag.Bool("d", false, "print your domain name")
)

func toString(d []byte) string {
	return string(d[:bytes.IndexByte(d[:], 0)])
}

func handleFlags(u *unix.Utsname) string {
	Sysname, Nodename := toString(u.Sysname[:]), toString(u.Nodename[:])
	Release, Version := toString(u.Release[:]), toString(u.Version[:])
	Machine, Domainname := toString(u.Machine[:]), toString(u.Domainname[:])
	info := make([]string, 0, 6)

	if *all || flag.NFlag() == 0 {
		info = append(info, Sysname, Nodename, Release, Version, Machine, Domainname)
		goto end
	}
	if *kernel {
		info = append(info, Sysname)
	}
	if *node {
		info = append(info, Nodename)
	}
	if *release {
		info = append(info, Release)
	}
	if *version {
		info = append(info, Version)
	}
	if *machine || *processor {
		info = append(info, Machine)
	}
	if *domain {
		info = append(info, Domainname)
	}

end:
	return strings.Join(info, " ")
}

func main() {
	flag.Parse()

	var u unix.Utsname
	if err := unix.Uname(&u); err != nil {
		log.Fatalf("%v", err)
	}

	info := handleFlags(&u)
	fmt.Println(info)
}
<|endoftext|>


<commit_before>package data

import (
	"fmt"
	"path"
	"regexp"
	"strings"
)

// Canonical base role names
const (
	CanonicalRootRole      = "root"
	CanonicalTargetsRole   = "targets"
	CanonicalSnapshotRole  = "snapshot"
	CanonicalTimestampRole = "timestamp"
)

// ValidRoles holds an overrideable mapping of canonical role names
// to any custom roles names a user wants to make use of. This allows
// us to be internally consistent while using different roles in the
// public TUF files.
var ValidRoles = map[string]string{
	CanonicalRootRole:      CanonicalRootRole,
	CanonicalTargetsRole:   CanonicalTargetsRole,
	CanonicalSnapshotRole:  CanonicalSnapshotRole,
	CanonicalTimestampRole: CanonicalTimestampRole,
}

// ErrNoSuchRole indicates the roles doesn't exist
type ErrNoSuchRole struct {
	Role string
}

func (e ErrNoSuchRole) Error() string {
	return fmt.Sprintf("role does not exist: %s", e.Role)
}

// ErrInvalidRole represents an error regarding a role. Typically
// something like a role for which sone of the public keys were
// not found in the TUF repo.
type ErrInvalidRole struct {
	Role   string
	Reason string
}

func (e ErrInvalidRole) Error() string {
	if e.Reason != "" {
		return fmt.Sprintf("tuf: invalid role %s. %s", e.Role, e.Reason)
	}
	return fmt.Sprintf("tuf: invalid role %s.", e.Role)
}

// SetValidRoles is a utility function to override some or all of the roles
func SetValidRoles(rs map[string]string) {
	// iterate ValidRoles
	for k := range ValidRoles {
		if v, ok := rs[k]; ok {
			ValidRoles[k] = v
		}
	}
}

// RoleName returns the (possibly overridden) role name for the provided
// canonical role name
func RoleName(canonicalRole string) string {
	if r, ok := ValidRoles[canonicalRole]; ok {
		return r
	}
	return canonicalRole
}

// CanonicalRole does a reverse lookup to get the canonical role name
// from the (possibly overridden) role name
func CanonicalRole(role string) string {
	name := strings.ToLower(role)
	if _, ok := ValidRoles[name]; ok {
		// The canonical version is always lower case
		// se ensure we return name, not role
		return name
	}
	targetsBase := fmt.Sprintf("%s/", ValidRoles[CanonicalTargetsRole])
	if strings.HasPrefix(name, targetsBase) {
		role = strings.TrimPrefix(role, targetsBase)
		role = fmt.Sprintf("%s/%s", CanonicalTargetsRole, role)
		return role
	}
	for r, v := range ValidRoles {
		if role == v {
			return r
		}
	}
	return ""
}

// ValidRole only determines the name is semantically
// correct. For target delegated roles, it does NOT check
// the the appropriate parent roles exist.
func ValidRole(name string) bool {
	name = strings.ToLower(name)
	if v, ok := ValidRoles[name]; ok {
		return name == v
	}

	if IsDelegation(name) {
		return true
	}

	for _, v := range ValidRoles {
		if name == v {
			return true
		}
	}
	return false
}

// IsDelegation checks if the role is a delegation or a root role
func IsDelegation(role string) bool {
	targetsBase := ValidRoles[CanonicalTargetsRole] + "/"

	delegationRegexp := regexp.MustCompile("^[-a-z0-9_/]+$")
	whitelistedChars := delegationRegexp.MatchString(role)

	// Limit size of full role string to 255 chars for db column size limit
	correctLength := len(role) < 256

	// Removes ., .., extra slashes, and trailing slash
	isClean := path.Clean(role) == role
	return strings.HasPrefix(role, targetsBase) &&
		whitelistedChars &&
		correctLength &&
		isClean
}

// RootRole is a cut down role as it appears in the root.json
type RootRole struct {
	KeyIDs    []string `json:"keyids"`
	Threshold int      `json:"threshold"`
}

// Role is a more verbose role as they appear in targets delegations
type Role struct {
	RootRole
	Name             string   `json:"name"`
	Paths            []string `json:"paths,omitempty"`
	PathHashPrefixes []string `json:"path_hash_prefixes,omitempty"`
	Email            string   `json:"email,omitempty"`
}

// NewRole creates a new Role object from the given parameters
func NewRole(name string, threshold int, keyIDs, paths, pathHashPrefixes []string) (*Role, error) {
	if len(paths) > 0 && len(pathHashPrefixes) > 0 {
		return nil, ErrInvalidRole{
			Role:   name,
			Reason: "roles may not have both Paths and PathHashPrefixes",
		}
	}
	if IsDelegation(name) {
		if len(paths) == 0 && len(pathHashPrefixes) == 0 {
			return nil, ErrInvalidRole{
				Role:   name,
				Reason: "roles with no Paths and no PathHashPrefixes will never be able to publish content",
			}
		}
	}
	if threshold < 1 {
		return nil, ErrInvalidRole{Role: name}
	}
	if !ValidRole(name) {
		return nil, ErrInvalidRole{Role: name}
	}
	return &Role{
		RootRole: RootRole{
			KeyIDs:    keyIDs,
			Threshold: threshold,
		},
		Name:             name,
		Paths:            paths,
		PathHashPrefixes: pathHashPrefixes,
	}, nil

}

// IsValid checks if the role has defined both paths and path hash prefixes,
// having both is invalid
func (r Role) IsValid() bool {
	return !(len(r.Paths) > 0 && len(r.PathHashPrefixes) > 0)
}

// ValidKey checks if the given id is a recognized signing key for the role
func (r Role) ValidKey(id string) bool {
	for _, key := range r.KeyIDs {
		if key == id {
			return true
		}
	}
	return false
}

// CheckPaths checks if a given path is valid for the role
func (r Role) CheckPaths(path string) bool {
	for _, p := range r.Paths {
		if strings.HasPrefix(path, p) {
			return true
		}
	}
	return false
}

// CheckPrefixes checks if a given hash matches the prefixes for the role
func (r Role) CheckPrefixes(hash string) bool {
	for _, p := range r.PathHashPrefixes {
		if strings.HasPrefix(hash, p) {
			return true
		}
	}
	return false
}

// IsDelegation checks if the role is a delegation or a root role
func (r Role) IsDelegation() bool {
	return IsDelegation(r.Name)
}

// AddKeys merges the ids into the current list of role key ids
func (r *Role) AddKeys(ids []string) {
	r.KeyIDs = mergeStrSlices(r.KeyIDs, ids)
}

// AddPaths merges the paths into the current list of role paths
func (r *Role) AddPaths(paths []string) error {
	if len(paths) == 0 {
		return nil
	}
	if len(r.PathHashPrefixes) > 0 {
		return ErrInvalidRole{Role: r.Name, Reason: "attempted to add paths to role that already has hash prefixes"}
	}
	r.Paths = mergeStrSlices(r.Paths, paths)
	return nil
}

// AddPathHashPrefixes merges the prefixes into the list of role path hash prefixes
func (r *Role) AddPathHashPrefixes(prefixes []string) error {
	if len(prefixes) == 0 {
		return nil
	}
	if len(r.Paths) > 0 {
		return ErrInvalidRole{Role: r.Name, Reason: "attempted to add hash prefixes to role that already has paths"}
	}
	r.PathHashPrefixes = mergeStrSlices(r.PathHashPrefixes, prefixes)
	return nil
}

// RemoveKeys removes the ids from the current list of key ids
func (r *Role) RemoveKeys(ids []string) {
	r.KeyIDs = subtractStrSlices(r.KeyIDs, ids)
}

// RemovePaths removes the paths from the current list of role paths
func (r *Role) RemovePaths(paths []string) {
	r.Paths = subtractStrSlices(r.Paths, paths)
}

// RemovePathHashPrefixes removes the prefixes from the current list of path hash prefixes
func (r *Role) RemovePathHashPrefixes(prefixes []string) {
	r.PathHashPrefixes = subtractStrSlices(r.PathHashPrefixes, prefixes)
}

func mergeStrSlices(orig, new []string) []string {
	have := make(map[string]bool)
	for _, e := range orig {
		have[e] = true
	}
	merged := make([]string, len(orig), len(orig)+len(new))
	copy(merged, orig)
	for _, e := range new {
		if !have[e] {
			merged = append(merged, e)
		}
	}
	return merged
}

func subtractStrSlices(orig, remove []string) []string {
	kill := make(map[string]bool)
	for _, e := range remove {
		kill[e] = true
	}
	var keep []string
	for _, e := range orig {
		if !kill[e] {
			keep = append(keep, e)
		}
	}
	return keep
}
<commit_msg>Move regex compilation to outside var for optimization<commit_after>package data

import (
	"fmt"
	"path"
	"regexp"
	"strings"
)

// Canonical base role names
const (
	CanonicalRootRole      = "root"
	CanonicalTargetsRole   = "targets"
	CanonicalSnapshotRole  = "snapshot"
	CanonicalTimestampRole = "timestamp"
)

// ValidRoles holds an overrideable mapping of canonical role names
// to any custom roles names a user wants to make use of. This allows
// us to be internally consistent while using different roles in the
// public TUF files.
var ValidRoles = map[string]string{
	CanonicalRootRole:      CanonicalRootRole,
	CanonicalTargetsRole:   CanonicalTargetsRole,
	CanonicalSnapshotRole:  CanonicalSnapshotRole,
	CanonicalTimestampRole: CanonicalTimestampRole,
}

// Regex for validating delegation names
var delegationRegexp = regexp.MustCompile("^[-a-z0-9_/]+$")

// ErrNoSuchRole indicates the roles doesn't exist
type ErrNoSuchRole struct {
	Role string
}

func (e ErrNoSuchRole) Error() string {
	return fmt.Sprintf("role does not exist: %s", e.Role)
}

// ErrInvalidRole represents an error regarding a role. Typically
// something like a role for which sone of the public keys were
// not found in the TUF repo.
type ErrInvalidRole struct {
	Role   string
	Reason string
}

func (e ErrInvalidRole) Error() string {
	if e.Reason != "" {
		return fmt.Sprintf("tuf: invalid role %s. %s", e.Role, e.Reason)
	}
	return fmt.Sprintf("tuf: invalid role %s.", e.Role)
}

// SetValidRoles is a utility function to override some or all of the roles
func SetValidRoles(rs map[string]string) {
	// iterate ValidRoles
	for k := range ValidRoles {
		if v, ok := rs[k]; ok {
			ValidRoles[k] = v
		}
	}
}

// RoleName returns the (possibly overridden) role name for the provided
// canonical role name
func RoleName(canonicalRole string) string {
	if r, ok := ValidRoles[canonicalRole]; ok {
		return r
	}
	return canonicalRole
}

// CanonicalRole does a reverse lookup to get the canonical role name
// from the (possibly overridden) role name
func CanonicalRole(role string) string {
	name := strings.ToLower(role)
	if _, ok := ValidRoles[name]; ok {
		// The canonical version is always lower case
		// se ensure we return name, not role
		return name
	}
	targetsBase := fmt.Sprintf("%s/", ValidRoles[CanonicalTargetsRole])
	if strings.HasPrefix(name, targetsBase) {
		role = strings.TrimPrefix(role, targetsBase)
		role = fmt.Sprintf("%s/%s", CanonicalTargetsRole, role)
		return role
	}
	for r, v := range ValidRoles {
		if role == v {
			return r
		}
	}
	return ""
}

// ValidRole only determines the name is semantically
// correct. For target delegated roles, it does NOT check
// the the appropriate parent roles exist.
func ValidRole(name string) bool {
	name = strings.ToLower(name)
	if v, ok := ValidRoles[name]; ok {
		return name == v
	}

	if IsDelegation(name) {
		return true
	}

	for _, v := range ValidRoles {
		if name == v {
			return true
		}
	}
	return false
}

// IsDelegation checks if the role is a delegation or a root role
func IsDelegation(role string) bool {
	targetsBase := ValidRoles[CanonicalTargetsRole] + "/"

	whitelistedChars := delegationRegexp.MatchString(role)

	// Limit size of full role string to 255 chars for db column size limit
	correctLength := len(role) < 256

	// Removes ., .., extra slashes, and trailing slash
	isClean := path.Clean(role) == role
	return strings.HasPrefix(role, targetsBase) &&
		whitelistedChars &&
		correctLength &&
		isClean
}

// RootRole is a cut down role as it appears in the root.json
type RootRole struct {
	KeyIDs    []string `json:"keyids"`
	Threshold int      `json:"threshold"`
}

// Role is a more verbose role as they appear in targets delegations
type Role struct {
	RootRole
	Name             string   `json:"name"`
	Paths            []string `json:"paths,omitempty"`
	PathHashPrefixes []string `json:"path_hash_prefixes,omitempty"`
	Email            string   `json:"email,omitempty"`
}

// NewRole creates a new Role object from the given parameters
func NewRole(name string, threshold int, keyIDs, paths, pathHashPrefixes []string) (*Role, error) {
	if len(paths) > 0 && len(pathHashPrefixes) > 0 {
		return nil, ErrInvalidRole{
			Role:   name,
			Reason: "roles may not have both Paths and PathHashPrefixes",
		}
	}
	if IsDelegation(name) {
		if len(paths) == 0 && len(pathHashPrefixes) == 0 {
			return nil, ErrInvalidRole{
				Role:   name,
				Reason: "roles with no Paths and no PathHashPrefixes will never be able to publish content",
			}
		}
	}
	if threshold < 1 {
		return nil, ErrInvalidRole{Role: name}
	}
	if !ValidRole(name) {
		return nil, ErrInvalidRole{Role: name}
	}
	return &Role{
		RootRole: RootRole{
			KeyIDs:    keyIDs,
			Threshold: threshold,
		},
		Name:             name,
		Paths:            paths,
		PathHashPrefixes: pathHashPrefixes,
	}, nil

}

// IsValid checks if the role has defined both paths and path hash prefixes,
// having both is invalid
func (r Role) IsValid() bool {
	return !(len(r.Paths) > 0 && len(r.PathHashPrefixes) > 0)
}

// ValidKey checks if the given id is a recognized signing key for the role
func (r Role) ValidKey(id string) bool {
	for _, key := range r.KeyIDs {
		if key == id {
			return true
		}
	}
	return false
}

// CheckPaths checks if a given path is valid for the role
func (r Role) CheckPaths(path string) bool {
	for _, p := range r.Paths {
		if strings.HasPrefix(path, p) {
			return true
		}
	}
	return false
}

// CheckPrefixes checks if a given hash matches the prefixes for the role
func (r Role) CheckPrefixes(hash string) bool {
	for _, p := range r.PathHashPrefixes {
		if strings.HasPrefix(hash, p) {
			return true
		}
	}
	return false
}

// IsDelegation checks if the role is a delegation or a root role
func (r Role) IsDelegation() bool {
	return IsDelegation(r.Name)
}

// AddKeys merges the ids into the current list of role key ids
func (r *Role) AddKeys(ids []string) {
	r.KeyIDs = mergeStrSlices(r.KeyIDs, ids)
}

// AddPaths merges the paths into the current list of role paths
func (r *Role) AddPaths(paths []string) error {
	if len(paths) == 0 {
		return nil
	}
	if len(r.PathHashPrefixes) > 0 {
		return ErrInvalidRole{Role: r.Name, Reason: "attempted to add paths to role that already has hash prefixes"}
	}
	r.Paths = mergeStrSlices(r.Paths, paths)
	return nil
}

// AddPathHashPrefixes merges the prefixes into the list of role path hash prefixes
func (r *Role) AddPathHashPrefixes(prefixes []string) error {
	if len(prefixes) == 0 {
		return nil
	}
	if len(r.Paths) > 0 {
		return ErrInvalidRole{Role: r.Name, Reason: "attempted to add hash prefixes to role that already has paths"}
	}
	r.PathHashPrefixes = mergeStrSlices(r.PathHashPrefixes, prefixes)
	return nil
}

// RemoveKeys removes the ids from the current list of key ids
func (r *Role) RemoveKeys(ids []string) {
	r.KeyIDs = subtractStrSlices(r.KeyIDs, ids)
}

// RemovePaths removes the paths from the current list of role paths
func (r *Role) RemovePaths(paths []string) {
	r.Paths = subtractStrSlices(r.Paths, paths)
}

// RemovePathHashPrefixes removes the prefixes from the current list of path hash prefixes
func (r *Role) RemovePathHashPrefixes(prefixes []string) {
	r.PathHashPrefixes = subtractStrSlices(r.PathHashPrefixes, prefixes)
}

func mergeStrSlices(orig, new []string) []string {
	have := make(map[string]bool)
	for _, e := range orig {
		have[e] = true
	}
	merged := make([]string, len(orig), len(orig)+len(new))
	copy(merged, orig)
	for _, e := range new {
		if !have[e] {
			merged = append(merged, e)
		}
	}
	return merged
}

func subtractStrSlices(orig, remove []string) []string {
	kill := make(map[string]bool)
	for _, e := range remove {
		kill[e] = true
	}
	var keep []string
	for _, e := range orig {
		if !kill[e] {
			keep = append(keep, e)
		}
	}
	return keep
}
<|endoftext|>


<commit_before>package main

const genCodecPath = "github.com/ugorji/go/codec"
<commit_msg>codec/codecgen: added copyright info<commit_after>// Copyright (c) 2012-2018 Ugorji Nwoke. All rights reserved.
// Use of this source code is governed by a MIT license found in the LICENSE file.

package main

const genCodecPath = "github.com/ugorji/go/codec"
<|endoftext|>


<commit_before>package objx

import (
	"regexp"
	"strconv"
	"strings"
)

// Obj gets the underlying object contained within this
// O.
func (o *O) Obj() interface{} {
	return o.obj
}

// arrayAccesRegexString is the regex used to extract the array number
// from the access path
const arrayAccesRegexString = `^(.+)\[([0-9]+)\]$`

// arrayAccesRegex is the compiled arrayAccesRegexString
var arrayAccesRegex = regexp.MustCompile(arrayAccesRegexString)

// Get gets a value from this object.
func (o *O) Get(selector interface{}) *O {

	switch selector.(type) {
	case string: // "address.postcode.inner"

		selStr := selector.(string)
		segs := strings.Split(selStr, ".")

		current := o.Obj()

		for _, field := range segs {

			arrayMatches := arrayAccesRegex.FindStringSubmatch(field)

			if len(arrayMatches) > 0 {
				// An array notation has been detected.
				mName := arrayMatches[1]

				// Ignore the error here since the regex would only match on 0-9
				index, _ := strconv.Atoi(arrayMatches[2])

				if m, ok := current.(map[string]interface{}); ok {
					if a, ok := m[mName].([]interface{}); ok {
						current = a[index]
					}
				}
			} else {
				if m, ok := current.(map[string]interface{}); ok {
					current = m[field]
				}
			}

		}

		return New(current)

	case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:

		selInt := uint64FromInterface(selector)
		if a, ok := o.Obj().([]interface{}); ok {
			return New(a[selInt])
		}

	}

	panic("objx: Get with invalid selector.")

}

// uint64FromInterface converts an interface object to the largest
// representation of an unsigned integer
func uint64FromInterface(selector interface{}) uint64 {
	var value uint64
	switch selector.(type) {
	case int:
		value = uint64(selector.(int))
	case int8:
		value = uint64(selector.(int8))
	case int16:
		value = uint64(selector.(int16))
	case int32:
		value = uint64(selector.(int32))
	case int64:
		value = uint64(selector.(int64))
	case uint:
		value = uint64(selector.(uint))
	case uint8:
		value = uint64(selector.(uint8))
	case uint16:
		value = uint64(selector.(uint16))
	case uint32:
		value = uint64(selector.(uint32))
	case uint64:
		value = selector.(uint64)
	default:
		panic("objx: array access argument is not an integer (this should never happen)")
	}

	return value
}
<commit_msg>Add panic for index not number<commit_after>package objx

import (
	"regexp"
	"strconv"
	"strings"
)

// Obj gets the underlying object contained within this
// O.
func (o *O) Obj() interface{} {
	return o.obj
}

// arrayAccesRegexString is the regex used to extract the array number
// from the access path
const arrayAccesRegexString = `^(.+)\[([0-9]+)\]$`

// arrayAccesRegex is the compiled arrayAccesRegexString
var arrayAccesRegex = regexp.MustCompile(arrayAccesRegexString)

// Get gets a value from this object.
func (o *O) Get(selector interface{}) *O {

	switch selector.(type) {
	case string: // "address.postcode.inner"

		selStr := selector.(string)
		segs := strings.Split(selStr, ".")

		current := o.Obj()

		for _, field := range segs {

			arrayMatches := arrayAccesRegex.FindStringSubmatch(field)

			if len(arrayMatches) > 0 {
				// An array notation has been detected.

				// Get the key into the map
				mName := arrayMatches[1]

				// Get the index into the array at the key
				index, err := strconv.Atoi(arrayMatches[2])
				if err != nil {
					// This should never happen. If it does, something has gone
					// seriously wrong. Panic.
					panic("objx: GEt - array index is not an integer. This should never happen.")
				}

				if m, ok := current.(map[string]interface{}); ok {
					if a, ok := m[mName].([]interface{}); ok {
						current = a[index]
					}
				}
			} else {
				if m, ok := current.(map[string]interface{}); ok {
					current = m[field]
				}
			}

		}

		return New(current)

	case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:

		selInt := uint64FromInterface(selector)
		if a, ok := o.Obj().([]interface{}); ok {
			return New(a[selInt])
		}

	}

	panic("objx: Get with invalid selector.")

}

// uint64FromInterface converts an interface object to the largest
// representation of an unsigned integer using a type switch and
// assertions
func uint64FromInterface(selector interface{}) uint64 {
	var value uint64
	switch selector.(type) {
	case int:
		value = uint64(selector.(int))
	case int8:
		value = uint64(selector.(int8))
	case int16:
		value = uint64(selector.(int16))
	case int32:
		value = uint64(selector.(int32))
	case int64:
		value = uint64(selector.(int64))
	case uint:
		value = uint64(selector.(uint))
	case uint8:
		value = uint64(selector.(uint8))
	case uint16:
		value = uint64(selector.(uint16))
	case uint32:
		value = uint64(selector.(uint32))
	case uint64:
		value = selector.(uint64)
	default:
		panic("objx: array access argument is not an integer (this should never happen)")
	}

	return value
}
<|endoftext|>