<commit_before>/*
Copyright (c) 2016, UPMC Enterprises
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name UPMC Enterprises nor the
      names of its contributors may be used to endorse or promote products
      derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL UPMC ENTERPRISES BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
*/

package main

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

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/ecr"
	flag "github.com/spf13/pflag"
	"golang.org/x/net/context"
	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"
	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/client/restclient"
	"k8s.io/kubernetes/pkg/client/unversioned"
	kubectl_util "k8s.io/kubernetes/pkg/kubectl/cmd/util"
)

const (
	dockerCfgTemplate  = `{"%s":{"username":"oauth2accesstoken","password":"%s","email":"none"}}`
	dockerJSONTemplate = `{"auths":{"%s":{"auth":"%s","email":"none"}}}`
)

var (
	flags               = flag.NewFlagSet("", flag.ContinueOnError)
	cluster             = flags.Bool("use-kubernetes-cluster-service", true, `If true, use the built in kubernetes cluster for creating the client`)
	argKubecfgFile      = flags.String("kubecfg-file", "", `Location of kubecfg file for access to kubernetes master service; --kube_master_url overrides the URL part of this; if neither this nor --kube_master_url are provided, defaults to service account tokens`)
	argKubeMasterURL    = flags.String("kube-master-url", "", `URL to reach kubernetes master. Env variables in this flag will be expanded.`)
	argAWSSecretName    = flags.String("aws-secret-name", "awsecr-cred", `Default aws secret name`)
	argGCRSecretName    = flags.String("gcr-secret-name", "gcr-secret", `Default gcr secret name`)
	argDefaultNamespace = flags.String("default-namespace", "default", `Default namespace`)
	argGCRURL           = flags.String("gcr-url", "https://gcr.io", `Default GCR URL`)
	argAWSRegion        = flags.String("aws-region", "us-east-1", `Default AWS region`)
	argRefreshMinutes   = flags.Int("refresh-mins", 60, `Default time to wait before refreshing (60 minutes)`)
)

var (
	awsAccountID string
)

type controller struct {
	kubeClient kubeInterface
	ecrClient  ecrInterface
	gcrClient  gcrInterface
	config     providerConfig
}

type providerConfig struct {
	ecrEnabled bool
	gcrEnabled bool
}

type kubeInterface interface {
	Secrets(namespace string) unversioned.SecretsInterface
	Namespaces() unversioned.NamespaceInterface
	ServiceAccounts(namespace string) unversioned.ServiceAccountsInterface
}

type ecrInterface interface {
	GetAuthorizationToken(input *ecr.GetAuthorizationTokenInput) (*ecr.GetAuthorizationTokenOutput, error)
}

type gcrInterface interface {
	DefaultTokenSource(ctx context.Context, scope ...string) (oauth2.TokenSource, error)
}

func newEcrClient() ecrInterface {
	return ecr.New(session.New(), aws.NewConfig().WithRegion(*argAWSRegion))
}

type gcrClient struct{}

func (gcr gcrClient) DefaultTokenSource(ctx context.Context, scope ...string) (oauth2.TokenSource, error) {
	return google.DefaultTokenSource(ctx, scope...)
}

func newGcrClient() gcrInterface {
	return gcrClient{}
}

func newKubeClient() kubeInterface {
	var kubeClient *unversioned.Client
	var config *restclient.Config
	var err error

	clientConfig := kubectl_util.DefaultClientConfig(flags)

	if *cluster {
		if kubeClient, err = unversioned.NewInCluster(); err != nil {
			log.Fatalf("Failed to create client: %v", err)
		}
	} else {
		config, err = clientConfig.ClientConfig()
		if err != nil {
			log.Fatalf("error connecting to the client: %v", err)
		}
		kubeClient, err = unversioned.New(config)

		if err != nil {
			log.Fatalf("Failed to create client: %v", err)
		}
	}

	return kubeClient
}

func (c *controller) getGCRAuthorizationKey() (AuthToken, error) {
	ts, err := c.gcrClient.DefaultTokenSource(context.TODO(), "https://www.googleapis.com/auth/cloud-platform")
	if err != nil {
		return AuthToken{}, err
	}

	token, err := ts.Token()
	if err != nil {
		return AuthToken{}, err
	}

	if !token.Valid() {
		return AuthToken{}, fmt.Errorf("token was invalid")
	}

	if token.Type() != "Bearer" {
		return AuthToken{}, fmt.Errorf(fmt.Sprintf("expected token type \"Bearer\" but got \"%s\"", token.Type()))
	}

	return AuthToken{
		AccessToken: token.AccessToken,
		Endpoint:    *argGCRURL}, nil
}

func (c *controller) getECRAuthorizationKey() (AuthToken, error) {
	params := &ecr.GetAuthorizationTokenInput{
		RegistryIds: []*string{
			aws.String(awsAccountID),
		},
	}

	resp, err := c.ecrClient.GetAuthorizationToken(params)

	if err != nil {
		// Print the error, cast err to awserr.Error to get the Code and
		// Message from an error.
		fmt.Println(err.Error())
		return AuthToken{}, err
	}

	token := resp.AuthorizationData[0]

	return AuthToken{
		AccessToken: *token.AuthorizationToken,
		Endpoint:    *token.ProxyEndpoint}, err
}

func generateSecretObj(token string, endpoint string, isJSONCfg bool, secretName string) *api.Secret {
	secret := &api.Secret{
		ObjectMeta: api.ObjectMeta{
			Name: secretName,
		},
	}
	if isJSONCfg {
		secret.Data = map[string][]byte{
			".dockerconfigjson": []byte(fmt.Sprintf(dockerJSONTemplate, endpoint, token))}
		secret.Type = "kubernetes.io/dockerconfigjson"
	} else {
		secret.Data = map[string][]byte{
			".dockercfg": []byte(fmt.Sprintf(dockerCfgTemplate, endpoint, token))}
		secret.Type = "kubernetes.io/dockercfg"
	}
	return secret
}

type AuthToken struct {
	AccessToken string
	Endpoint    string
}

type SecretGenerator struct {
	TokenGenFxn func() (AuthToken, error)
	IsJSONCfg   bool
	SecretName  string
}

func getSecretGenerators(c *controller) []SecretGenerator {
	secretGenerators := []SecretGenerator{}

	if c.config.gcrEnabled {
		secretGenerators = append(secretGenerators, SecretGenerator{
			TokenGenFxn: c.getGCRAuthorizationKey,
			IsJSONCfg:   false,
			SecretName:  *argGCRSecretName,
		})
	}

	if c.config.ecrEnabled {
		secretGenerators = append(secretGenerators, SecretGenerator{
			TokenGenFxn: c.getECRAuthorizationKey,
			IsJSONCfg:   true,
			SecretName:  *argAWSSecretName,
		})
	}

	return secretGenerators
}

func (c *controller) process() error {
	secretGenerators := getSecretGenerators(c)

	for _, secretGenerator := range secretGenerators {
		newToken, err := secretGenerator.TokenGenFxn()
		if err != nil {
			return err
		}
		newSecret := generateSecretObj(newToken.AccessToken, newToken.Endpoint, secretGenerator.IsJSONCfg, secretGenerator.SecretName)

		// Get all namespaces
		namespaces, err := c.kubeClient.Namespaces().List(api.ListOptions{})
		if err != nil {
			return err
		}

		for _, namespace := range namespaces.Items {

			if namespace.GetName() == "kube-system" {
				continue
			}

			// Check if the secret exists for the namespace
			_, err := c.kubeClient.Secrets(namespace.GetName()).Get(secretGenerator.SecretName)

			if err != nil {
				// Secret not found, create
				_, err := c.kubeClient.Secrets(namespace.GetName()).Create(newSecret)
				if err != nil {
					return err
				}
			} else {
				// Existing secret needs updated
				_, err := c.kubeClient.Secrets(namespace.GetName()).Update(newSecret)
				if err != nil {
					return err
				}
			}

			// Check if ServiceAccount exists
			serviceAccount, err := c.kubeClient.ServiceAccounts(namespace.GetName()).Get("default")

			if err != nil {
				return err
			}

			// Update existing one if image pull secrets already exists for aws ecr token
			imagePullSecretFound := false
			for i, imagePullSecret := range serviceAccount.ImagePullSecrets {
				if imagePullSecret.Name == secretGenerator.SecretName {
					serviceAccount.ImagePullSecrets[i] = api.LocalObjectReference{Name: secretGenerator.SecretName}
					imagePullSecretFound = true
					break
				}
			}

			// Append to list of existing service accounts if there isn't one already
			if !imagePullSecretFound {
				serviceAccount.ImagePullSecrets = append(serviceAccount.ImagePullSecrets, api.LocalObjectReference{Name: secretGenerator.SecretName})
			}

			_, err = c.kubeClient.ServiceAccounts(namespace.GetName()).Update(serviceAccount)
			if err != nil {
				return err
			}
		}
		log.Print("Finished processing secret for: ", secretGenerator.SecretName)
	}

	return nil
}

func validateParams() providerConfig {
	var gcrEnabled bool
	var ecrEnabled bool

	awsAccountID = os.Getenv("awsaccount")
	if len(awsAccountID) == 0 {
		log.Print("Missing awsaccount env variable, assuming GCR usage")
		gcrEnabled = true
		ecrEnabled = false
	} else {
		gcrEnabled = false
		ecrEnabled = true
	}

	awsRegionEnv := os.Getenv("awsregion")

	if len(awsRegionEnv) > 0 {
		argAWSRegion = &awsRegionEnv
	}

	return providerConfig{gcrEnabled, ecrEnabled}
}

func main() {
	log.Print("Starting up...")
	flags.Parse(os.Args)

	config := validateParams()

	log.Print("Using AWS Account: ", awsAccountID)
	log.Printf("Using AWS Region: %s", *argAWSRegion)
	log.Print("Refresh Interval (minutes): ", *argRefreshMinutes)

	kubeClient := newKubeClient()
	ecrClient := newEcrClient()
	gcrClient := newGcrClient()
	c := &controller{kubeClient, ecrClient, gcrClient, config}

	tick := time.Tick(time.Duration(*argRefreshMinutes) * time.Minute)

	// Process once now, then wait for tick
	c.process()

	for {
		select {
		case <-tick:
			log.Print("Refreshing credentials...")
			if err := c.process(); err != nil {
				log.Fatalf("Failed to load ecr credentials: %v", err)
			}
		}
	}

}
<commit_msg>fix struct ordering<commit_after>/*
Copyright (c) 2016, UPMC Enterprises
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name UPMC Enterprises nor the
      names of its contributors may be used to endorse or promote products
      derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL UPMC ENTERPRISES BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
*/

package main

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

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/ecr"
	flag "github.com/spf13/pflag"
	"golang.org/x/net/context"
	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"
	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/client/restclient"
	"k8s.io/kubernetes/pkg/client/unversioned"
	kubectl_util "k8s.io/kubernetes/pkg/kubectl/cmd/util"
)

const (
	dockerCfgTemplate  = `{"%s":{"username":"oauth2accesstoken","password":"%s","email":"none"}}`
	dockerJSONTemplate = `{"auths":{"%s":{"auth":"%s","email":"none"}}}`
)

var (
	flags               = flag.NewFlagSet("", flag.ContinueOnError)
	cluster             = flags.Bool("use-kubernetes-cluster-service", true, `If true, use the built in kubernetes cluster for creating the client`)
	argKubecfgFile      = flags.String("kubecfg-file", "", `Location of kubecfg file for access to kubernetes master service; --kube_master_url overrides the URL part of this; if neither this nor --kube_master_url are provided, defaults to service account tokens`)
	argKubeMasterURL    = flags.String("kube-master-url", "", `URL to reach kubernetes master. Env variables in this flag will be expanded.`)
	argAWSSecretName    = flags.String("aws-secret-name", "awsecr-cred", `Default aws secret name`)
	argGCRSecretName    = flags.String("gcr-secret-name", "gcr-secret", `Default gcr secret name`)
	argDefaultNamespace = flags.String("default-namespace", "default", `Default namespace`)
	argGCRURL           = flags.String("gcr-url", "https://gcr.io", `Default GCR URL`)
	argAWSRegion        = flags.String("aws-region", "us-east-1", `Default AWS region`)
	argRefreshMinutes   = flags.Int("refresh-mins", 60, `Default time to wait before refreshing (60 minutes)`)
)

var (
	awsAccountID string
)

type controller struct {
	kubeClient kubeInterface
	ecrClient  ecrInterface
	gcrClient  gcrInterface
	config     providerConfig
}

type providerConfig struct {
	ecrEnabled bool
	gcrEnabled bool
}

type kubeInterface interface {
	Secrets(namespace string) unversioned.SecretsInterface
	Namespaces() unversioned.NamespaceInterface
	ServiceAccounts(namespace string) unversioned.ServiceAccountsInterface
}

type ecrInterface interface {
	GetAuthorizationToken(input *ecr.GetAuthorizationTokenInput) (*ecr.GetAuthorizationTokenOutput, error)
}

type gcrInterface interface {
	DefaultTokenSource(ctx context.Context, scope ...string) (oauth2.TokenSource, error)
}

func newEcrClient() ecrInterface {
	return ecr.New(session.New(), aws.NewConfig().WithRegion(*argAWSRegion))
}

type gcrClient struct{}

func (gcr gcrClient) DefaultTokenSource(ctx context.Context, scope ...string) (oauth2.TokenSource, error) {
	return google.DefaultTokenSource(ctx, scope...)
}

func newGcrClient() gcrInterface {
	return gcrClient{}
}

func newKubeClient() kubeInterface {
	var kubeClient *unversioned.Client
	var config *restclient.Config
	var err error

	clientConfig := kubectl_util.DefaultClientConfig(flags)

	if *cluster {
		if kubeClient, err = unversioned.NewInCluster(); err != nil {
			log.Fatalf("Failed to create client: %v", err)
		}
	} else {
		config, err = clientConfig.ClientConfig()
		if err != nil {
			log.Fatalf("error connecting to the client: %v", err)
		}
		kubeClient, err = unversioned.New(config)

		if err != nil {
			log.Fatalf("Failed to create client: %v", err)
		}
	}

	return kubeClient
}

func (c *controller) getGCRAuthorizationKey() (AuthToken, error) {
	ts, err := c.gcrClient.DefaultTokenSource(context.TODO(), "https://www.googleapis.com/auth/cloud-platform")
	if err != nil {
		return AuthToken{}, err
	}

	token, err := ts.Token()
	if err != nil {
		return AuthToken{}, err
	}

	if !token.Valid() {
		return AuthToken{}, fmt.Errorf("token was invalid")
	}

	if token.Type() != "Bearer" {
		return AuthToken{}, fmt.Errorf(fmt.Sprintf("expected token type \"Bearer\" but got \"%s\"", token.Type()))
	}

	return AuthToken{
		AccessToken: token.AccessToken,
		Endpoint:    *argGCRURL}, nil
}

func (c *controller) getECRAuthorizationKey() (AuthToken, error) {
	params := &ecr.GetAuthorizationTokenInput{
		RegistryIds: []*string{
			aws.String(awsAccountID),
		},
	}

	resp, err := c.ecrClient.GetAuthorizationToken(params)

	if err != nil {
		// Print the error, cast err to awserr.Error to get the Code and
		// Message from an error.
		fmt.Println(err.Error())
		return AuthToken{}, err
	}

	token := resp.AuthorizationData[0]

	return AuthToken{
		AccessToken: *token.AuthorizationToken,
		Endpoint:    *token.ProxyEndpoint}, err
}

func generateSecretObj(token string, endpoint string, isJSONCfg bool, secretName string) *api.Secret {
	secret := &api.Secret{
		ObjectMeta: api.ObjectMeta{
			Name: secretName,
		},
	}
	if isJSONCfg {
		secret.Data = map[string][]byte{
			".dockerconfigjson": []byte(fmt.Sprintf(dockerJSONTemplate, endpoint, token))}
		secret.Type = "kubernetes.io/dockerconfigjson"
	} else {
		secret.Data = map[string][]byte{
			".dockercfg": []byte(fmt.Sprintf(dockerCfgTemplate, endpoint, token))}
		secret.Type = "kubernetes.io/dockercfg"
	}
	return secret
}

type AuthToken struct {
	AccessToken string
	Endpoint    string
}

type SecretGenerator struct {
	TokenGenFxn func() (AuthToken, error)
	IsJSONCfg   bool
	SecretName  string
}

func getSecretGenerators(c *controller) []SecretGenerator {
	secretGenerators := []SecretGenerator{}

	if c.config.gcrEnabled {
		secretGenerators = append(secretGenerators, SecretGenerator{
			TokenGenFxn: c.getGCRAuthorizationKey,
			IsJSONCfg:   false,
			SecretName:  *argGCRSecretName,
		})
	}

	if c.config.ecrEnabled {
		secretGenerators = append(secretGenerators, SecretGenerator{
			TokenGenFxn: c.getECRAuthorizationKey,
			IsJSONCfg:   true,
			SecretName:  *argAWSSecretName,
		})
	}

	return secretGenerators
}

func (c *controller) process() error {
	secretGenerators := getSecretGenerators(c)

	for _, secretGenerator := range secretGenerators {
		newToken, err := secretGenerator.TokenGenFxn()
		if err != nil {
			return err
		}
		newSecret := generateSecretObj(newToken.AccessToken, newToken.Endpoint, secretGenerator.IsJSONCfg, secretGenerator.SecretName)

		// Get all namespaces
		namespaces, err := c.kubeClient.Namespaces().List(api.ListOptions{})
		if err != nil {
			return err
		}

		for _, namespace := range namespaces.Items {

			if namespace.GetName() == "kube-system" {
				continue
			}

			// Check if the secret exists for the namespace
			_, err := c.kubeClient.Secrets(namespace.GetName()).Get(secretGenerator.SecretName)

			if err != nil {
				// Secret not found, create
				_, err := c.kubeClient.Secrets(namespace.GetName()).Create(newSecret)
				if err != nil {
					return err
				}
			} else {
				// Existing secret needs updated
				_, err := c.kubeClient.Secrets(namespace.GetName()).Update(newSecret)
				if err != nil {
					return err
				}
			}

			// Check if ServiceAccount exists
			serviceAccount, err := c.kubeClient.ServiceAccounts(namespace.GetName()).Get("default")

			if err != nil {
				return err
			}

			// Update existing one if image pull secrets already exists for aws ecr token
			imagePullSecretFound := false
			for i, imagePullSecret := range serviceAccount.ImagePullSecrets {
				if imagePullSecret.Name == secretGenerator.SecretName {
					serviceAccount.ImagePullSecrets[i] = api.LocalObjectReference{Name: secretGenerator.SecretName}
					imagePullSecretFound = true
					break
				}
			}

			// Append to list of existing service accounts if there isn't one already
			if !imagePullSecretFound {
				serviceAccount.ImagePullSecrets = append(serviceAccount.ImagePullSecrets, api.LocalObjectReference{Name: secretGenerator.SecretName})
			}

			_, err = c.kubeClient.ServiceAccounts(namespace.GetName()).Update(serviceAccount)
			if err != nil {
				return err
			}
		}
		log.Print("Finished processing secret for: ", secretGenerator.SecretName)
	}

	return nil
}

func validateParams() providerConfig {
	var gcrEnabled bool
	var ecrEnabled bool

	awsAccountID = os.Getenv("awsaccount")
	if len(awsAccountID) == 0 {
		log.Print("Missing awsaccount env variable, assuming GCR usage")
		gcrEnabled = true
		ecrEnabled = false
	} else {
		gcrEnabled = false
		ecrEnabled = true
	}

	awsRegionEnv := os.Getenv("awsregion")

	if len(awsRegionEnv) > 0 {
		argAWSRegion = &awsRegionEnv
	}

	return providerConfig{ecrEnabled, gcrEnabled}
}

func main() {
	log.Print("Starting up...")
	flags.Parse(os.Args)

	config := validateParams()

	log.Print("Using AWS Account: ", awsAccountID)
	log.Printf("Using AWS Region: %s", *argAWSRegion)
	log.Print("Refresh Interval (minutes): ", *argRefreshMinutes)

	kubeClient := newKubeClient()
	ecrClient := newEcrClient()
	gcrClient := newGcrClient()
	c := &controller{kubeClient, ecrClient, gcrClient, config}

	tick := time.Tick(time.Duration(*argRefreshMinutes) * time.Minute)

	// Process once now, then wait for tick
	c.process()

	for {
		select {
		case <-tick:
			log.Print("Refreshing credentials...")
			if err := c.process(); err != nil {
				log.Fatalf("Failed to load ecr credentials: %v", err)
			}
		}
	}

}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"net/url"
	"os"
	"strconv"
	"strings"

	"github.com/cloudfoundry-incubator/routing-api"
	"github.com/cloudfoundry-incubator/routing-api-cli/commands"
	"github.com/cloudfoundry-incubator/routing-api/db"
	trace "github.com/cloudfoundry-incubator/trace-logger"
	token_fetcher "github.com/cloudfoundry-incubator/uaa-token-fetcher"
	"github.com/codegangsta/cli"
)

const RTR_TRACE = "RTR_TRACE"

var flags = []cli.Flag{
	cli.StringFlag{
		Name:  "api",
		Usage: "Endpoint for the routing-api. (required)",
	},
	cli.StringFlag{
		Name:  "client-id",
		Usage: "Id of the OAuth client. (required)",
	},
	cli.StringFlag{
		Name:  "client-secret",
		Usage: "Secret for OAuth client. (required)",
	},
	cli.StringFlag{
		Name:  "oauth-url",
		Usage: "URL for OAuth client. (required)",
	},
}

var cliCommands = []cli.Command{
	{
		Name:  "register",
		Usage: "Registers routes with the routing-api",
		Description: `Routes must be specified in JSON format, like so:
'[{"route":"foo.com", "port":12345, "ip":"1.2.3.4", "ttl":5, "log_guid":"log-guid"}]'`,
		Action: registerRoutes,
		Flags:  flags,
	},
	{
		Name:  "unregister",
		Usage: "Unregisters routes with the routing-api",
		Description: `Routes must be specified in JSON format, like so:
'[{"route":"foo.com", "port":12345, "ip":"1.2.3.4"]'`,
		Action: unregisterRoutes,
		Flags:  flags,
	},
	{
		Name:   "list",
		Usage:  "Lists the currently registered routes",
		Action: listRoutes,
		Flags:  flags,
	},
	{
		Name:   "events",
		Usage:  "Stream events from the Routing API",
		Action: streamEvents,
		Flags:  flags,
	},
}

var environmentVariableHelp = `ENVIRONMENT VARIABLES:
   RTR_TRACE=true	Print API request diagnostics to stdout`

func main() {
	fmt.Println()
	app := cli.NewApp()
	app.Name = "rtr"
	app.Usage = "A CLI for the Router API server."
	authors := []cli.Author{cli.Author{Name: "Cloud Foundry Routing Team", Email: "cf-dev@lists.cloudfoundry.org"}}
	app.Authors = authors
	app.Commands = cliCommands
	app.CommandNotFound = commandNotFound
	app.Version = "2.0.0"

	cli.AppHelpTemplate = cli.AppHelpTemplate + environmentVariableHelp + "\n"

	trace.NewLogger(os.Getenv(RTR_TRACE))

	app.Run(os.Args)
	os.Exit(0)
}

func registerRoutes(c *cli.Context) {
	issues := checkFlags(c)
	issues = append(issues, checkArguments(c, "register")...)

	if len(issues) > 0 {
		printHelpForCommand(c, issues, "register")
	}

	client := routing_api.NewClient(c.String("api"))

	config := buildOauthConfig(c)
	fetcher := token_fetcher.NewTokenFetcher(&config)

	desiredRoutes := c.Args().First()
	var routes []db.Route

	err := json.Unmarshal([]byte(desiredRoutes), &routes)
	if err != nil {
		fmt.Println("Invalid json format.")
		os.Exit(3)
	}

	err = commands.Register(client, fetcher, routes)
	if err != nil {
		fmt.Println("route registration failed:", err)
		os.Exit(3)
	}

	fmt.Printf("Successfully registered routes: %s\n", desiredRoutes)
}

func unregisterRoutes(c *cli.Context) {
	issues := checkFlags(c)
	issues = append(issues, checkArguments(c, "unregister")...)

	if len(issues) > 0 {
		printHelpForCommand(c, issues, "unregister")
	}

	client := routing_api.NewClient(c.String("api"))

	config := buildOauthConfig(c)
	fetcher := token_fetcher.NewTokenFetcher(&config)

	desiredRoutes := c.Args().First()
	var routes []db.Route
	err := json.Unmarshal([]byte(desiredRoutes), &routes)
	if err != nil {
		fmt.Println("Invalid json format.")
		os.Exit(3)
	}

	err = commands.UnRegister(client, fetcher, routes)
	if err != nil {
		fmt.Println("route unregistration failed:", err)
		os.Exit(3)
	}

	fmt.Printf("Successfully unregistered routes: %s\n", desiredRoutes)
}

func listRoutes(c *cli.Context) {
	issues := checkFlags(c)
	issues = append(issues, checkArguments(c, "list")...)

	if len(issues) > 0 {
		printHelpForCommand(c, issues, "list")
	}

	client := routing_api.NewClient(c.String("api"))

	config := buildOauthConfig(c)
	fetcher := token_fetcher.NewTokenFetcher(&config)
	routes, err := commands.List(client, fetcher)
	if err != nil {
		fmt.Println("listing routes failed:", err)
		os.Exit(3)
	}

	prettyRoutes, _ := json.Marshal(routes)

	fmt.Printf("%v\n", string(prettyRoutes))
}

func streamEvents(c *cli.Context) {
	issues := checkFlags(c)
	issues = append(issues, checkArguments(c, "events")...)

	if len(issues) > 0 {
		printHelpForCommand(c, issues, "events")
	}

	client := routing_api.NewClient(c.String("api"))

	config := buildOauthConfig(c)
	fetcher := token_fetcher.NewTokenFetcher(&config)
	eventSource, err := commands.Events(client, fetcher)
	if err != nil {
		fmt.Println("streaming events failed:", err)
		os.Exit(3)
	}

	for {
		e, err := eventSource.Next()
		if err != nil {
			fmt.Fprintf(os.Stderr, "Connection closed: %s", err.Error())
			break
		}

		event, _ := json.Marshal(e)
		fmt.Printf("%v\n", string(event))
	}
}

func buildOauthConfig(c *cli.Context) token_fetcher.OAuthConfig {
	var port int
	oauthUrl, _ := url.Parse(c.String("oauth-url"))
	addr := strings.Split(oauthUrl.Host, ":")
	host := addr[0]

	if len(addr) > 1 {
		port, _ = strconv.Atoi(addr[1])
	} else {
		if strings.ToLower(oauthUrl.Scheme) == "https" {
			port = 443
		} else if strings.ToLower(oauthUrl.Scheme) == "http" {
			port = 80
		}
	}

	return token_fetcher.OAuthConfig{
		TokenEndpoint: oauthUrl.Scheme + "://" + host,
		ClientName:    c.String("client-id"),
		ClientSecret:  c.String("client-secret"),
		Port:          port,
	}
}

func checkFlags(c *cli.Context) []string {
	var issues []string

	if c.String("api") == "" {
		issues = append(issues, "Must provide an API endpoint for the routing-api component.")
	}

	if c.String("client-id") == "" {
		issues = append(issues, "Must provide the id of an OAuth client.")
	}

	if c.String("client-secret") == "" {
		issues = append(issues, "Must provide an OAuth secret.")
	}

	if c.String("oauth-url") == "" {
		issues = append(issues, "Must provide an URL to the OAuth client.")
	}

	_, err := url.Parse(c.String("oauth-url"))
	if err != nil {
		issues = append(issues, "Invalid OAuth client URL")
	}

	return issues
}

func checkArguments(c *cli.Context, cmd string) []string {
	var issues []string

	switch cmd {
	case "register", "unregister":
		if len(c.Args()) > 1 {
			issues = append(issues, "Unexpected arguments.")
		} else if len(c.Args()) < 1 {
			issues = append(issues, "Must provide routes JSON.")
		}
	case "list", "events":
		if len(c.Args()) > 0 {
			issues = append(issues, "Unexpected arguments.")
		}
	}

	return issues
}

func printHelpForCommand(c *cli.Context, issues []string, cmd string) {
	for _, issue := range issues {
		fmt.Println(issue)
	}
	fmt.Println()
	cli.ShowCommandHelp(c, cmd)
	os.Exit(1)
}

func commandNotFound(c *cli.Context, cmd string) {
	fmt.Println("Not a valid command:", cmd)
	os.Exit(1)
}
<commit_msg>Bump CLI version to 2.1.0<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"net/url"
	"os"
	"strconv"
	"strings"

	"github.com/cloudfoundry-incubator/routing-api"
	"github.com/cloudfoundry-incubator/routing-api-cli/commands"
	"github.com/cloudfoundry-incubator/routing-api/db"
	trace "github.com/cloudfoundry-incubator/trace-logger"
	token_fetcher "github.com/cloudfoundry-incubator/uaa-token-fetcher"
	"github.com/codegangsta/cli"
)

const RTR_TRACE = "RTR_TRACE"

var flags = []cli.Flag{
	cli.StringFlag{
		Name:  "api",
		Usage: "Endpoint for the routing-api. (required)",
	},
	cli.StringFlag{
		Name:  "client-id",
		Usage: "Id of the OAuth client. (required)",
	},
	cli.StringFlag{
		Name:  "client-secret",
		Usage: "Secret for OAuth client. (required)",
	},
	cli.StringFlag{
		Name:  "oauth-url",
		Usage: "URL for OAuth client. (required)",
	},
}

var cliCommands = []cli.Command{
	{
		Name:  "register",
		Usage: "Registers routes with the routing-api",
		Description: `Routes must be specified in JSON format, like so:
'[{"route":"foo.com", "port":12345, "ip":"1.2.3.4", "ttl":5, "log_guid":"log-guid"}]'`,
		Action: registerRoutes,
		Flags:  flags,
	},
	{
		Name:  "unregister",
		Usage: "Unregisters routes with the routing-api",
		Description: `Routes must be specified in JSON format, like so:
'[{"route":"foo.com", "port":12345, "ip":"1.2.3.4"]'`,
		Action: unregisterRoutes,
		Flags:  flags,
	},
	{
		Name:   "list",
		Usage:  "Lists the currently registered routes",
		Action: listRoutes,
		Flags:  flags,
	},
	{
		Name:   "events",
		Usage:  "Stream events from the Routing API",
		Action: streamEvents,
		Flags:  flags,
	},
}

var environmentVariableHelp = `ENVIRONMENT VARIABLES:
   RTR_TRACE=true	Print API request diagnostics to stdout`

func main() {
	fmt.Println()
	app := cli.NewApp()
	app.Name = "rtr"
	app.Usage = "A CLI for the Router API server."
	authors := []cli.Author{cli.Author{Name: "Cloud Foundry Routing Team", Email: "cf-dev@lists.cloudfoundry.org"}}
	app.Authors = authors
	app.Commands = cliCommands
	app.CommandNotFound = commandNotFound
	app.Version = "2.1.0"

	cli.AppHelpTemplate = cli.AppHelpTemplate + environmentVariableHelp + "\n"

	trace.NewLogger(os.Getenv(RTR_TRACE))

	app.Run(os.Args)
	os.Exit(0)
}

func registerRoutes(c *cli.Context) {
	issues := checkFlags(c)
	issues = append(issues, checkArguments(c, "register")...)

	if len(issues) > 0 {
		printHelpForCommand(c, issues, "register")
	}

	client := routing_api.NewClient(c.String("api"))

	config := buildOauthConfig(c)
	fetcher := token_fetcher.NewTokenFetcher(&config)

	desiredRoutes := c.Args().First()
	var routes []db.Route

	err := json.Unmarshal([]byte(desiredRoutes), &routes)
	if err != nil {
		fmt.Println("Invalid json format.")
		os.Exit(3)
	}

	err = commands.Register(client, fetcher, routes)
	if err != nil {
		fmt.Println("route registration failed:", err)
		os.Exit(3)
	}

	fmt.Printf("Successfully registered routes: %s\n", desiredRoutes)
}

func unregisterRoutes(c *cli.Context) {
	issues := checkFlags(c)
	issues = append(issues, checkArguments(c, "unregister")...)

	if len(issues) > 0 {
		printHelpForCommand(c, issues, "unregister")
	}

	client := routing_api.NewClient(c.String("api"))

	config := buildOauthConfig(c)
	fetcher := token_fetcher.NewTokenFetcher(&config)

	desiredRoutes := c.Args().First()
	var routes []db.Route
	err := json.Unmarshal([]byte(desiredRoutes), &routes)
	if err != nil {
		fmt.Println("Invalid json format.")
		os.Exit(3)
	}

	err = commands.UnRegister(client, fetcher, routes)
	if err != nil {
		fmt.Println("route unregistration failed:", err)
		os.Exit(3)
	}

	fmt.Printf("Successfully unregistered routes: %s\n", desiredRoutes)
}

func listRoutes(c *cli.Context) {
	issues := checkFlags(c)
	issues = append(issues, checkArguments(c, "list")...)

	if len(issues) > 0 {
		printHelpForCommand(c, issues, "list")
	}

	client := routing_api.NewClient(c.String("api"))

	config := buildOauthConfig(c)
	fetcher := token_fetcher.NewTokenFetcher(&config)
	routes, err := commands.List(client, fetcher)
	if err != nil {
		fmt.Println("listing routes failed:", err)
		os.Exit(3)
	}

	prettyRoutes, _ := json.Marshal(routes)

	fmt.Printf("%v\n", string(prettyRoutes))
}

func streamEvents(c *cli.Context) {
	issues := checkFlags(c)
	issues = append(issues, checkArguments(c, "events")...)

	if len(issues) > 0 {
		printHelpForCommand(c, issues, "events")
	}

	client := routing_api.NewClient(c.String("api"))

	config := buildOauthConfig(c)
	fetcher := token_fetcher.NewTokenFetcher(&config)
	eventSource, err := commands.Events(client, fetcher)
	if err != nil {
		fmt.Println("streaming events failed:", err)
		os.Exit(3)
	}

	for {
		e, err := eventSource.Next()
		if err != nil {
			fmt.Fprintf(os.Stderr, "Connection closed: %s", err.Error())
			break
		}

		event, _ := json.Marshal(e)
		fmt.Printf("%v\n", string(event))
	}
}

func buildOauthConfig(c *cli.Context) token_fetcher.OAuthConfig {
	var port int
	oauthUrl, _ := url.Parse(c.String("oauth-url"))
	addr := strings.Split(oauthUrl.Host, ":")
	host := addr[0]

	if len(addr) > 1 {
		port, _ = strconv.Atoi(addr[1])
	} else {
		if strings.ToLower(oauthUrl.Scheme) == "https" {
			port = 443
		} else if strings.ToLower(oauthUrl.Scheme) == "http" {
			port = 80
		}
	}

	return token_fetcher.OAuthConfig{
		TokenEndpoint: oauthUrl.Scheme + "://" + host,
		ClientName:    c.String("client-id"),
		ClientSecret:  c.String("client-secret"),
		Port:          port,
	}
}

func checkFlags(c *cli.Context) []string {
	var issues []string

	if c.String("api") == "" {
		issues = append(issues, "Must provide an API endpoint for the routing-api component.")
	}

	if c.String("client-id") == "" {
		issues = append(issues, "Must provide the id of an OAuth client.")
	}

	if c.String("client-secret") == "" {
		issues = append(issues, "Must provide an OAuth secret.")
	}

	if c.String("oauth-url") == "" {
		issues = append(issues, "Must provide an URL to the OAuth client.")
	}

	_, err := url.Parse(c.String("oauth-url"))
	if err != nil {
		issues = append(issues, "Invalid OAuth client URL")
	}

	return issues
}

func checkArguments(c *cli.Context, cmd string) []string {
	var issues []string

	switch cmd {
	case "register", "unregister":
		if len(c.Args()) > 1 {
			issues = append(issues, "Unexpected arguments.")
		} else if len(c.Args()) < 1 {
			issues = append(issues, "Must provide routes JSON.")
		}
	case "list", "events":
		if len(c.Args()) > 0 {
			issues = append(issues, "Unexpected arguments.")
		}
	}

	return issues
}

func printHelpForCommand(c *cli.Context, issues []string, cmd string) {
	for _, issue := range issues {
		fmt.Println(issue)
	}
	fmt.Println()
	cli.ShowCommandHelp(c, cmd)
	os.Exit(1)
}

func commandNotFound(c *cli.Context, cmd string) {
	fmt.Println("Not a valid command:", cmd)
	os.Exit(1)
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"os/signal"

	"github.com/tinzenite/encrypted"
	"github.com/tinzenite/shared"
)

func main() {
	log.Println("Starting server.")
	// define required flags
	var path string
	flag.StringVar(&path, "path", "temp", "File directory path in which to run the server.")
	// parse flags
	flag.Parse()
	// TODO check & ask whether to create if none currently exists (also add flag for this?)
	// TODO if path wasn't given, ask for it (see shared code from tinzenite/tin)
	log.Println("Path:", path)

	if exists, _ := shared.DirectoryExists(path); !exists {
		shared.MakeDirectory(path)
	}

	enc, err := encrypted.Create(path, "d_server")
	if err != nil {
		log.Println("Server: failed to create:", err)
		return
	}
	// print important info
	address, _ := enc.Address()
	fmt.Printf("Running peer <%s>.\nID: %s\n", enc.Name(), address)
	// prepare quitting via ctrl-c
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)
	// loop until close
	for {
		select {
		case <-c:
			log.Println("Server: quitting.")
			return
		} // select
	} // for
}
<commit_msg>update<commit_after>package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"os/signal"

	"github.com/tinzenite/encrypted"
	"github.com/tinzenite/shared"
)

func main() {
	log.Println("Starting server.")
	// define required flags
	var path string
	flag.StringVar(&path, "path", "temp", "File directory path in which to run the server.")
	// parse flags
	flag.Parse()
	// TODO check & ask whether to create if none currently exists (also add flag for this?)
	// TODO if path wasn't given, ask for it (see shared code from tinzenite/tin)
	log.Println("Path:", path)

	if exists, _ := shared.DirectoryExists(path); !exists {
		shared.MakeDirectory(path)
	}

	enc, err := encrypted.Create(path, "d_server")
	if err != nil {
		log.Println("Server: failed to create:", err)
		return
	}
	// print important info
	address, _ := enc.Address()
	fmt.Printf("Running server <%s>.\nID: %s\n", enc.Name(), address)
	// prepare quitting via ctrl-c
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)
	// loop until close
	for {
		select {
		case <-c:
			enc.Close()
			log.Println("Server: quitting.")
			return
		} // select
	} // for
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	stdlog "log"
	"net/http"
	"os"
	"runtime"

	"github.com/alecthomas/kingpin"

	"github.com/rs/zerolog"
	"github.com/rs/zerolog/log"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
)

var (
	version   string
	branch    string
	revision  string
	buildDate string
	goVersion = runtime.Version()
)

var (
	// flags
	prometheusMetricsAddress = kingpin.Flag("metrics-listen-address", "The address to listen on for Prometheus metrics requests.").Default(":9001").String()
	prometheusMetricsPath    = kingpin.Flag("metrics-path", "The path to listen for Prometheus metrics requests.").Default("/metrics").String()

	apiAddress = kingpin.Flag("api-listen-address", "The address to listen on for api HTTP requests.").Default(":5000").String()

	githubAppPrivateKeyPath    = kingpin.Flag("github-app-privatey-key-path", "The path to the pem file for the private key of the Github App.").Default("/github-app-key/private-key.pem").String()
	githubAppID                = kingpin.Flag("github-app-id", "The Github App id.").Envar("GITHUB_APP_ID").String()
	githubAppOAuthClientID     = kingpin.Flag("github-app-oauth-client-id", "The OAuth client id for the Github App.").Envar("GITHUB_APP_OAUTH_CLIENT_ID").String()
	githubAppOAuthClientSecret = kingpin.Flag("github-app-oauth-client-secret", "The OAuth client secret for the Github App.").Envar("GITHUB_APP_OAUTH_CLIENT_SECRET").String()

	bitbucketAPIKey         = kingpin.Flag("bitbucket-api-key", "The api key for Bitbucket.").Envar("BITBUCKET_API_KEY").String()
	bitbucketAppOAuthKey    = kingpin.Flag("bitbucket-app-oauth-key", "The OAuth key for the Bitbucket App.").Envar("BITBUCKET_APP_OAUTH_KEY").String()
	bitbucketAppOAuthSecret = kingpin.Flag("bitbucket-app-oauth-secret", "The OAuth secret for the Bitbucket App.").Envar("BITBUCKET_APP_OAUTH_SECRET").String()

	estafetteCiBuilderVersion = kingpin.Flag("estafette-ci-builder-version", "The version of estafette/estafette-ci-builder to use.").Envar("ESTAFETTE_CI_BUILDER_VERSION").String()

	// define prometheus counter
	webhookTotal = prometheus.NewCounterVec(
		prometheus.CounterOpts{
			Name: "estafette_ci_api_webhook_totals",
			Help: "Total of received webhooks.",
		},
		[]string{"event", "source"},
	)

	outgoingAPIRequestTotal = prometheus.NewCounterVec(
		prometheus.CounterOpts{
			Name: "estafette_ci_api_outgoing_api_request_totals",
			Help: "Total of outgoing api calls.",
		},
		[]string{"target"},
	)
)

func init() {
	// Metrics have to be registered to be exposed:
	prometheus.MustRegister(webhookTotal)
	prometheus.MustRegister(outgoingAPIRequestTotal)
}

func main() {

	// parse command line parameters
	kingpin.Parse()

	// log as severity for stackdriver logging to recognize the level
	zerolog.LevelFieldName = "severity"

	// set some default fields added to all logs
	log.Logger = zerolog.New(os.Stdout).With().
		Timestamp().
		Str("app", "estafette-ci-api").
		Str("version", version).
		Logger()

	// use zerolog for any logs sent via standard log library
	stdlog.SetFlags(0)
	stdlog.SetOutput(log.Logger)

	// log startup message
	log.Info().
		Str("branch", branch).
		Str("revision", revision).
		Str("buildDate", buildDate).
		Str("goVersion", goVersion).
		Msg("Starting estafette-ci-api...")

	// start prometheus
	go func() {
		log.Debug().
			Str("port", *prometheusMetricsAddress).
			Str("path", *prometheusMetricsPath).
			Msg("Serving Prometheus metrics...")

		http.Handle(*prometheusMetricsPath, promhttp.Handler())

		if err := http.ListenAndServe(*prometheusMetricsAddress, nil); err != nil {
			log.Fatal().Err(err).Msg("Starting Prometheus listener failed")
		}
	}()

	log.Debug().
		Str("port", *apiAddress).
		Msg("Serving api calls...")

	http.HandleFunc("/webhook/github", githubWebhookHandler)
	http.HandleFunc("/webhook/bitbucket", bitbucketWebhookHandler)
	http.HandleFunc("/liveness", livenessHandler)
	http.HandleFunc("/readiness", readinessHandler)

	if err := http.ListenAndServe(*apiAddress, nil); err != nil {
		log.Fatal().Err(err).Msg("Starting api listener failed")
	}
}

func livenessHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "I'm alive!")
}

func readinessHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "I'm ready!")
}
<commit_msg>use http connection draining<commit_after>package main

import (
	"context"
	"fmt"
	stdlog "log"
	"net/http"
	"os"
	"os/signal"
	"runtime"
	"syscall"
	"time"

	"github.com/alecthomas/kingpin"

	"github.com/rs/zerolog"
	"github.com/rs/zerolog/log"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
)

var (
	version   string
	branch    string
	revision  string
	buildDate string
	goVersion = runtime.Version()
)

var (
	// flags
	prometheusMetricsAddress = kingpin.Flag("metrics-listen-address", "The address to listen on for Prometheus metrics requests.").Default(":9001").String()
	prometheusMetricsPath    = kingpin.Flag("metrics-path", "The path to listen for Prometheus metrics requests.").Default("/metrics").String()

	apiAddress = kingpin.Flag("api-listen-address", "The address to listen on for api HTTP requests.").Default(":5000").String()

	githubAppPrivateKeyPath    = kingpin.Flag("github-app-privatey-key-path", "The path to the pem file for the private key of the Github App.").Default("/github-app-key/private-key.pem").String()
	githubAppID                = kingpin.Flag("github-app-id", "The Github App id.").Envar("GITHUB_APP_ID").String()
	githubAppOAuthClientID     = kingpin.Flag("github-app-oauth-client-id", "The OAuth client id for the Github App.").Envar("GITHUB_APP_OAUTH_CLIENT_ID").String()
	githubAppOAuthClientSecret = kingpin.Flag("github-app-oauth-client-secret", "The OAuth client secret for the Github App.").Envar("GITHUB_APP_OAUTH_CLIENT_SECRET").String()

	bitbucketAPIKey         = kingpin.Flag("bitbucket-api-key", "The api key for Bitbucket.").Envar("BITBUCKET_API_KEY").String()
	bitbucketAppOAuthKey    = kingpin.Flag("bitbucket-app-oauth-key", "The OAuth key for the Bitbucket App.").Envar("BITBUCKET_APP_OAUTH_KEY").String()
	bitbucketAppOAuthSecret = kingpin.Flag("bitbucket-app-oauth-secret", "The OAuth secret for the Bitbucket App.").Envar("BITBUCKET_APP_OAUTH_SECRET").String()

	estafetteCiBuilderVersion = kingpin.Flag("estafette-ci-builder-version", "The version of estafette/estafette-ci-builder to use.").Envar("ESTAFETTE_CI_BUILDER_VERSION").String()

	// define prometheus counter
	webhookTotal = prometheus.NewCounterVec(
		prometheus.CounterOpts{
			Name: "estafette_ci_api_webhook_totals",
			Help: "Total of received webhooks.",
		},
		[]string{"event", "source"},
	)

	outgoingAPIRequestTotal = prometheus.NewCounterVec(
		prometheus.CounterOpts{
			Name: "estafette_ci_api_outgoing_api_request_totals",
			Help: "Total of outgoing api calls.",
		},
		[]string{"target"},
	)
)

func init() {
	// Metrics have to be registered to be exposed:
	prometheus.MustRegister(webhookTotal)
	prometheus.MustRegister(outgoingAPIRequestTotal)
}

func main() {

	// parse command line parameters
	kingpin.Parse()

	// log as severity for stackdriver logging to recognize the level
	zerolog.LevelFieldName = "severity"

	// set some default fields added to all logs
	log.Logger = zerolog.New(os.Stdout).With().
		Timestamp().
		Str("app", "estafette-ci-api").
		Str("version", version).
		Logger()

	// use zerolog for any logs sent via standard log library
	stdlog.SetFlags(0)
	stdlog.SetOutput(log.Logger)

	// log startup message
	log.Info().
		Str("branch", branch).
		Str("revision", revision).
		Str("buildDate", buildDate).
		Str("goVersion", goVersion).
		Msg("Starting estafette-ci-api...")

	// define channel and wait group to gracefully shutdown the application
	stopChan := make(chan os.Signal)
	signal.Notify(stopChan, syscall.SIGTERM, syscall.SIGINT)

	// start prometheus
	go func() {
		log.Debug().
			Str("port", *prometheusMetricsAddress).
			Str("path", *prometheusMetricsPath).
			Msg("Serving Prometheus metrics...")

		http.Handle(*prometheusMetricsPath, promhttp.Handler())

		if err := http.ListenAndServe(*prometheusMetricsAddress, nil); err != nil {
			log.Fatal().Err(err).Msg("Starting Prometheus listener failed")
		}
	}()

	log.Debug().
		Str("port", *apiAddress).
		Msg("Serving api calls...")

	srv := &http.Server{Addr: *apiAddress}

	http.HandleFunc("/webhook/github", githubWebhookHandler)
	http.HandleFunc("/webhook/bitbucket", bitbucketWebhookHandler)
	http.HandleFunc("/liveness", livenessHandler)
	http.HandleFunc("/readiness", readinessHandler)

	go func() {
		if err := srv.ListenAndServe(); err != nil {
			log.Fatal().Err(err).Msg("Starting api listener failed")
		}
	}()

	// wait for graceful shutdown to finish
	<-stopChan // wait for SIGINT
	log.Info().Msg("Shutting down server...")

	// shut down gracefully
	ctx, _ := context.WithTimeout(context.Background(), 30*time.Second)
	srv.Shutdown(ctx)

	log.Info().Msg("Server gracefully stopped")
}

func livenessHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "I'm alive!")
}

func readinessHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "I'm ready!")
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"path"
	"strings"

	"github.com/Sirupsen/logrus"
	"github.com/atotto/clipboard"
	"github.com/codegangsta/cli"

	"eriol.xyz/piken/format"
	"eriol.xyz/piken/sql"
)

const (
	unicodeDataUrl      = "http://www.unicode.org/Public/UNIDATA/UnicodeData.txt"
	pikenHome           = ".piken"
	defaultDatabaseFile = "piken.sqlite3"
	defaultDataFile     = "UnicodeData.txt"
	version             = "0.1a"
)

var (
	baseDir      = path.Join(getHome(), pikenHome)
	databaseFile = path.Join(baseDir, defaultDatabaseFile)
	dataFile     = path.Join(baseDir, defaultDataFile)
	store        sql.Store
)

func main() {

	app := cli.NewApp()
	app.Name = "piken"
	app.Version = version
	app.Author = "Daniele Tricoli"
	app.Email = "eriol@mornie.org"
	app.Usage = "unicode search tool"

	if _, err := os.Stat(baseDir); os.IsNotExist(err) {
		os.Mkdir(baseDir, 0755)
	}

	if err := store.Open(databaseFile); err != nil {
		logrus.Fatal(err)
	}
	defer store.Close()

	app.Commands = []cli.Command{
		{
			Name:  "update",
			Usage: "Update unicode data",
			Action: func(c *cli.Context) {
				modifiedTime, err := checkLastModified(unicodeDataUrl)
				if err != nil {
					logrus.Fatal(err)
				}

				lastUpdate, err := store.GetLastUpdate(defaultDataFile)
				if err != nil {
					logrus.Fatal(err)
				}

				if lastUpdate.Before(modifiedTime) {
					download(unicodeDataUrl, dataFile)

					records, err := readCsvFile(dataFile)
					if err != nil {
						logrus.Fatal(err)
					}

					if err := store.LoadFromRecords(records); err != nil {
						logrus.Fatal(err)
					}

					if err := store.CreateLastUpdate(defaultDataFile,
						modifiedTime); err != nil {
						logrus.Fatal(err)
					}
				} else {
					logrus.Info("Already up to date.")
				}

			},
		},
		{
			Name:    "search",
			Aliases: []string{"s"},
			Usage:   "Search for unicode",
			Flags: []cli.Flag{
				cli.BoolFlag{
					Name:  "copy, c",
					Usage: "copy glyph to clipboard",
				},
			},
			Action: func(c *cli.Context) {
				args := strings.Join(c.Args(), " ")
				rows, err := store.SearchUnicode(args)
				if err != nil {
					logrus.Fatal(err)
				}

				formatter := format.NewTextFormatter(
					[]string{"CodePoint", "Name"},
					" -- ",
					true)

				if c.Bool("copy") && len(rows) > 1 {
					logrus.Warn("Copy to clipboard not allowed for multiple rows.")
				}

				for _, row := range rows {

					b, err := formatter.Format(&row)
					if err != nil {
						logrus.Fatal(err)
					}
					fmt.Println(b)

					// Copy to clipboard only when one row is returned by search.
					if c.Bool("copy") && len(rows) == 1 {
						glyph, err := format.CodePointToGlyph(row.CodePoint)
						if err != nil {
							logrus.Fatalf("Impossible to convert %s to glyph.",
								row.CodePoint)
						}
						if err := clipboard.WriteAll(glyph); err != nil {
							logrus.Fatalf("Copy to clipboard failed: %v", err)
						}
					}
				}
			},
		},
	}

	app.Run(os.Args)

}
<commit_msg>Add show-glyph flag to enable/disable glyph printing<commit_after>package main

import (
	"fmt"
	"os"
	"path"
	"strings"

	"github.com/Sirupsen/logrus"
	"github.com/atotto/clipboard"
	"github.com/codegangsta/cli"

	"eriol.xyz/piken/format"
	"eriol.xyz/piken/sql"
)

const (
	unicodeDataUrl      = "http://www.unicode.org/Public/UNIDATA/UnicodeData.txt"
	pikenHome           = ".piken"
	defaultDatabaseFile = "piken.sqlite3"
	defaultDataFile     = "UnicodeData.txt"
	version             = "0.1a"
)

var (
	baseDir      = path.Join(getHome(), pikenHome)
	databaseFile = path.Join(baseDir, defaultDatabaseFile)
	dataFile     = path.Join(baseDir, defaultDataFile)
	store        sql.Store
)

func main() {

	app := cli.NewApp()
	app.Name = "piken"
	app.Version = version
	app.Author = "Daniele Tricoli"
	app.Email = "eriol@mornie.org"
	app.Usage = "unicode search tool"

	if _, err := os.Stat(baseDir); os.IsNotExist(err) {
		os.Mkdir(baseDir, 0755)
	}

	if err := store.Open(databaseFile); err != nil {
		logrus.Fatal(err)
	}
	defer store.Close()

	app.Commands = []cli.Command{
		{
			Name:  "update",
			Usage: "Update unicode data",
			Action: func(c *cli.Context) {
				modifiedTime, err := checkLastModified(unicodeDataUrl)
				if err != nil {
					logrus.Fatal(err)
				}

				lastUpdate, err := store.GetLastUpdate(defaultDataFile)
				if err != nil {
					logrus.Fatal(err)
				}

				if lastUpdate.Before(modifiedTime) {
					download(unicodeDataUrl, dataFile)

					records, err := readCsvFile(dataFile)
					if err != nil {
						logrus.Fatal(err)
					}

					if err := store.LoadFromRecords(records); err != nil {
						logrus.Fatal(err)
					}

					if err := store.CreateLastUpdate(defaultDataFile,
						modifiedTime); err != nil {
						logrus.Fatal(err)
					}
				} else {
					logrus.Info("Already up to date.")
				}

			},
		},
		{
			Name:    "search",
			Aliases: []string{"s"},
			Usage:   "Search for unicode",
			Flags: []cli.Flag{
				cli.BoolFlag{
					Name:  "copy, c",
					Usage: "copy glyph to clipboard",
				},
				cli.BoolTFlag{
					Name:  "show-glyph",
					Usage: "show glyph (defaults to true, use --show-glyph=false to disable)",
				},
			},
			Action: func(c *cli.Context) {
				args := strings.Join(c.Args(), " ")
				rows, err := store.SearchUnicode(args)
				if err != nil {
					logrus.Fatal(err)
				}

				formatter := format.NewTextFormatter(
					[]string{"CodePoint", "Name"},
					" -- ",
					c.Bool("show-glyph"))

				if c.Bool("copy") && len(rows) > 1 {
					logrus.Warn("Copy to clipboard not allowed for multiple rows.")
				}

				for _, row := range rows {

					b, err := formatter.Format(&row)
					if err != nil {
						logrus.Fatal(err)
					}
					fmt.Println(b)

					// Copy to clipboard only when one row is returned by search.
					if c.Bool("copy") && len(rows) == 1 {
						glyph, err := format.CodePointToGlyph(row.CodePoint)
						if err != nil {
							logrus.Fatalf("Impossible to convert %s to glyph.",
								row.CodePoint)
						}
						if err := clipboard.WriteAll(glyph); err != nil {
							logrus.Fatalf("Copy to clipboard failed: %v", err)
						}
					}
				}
			},
		},
	}

	app.Run(os.Args)

}
<|endoftext|>


<commit_before>package main

import (
	"github.com/kisielk/whisper-go/whisper"
	pickle "github.com/kisielk/og-rek"

	"net/http"
	"encoding/json"
	"path/filepath"
	"strings"
	"strconv"
	"fmt"
)

var config = struct {
	WhisperData	string
}{
	WhisperData: "/var/lib/carbon/whisper",
	//WhisperData: "..",
}

type WhisperFetchResponse struct {
	Name		string		`json:"name"`
	StartTime	uint32		`json:"startTime"`
	StopTime	uint32		`json:"stopTime"`
	StepTime	uint32		`json:"stepTime"`
	Values		[]float64	`json:"values"`
}

type WhisperGlobResponse struct {
	Name		string		`json:"name"`
	Paths		[]string	`json:"paths"`
}

func findHandler(wr http.ResponseWriter, req *http.Request) {
//	GET /metrics/find/?local=1&format=pickle&query=general.hadoop.lhr4.ha201jobtracker-01.jobtracker.NonHeapMemoryUsage.committed HTTP/1.1
//	http://localhost:8080/metrics/glob/?query=test
	req.ParseForm()
	glob := req.FormValue("query")
	format := req.FormValue("format")

	if format != "json" && format != "pickle" {
		fmt.Printf("dropping invalid uri (format=%s): %s\n",
				format, req.URL.RequestURI())
		return
	}

	/* things to glob:
	 * - carbon.relays  -> carbon.relays
	 * - carbon.re      -> carbon.relays, carbon.rewhatever
	 * - implicit * at the end of each query
	 * - match is either dir or .wsp file
	 * (this is less featureful than original carbon)
	 */
	path := config.WhisperData + "/" + strings.Replace(glob, ".", "/", -1) + "*"
	files, err := filepath.Glob(path)
	if err != nil {
		files = make([]string, 0)
	}

	leafs := make([]bool, len(files))
	for i, p := range files {
		p = p[len(config.WhisperData + "/"):]
		if strings.HasSuffix(p, ".wsp") {
			p = p[:len(p) - 4]
			leafs[i] = true
		} else {
			leafs[i] = false
		}
		files[i] = strings.Replace(p, "/", ".", -1)
	}

	if format == "json" {
		response := WhisperGlobResponse {
			Name:		glob,
			Paths:		make([]string, 0),
		}
		for _, p := range files {
			response.Paths = append(response.Paths, p)
		}
		b, err := json.Marshal(response)
		if err != nil {
			fmt.Printf("failed to create JSON data for %s: %s\n", glob, err)
			return
		}
		wr.Write(b)
	} else if format == "pickle" {
		// [{'metric_path': 'metric', 'intervals': [(x,y)], 'isLeaf': True},]
		var metrics []map[string]interface{}
		var m map[string]interface{}

		for i, p := range files {
			m = make(map[string]interface{})
			m["metric_path"] = p
			// m["intervals"] = dunno how to do a tuple here
			m["isLeaf"] = leafs[i]
			metrics = append(metrics, m)
		}

		wr.Header().Set("Content-Type", "application/pickle")
		pEnc := pickle.NewEncoder(wr)
		pEnc.Encode(metrics)
	}
	fmt.Printf("served %d points\n", len(files))
	return
}

func fetchHandler(wr http.ResponseWriter, req *http.Request) {
//	GET /render/?target=general.me.1.percent_time_active.pfnredis&format=pickle&from=1396008021&until=1396022421 HTTP/1.1
//	http://localhost:8080/metrics/fetch/?target=testmetric&format=json&from=1395961200&until=1395961800
	req.ParseForm()
	metric := req.FormValue("target")
	format := req.FormValue("format")
	from := req.FormValue("from")
	until := req.FormValue("until")

	if format != "json" && format != "pickle" {
		fmt.Printf("dropping invalid uri (format=%s): %s\n",
				format, req.URL.RequestURI())
		return
	}

	path := config.WhisperData + "/" + strings.Replace(metric, ".", "/", -1) + ".wsp"
	w, err := whisper.Open(path)
	if err != nil {
		fmt.Printf("failed to open %s: %s\n", path, err)
		return
	}
	defer w.Close()

	i, err := strconv.Atoi(from)
	if err != nil {
		fmt.Printf("fromTime (%s) invalid: %s\n", from, err)
	}
	fromTime := uint32(i)
	i, err = strconv.Atoi(until)
	if err != nil {
		fmt.Printf("untilTime (%s) invalid: %s\n", from, err)
	}
	untilTime := uint32(i)

	interval, points, err := w.FetchUntil(fromTime, untilTime)
	if err != nil {
		fmt.Printf("failed to getch points from %s: %s\n", path, err)
		return
	}

	if format == "json" {
		response := WhisperFetchResponse {
			Name:		metric,
			StartTime:	interval.FromTimestamp,
			StopTime:	interval.UntilTimestamp,
			StepTime:	interval.Step,
		}
		for _, p := range points {
			response.Values = append(response.Values, p.Value)
		}

		b, err := json.Marshal(response)
		if err != nil {
			fmt.Printf("failed to create JSON data for %s: %s\n", path, err)
			return
		}
		wr.Write(b)
	} else if format == "pickle" {
		//[{'start': 1396271100, 'step': 60, 'name': 'metric',
		//'values': [9.0, 19.0, None], 'end': 1396273140}
		var metrics []map[string]interface{}
		var m map[string]interface{}

		m = make(map[string]interface{})
		m["start"] = interval.FromTimestamp
		m["step"] = interval.Step
		m["end"] = interval.UntilTimestamp
		m["name"] = metric

		values := make([]interface{}, len(points))
		for i, p := range points {
			values[i] = p.Value
		}
		m["values"] = values

		metrics = append(metrics, m)

		wr.Header().Set("Content-Type", "application/pickle")
		pEnc := pickle.NewEncoder(wr)
		pEnc.Encode(metrics)
	}

	fmt.Printf("served %d points\n", len(points))
	return
}

func main() {
	http.HandleFunc("/metrics/find/", findHandler)
	http.HandleFunc("/render/", fetchHandler)

	http.ListenAndServe(":8080", nil)
}
<commit_msg>update urls<commit_after>package main

import (
	"github.com/kisielk/whisper-go/whisper"
	pickle "github.com/kisielk/og-rek"

	"net/http"
	"encoding/json"
	"path/filepath"
	"strings"
	"strconv"
	"fmt"
)

var config = struct {
	WhisperData	string
}{
	WhisperData: "/var/lib/carbon/whisper",
	//WhisperData: "..",
}

type WhisperFetchResponse struct {
	Name		string		`json:"name"`
	StartTime	uint32		`json:"startTime"`
	StopTime	uint32		`json:"stopTime"`
	StepTime	uint32		`json:"stepTime"`
	Values		[]float64	`json:"values"`
}

type WhisperGlobResponse struct {
	Name		string		`json:"name"`
	Paths		[]string	`json:"paths"`
}

func findHandler(wr http.ResponseWriter, req *http.Request) {
//	GET /metrics/find/?local=1&format=pickle&query=general.hadoop.lhr4.ha201jobtracker-01.jobtracker.NonHeapMemoryUsage.committed HTTP/1.1
//	http://localhost:8080/metrics/find/?query=test
	req.ParseForm()
	glob := req.FormValue("query")
	format := req.FormValue("format")

	if format != "json" && format != "pickle" {
		fmt.Printf("dropping invalid uri (format=%s): %s\n",
				format, req.URL.RequestURI())
		return
	}

	/* things to glob:
	 * - carbon.relays  -> carbon.relays
	 * - carbon.re      -> carbon.relays, carbon.rewhatever
	 * - implicit * at the end of each query
	 * - match is either dir or .wsp file
	 * (this is less featureful than original carbon)
	 */
	path := config.WhisperData + "/" + strings.Replace(glob, ".", "/", -1) + "*"
	files, err := filepath.Glob(path)
	if err != nil {
		files = make([]string, 0)
	}

	leafs := make([]bool, len(files))
	for i, p := range files {
		p = p[len(config.WhisperData + "/"):]
		if strings.HasSuffix(p, ".wsp") {
			p = p[:len(p) - 4]
			leafs[i] = true
		} else {
			leafs[i] = false
		}
		files[i] = strings.Replace(p, "/", ".", -1)
	}

	if format == "json" {
		response := WhisperGlobResponse {
			Name:		glob,
			Paths:		make([]string, 0),
		}
		for _, p := range files {
			response.Paths = append(response.Paths, p)
		}
		b, err := json.Marshal(response)
		if err != nil {
			fmt.Printf("failed to create JSON data for %s: %s\n", glob, err)
			return
		}
		wr.Write(b)
	} else if format == "pickle" {
		// [{'metric_path': 'metric', 'intervals': [(x,y)], 'isLeaf': True},]
		var metrics []map[string]interface{}
		var m map[string]interface{}

		for i, p := range files {
			m = make(map[string]interface{})
			m["metric_path"] = p
			// m["intervals"] = dunno how to do a tuple here
			m["isLeaf"] = leafs[i]
			metrics = append(metrics, m)
		}

		wr.Header().Set("Content-Type", "application/pickle")
		pEnc := pickle.NewEncoder(wr)
		pEnc.Encode(metrics)
	}
	fmt.Printf("served %d points\n", len(files))
	return
}

func fetchHandler(wr http.ResponseWriter, req *http.Request) {
//	GET /render/?target=general.me.1.percent_time_active.pfnredis&format=pickle&from=1396008021&until=1396022421 HTTP/1.1
//	http://localhost:8080/render/?target=testmetric&format=json&from=1395961200&until=1395961800
	req.ParseForm()
	metric := req.FormValue("target")
	format := req.FormValue("format")
	from := req.FormValue("from")
	until := req.FormValue("until")

	if format != "json" && format != "pickle" {
		fmt.Printf("dropping invalid uri (format=%s): %s\n",
				format, req.URL.RequestURI())
		return
	}

	path := config.WhisperData + "/" + strings.Replace(metric, ".", "/", -1) + ".wsp"
	w, err := whisper.Open(path)
	if err != nil {
		fmt.Printf("failed to open %s: %s\n", path, err)
		return
	}
	defer w.Close()

	i, err := strconv.Atoi(from)
	if err != nil {
		fmt.Printf("fromTime (%s) invalid: %s\n", from, err)
	}
	fromTime := uint32(i)
	i, err = strconv.Atoi(until)
	if err != nil {
		fmt.Printf("untilTime (%s) invalid: %s\n", from, err)
	}
	untilTime := uint32(i)

	interval, points, err := w.FetchUntil(fromTime, untilTime)
	if err != nil {
		fmt.Printf("failed to getch points from %s: %s\n", path, err)
		return
	}

	if format == "json" {
		response := WhisperFetchResponse {
			Name:		metric,
			StartTime:	interval.FromTimestamp,
			StopTime:	interval.UntilTimestamp,
			StepTime:	interval.Step,
		}
		for _, p := range points {
			response.Values = append(response.Values, p.Value)
		}

		b, err := json.Marshal(response)
		if err != nil {
			fmt.Printf("failed to create JSON data for %s: %s\n", path, err)
			return
		}
		wr.Write(b)
	} else if format == "pickle" {
		//[{'start': 1396271100, 'step': 60, 'name': 'metric',
		//'values': [9.0, 19.0, None], 'end': 1396273140}
		var metrics []map[string]interface{}
		var m map[string]interface{}

		m = make(map[string]interface{})
		m["start"] = interval.FromTimestamp
		m["step"] = interval.Step
		m["end"] = interval.UntilTimestamp
		m["name"] = metric

		values := make([]interface{}, len(points))
		for i, p := range points {
			values[i] = p.Value
		}
		m["values"] = values

		metrics = append(metrics, m)

		wr.Header().Set("Content-Type", "application/pickle")
		pEnc := pickle.NewEncoder(wr)
		pEnc.Encode(metrics)
	}

	fmt.Printf("served %d points\n", len(points))
	return
}

func main() {
	http.HandleFunc("/metrics/find/", findHandler)
	http.HandleFunc("/render/", fetchHandler)

	http.ListenAndServe(":8080", nil)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"net/http"
	"os"

	"github.com/vikashvverma/greeter/config"
	"github.com/vikashvverma/greeter/job"
)

func handler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Hi there, I love %s!", r.URL.Path[1:])
}
func main() {
	c := config.ReadConfig("./config.json")
	if c == nil {
		log.Fatal("Could not read config file!")
	}
	g := job.NewGreeter(c)
	s := job.NewScheduler(c.Time, g)
	gocron, err := s.Schedule()
	if err != nil {
		log.Fatalf("ListenAndServe: %s", err)
	}
	<-gocron.Start()

	// No need of a server, a server is just to check the app status easily on cloud
	port := os.Getenv("PORT")
	if port == "" {
		log.Fatal("$PORT must be set")
	}
	http.HandleFunc("/", handler)
	http.ListenAndServe(fmt.Sprintf(":%s", port), nil)
}
<commit_msg>Don't start server<commit_after>package main

import (
	"log"

	"github.com/vikashvverma/greeter/config"
	"github.com/vikashvverma/greeter/job"
)

func main() {
	c := config.ReadConfig("./config.json")
	if c == nil {
		log.Fatal("Could not read config file!")
	}
	g := job.NewGreeter(c)
	s := job.NewScheduler(c.Time, g)
	gocron, err := s.Schedule()
	if err != nil {
		log.Fatalf("ListenAndServe: %s", err)
	}
	<-gocron.Start()
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
	"os/exec"
	"time"

	"github.com/crgimenes/goConfig"
	l "github.com/crgimenes/logSys"
)

type Config struct {
	Server string `json:"server" cfg:"server" cfgDefault:"localhost:8080"`
}

type Data struct {
	Origin    string
	Payload   string
	TimeEntry time.Time
}

var cfg = &Config{}

func mainHandle(w http.ResponseWriter, req *http.Request) {
	io.WriteString(w, "...")
}

func statusHandle(w http.ResponseWriter, req *http.Request) {
	io.WriteString(w, "status\n")
}

func main() {
	l.Println(l.Message, "Starting")

	c := make(chan Data)

	/******************************
	 ** Load configuration
	 ******************************/

	goConfig.PrefixEnv = "ROY"
	err := goConfig.Parse(cfg)
	if err != nil {
		l.Println(l.Error, err)
		return
	}

	/******************************
	 ** Start queues
	 ******************************/

	/******************************
	 ** Start sensor scheduler
	 ******************************/

	go func() { // fake sensor
		for {
			time.Sleep(time.Second)

			// run sensor

			cmd := exec.Command("./sensors/fake/fake")
			var stdout, stderr bytes.Buffer
			cmd.Stdout = &stdout
			cmd.Stderr = &stderr
			err := cmd.Run()
			if err != nil {
				l.Println(l.Error, err, stderr.String())
				continue
			}
			l.Println(l.Debug, "out:", stdout.String(), "err:", stderr.String())

			// convert stdout from sensor to send to dispatcher
			var d Data
			err = json.Unmarshal(stdout.Bytes(), &d)
			if err != nil {
				l.Println(l.Error, err)
				continue
			}

			c <- d
		}
	}()

	/******************************
	 ** Start actuator dispatcher
	 ******************************/

	go func() {
		for {
			d := <-c
			fmt.Println(">", d.Origin, d.Payload, d.TimeEntry)
		}
	}()

	/******************************
	 ** Start HTTP server
	 ******************************/

	http.HandleFunc("/", mainHandle)
	http.HandleFunc("/status", statusHandle)

	l.Println(l.Message, "Listen on http://", cfg.Server)
	err = http.ListenAndServe(cfg.Server, nil)
	if err != nil {
		log.Fatal("ListenAndServe: ", err)
	}

}
<commit_msg>update gofn<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
	"os/exec"
	"time"

	"github.com/crgimenes/goconfig"
	l "github.com/crgimenes/log"
)

type Config struct {
	Server string `json:"server" cfg:"server" cfgDefault:"localhost:8080"`
}

type Data struct {
	Origin    string
	Payload   string
	TimeEntry time.Time
}

var cfg = &Config{}

func mainHandle(w http.ResponseWriter, req *http.Request) {
	io.WriteString(w, "...")
}

func statusHandle(w http.ResponseWriter, req *http.Request) {
	io.WriteString(w, "status\n")
}

func main() {
	l.Println(l.Message, "Starting")

	c := make(chan Data)

	/******************************
	 ** Load configuration
	 ******************************/

	goconfig.PrefixEnv = "ROY"
	err := goconfig.Parse(cfg)
	if err != nil {
		l.Println(l.Error, err)
		return
	}

	/******************************
	 ** Start queues
	 ******************************/

	/******************************
	 ** Start sensor scheduler
	 ******************************/

	go func() { // fake sensor
		for {
			time.Sleep(time.Second)

			// run sensor

			cmd := exec.Command("./sensors/fake/fake")
			var stdout, stderr bytes.Buffer
			cmd.Stdout = &stdout
			cmd.Stderr = &stderr
			err := cmd.Run()
			if err != nil {
				l.Println(l.Error, err, stderr.String())
				continue
			}
			l.Println(l.Debug, "out:", stdout.String(), "err:", stderr.String())

			// convert stdout from sensor to send to dispatcher
			var d Data
			err = json.Unmarshal(stdout.Bytes(), &d)
			if err != nil {
				l.Println(l.Error, err)
				continue
			}

			c <- d
		}
	}()

	/******************************
	 ** Start actuator dispatcher
	 ******************************/

	go func() {
		for {
			d := <-c
			fmt.Println(">", d.Origin, d.Payload, d.TimeEntry)
		}
	}()

	/******************************
	 ** Start HTTP server
	 ******************************/

	http.HandleFunc("/", mainHandle)
	http.HandleFunc("/status", statusHandle)

	l.Println(l.Message, "Listen on http://", cfg.Server)
	err = http.ListenAndServe(cfg.Server, nil)
	if err != nil {
		log.Fatal("ListenAndServe: ", err)
	}

}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"

	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/container"
	"github.com/docker/docker/client"
	"gopkg.in/yaml.v2"
)

/*
version: '3'
services:
  redis:
    image: 'redis:3.0-alpine'

  busybox:
    image: busybox
*/
type serviceConfig struct {
	Build string `yaml:"build,omitempty"`
	//Command        yaml.Command         `yaml:"command,flow,omitempty"`
	Dockerfile string `yaml:"dockerfile,omitempty"`
	//Environment    yaml.MaporEqualSlice `yaml:"environment,omitempty"`
	Image string `yaml:"image,omitempty"`
	//Links          yaml.MaporColonSlice `yaml:"links,omitempty"`
	Name        string   `yaml:"name,omitempty"`
	Ports       []string `yaml:"ports,omitempty"`
	Restart     string   `yaml:"restart,omitempty"`
	Volumes     []string `yaml:"volumes,omitempty"`
	VolumesFrom []string `yaml:"volumes_from,omitempty"`
	Expose      []string `yaml:"expose,omitempty"`
}

type dockerComposeConfig struct {
	Version  string                   `yaml:"version,omitempty"`
	Services map[string]serviceConfig `yaml:"services"`
	//networks map[string]     `yaml:"networks,omitempty"`
	//volumes map[string]                  `yaml:"volumes,omitempty"`
}

func (dcy *dockerComposeConfig) Parse(data []byte) error {
	return yaml.Unmarshal(data, dcy)
}

func main() {
	data, err := ioutil.ReadFile("docker-compose.yml")

	if err != nil {
		log.Fatal(err)
	}

	var dockerCyaml dockerComposeConfig
	if err := dockerCyaml.Parse(data); err != nil {
		log.Fatal(err)
	}

	for _, v := range dockerCyaml.Services {
		fmt.Println()
		fmt.Println(v.Image)
		fmt.Println()
		pullImage(v.Image)
	}

}

func pullImage(imagename string) {
	ctx := context.Background()
	cli, err := client.NewEnvClient()
	if err != nil {
		panic(err)
	}

	closer, err := cli.ImagePull(ctx, imagename, types.ImagePullOptions{})
	if err != nil {
		panic(err)
	}
	err = closer.Close()
	if err != nil {
		panic(err)
	}

	resp, err := cli.ContainerCreate(ctx, &container.Config{Image: imagename}, nil, nil, "")
	if err != nil {
		log.Fatal(err)
	}

	if err := cli.ContainerStart(ctx, resp.ID, types.ContainerStartOptions{}); err != nil {
		panic(err)
	}

	/*if _, err = cli.ContainerWait(ctx, resp.ID); err != nil {
		panic(err)
	}*/

	out, err := cli.ContainerLogs(ctx, resp.ID, types.ContainerLogsOptions{ShowStdout: true})
	if err != nil {
		panic(err)
	}

	io.Copy(os.Stdout, out)
}
<commit_msg>dont wait for container start<commit_after>package main

import (
	"context"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"

	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/container"
	"github.com/docker/docker/client"
	"gopkg.in/yaml.v2"
)

/*
version: '3'
services:
  redis:
    image: 'redis:3.0-alpine'

  busybox:
    image: busybox
*/
type serviceConfig struct {
	Build string `yaml:"build,omitempty"`
	//Command        yaml.Command         `yaml:"command,flow,omitempty"`
	Dockerfile string `yaml:"dockerfile,omitempty"`
	//Environment    yaml.MaporEqualSlice `yaml:"environment,omitempty"`
	Image string `yaml:"image,omitempty"`
	//Links          yaml.MaporColonSlice `yaml:"links,omitempty"`
	Name        string   `yaml:"name,omitempty"`
	Ports       []string `yaml:"ports,omitempty"`
	Restart     string   `yaml:"restart,omitempty"`
	Volumes     []string `yaml:"volumes,omitempty"`
	VolumesFrom []string `yaml:"volumes_from,omitempty"`
	Expose      []string `yaml:"expose,omitempty"`
}

type dockerComposeConfig struct {
	Version  string                   `yaml:"version,omitempty"`
	Services map[string]serviceConfig `yaml:"services"`
	//networks map[string]     `yaml:"networks,omitempty"`
	//volumes map[string]                  `yaml:"volumes,omitempty"`
}

func (dcy *dockerComposeConfig) Parse(data []byte) error {
	return yaml.Unmarshal(data, dcy)
}

func main() {
	data, err := ioutil.ReadFile("docker-compose.yml")

	if err != nil {
		log.Fatal(err)
	}

	var dockerCyaml dockerComposeConfig
	if err := dockerCyaml.Parse(data); err != nil {
		log.Fatal(err)
	}

	for _, v := range dockerCyaml.Services {
		fmt.Println()
		fmt.Println(v.Image)
		fmt.Println()
		pullImage(v.Image)
	}

}

func pullImage(imagename string) {
	ctx := context.Background()
	cli, err := client.NewEnvClient()
	if err != nil {
		panic(err)
	}

	closer, err := cli.ImagePull(ctx, imagename, types.ImagePullOptions{})
	if err != nil {
		panic(err)
	}
	err = closer.Close()
	if err != nil {
		panic(err)
	}

	resp, err := cli.ContainerCreate(ctx, &container.Config{Image: imagename}, nil, nil, "")
	if err != nil {
		log.Fatal(err)
	}

	if err := cli.ContainerStart(ctx, resp.ID, types.ContainerStartOptions{}); err != nil {
		panic(err)
	}

	out, err := cli.ContainerLogs(ctx, resp.ID, types.ContainerLogsOptions{ShowStdout: true})
	if err != nil {
		panic(err)
	}

	io.Copy(os.Stdout, out)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"os"

	termutil "github.com/andrew-d/go-termutil"
	"gopkg.in/alecthomas/kingpin.v2"
	"gopkg.in/yaml.v2"
)

var (
	inFile = kingpin.Arg("file", "YAML file.").String()
	pretty = kingpin.Flag("pretty", "Pretty print result.").Short('p').Bool()
	quiet  = kingpin.Flag("quiet", "Don't output on success.").Short('q').Bool()
)

func main() {

	// support -h for --help
	kingpin.CommandLine.HelpFlag.Short('h')
	kingpin.Parse()

	data, err := readPipeOrFile(*inFile)
	if err != nil {
		fmt.Println("error:", err)
		os.Exit(1)
	}

	var f interface{}
	err = yaml.Unmarshal(data, &f)
	if err != nil {
		fmt.Println("ERROR:", *inFile, err)
		os.Exit(1)
	}

	if *pretty {
		b, err := yaml.Marshal(f)
		if err != nil {
			fmt.Println("ERROR:", err)
		}
		fmt.Printf(string(b))
	} else {
		if !*quiet {
			fmt.Println("OK:", *inFile)
		}
	}
}

// readPipeOrFile reads from stdin if pipe exists, else from provided file
func readPipeOrFile(fileName string) ([]byte, error) {
	if !termutil.Isatty(os.Stdin.Fd()) {
		return ioutil.ReadAll(os.Stdin)
	}
	if fileName == "" {
		return nil, fmt.Errorf("no piped data and no file provided")
	}
	return ioutil.ReadFile(fileName)
}
<commit_msg>show '-' as filename for piped input<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"os"

	termutil "github.com/andrew-d/go-termutil"
	"gopkg.in/alecthomas/kingpin.v2"
	"gopkg.in/yaml.v2"
)

var (
	inFile = kingpin.Arg("file", "YAML file.").String()
	pretty = kingpin.Flag("pretty", "Pretty print result.").Short('p').Bool()
	quiet  = kingpin.Flag("quiet", "Don't output on success.").Short('q').Bool()
)

func main() {

	// support -h for --help
	kingpin.CommandLine.HelpFlag.Short('h')
	kingpin.Parse()

	data, err := readPipeOrFile(*inFile)
	if err != nil {
		fmt.Println("error:", err)
		os.Exit(1)
	}

	filename := "-"
	if *inFile != "" {
		filename = *inFile
	}

	var f interface{}
	err = yaml.Unmarshal(data, &f)
	if err != nil {
		fmt.Println("ERROR:", filename, err)
		os.Exit(1)
	}

	if *pretty {
		b, err := yaml.Marshal(f)
		if err != nil {
			fmt.Println("ERROR:", err)
		}
		fmt.Printf(string(b))
	} else {
		if !*quiet {
			fmt.Println("OK:", filename)
		}
	}
}

// readPipeOrFile reads from stdin if pipe exists, else from provided file
func readPipeOrFile(fileName string) ([]byte, error) {
	if !termutil.Isatty(os.Stdin.Fd()) {
		return ioutil.ReadAll(os.Stdin)
	}
	if fileName == "" {
		return nil, fmt.Errorf("no piped data and no file provided")
	}
	return ioutil.ReadFile(fileName)
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"log"
	"strings"
	"time"

	"gopkg.in/telegram-bot-api.v4"
)

type ReplyFunc func(msg *tgbotapi.Message, fields []string) (string, []string)

func getHandlers() map[string]ReplyFunc {
	interfaces := &Interfaces{}
	vlans := &VLAN{Selected: 0, Interfaces: interfaces}
	interfaces.Update()
	return map[string]ReplyFunc{
		"ip": func(msg *tgbotapi.Message, fields []string) (string, []string) {
			return interfaces.ReplyToIP(msg, fields)
		},
		"vlan": func(msg *tgbotapi.Message, fields []string) (string, []string) {
			return vlans.ReplyToVLAN(msg, fields)
		},
		"out": func(msg *tgbotapi.Message, fields []string) (string, []string) {
			return vlans.ReplyToOut(msg, fields)
		},
		"in": func(msg *tgbotapi.Message, fields []string) (string, []string) {
			return vlans.ReplyToIn(msg, fields)
		},
	}
}

func main() {

	token := flag.String("token", "", "Telegram API token")
	flag.Parse()
	if token == nil || *token == "" {
		log.Fatal("You must provide Telegram token (-token <telegram token>)")
	}

	for {
		if err := loop(*token); err != nil {
			log.Print("Error: ", err, "\nRetrying in five minutes...")
			time.Sleep(5 * time.Minute)
		}
	}
}

func loop(token string) error {

	bot, err := tgbotapi.NewBotAPI(token)
	if err != nil {
		return err
	}

	bot.Debug = true
	log.Printf("Bot username %s", bot.Self.UserName)

	u := tgbotapi.NewUpdate(0)
	u.Timeout = 60

	handlers := getHandlers()
	orderlist := make([]string, 0, len(handlers))

	for name := range handlers {
		orderlist = append(orderlist, name)
	}
	orders := strings.Join(orderlist, "\n  - ")

	updates, err := bot.GetUpdatesChan(u)

	for update := range updates {

		if update.Message == nil {
			continue
		}
		fields := strings.Fields(update.Message.Text)

		// Can take several orders in a single line
		for len(fields) > 0 {
			log.Printf("[%s] %s", update.Message.From.UserName, strings.Join(fields, " "))
			reply := ""
			remainder := []string{}
			for command, handler := range handlers {
				if strings.EqualFold(fields[0], command) {
					reply, remainder = handler(update.Message, fields)
					break
				}
			}
			if reply == "" {
				reply = fmt.Sprintf("Command %s is not known.\nKnown commands:\n  - %s", update.Message.Text, orders)
				remainder = nil
			}
			if remainder != nil && len(remainder) > 0 {
				if len(remainder) < len(fields) {
					fields = remainder
				} else {
					reply = strings.Join([]string{
						reply,
						fmt.Sprintf("Possible loop in command %s, len(remainder) >= len(fields)", remainder[0]),
					}, "\n")
					fields = nil
				}
			} else {
				fields = nil
			}

			msg := tgbotapi.NewMessage(update.Message.Chat.ID, reply)
			//msg.ReplyToMessageID = update.Message.MessageID
			bot.Send(msg)
		}
	}

	return nil
}
<commit_msg>Added support for master mechanism<commit_after>package main

import (
	"flag"
	"fmt"
	"log"
	"strings"
	"time"

	"gopkg.in/telegram-bot-api.v4"
)

type ReplyFunc func(msg *tgbotapi.Message, fields []string) (string, []string)

func getHandlers() map[string]ReplyFunc {
	interfaces := &Interfaces{}
	vlans := &VLAN{Selected: 0, Interfaces: interfaces}
	interfaces.Update()
	return map[string]ReplyFunc{
		"ip": func(msg *tgbotapi.Message, fields []string) (string, []string) {
			return interfaces.ReplyToIP(msg, fields)
		},
		"vlan": func(msg *tgbotapi.Message, fields []string) (string, []string) {
			return vlans.ReplyToVLAN(msg, fields)
		},
		"out": func(msg *tgbotapi.Message, fields []string) (string, []string) {
			return vlans.ReplyToOut(msg, fields)
		},
		"in": func(msg *tgbotapi.Message, fields []string) (string, []string) {
			return vlans.ReplyToIn(msg, fields)
		},
	}
}

func main() {

	token := flag.String("token", "", "Telegram API token")
	flag.Parse()
	if token == nil || *token == "" {
		log.Fatal("You must provide Telegram token (-token <telegram token>)")
	}

	masters := make([]string, 0, 10)
	for {
		var err error
		if masters, err = loop(masters, *token); err != nil {
			log.Print("Error: ", err, "\nRetrying in five minutes...")
			time.Sleep(5 * time.Minute)
		}
	}
}

func loop(masters []string, token string) ([]string, error) {

	bot, err := tgbotapi.NewBotAPI(token)
	if err != nil {
		return masters, err
	}

	bot.Debug = true
	log.Printf("Bot username %s", bot.Self.UserName)

	u := tgbotapi.NewUpdate(0)
	u.Timeout = 60

	handlers := getHandlers()
	orderlist := make([]string, 0, len(handlers))

	for name := range handlers {
		orderlist = append(orderlist, name)
	}
	orders := strings.Join(orderlist, "\n  - ")

	updates, err := bot.GetUpdatesChan(u)

	for update := range updates {

		if update.Message == nil {
			continue
		}
		fields := strings.Fields(update.Message.Text)

		// Only accept commands from master
		current := update.Message.From.UserName
		if len(masters) <= 0 {
			masters = append(masters, current)
			msg := tgbotapi.NewMessage(update.Message.Chat.ID, fmt.Sprintf("%s is a new master", current))
			//msg.ReplyToMessageID = update.Message.MessageID
			bot.Send(msg)
		} else {
			found := false
			for _, master := range masters {
				if master == current {
					found = true
					break
				}
			}
			if !found {
				msg := tgbotapi.NewMessage(update.Message.Chat.ID, fmt.Sprintf("%s is not my master", current))
				//msg.ReplyToMessageID = update.Message.MessageID
				bot.Send(msg)
				continue
			}
		}

		// Can take several orders in a single line
		for len(fields) > 0 {
			log.Printf("[%s] %s", update.Message.From.UserName, strings.Join(fields, " "))
			var remainder []string
			reply := ""
			// Check for Master command
			if strings.EqualFold(fields[0], "master") {
				if len(fields) < 2 {
					reply = "Must provide new master username (master <username>)"
				} else {
					masters = append(masters, fields[1])
					reply = fmt.Sprintf("%s is a new master", current)
					remainder = fields[2:]
				}
			} else {
				// Check for other commands
				for command, handler := range handlers {
					if strings.EqualFold(fields[0], command) {
						reply, remainder = handler(update.Message, fields)
						break
					}
				}
			}
			// Check for errors
			if reply == "" {
				reply = fmt.Sprintf("Command %s is not known.\nKnown commands:\n  - %s", update.Message.Text, orders)
				remainder = nil
			}
			// Check for a remainder
			if remainder != nil && len(remainder) > 0 {
				if len(remainder) < len(fields) {
					fields = remainder
				} else {
					reply = strings.Join([]string{
						reply,
						fmt.Sprintf("Possible loop in command %s, len(remainder) >= len(fields)", remainder[0]),
					}, "\n")
					fields = nil
				}
			} else {
				fields = nil
			}

			msg := tgbotapi.NewMessage(update.Message.Chat.ID, reply)
			//msg.ReplyToMessageID = update.Message.MessageID
			bot.Send(msg)
		}
	}

	return masters, nil
}
<|endoftext|>


<commit_before>package hush // import "github.com/mndrix/hush"

import (
	"fmt"
	"os"
)

// Main implements the main() function of the hush command line tool.
func Main() {
	if len(os.Args) < 2 {
		usage()
		return
	}

	// handle init command before loading tree
	switch os.Args[1] {
	case "help":
		CmdHelp(os.Stdout)
		return
	case "init":
		err := CmdInit(os.Stderr, os.Stdin)
		if err != nil {
			die("%s", err.Error())
		}
		return
	}

	// load tree for all other commands
	tree, err := LoadTree()
	if os.IsNotExist(err) {
		filename, _ := HushPath()
		fmt.Fprintf(os.Stderr, "hush file does not exist: %s\n", filename)
		fmt.Fprintf(os.Stderr, "Maybe you need to run 'hush init'?\n")
		os.Exit(1)
	}
	if err == nil {
		err = setPassphrase(tree)
	}
	if err != nil {
		die("%s", err.Error())
	}

	// dispatch to command
	switch os.Args[1] {
	case "export": // hush export
		err = CmdExport(os.Stdout, tree)
	case "import":
		var warnings []string
		warnings, err = CmdImport(os.Stdin, tree)
		for _, warning := range warnings {
			warn(warning)
		}
	case "ls":
		if len(os.Args) < 3 {
			tree.Print(os.Stdout)
			return
		}
		err = CmdLs(os.Stdout, tree, os.Args[2])
	case "rm":
		paths := make([]Path, len(os.Args)-2)
		for i := 2; i < len(os.Args); i++ {
			paths[i-2] = NewPath(os.Args[i])
		}
		err = CmdRm(tree, paths)
	case "set":
		if len(os.Args) < 4 {
			die("Usage: hush set path value")
		}
		p := NewPath(os.Args[2])
		var v *Value
		v, err = CaptureValue(os.Args[3])
		if err != nil {
			die("%s", err.Error())
		}
		err = CmdSet(os.Stdout, tree, p, v)
	default:
		usage()
	}
	if err != nil {
		die("%s", err.Error())
	}
}

func usage() {
	die("Usage: hush [command [arguments]]")
}

func setPassphrase(t *Tree) error {
	password, err := AskPassword(os.Stderr, "Password")
	if err != nil {
		return err
	}

	return t.SetPassphrase(password)
}

func die(format string, args ...interface{}) {
	fmt.Fprintf(os.Stderr, format+"\n", args...)
	os.Exit(1)
}

func warn(format string, args ...interface{}) {
	fmt.Fprintf(os.Stderr, format+"\n", args...)
}
<commit_msg>Make it clearer when errors occur<commit_after>package hush // import "github.com/mndrix/hush"

import (
	"fmt"
	"os"
)

// Main implements the main() function of the hush command line tool.
func Main() {
	if len(os.Args) < 2 {
		usage()
		return
	}

	// handle init command before loading tree
	switch os.Args[1] {
	case "help":
		CmdHelp(os.Stdout)
		return
	case "init":
		err := CmdInit(os.Stderr, os.Stdin)
		if err != nil {
			die("%s", err.Error())
		}
		return
	}

	// load tree for all other commands
	tree, err := LoadTree()
	if os.IsNotExist(err) {
		filename, _ := HushPath()
		fmt.Fprintf(os.Stderr, "hush file does not exist: %s\n", filename)
		fmt.Fprintf(os.Stderr, "Maybe you need to run 'hush init'?\n")
		os.Exit(1)
	}
	if err == nil {
		err = setPassphrase(tree)
	}
	if err != nil {
		die("%s", err.Error())
	}

	// dispatch to command
	switch os.Args[1] {
	case "export": // hush export
		err = CmdExport(os.Stdout, tree)
	case "import":
		var warnings []string
		warnings, err = CmdImport(os.Stdin, tree)
		for _, warning := range warnings {
			warn(warning)
		}
	case "ls":
		if len(os.Args) < 3 {
			tree.Print(os.Stdout)
			return
		}
		err = CmdLs(os.Stdout, tree, os.Args[2])
	case "rm":
		paths := make([]Path, len(os.Args)-2)
		for i := 2; i < len(os.Args); i++ {
			paths[i-2] = NewPath(os.Args[i])
		}
		err = CmdRm(tree, paths)
	case "set":
		if len(os.Args) < 4 {
			die("Usage: hush set path value")
		}
		p := NewPath(os.Args[2])
		var v *Value
		v, err = CaptureValue(os.Args[3])
		if err != nil {
			die("%s", err.Error())
		}
		err = CmdSet(os.Stdout, tree, p, v)
	default:
		usage()
	}
	if err != nil {
		die("error: %s", err.Error())
	}
}

func usage() {
	die("Usage: hush [command [arguments]]")
}

func setPassphrase(t *Tree) error {
	password, err := AskPassword(os.Stderr, "Password")
	if err != nil {
		return err
	}

	return t.SetPassphrase(password)
}

func die(format string, args ...interface{}) {
	fmt.Fprintf(os.Stderr, format+"\n", args...)
	os.Exit(1)
}

func warn(format string, args ...interface{}) {
	fmt.Fprintf(os.Stderr, format+"\n", args...)
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/johto/notifyutils/notifydispatcher"
	"github.com/lib/pq"

	"fmt"
	"github.com/prometheus/client_golang/prometheus"
	"os"
	"sync"
	"time"
)

// Implements a wrapper for pq.Listener for use between the PostgreSQL server
// and NotifyDispatcher.  Here we collect some statistics and pass the
// notifications on to the dispatcher.
type pqListenerWrapper struct {
	l *pq.Listener
	ch chan *pq.Notification

	inputChannelSaturationRatio *prometheus.Desc
	dispatcherChannelSaturationRatio *prometheus.Desc
}

func newPqListenerWrapper(l *pq.Listener) (*pqListenerWrapper, error) {
	w := &pqListenerWrapper{
		l: l,
		ch: make(chan *pq.Notification, 4),
	}

	w.inputChannelSaturationRatio = prometheus.NewDesc(
		"allas_input_channel_saturation_ratio",
		"main notification input Go channel saturation",
		nil,
		nil,
	)
	w.dispatcherChannelSaturationRatio = prometheus.NewDesc(
		"allas_dispatcher_channel_saturation_ratio",
		"dispatcher notification Go channel saturation",
		nil,
		nil,
	)

	err := Config.Prometheus.RegisterMetricsCollector(w)
	if err != nil {
		return nil, err
	}
	go w.workerGoroutine()
	return w, nil
}

func (w *pqListenerWrapper) Describe(ch chan<- *prometheus.Desc) {
	ch <- w.inputChannelSaturationRatio
	ch <- w.dispatcherChannelSaturationRatio
}

func (w *pqListenerWrapper) Collect(ch chan<- prometheus.Metric) {
	inputChSaturation := float64(len(w.l.Notify)) / float64(cap(w.l.Notify))
	ch <- prometheus.MustNewConstMetric(w.inputChannelSaturationRatio, prometheus.GaugeValue, inputChSaturation)
	dispatcherChSaturation := float64(len(w.ch)) / float64(cap(w.ch))
	ch <- prometheus.MustNewConstMetric(w.dispatcherChannelSaturationRatio, prometheus.GaugeValue, dispatcherChSaturation)

}

func (w *pqListenerWrapper) workerGoroutine() {
	input := w.l.NotificationChannel()
	for {
		m := <-input
		MetricNotificationsReceived.Inc()
		w.ch <- m
	}
}

func (w *pqListenerWrapper) Listen(channel string) error {
	return w.l.Listen(channel)
}

func (w *pqListenerWrapper) Unlisten(channel string) error {
	return w.l.Listen(channel)
}

func (w *pqListenerWrapper) NotificationChannel() <-chan *pq.Notification {
	return w.ch
}

func printUsage() {
    fmt.Fprintf(os.Stderr, `Usage:
  %s [--help] configfile

Options:
  --help                display this help and exit
`, os.Args[0])
}

func main() {
	InitErrorLog(os.Stderr)

	if len(os.Args) != 2 {
		printUsage()
		os.Exit(1)
	} else if os.Args[1] == "--help" {
		printUsage()
		os.Exit(1)
	}

	err := readConfigFile(os.Args[1])
	if err != nil {
		elog.Fatalf("error while reading configuration file: %s", err)
	}
	if len(Config.Databases) == 0 {
		elog.Fatalf("at least one database must be configured")
	}

	l, err := Config.Listen.Listen()
	if err != nil {
		elog.Fatalf("could not open listen socket: %s", err)
	}

	err = Config.Prometheus.Setup()
	if err != nil {
		elog.Fatalf("Prometheus exporter setup failed: %s", err)
	}

	var m sync.Mutex
	var connStatusNotifier chan struct{}

	listenerStateChange := func(ev pq.ListenerEventType, err error) {
		switch ev {
		case pq.ListenerEventConnectionAttemptFailed:
			elog.Warningf("Listener: could not connect to the database: %s", err.Error())

		case pq.ListenerEventDisconnected:
			elog.Warningf("Listener: lost connection to the database: %s", err.Error())
			m.Lock()
			close(connStatusNotifier)
			connStatusNotifier = nil
			m.Unlock()

		case pq.ListenerEventReconnected,
			pq.ListenerEventConnected:
			elog.Logf("Listener: connected to the database")
			m.Lock()
			connStatusNotifier = make(chan struct{})
			m.Unlock()
		}
	}

	// make sure pq.Listener doesn't pick up any env variables
	os.Clearenv()

	clientConnectionString := fmt.Sprintf("fallback_application_name=allas %s", Config.ClientConnInfo)
	listener := pq.NewListener(
		clientConnectionString,
		250*time.Millisecond, 3*time.Second,
		listenerStateChange,
	)
	listenerWrapper, err := newPqListenerWrapper(listener)
	if err != nil {
		elog.Fatalf("%s", err)
	}
	nd := notifydispatcher.NewNotifyDispatcher(listenerWrapper)
	nd.SetBroadcastOnConnectionLoss(false)
	nd.SetSlowReaderEliminationStrategy(notifydispatcher.NeglectSlowReaders)

	for {
		c, err := l.Accept()
		if err != nil {
			panic(err)
		}

		Config.Listen.MaybeEnableKeepAlive(c)

		var myConnStatusNotifier chan struct{}

		m.Lock()
		if connStatusNotifier == nil {
			m.Unlock()
			go RejectFrontendConnection(c)
			continue
		} else {
			myConnStatusNotifier = connStatusNotifier
		}
		m.Unlock()

		newConn := NewFrontendConnection(c, nd, myConnStatusNotifier)
		go newConn.mainLoop(Config.StartupParameters, Config.Databases)
	}
}
<commit_msg>Fix copypaste-o in Listener wrapper<commit_after>package main

import (
	"github.com/johto/notifyutils/notifydispatcher"
	"github.com/lib/pq"

	"fmt"
	"github.com/prometheus/client_golang/prometheus"
	"os"
	"sync"
	"time"
)

// Implements a wrapper for pq.Listener for use between the PostgreSQL server
// and NotifyDispatcher.  Here we collect some statistics and pass the
// notifications on to the dispatcher.
type pqListenerWrapper struct {
	l *pq.Listener
	ch chan *pq.Notification

	inputChannelSaturationRatio *prometheus.Desc
	dispatcherChannelSaturationRatio *prometheus.Desc
}

func newPqListenerWrapper(l *pq.Listener) (*pqListenerWrapper, error) {
	w := &pqListenerWrapper{
		l: l,
		ch: make(chan *pq.Notification, 4),
	}

	w.inputChannelSaturationRatio = prometheus.NewDesc(
		"allas_input_channel_saturation_ratio",
		"main notification input Go channel saturation",
		nil,
		nil,
	)
	w.dispatcherChannelSaturationRatio = prometheus.NewDesc(
		"allas_dispatcher_channel_saturation_ratio",
		"dispatcher notification Go channel saturation",
		nil,
		nil,
	)

	err := Config.Prometheus.RegisterMetricsCollector(w)
	if err != nil {
		return nil, err
	}
	go w.workerGoroutine()
	return w, nil
}

func (w *pqListenerWrapper) Describe(ch chan<- *prometheus.Desc) {
	ch <- w.inputChannelSaturationRatio
	ch <- w.dispatcherChannelSaturationRatio
}

func (w *pqListenerWrapper) Collect(ch chan<- prometheus.Metric) {
	inputChSaturation := float64(len(w.l.Notify)) / float64(cap(w.l.Notify))
	ch <- prometheus.MustNewConstMetric(w.inputChannelSaturationRatio, prometheus.GaugeValue, inputChSaturation)
	dispatcherChSaturation := float64(len(w.ch)) / float64(cap(w.ch))
	ch <- prometheus.MustNewConstMetric(w.dispatcherChannelSaturationRatio, prometheus.GaugeValue, dispatcherChSaturation)

}

func (w *pqListenerWrapper) workerGoroutine() {
	input := w.l.NotificationChannel()
	for {
		m := <-input
		MetricNotificationsReceived.Inc()
		w.ch <- m
	}
}

func (w *pqListenerWrapper) Listen(channel string) error {
	return w.l.Listen(channel)
}

func (w *pqListenerWrapper) Unlisten(channel string) error {
	return w.l.Unlisten(channel)
}

func (w *pqListenerWrapper) NotificationChannel() <-chan *pq.Notification {
	return w.ch
}

func printUsage() {
    fmt.Fprintf(os.Stderr, `Usage:
  %s [--help] configfile

Options:
  --help                display this help and exit
`, os.Args[0])
}

func main() {
	InitErrorLog(os.Stderr)

	if len(os.Args) != 2 {
		printUsage()
		os.Exit(1)
	} else if os.Args[1] == "--help" {
		printUsage()
		os.Exit(1)
	}

	err := readConfigFile(os.Args[1])
	if err != nil {
		elog.Fatalf("error while reading configuration file: %s", err)
	}
	if len(Config.Databases) == 0 {
		elog.Fatalf("at least one database must be configured")
	}

	l, err := Config.Listen.Listen()
	if err != nil {
		elog.Fatalf("could not open listen socket: %s", err)
	}

	err = Config.Prometheus.Setup()
	if err != nil {
		elog.Fatalf("Prometheus exporter setup failed: %s", err)
	}

	var m sync.Mutex
	var connStatusNotifier chan struct{}

	listenerStateChange := func(ev pq.ListenerEventType, err error) {
		switch ev {
		case pq.ListenerEventConnectionAttemptFailed:
			elog.Warningf("Listener: could not connect to the database: %s", err.Error())

		case pq.ListenerEventDisconnected:
			elog.Warningf("Listener: lost connection to the database: %s", err.Error())
			m.Lock()
			close(connStatusNotifier)
			connStatusNotifier = nil
			m.Unlock()

		case pq.ListenerEventReconnected,
			pq.ListenerEventConnected:
			elog.Logf("Listener: connected to the database")
			m.Lock()
			connStatusNotifier = make(chan struct{})
			m.Unlock()
		}
	}

	// make sure pq.Listener doesn't pick up any env variables
	os.Clearenv()

	clientConnectionString := fmt.Sprintf("fallback_application_name=allas %s", Config.ClientConnInfo)
	listener := pq.NewListener(
		clientConnectionString,
		250*time.Millisecond, 3*time.Second,
		listenerStateChange,
	)
	listenerWrapper, err := newPqListenerWrapper(listener)
	if err != nil {
		elog.Fatalf("%s", err)
	}
	nd := notifydispatcher.NewNotifyDispatcher(listenerWrapper)
	nd.SetBroadcastOnConnectionLoss(false)
	nd.SetSlowReaderEliminationStrategy(notifydispatcher.NeglectSlowReaders)

	for {
		c, err := l.Accept()
		if err != nil {
			panic(err)
		}

		Config.Listen.MaybeEnableKeepAlive(c)

		var myConnStatusNotifier chan struct{}

		m.Lock()
		if connStatusNotifier == nil {
			m.Unlock()
			go RejectFrontendConnection(c)
			continue
		} else {
			myConnStatusNotifier = connStatusNotifier
		}
		m.Unlock()

		newConn := NewFrontendConnection(c, nd, myConnStatusNotifier)
		go newConn.mainLoop(Config.StartupParameters, Config.Databases)
	}
}
<|endoftext|>